Implemented IPsec policies restricted to given network interface
authorAndreas Steffen <andreas.steffen@strongswan.org>
Sun, 27 Mar 2016 08:18:19 +0000 (10:18 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Sat, 9 Apr 2016 14:51:02 +0000 (16:51 +0200)
src/libcharon/config/child_cfg.c
src/libcharon/config/child_cfg.h
src/libcharon/kernel/kernel_ipsec.h
src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
src/libcharon/plugins/vici/vici_config.c
src/libcharon/sa/child_sa.c
src/libcharon/sa/shunt_manager.c
src/swanctl/swanctl.opt

index a7ec167..be84621 100644 (file)
@@ -139,6 +139,11 @@ struct private_child_cfg_t {
        uint32_t manual_prio;
 
        /**
+        * Optional restriction of IPsec policy to a given network interface
+        */
+       char *interface;
+
+       /**
         * set up IPsec transport SA in MIPv6 proxy mode
         */
        bool proxy_mode;
@@ -512,6 +517,12 @@ METHOD(child_cfg_t, get_manual_prio, uint32_t,
        return this->manual_prio;
 }
 
+METHOD(child_cfg_t, get_interface, char*,
+       private_child_cfg_t *this)
+{
+       return this->interface;
+}
+
 METHOD(child_cfg_t, get_replay_window, uint32_t,
        private_child_cfg_t *this)
 {
@@ -537,7 +548,7 @@ METHOD(child_cfg_t, install_policy, bool,
 }
 
 #define LT_PART_EQUALS(a, b) ({ a.life == b.life && a.rekey == b.rekey && a.jitter == b.jitter; })
-#define LIFETIME_EQUALS(a, b) ({  LT_PART_EQUALS(a.time, b.time) && LT_PART_EQUALS(a.bytes, b.bytes) && LT_PART_EQUALS(a.packets, b.packets); })
+#define LIFETIME_EQUALS(a, b) ({ LT_PART_EQUALS(a.time, b.time) && LT_PART_EQUALS(a.bytes, b.bytes) && LT_PART_EQUALS(a.packets, b.packets); })
 
 METHOD(child_cfg_t, equals, bool,
        private_child_cfg_t *this, child_cfg_t *other_pub)
@@ -585,7 +596,8 @@ METHOD(child_cfg_t, equals, bool,
                this->replay_window == other->replay_window &&
                this->proxy_mode == other->proxy_mode &&
                this->install_policy == other->install_policy &&
-               streq(this->updown, other->updown);
+               streq(this->updown, other->updown) &&
+               streq(this->interface, other->interface);
 }
 
 METHOD(child_cfg_t, get_ref, child_cfg_t*,
@@ -603,10 +615,8 @@ METHOD(child_cfg_t, destroy, void,
                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));
-               if (this->updown)
-               {
-                       free(this->updown);
-               }
+               free(this->updown);
+               free(this->interface);
                free(this->name);
                free(this);
        }
@@ -641,6 +651,7 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
                        .get_mark = _get_mark,
                        .get_tfc = _get_tfc,
                        .get_manual_prio = _get_manual_prio,
+                       .get_interface = _get_interface,
                        .get_replay_window = _get_replay_window,
                        .set_replay_window = _set_replay_window,
                        .use_proxy_mode = _use_proxy_mode,
@@ -665,6 +676,7 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
                .use_ipcomp = data->ipcomp,
                .tfc = data->tfc,
                .manual_prio = data->priority,
+               .interface = strdupnull(data->interface),
                .install_policy = !data->suppress_policies,
                .refcount = 1,
                .proposals = linked_list_create(),
index 8ac9c30..7d348fd 100644 (file)
@@ -244,6 +244,13 @@ struct child_cfg_t {
        uint32_t (*get_manual_prio)(child_cfg_t *this);
 
        /**
+        * Get optional network interface restricting IPsec policy
+        *
+        * @return                              network interface)
+        */
+       char* (*get_interface)(child_cfg_t *this);
+
+       /**
         * Get anti-replay window size
         *
         * @return                              anti-replay window size
@@ -318,6 +325,8 @@ struct child_cfg_create_t {
        uint32_t tfc;
        /** Optional manually-set IPsec policy priority */
        uint32_t priority;
+       /** Optional network interface restricting IPsec policy (cloned) */
+       char *interface;
        /** lifetime_cfg_t for this child_cfg */
        lifetime_cfg_t lifetime;
        /** Inactivity timeout in s before closing a CHILD_SA */
index 3a06ce6..0ad5660 100644 (file)
@@ -69,6 +69,8 @@ struct kernel_ipsec_add_sa_t {
        linked_list_t *src_ts;
        /** List of destination traffic selectors */
        linked_list_t *dst_ts;
+       /** Network interface restricting policy */
+       char *interface;
        /** Lifetime configuration */
        lifetime_cfg_t *lifetime;
        /** Encryption algorithm */
@@ -142,6 +144,8 @@ struct kernel_ipsec_policy_id_t {
        traffic_selector_t *dst_ts;
        /** Optional mark */
        mark_t mark;
+       /** Network interface restricting policy */
+       char *interface;
 };
 
 /**
index bad343e..95c0e3c 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/xfrm.h>
 #include <linux/udp.h>
+#include <net/if.h>
 #include <unistd.h>
 #include <time.h>
 #include <errno.h>
@@ -739,7 +740,8 @@ static void ts2ports(traffic_selector_t* ts,
  * Convert a pair of traffic_selectors to an xfrm_selector
  */
 static struct xfrm_selector ts2selector(traffic_selector_t *src,
-                                                                               traffic_selector_t *dst)
+                                                                               traffic_selector_t *dst,
+                                                                               char *interface)
 {
        struct xfrm_selector sel;
        uint16_t port;
@@ -763,7 +765,7 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src,
                sel.dport = htons(traffic_selector_icmp_code(port));
                sel.dport_mask = sel.dport ? ~0 : 0;
        }
-       sel.ifindex = 0;
+       sel.ifindex = interface ? if_nametoindex(interface) : 0;
        sel.user = 0;
 
        return sel;
@@ -1279,7 +1281,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
                                data->dst_ts->get_first(data->dst_ts,
                                                                                (void**)&first_dst_ts) == SUCCESS)
                        {
-                               sa->sel = ts2selector(first_src_ts, first_dst_ts);
+                               sa->sel = ts2selector(first_src_ts, first_dst_ts,
+                                                                         data->interface);
                                if (!this->proto_port_transport)
                                {
                                        /* don't install proto/port on SA. This would break
@@ -2358,7 +2361,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
 
        /* create a policy */
        INIT(policy,
-               .sel = ts2selector(id->src_ts, id->dst_ts),
+               .sel = ts2selector(id->src_ts, id->dst_ts, id->interface),
                .mark = id->mark.value & id->mark.mask,
                .direction = id->dir,
                .reqid = data->sa->reqid,
@@ -2468,7 +2471,7 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
        hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id));
 
        policy_id = NLMSG_DATA(hdr);
-       policy_id->sel = ts2selector(id->src_ts, id->dst_ts);
+       policy_id->sel = ts2selector(id->src_ts, id->dst_ts, id->interface);
        policy_id->dir = id->dir;
 
        if (!add_mark(hdr, sizeof(request), id->mark))
@@ -2552,7 +2555,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
 
        /* create a policy */
        memset(&policy, 0, sizeof(policy_entry_t));
-       policy.sel = ts2selector(id->src_ts, id->dst_ts);
+       policy.sel = ts2selector(id->src_ts, id->dst_ts, id->interface);
        policy.mark = id->mark.value & id->mark.mask;
        policy.direction = id->dir;
 
index 8358336..a1ab56d 100644 (file)
@@ -462,6 +462,7 @@ static void log_child_data(child_data_t *data, char *name)
        DBG2(DBG_CFG, "   reqid = %u", cfg->reqid);
        DBG2(DBG_CFG, "   tfc = %d", cfg->tfc);
        DBG2(DBG_CFG, "   priority = %d", cfg->priority);
+       DBG2(DBG_CFG, "   interface = %s", cfg->interface);
        DBG2(DBG_CFG, "   mark_in = %u/%u",
                 cfg->mark_in.value, cfg->mark_in.mask);
        DBG2(DBG_CFG, "   mark_out = %u/%u",
@@ -484,6 +485,7 @@ static void free_child_data(child_data_t *data)
        data->remote_ts->destroy_offset(data->remote_ts,
                                                                        offsetof(traffic_selector_t, destroy));
        free(data->cfg.updown);
+       free(data->cfg.interface);
 }
 
 /**
@@ -1342,6 +1344,7 @@ CALLBACK(child_kv, bool,
                { "mark_out",           parse_mark,                     &child->cfg.mark_out                            },
                { "tfc_padding",        parse_tfc,                      &child->cfg.tfc                                         },
                { "priority",           parse_uint32,           &child->cfg.priority                            },
+               { "interface",          parse_string,           &child->cfg.interface                           },
        };
 
        return parse_rules(rules, countof(rules), name, value,
index a1e47a2..6e0113e 100644 (file)
@@ -576,6 +576,7 @@ static bool update_usetime(private_child_sa_t *this, bool inbound)
                                .src_ts = my_ts,
                                .dst_ts = other_ts,
                                .mark = this->mark_out,
+                               .interface = this->config->get_interface(this->config),
                        };
                        kernel_ipsec_query_policy_t query = {};
 
@@ -798,6 +799,7 @@ METHOD(child_sa_t, install, status_t,
                .mode = this->mode,
                .src_ts = src_ts,
                .dst_ts = dst_ts,
+               .interface = inbound ? NULL : this->config->get_interface(this->config),
                .lifetime = lifetime,
                .enc_alg = enc_alg,
                .enc_key = encr,
@@ -890,6 +892,7 @@ static status_t install_policies_internal(private_child_sa_t *this,
                .src_ts = my_ts,
                .dst_ts = other_ts,
                .mark = this->mark_out,
+               .interface = this->config->get_interface(this->config),
        }, in_id = {
                .dir = POLICY_IN,
                .src_ts = other_ts,
@@ -948,6 +951,7 @@ static void del_policies_internal(private_child_sa_t *this,
                .src_ts = my_ts,
                .dst_ts = other_ts,
                .mark = this->mark_out,
+               .interface = this->config->get_interface(this->config),
        }, in_id = {
                .dir = POLICY_IN,
                .src_ts = other_ts,
index b74b454..40e291b 100644 (file)
@@ -68,6 +68,8 @@ static bool install_shunt_policy(child_cfg_t *child)
        policy_type_t policy_type;
        policy_priority_t policy_prio;
        status_t status = SUCCESS;
+       uint32_t manual_prio;
+       char *interface;
        ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
 
        switch (child->get_mode(child))
@@ -92,6 +94,9 @@ static bool install_shunt_policy(child_cfg_t *child)
        other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts);
        hosts->destroy(hosts);
 
+       manual_prio = child->get_manual_prio(child);
+       interface = child->get_interface(child);
+
        /* enumerate pairs of traffic selectors */
        e_my_ts = my_ts_list->create_enumerator(my_ts_list);
        while (e_my_ts->enumerate(e_my_ts, &my_ts))
@@ -115,11 +120,12 @@ static bool install_shunt_policy(child_cfg_t *child)
                                .src_ts = my_ts,
                                .dst_ts = other_ts,
                                .mark = child->get_mark(child, FALSE),
+                               .interface = interface,
                        };
                        kernel_ipsec_manage_policy_t policy = {
                                .type = policy_type,
                                .prio = policy_prio,
-                               .manual_prio = child->get_manual_prio(child),
+                               .manual_prio = manual_prio,
                                .src = host_any,
                                .dst = host_any,
                                .sa = &sa,
@@ -134,6 +140,7 @@ static bool install_shunt_policy(child_cfg_t *child)
                                .src_ts = other_ts,
                                .dst_ts = my_ts,
                                .mark = child->get_mark(child, TRUE),
+                               .interface = interface,
                        };
                        status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
                        /* install "inbound" forward policy */
@@ -215,6 +222,8 @@ static void uninstall_shunt_policy(child_cfg_t *child)
        policy_type_t policy_type;
        policy_priority_t policy_prio;
        status_t status = SUCCESS;
+       uint32_t manual_prio;
+       char *interface;
        ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
 
        switch (child->get_mode(child))
@@ -239,6 +248,9 @@ static void uninstall_shunt_policy(child_cfg_t *child)
        other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts);
        hosts->destroy(hosts);
 
+       manual_prio = child->get_manual_prio(child);
+       interface = child->get_interface(child);
+
        /* enumerate pairs of traffic selectors */
        e_my_ts = my_ts_list->create_enumerator(my_ts_list);
        while (e_my_ts->enumerate(e_my_ts, &my_ts))
@@ -262,11 +274,12 @@ static void uninstall_shunt_policy(child_cfg_t *child)
                                .src_ts = my_ts,
                                .dst_ts = other_ts,
                                .mark = child->get_mark(child, FALSE),
+                               .interface = interface,
                        };
                        kernel_ipsec_manage_policy_t policy = {
                                .type = policy_type,
                                .prio = policy_prio,
-                               .manual_prio = child->get_manual_prio(child),
+                               .manual_prio = manual_prio,
                                .src = host_any,
                                .dst = host_any,
                                .sa = &sa,
@@ -281,6 +294,7 @@ static void uninstall_shunt_policy(child_cfg_t *child)
                                .src_ts = other_ts,
                                .dst_ts = my_ts,
                                .mark = child->get_mark(child, TRUE),
+                               .interface = interface,
                        };
                        status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
                        /* uninstall "inbound" forward policy */
index 08d3cf1..fe5b293 100644 (file)
@@ -691,6 +691,9 @@ connections.<conn>.children.<child>.priority = 0
        high-priority drop policies.  The default of _0_ uses dynamically calculated
        priorities based on the size of the traffic selectors.
 
+connections.<conn>.children.<child>.interface =
+       Optional interface name to restrict IPsec policies.
+
 connections.<conn>.children.<child>.mark_in = 0/0x00000000
        Netfilter mark and mask for input traffic.