]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
kernel-wfp: Install tunnel and trap forward policies
authorMartin Willi <martin@revosec.ch>
Fri, 20 Dec 2013 13:42:10 +0000 (14:42 +0100)
committerMartin Willi <martin@revosec.ch>
Wed, 4 Jun 2014 14:32:10 +0000 (16:32 +0200)
src/libcharon/plugins/kernel_wfp/ipsecdump.c
src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.c
src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c

index 55e64fe938e743166b266dfaa9e9aaf46c4eab48..7ca7df5a1702e01c419f1241b482f07305fe3d0f 100644 (file)
@@ -92,13 +92,6 @@ static char *guid2string(GUID *guid)
        return buf;
 }
 
-const GUID FWPM_CONDITION_IP_SOURCE_ADDRESS = {
-       0xae96897e, 0x2e94, 0x4bc9, { 0xb3,0x13,0xb2,0x7e,0xe8,0x0e,0x57,0x4d }
-};
-const GUID FWPM_CONDITION_IP_DESTINATION_ADDRESS = {
-       0x2d79133b, 0xb390, 0x45c6, { 0x86,0x99,0xac,0xac,0xea,0xaf,0xed,0x33 }
-};
-
 /**
  * Convert filter condition key GUID to some known strings
  */
@@ -431,15 +424,9 @@ const GUID FWPM_LAYER_OUTBOUND_IPPACKET_V6 = {
 const GUID FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD = {
        0x9513d7c4, 0xa934, 0x49dc, { 0x91, 0xa7, 0x6c, 0xcb, 0x80, 0xcc, 0x02, 0xe3 }
 };
-const GUID FWPM_LAYER_IPFORWARD_V4 = {
-       0xa82acc24, 0x4ee1, 0x4ee1, { 0xb4, 0x65, 0xfd, 0x1d, 0x25, 0xcb, 0x10, 0xa4 }
-};
 const GUID FWPM_LAYER_IPFORWARD_V4_DISCARD = {
        0x9e9ea773, 0x2fae, 0x4210, { 0x8f, 0x17, 0x34, 0x12, 0x9e, 0xf3, 0x69, 0xeb }
 };
-const GUID FWPM_LAYER_IPFORWARD_V6 = {
-       0x7b964818, 0x19c7, 0x493a, { 0xb7, 0x1f, 0x83, 0x2c, 0x36, 0x84, 0xd2, 0x8c }
-};
 const GUID FWPM_LAYER_IPFORWARD_V6_DISCARD = {
        0x31524a5d, 0x1dfe, 0x472f, { 0xbb, 0x93, 0x51, 0x8e, 0xe9, 0x45, 0xd8, 0xa2 }
 };
@@ -504,19 +491,6 @@ static char* layer2name(GUID *guid)
        return NULL;
 }
 
-const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 = {
-       0x28829633, 0xc4f0, 0x4e66, { 0x87,0x3f,0x84,0x4d,0xb2,0xa8,0x99,0xc7 }
-};
-const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 = {
-       0xaf50bec2, 0xc686, 0x429a, { 0x88,0x4d,0xb7,0x44,0x43,0xe7,0xb0,0xb4 }
-};
-const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 = {
-       0xfb532136, 0x15cb, 0x440b, { 0x93,0x7c,0x17,0x17,0xca,0x32,0x0c,0x40 }
-};
-const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 = {
-       0xdae640cc, 0xe021, 0x4bee, { 0x9e,0xb6,0xa4,0x8b,0x27,0x5c,0x8c,0x1d }
-};
-
 /**
  * Convert filter callout GUID to name
  */
index 3660ac9d629e3aa866c7c66231467404adeb19e3..41f85ba5c17f5bd8d6a721aadb462fadb270484f 100644 (file)
@@ -21,6 +21,12 @@ const GUID FWPM_CONDITION_IP_REMOTE_ADDRESS = {
 const GUID FWPM_CONDITION_IP_LOCAL_ADDRESS = {
        0xd9ee00de, 0xc1ef, 0x4617, { 0xbf,0xe3,0xff,0xd8,0xf5,0xa0,0x89,0x57 }
 };
+const GUID FWPM_CONDITION_IP_SOURCE_ADDRESS = {
+       0xae96897e, 0x2e94, 0x4bc9, { 0xb3,0x13,0xb2,0x7e,0xe8,0x0e,0x57,0x4d }
+};
+const GUID FWPM_CONDITION_IP_DESTINATION_ADDRESS = {
+       0x2d79133b, 0xb390, 0x45c6, { 0x86,0x99,0xac,0xac,0xea,0xaf,0xed,0x33 }
+};
 const GUID FWPM_CONDITION_IP_LOCAL_PORT = {
        0x0c1ba1af, 0x5765, 0x453f, { 0xaf,0x22,0xa8,0xf7,0x91,0xac,0x77,0x5b }
 };
@@ -42,6 +48,12 @@ const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V4 = {
 const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V6 = {
        0xe1735bde, 0x013f, 0x4655, { 0xb3,0x51,0xa4,0x9e,0x15,0x76,0x2d,0xf0 }
 };
+const GUID FWPM_LAYER_IPFORWARD_V4 = {
+       0xa82acc24, 0x4ee1, 0x4ee1, { 0xb4,0x65,0xfd,0x1d,0x25,0xcb,0x10,0xa4}
+};
+const GUID FWPM_LAYER_IPFORWARD_V6 = {
+       0x7b964818, 0x19c7, 0x493a, { 0xb7,0x1f,0x83,0x2c,0x36,0x84,0xd2,0x8c }
+};
 const GUID FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 = {
        0x5132900d, 0x5e84, 0x4b5f, { 0x80,0xe4,0x01,0x74,0x1e,0x81,0xff,0x10 }
 };
@@ -66,6 +78,18 @@ const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 = {
 const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 = {
        0xf1835363, 0xa6a5, 0x4e62, { 0xb1,0x80,0x23,0xdb,0x78,0x9d,0x8d,0xa6 }
 };
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 = {
+       0x28829633, 0xc4f0, 0x4e66, { 0x87,0x3f,0x84,0x4d,0xb2,0xa8,0x99,0xc7 }
+};
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 = {
+       0xaf50bec2, 0xc686, 0x429a, { 0x88,0x4d,0xb7,0x44,0x43,0xe7,0xb0,0xb4 }
+};
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 = {
+       0xfb532136, 0x15cb, 0x440b, { 0x93,0x7c,0x17,0x17,0xca,0x32,0x0c,0x40 }
+};
+const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 = {
+       0xdae640cc, 0xe021, 0x4bee, { 0x9e,0xb6,0xa4,0x8b,0x27,0x5c,0x8c,0x1d }
+};
 
 /**
  * Load a function symbol from a loaded dll
index 5c956ea690bcce3a14bfcf64d9d9b87faa78fe09..b6d7d7a58f8d563a80ba86302f67470573d5e870 100644 (file)
@@ -178,6 +178,10 @@ typedef struct {
        u_int64_t policy_in;
        /** WFP allocated LUID for outbound filter ID */
        u_int64_t policy_out;
+       /** WFP allocated LUID for forward inbound filter ID, tunnel mode only */
+       u_int64_t policy_fwd_in;
+       /** WFP allocated LUID for forward outbound filter ID, tunnel mode only */
+       u_int64_t policy_fwd_out;
        /** provider context, for tunnel mode only */
        u_int64_t provider;
        /** WFP allocated LUID for SA context */
@@ -252,6 +256,16 @@ static void cleanup_policies(private_kernel_wfp_ipsec_t *this, entry_t *entry)
        if (entry->mode == MODE_TUNNEL)
        {
                manage_routes(this, entry, FALSE);
+               if (entry->policy_fwd_in)
+               {
+                       FwpmFilterDeleteById0(this->handle, entry->policy_fwd_in);
+                       entry->policy_fwd_in = 0;
+               }
+               if (entry->policy_fwd_out)
+               {
+                       FwpmFilterDeleteById0(this->handle, entry->policy_fwd_out);
+                       entry->policy_fwd_out = 0;
+               }
        }
 }
 
@@ -343,7 +357,7 @@ static void range2cond(FWPM_FILTER_CONDITION0 *cond,
 /**
  * (Re-)allocate filter conditions for given local or remote traffic selector
  */
-static bool ts2condition(traffic_selector_t *ts, bool local,
+static bool ts2condition(traffic_selector_t *ts, const GUID *target,
                                                 FWPM_FILTER_CONDITION0 *conds[], int *count)
 {
        FWPM_FILTER_CONDITION0 *cond;
@@ -361,14 +375,7 @@ static bool ts2condition(traffic_selector_t *ts, bool local,
        to_port = ts->get_to_port(ts);
 
        cond = append_condition(conds, count);
-       if (local)
-       {
-               cond->fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS;
-       }
-       else
-       {
-               cond->fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
-       }
+       cond->fieldKey = *target;
        if (ts->is_host(ts, NULL))
        {
                cond->matchType = FWP_MATCH_EQUAL;
@@ -440,7 +447,7 @@ static bool ts2condition(traffic_selector_t *ts, bool local,
        }
 
        proto = ts->get_protocol(ts);
-       if (proto && local)
+       if (proto && target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
        {
                cond = append_condition(conds, count);
                cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
@@ -451,7 +458,7 @@ static bool ts2condition(traffic_selector_t *ts, bool local,
 
        if (proto == IPPROTO_ICMP)
        {
-               if (local)
+               if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
                {
                        u_int8_t from_type, to_type, from_code, to_code;
 
@@ -476,16 +483,18 @@ static bool ts2condition(traffic_selector_t *ts, bool local,
        }
        else if (from_port != 0 || to_port != 0xFFFF)
        {
-               cond = append_condition(conds, count);
-               if (local)
+               if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
                {
+                       cond = append_condition(conds, count);
                        cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
+                       range2cond(cond, from_port, to_port);
                }
-               else
+               if (target == &FWPM_CONDITION_IP_REMOTE_ADDRESS)
                {
+                       cond = append_condition(conds, count);
                        cond->fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
+                       range2cond(cond, from_port, to_port);
                }
-               range2cond(cond, from_port, to_port);
        }
        return TRUE;
 }
@@ -529,17 +538,73 @@ static void free_conditions(FWPM_FILTER_CONDITION0 *conds, int count)
        free(conds);
 }
 
+/**
+ * Find the callout GUID for given parameters
+ */
+static bool find_callout(bool tunnel, bool v6, bool inbound, bool forward,
+                                                GUID *layer, GUID *callout)
+{
+       struct {
+               bool tunnel;
+               bool v6;
+               bool inbound;
+               bool forward;
+               const GUID *layer;
+               const GUID *callout;
+       } map[] = {
+               { 0, 0, 0, 0,   &FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
+                                               &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4                       },
+               { 0, 0, 1, 0,   &FWPM_LAYER_INBOUND_TRANSPORT_V4,
+                                               &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4                        },
+               { 0, 1, 0, 0,   &FWPM_LAYER_OUTBOUND_TRANSPORT_V6,
+                                               &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6                       },
+               { 0, 1, 1, 0,   &FWPM_LAYER_INBOUND_TRANSPORT_V6,
+                                               &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6                        },
+               { 1, 0, 0, 0,   &FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
+                                               &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4                          },
+               { 1, 0, 0, 1,   &FWPM_LAYER_IPFORWARD_V4,
+                                               &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4          },
+               { 1, 0, 1, 0,   &FWPM_LAYER_INBOUND_TRANSPORT_V4,
+                                               &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4                           },
+               { 1, 0, 1, 1,   &FWPM_LAYER_IPFORWARD_V4,
+                                               &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4           },
+               { 1, 1, 0, 0,   &FWPM_LAYER_OUTBOUND_TRANSPORT_V6,
+                                               &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6                          },
+               { 1, 1, 0, 1,   &FWPM_LAYER_IPFORWARD_V6,
+                                               &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6          },
+               { 1, 1, 1, 0,   &FWPM_LAYER_INBOUND_TRANSPORT_V6,
+                                               &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6                           },
+               { 1, 1, 1, 1,   &FWPM_LAYER_IPFORWARD_V6,
+                                               &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6           },
+       };
+       int i;
+
+       for (i = 0; i < countof(map); i++)
+       {
+               if (tunnel == map[i].tunnel &&
+                       v6 == map[i].v6 &&
+                       inbound == map[i].inbound &&
+                       forward == map[i].forward)
+               {
+                       *callout = *map[i].callout;
+                       *layer = *map[i].layer;
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
 /**
  * Install a single policy in to the kernel
  */
-static bool install_sp(private_kernel_wfp_ipsec_t *this,
-                                          entry_t *entry, GUID *context, bool inbound)
+static bool install_sp(private_kernel_wfp_ipsec_t *this, sp_entry_t *sp,
+                                          GUID *context, bool inbound, bool fwd, UINT64 *filter_id)
 {
        FWPM_FILTER_CONDITION0 *conds = NULL;
-       int count = 0;
-       enumerator_t *enumerator;
        traffic_selector_t *local, *remote;
-       sp_entry_t *sp;
+       const GUID *ltarget, *rtarget;
+       int count = 0;
+       bool v6;
        DWORD res;
        FWPM_FILTER0 filter = {
                .displayData = {
@@ -548,78 +613,111 @@ static bool install_sp(private_kernel_wfp_ipsec_t *this,
                .action = {
                        .type = FWP_ACTION_CALLOUT_TERMINATING,
                },
-               .layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V4 :
-                                                         FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
        };
 
        if (context)
        {
-               if (inbound)
-               {
-                       filter.action.calloutKey = FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4;
-               }
-               else
-               {
-                       filter.action.calloutKey = FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4;
-               }
                filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
-               filter.providerKey = (GUID*)&this->provider.providerKey,
-               memcpy(&filter.providerContextKey, context, sizeof(GUID));
+               filter.providerKey = (GUID*)&this->provider.providerKey;
+               filter.providerContextKey = *context;
+       }
+
+       v6 = sp->src->get_type(sp->src) == TS_IPV6_ADDR_RANGE;
+       if (!find_callout(context != NULL, v6, inbound, fwd,
+                                         &filter.layerKey, &filter.action.calloutKey))
+       {
+               return FALSE;
+       }
+
+       if (inbound && fwd)
+       {
+               local = sp->dst;
+               remote = sp->src;
        }
        else
        {
-               if (inbound)
-               {
-                       filter.action.calloutKey = FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4;
-               }
-               else
-               {
-                       filter.action.calloutKey = FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4;
-               }
+               local = sp->src;
+               remote = sp->dst;
+       }
+       if (fwd)
+       {
+               ltarget = &FWPM_CONDITION_IP_SOURCE_ADDRESS;
+               rtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS;
+       }
+       else
+       {
+               ltarget = &FWPM_CONDITION_IP_LOCAL_ADDRESS;
+               rtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS;
        }
+       if (!ts2condition(local, ltarget, &conds, &count) ||
+               !ts2condition(remote, rtarget, &conds, &count))
+       {
+               free_conditions(conds, count);
+               return FALSE;
+       }
+
+       filter.numFilterConditions = count;
+       filter.filterCondition = conds;
+
+       res = FwpmFilterAdd0(this->handle, &filter, NULL, filter_id);
+       free_conditions(conds, count);
+       if (res != ERROR_SUCCESS)
+       {
+               DBG1(DBG_KNL, "installing %s%sbound WFP filter failed: 0x%08x",
+                        fwd ? "forward " : "", inbound ? "in" : "out", res);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * Install a set of policies in to the kernel
+ */
+static bool install_sps(private_kernel_wfp_ipsec_t *this,
+                                               entry_t *entry, GUID *context)
+{
+       enumerator_t *enumerator;
+       sp_entry_t *sp;
 
        enumerator = array_create_enumerator(entry->sps);
        while (enumerator->enumerate(enumerator, &sp))
        {
-               if (inbound)
+               /* inbound policy */
+               if (!install_sp(this, sp, context, TRUE, FALSE, &entry->policy_in))
                {
-                       local = sp->dst;
-                       remote = sp->src;
-               }
-               else
-               {
-                       local = sp->src;
-                       remote = sp->dst;
+                       enumerator->destroy(enumerator);
+                       return FALSE;
                }
-
-               if (!ts2condition(local, TRUE, &conds, &count) ||
-                       !ts2condition(remote, FALSE, &conds, &count))
+               /* outbound policy */
+               if (!install_sp(this, sp, context, FALSE, FALSE, &entry->policy_out))
                {
-                       free_conditions(conds, count);
                        enumerator->destroy(enumerator);
                        return FALSE;
                }
+               if (context)
+               {
+                       if (!sp->src->is_host(sp->src, entry->local) ||
+                               !sp->dst->is_host(sp->dst, entry->remote))
+                       {
+                               /* inbound forward policy, from decapsulation */
+                               if (!install_sp(this, sp, context,
+                                                               TRUE, TRUE, &entry->policy_fwd_in))
+                               {
+                                       enumerator->destroy(enumerator);
+                                       return FALSE;
+                               }
+                               /* outbound forward policy, to encapsulate */
+                               if (!install_sp(this, sp, context,
+                                                               FALSE, TRUE, &entry->policy_fwd_out))
+                               {
+                                       enumerator->destroy(enumerator);
+                                       return FALSE;
+                               }
+                       }
+               }
        }
        enumerator->destroy(enumerator);
 
-       filter.numFilterConditions = count;
-       filter.filterCondition = conds;
-
-       if (inbound)
-       {
-               res = FwpmFilterAdd0(this->handle, &filter, NULL, &entry->policy_in);
-       }
-       else
-       {
-               res = FwpmFilterAdd0(this->handle, &filter, NULL, &entry->policy_out);
-       }
-       free_conditions(conds, count);
-       if (res != ERROR_SUCCESS)
-       {
-               DBG1(DBG_KNL, "installing %sbound FWP filter failed: 0x%08x",
-                        inbound ? "in" : "out", res);
-               return FALSE;
-       }
        return TRUE;
 }
 
@@ -971,8 +1069,7 @@ static bool install_sas(private_kernel_wfp_ipsec_t *this, entry_t *entry,
  */
 static bool install_transport(private_kernel_wfp_ipsec_t *this, entry_t *entry)
 {
-       if (install_sp(this, entry, NULL, TRUE) &&
-               install_sp(this, entry, NULL, FALSE) &&
+       if (install_sps(this, entry, NULL) &&
                install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TRANSPORT))
        {
                return TRUE;
@@ -1000,9 +1097,10 @@ static bool generate_guid(private_kernel_wfp_ipsec_t *this, GUID *guid)
 }
 
 /**
- * Install tunnel mode SPs to the kernel
+ * Register a dummy tunnel provider to associate tunnel filters to
  */
-static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
+static bool add_tunnel_provider(private_kernel_wfp_ipsec_t *this,
+                                                               entry_t *entry, GUID *guid, UINT64 *luid)
 {
        DWORD res;
 
@@ -1037,7 +1135,7 @@ static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
        };
        FWPM_PROVIDER_CONTEXT0 qm = {
                .displayData = {
-                       .name = L"charon tunnel provider context",
+                       .name = L"charon tunnel provider",
                },
                .providerKey = (GUID*)&this->provider.providerKey,
                .type = FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT,
@@ -1055,10 +1153,8 @@ static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
                        break;
                case AF_INET6:
                        policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V6;
-                       memcpy(&policy.tunnelEndpoints.localV6Address,
-                                  entry->local->get_address(entry->local).ptr, 16);
-                       memcpy(&policy.tunnelEndpoints.remoteV6Address,
-                                  entry->remote->get_address(entry->remote).ptr, 16);
+                       host2address6(entry->local, &policy.tunnelEndpoints.localV6Address);
+                       host2address6(entry->remote, &policy.tunnelEndpoints.remoteV6Address);
                        break;
                default:
                        return FALSE;
@@ -1069,15 +1165,28 @@ static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
                return FALSE;
        }
 
-       res = FwpmProviderContextAdd0(this->handle, &qm, NULL, &entry->provider);
+       res = FwpmProviderContextAdd0(this->handle, &qm, NULL, luid);
        if (res != ERROR_SUCCESS)
        {
                DBG1(DBG_KNL, "adding provider context failed: 0x%08x", res);
                return FALSE;
        }
+       *guid = qm.providerContextKey;
+       return TRUE;
+}
 
-       if (!install_sp(this, entry, &qm.providerContextKey, TRUE) ||
-               !install_sp(this, entry, &qm.providerContextKey, FALSE))
+/**
+ * Install tunnel mode SPs to the kernel
+ */
+static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
+{
+       GUID guid;
+
+       if (!add_tunnel_provider(this, entry, &guid, &entry->provider))
+       {
+               return FALSE;
+       }
+       if (!install_sps(this, entry, &guid))
        {
                return FALSE;
        }
@@ -1267,10 +1376,12 @@ static bool install(private_kernel_wfp_ipsec_t *this, entry_t *entry)
 typedef struct {
        /** reqid this trap is installed for */
        u_int32_t reqid;
-       /** local traffic selector */
-       traffic_selector_t *local;
-       /** remote traffic selector */
-       traffic_selector_t *remote;
+       /** is this a forward policy trap for tunnel mode? */
+       bool fwd;
+       /** src traffic selector */
+       traffic_selector_t *src;
+       /** dst traffic selector */
+       traffic_selector_t *dst;
        /** LUID of installed tunnel policy filter */
        UINT64 filter_id;
 } trap_t;
@@ -1280,8 +1391,8 @@ typedef struct {
  */
 static void destroy_trap(trap_t *this)
 {
-       this->local->destroy(this->local);
-       this->remote->destroy(this->remote);
+       this->src->destroy(this->src);
+       this->dst->destroy(this->dst);
        free(this);
 }
 
@@ -1438,6 +1549,7 @@ static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
        FWPM_FILTER_CONDITION0 *conds = NULL;
        int count = 0;
        DWORD res;
+       const GUID *starget, *dtarget;
        UINT64 weight = 0x000000000000ff00;
        FWPM_FILTER0 filter = {
                .displayData = {
@@ -1452,18 +1564,35 @@ static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
                },
        };
 
-       switch (trap->local->get_type(trap->local))
+       if (trap->fwd)
        {
-               case TS_IPV4_ADDR_RANGE:
+               if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE)
+               {
+                       filter.layerKey = FWPM_LAYER_IPFORWARD_V4;
+               }
+               else
+               {
+                       filter.layerKey = FWPM_LAYER_IPFORWARD_V6;
+               }
+               starget = &FWPM_CONDITION_IP_SOURCE_ADDRESS;
+               dtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS;
+       }
+       else
+       {
+               if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE)
+               {
                        filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
-                       break;
-               case TS_IPV6_ADDR_RANGE:
+               }
+               else
+               {
                        filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
-                       break;
+               }
+               starget = &FWPM_CONDITION_IP_LOCAL_ADDRESS;
+               dtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS;
        }
 
-       if (!ts2condition(trap->local, TRUE, &conds, &count) ||
-               !ts2condition(trap->remote, FALSE, &conds, &count))
+       if (!ts2condition(trap->src, starget, &conds, &count) ||
+               !ts2condition(trap->dst, dtarget, &conds, &count))
        {
                free_conditions(conds, count);
                return FALSE;
@@ -1476,7 +1605,7 @@ static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
        free_conditions(conds, count);
        if (res != ERROR_SUCCESS)
        {
-               DBG1(DBG_KNL, "installing FWP trap filter failed: 0x%08x", res);
+               DBG1(DBG_KNL, "installing WFP trap filter failed: 0x%08x", res);
                return FALSE;
        }
        return TRUE;
@@ -1492,7 +1621,7 @@ static bool uninstall_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
        res = FwpmFilterDeleteById0(this->handle, trap->filter_id);
        if (res != ERROR_SUCCESS)
        {
-               DBG1(DBG_KNL, "uninstalling FWP trap filter failed: 0x%08x", res);
+               DBG1(DBG_KNL, "uninstalling WFP trap filter failed: 0x%08x", res);
                return FALSE;
        }
        return TRUE;
@@ -1501,15 +1630,17 @@ static bool uninstall_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
 /**
  * Create and install a new trap entry
  */
-static bool add_trap(private_kernel_wfp_ipsec_t *this, u_int32_t reqid,
-                                        traffic_selector_t *local, traffic_selector_t *remote)
+static bool add_trap(private_kernel_wfp_ipsec_t *this,
+                                        u_int32_t reqid, bool fwd,
+                                        traffic_selector_t *src, traffic_selector_t *dst)
 {
        trap_t *trap;
 
        INIT(trap,
                .reqid = reqid,
-               .local = local->clone(local),
-               .remote = remote->clone(remote),
+               .fwd = fwd,
+               .src = src->clone(src),
+               .dst = dst->clone(dst),
        );
 
        if (!install_trap(this, trap))
@@ -1526,8 +1657,9 @@ static bool add_trap(private_kernel_wfp_ipsec_t *this, u_int32_t reqid,
 /**
  * Uninstall and remove a new trap entry
  */
-static bool remove_trap(private_kernel_wfp_ipsec_t *this, u_int32_t reqid,
-                                               traffic_selector_t *local, traffic_selector_t *remote)
+static bool remove_trap(private_kernel_wfp_ipsec_t *this,
+                                               u_int32_t reqid, bool fwd,
+                                               traffic_selector_t *src, traffic_selector_t *dst)
 {
        enumerator_t *enumerator;
        trap_t *trap, *found = NULL;
@@ -1537,8 +1669,9 @@ static bool remove_trap(private_kernel_wfp_ipsec_t *this, u_int32_t reqid,
        while (enumerator->enumerate(enumerator, NULL, &trap))
        {
                if (reqid == trap->reqid &&
-                       local->equals(local, trap->local) &&
-                       remote->equals(remote, trap->remote))
+                       fwd == trap->fwd &&
+                       src->equals(src, trap->src) &&
+                       dst->equals(dst, trap->dst))
                {
                        this->traps->remove_at(this->traps, enumerator);
                        found = trap;
@@ -2014,11 +2147,18 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
                case POLICY_PRIORITY_DEFAULT:
                        break;
                case POLICY_PRIORITY_ROUTED:
-                       if (add_trap(this, sa->reqid, src_ts, dst_ts))
+                       if (!add_trap(this, sa->reqid, FALSE, src_ts, dst_ts))
+                       {
+                               return FAILED;
+                       }
+                       if (sa->mode == MODE_TUNNEL)
                        {
-                               return SUCCESS;
+                               if (!add_trap(this, sa->reqid, TRUE, src_ts, dst_ts))
+                               {
+                                       return FAILED;
+                               }
                        }
-                       return FAILED;
+                       return SUCCESS;
                case POLICY_PRIORITY_FALLBACK:
                default:
                        return NOT_SUPPORTED;
@@ -2074,8 +2214,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
 {
        if (direction == POLICY_OUT && priority == POLICY_PRIORITY_ROUTED)
        {
-               if (remove_trap(this, reqid, src_ts, dst_ts))
+               if (remove_trap(this, reqid, FALSE, src_ts, dst_ts))
                {
+                       remove_trap(this, reqid, TRUE, src_ts, dst_ts);
                        return SUCCESS;
                }
                return NOT_FOUND;
@@ -2146,7 +2287,7 @@ static bool add_bypass(private_kernel_wfp_ipsec_t *this,
        free_conditions(conds, count);
        if (res != ERROR_SUCCESS)
        {
-               DBG1(DBG_KNL, "installing FWP trap filter failed: 0x%08x", res);
+               DBG1(DBG_KNL, "installing WFP bypass filter failed: 0x%08x", res);
                return FALSE;
        }
        return TRUE;