aboutsummaryrefslogtreecommitdiffstats
path: root/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
diff options
context:
space:
mode:
authorMartin Willi <martin@revosec.ch>2013-11-18 12:57:36 +0100
committerMartin Willi <martin@revosec.ch>2014-06-04 16:32:06 +0200
commit149fc48e030981aa41ab5f282691a865449504e7 (patch)
treee488e4a4afeee73bf20b04aba0a3bb0c086750ff /src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
parentb1ba0a666c4a46740bae63fd3cc005b7abfbe86d (diff)
downloadstrongswan-149fc48e030981aa41ab5f282691a865449504e7.tar.bz2
strongswan-149fc48e030981aa41ab5f282691a865449504e7.tar.xz
kernel-wfp: Preliminary support for transport mode connections
Diffstat (limited to 'src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c')
-rw-r--r--src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c692
1 files changed, 689 insertions, 3 deletions
diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
index caf955d55..0910efc9b 100644
--- a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
+++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
@@ -147,6 +147,12 @@ typedef struct {
ipsec_mode_t mode;
/** UDP encapsulation */
bool encap;
+ /** WFP allocated LUID for inbound filter/tunnel policy ID */
+ u_int64_t policy_in;
+ /** WFP allocated LUID for outbound filter/tunnel policy ID */
+ u_int64_t policy_out;
+ /** WFP allocated LUID for SA context */
+ u_int64_t sa_id;
} entry_t;
/**
@@ -173,8 +179,20 @@ static entry_t *entry_create(u_int32_t reqid, host_t *local, host_t *remote,
/**
* Destroy a SA/SP entry set
*/
-static void entry_destroy(entry_t *entry)
+static void entry_destroy(private_kernel_wfp_ipsec_t *this, entry_t *entry)
{
+ if (entry->sa_id)
+ {
+ IPsecSaContextDeleteById0(this->handle, entry->sa_id);
+ }
+ if (entry->policy_in)
+ {
+ FwpmFilterDeleteById0(this->handle, entry->policy_in);
+ }
+ if (entry->policy_out)
+ {
+ FwpmFilterDeleteById0(this->handle, entry->policy_out);
+ }
array_destroy(entry->sas);
array_destroy(entry->sps);
entry->local->destroy(entry->local);
@@ -208,6 +226,666 @@ static entry_t *get_or_create_entry(private_kernel_wfp_ipsec_t *this,
return NULL;
}
+/**
+ * Append/Realloc a filter condition to an existing condition set
+ */
+static FWPM_FILTER_CONDITION0 *append_condition(FWPM_FILTER_CONDITION0 *conds[],
+ int *count)
+{
+ FWPM_FILTER_CONDITION0 *cond;
+
+ (*count)++;
+ *conds = realloc(*conds, *count * sizeof(*cond));
+ cond = *conds + *count - 1;
+ memset(cond, 0, sizeof(*cond));
+
+ return cond;
+}
+
+/**
+ * Convert an IPv4 prefix to a host order subnet mask
+ */
+static u_int32_t prefix2mask(u_int8_t prefix)
+{
+ u_int8_t netmask[4] = {};
+ int i;
+
+ for (i = 0; i < sizeof(netmask); i++)
+ {
+ if (prefix < 8)
+ {
+ netmask[i] = 0xFF << (8 - prefix);
+ break;
+ }
+ netmask[i] = 0xFF;
+ prefix -= 8;
+ }
+ return untoh32(netmask);
+}
+
+/**
+ * Convert a 16-bit range to a WFP condition
+ */
+static void range2cond(FWPM_FILTER_CONDITION0 *cond,
+ u_int16_t from, u_int16_t to)
+{
+ if (from == to)
+ {
+ cond->matchType = FWP_MATCH_EQUAL;
+ cond->conditionValue.type = FWP_UINT16;
+ cond->conditionValue.uint16 = from;
+ }
+ else
+ {
+ cond->matchType = FWP_MATCH_RANGE;
+ cond->conditionValue.type = FWP_RANGE_TYPE;
+ cond->conditionValue.rangeValue = calloc(1, sizeof(FWP_RANGE0));
+ cond->conditionValue.rangeValue->valueLow.type = FWP_UINT16;
+ cond->conditionValue.rangeValue->valueLow.uint16 = from;
+ cond->conditionValue.rangeValue->valueHigh.type = FWP_UINT16;
+ cond->conditionValue.rangeValue->valueHigh.uint16 = to;
+ }
+}
+
+/**
+ * (Re-)allocate filter conditions for given local or remote traffic selector
+ */
+static bool ts2condition(traffic_selector_t *ts, bool local,
+ FWPM_FILTER_CONDITION0 *conds[], int *count)
+{
+ FWPM_FILTER_CONDITION0 *cond;
+ FWP_BYTE_ARRAY16 *addr;
+ FWP_RANGE0 *range;
+ u_int16_t from_port, to_port;
+ void *from, *to;
+ u_int8_t proto;
+ host_t *net;
+ u_int8_t prefix;
+
+ from = ts->get_from_address(ts).ptr;
+ to = ts->get_to_address(ts).ptr;
+ from_port = ts->get_from_port(ts);
+ 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;
+ }
+ if (ts->is_host(ts, NULL))
+ {
+ cond->matchType = FWP_MATCH_EQUAL;
+ switch (ts->get_type(ts))
+ {
+ case TS_IPV4_ADDR_RANGE:
+ cond->conditionValue.type = FWP_UINT32;
+ cond->conditionValue.uint32 = untoh32(from);
+ break;
+ case TS_IPV6_ADDR_RANGE:
+ cond->conditionValue.type = FWP_BYTE_ARRAY16_TYPE;
+ cond->conditionValue.byteArray16 = addr = malloc(sizeof(*addr));
+ memcpy(addr, from, sizeof(*addr));
+ break;
+ default:
+ return FALSE;
+ }
+ }
+ else if (ts->to_subnet(ts, &net, &prefix))
+ {
+ FWP_V6_ADDR_AND_MASK *m6;
+ FWP_V4_ADDR_AND_MASK *m4;
+
+ cond->matchType = FWP_MATCH_EQUAL;
+ switch (net->get_family(net))
+ {
+ case AF_INET:
+ cond->conditionValue.type = FWP_V4_ADDR_MASK;
+ cond->conditionValue.v4AddrMask = m4 = calloc(1, sizeof(*m4));
+ m4->addr = untoh32(from);
+ m4->mask = prefix2mask(prefix);
+ break;
+ case AF_INET6:
+ cond->conditionValue.type = FWP_V6_ADDR_MASK;
+ cond->conditionValue.v6AddrMask = m6 = calloc(1, sizeof(*m6));
+ memcpy(m6->addr, from, sizeof(m6->addr));
+ m6->prefixLength = prefix;
+ break;
+ default:
+ net->destroy(net);
+ return FALSE;
+ }
+ net->destroy(net);
+ }
+ else
+ {
+ cond->matchType = FWP_MATCH_RANGE;
+ cond->conditionValue.type = FWP_RANGE_TYPE;
+ cond->conditionValue.rangeValue = range = calloc(1, sizeof(*range));
+ switch (ts->get_type(ts))
+ {
+ case TS_IPV4_ADDR_RANGE:
+ range->valueLow.type = FWP_UINT32;
+ range->valueLow.uint32 = untoh32(from);
+ range->valueHigh.type = FWP_UINT32;
+ range->valueHigh.uint32 = untoh32(to);
+ break;
+ case TS_IPV6_ADDR_RANGE:
+ range->valueLow.type = FWP_BYTE_ARRAY16_TYPE;
+ range->valueLow.byteArray16 = addr = malloc(sizeof(*addr));
+ memcpy(addr, from, sizeof(*addr));
+ range->valueHigh.type = FWP_BYTE_ARRAY16_TYPE;
+ range->valueHigh.byteArray16 = addr = malloc(sizeof(*addr));
+ memcpy(addr, to, sizeof(*addr));
+ break;
+ default:
+ return FALSE;
+ }
+ }
+
+ proto = ts->get_protocol(ts);
+ if (proto && local)
+ {
+ cond = append_condition(conds, count);
+ cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
+ cond->matchType = FWP_MATCH_EQUAL;
+ cond->conditionValue.type = FWP_UINT8;
+ cond->conditionValue.uint8 = proto;
+ }
+
+ if (proto == IPPROTO_ICMP)
+ {
+ if (local)
+ {
+ u_int8_t from_type, to_type, from_code, to_code;
+
+ from_type = traffic_selector_icmp_type(from_port);
+ to_type = traffic_selector_icmp_type(to_port);
+ from_code = traffic_selector_icmp_code(from_port);
+ to_code = traffic_selector_icmp_code(to_port);
+
+ if (from_type != 0 || to_type != 0xFF)
+ {
+ cond = append_condition(conds, count);
+ cond->fieldKey = FWPM_CONDITION_ICMP_TYPE;
+ range2cond(cond, from_type, to_type);
+ }
+ if (from_code != 0 || to_code != 0xFF)
+ {
+ cond = append_condition(conds, count);
+ cond->fieldKey = FWPM_CONDITION_ICMP_CODE;
+ range2cond(cond, from_code, to_code);
+ }
+ }
+ }
+ else if (from_port != 0 || to_port != 0xFFFF)
+ {
+ cond = append_condition(conds, count);
+ if (local)
+ {
+ cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
+ }
+ else
+ {
+ cond->fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
+ }
+ range2cond(cond, from_port, to_port);
+ }
+ return TRUE;
+}
+
+/**
+ * Free memory associated to a single condition
+ */
+static void free_condition(FWP_DATA_TYPE type, void *value)
+{
+ FWP_RANGE0 *range;
+
+ switch (type)
+ {
+ case FWP_BYTE_ARRAY16_TYPE:
+ case FWP_V4_ADDR_MASK:
+ case FWP_V6_ADDR_MASK:
+ free(value);
+ break;
+ case FWP_RANGE_TYPE:
+ range = value;
+ free_condition(range->valueLow.type, range->valueLow.sd);
+ free_condition(range->valueHigh.type, range->valueHigh.sd);
+ free(range);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Free memory used by a set of conditions
+ */
+static void free_conditions(FWPM_FILTER_CONDITION0 *conds, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ {
+ free_condition(conds[i].conditionValue.type, conds[i].conditionValue.sd);
+ }
+ free(conds);
+}
+
+/**
+ * Install transport mode SP to the kernel
+ */
+static bool install_transport_sp(private_kernel_wfp_ipsec_t *this,
+ entry_t *entry, bool inbound)
+{
+ FWPM_FILTER_CONDITION0 *conds = NULL;
+ int count = 0;
+ enumerator_t *enumerator;
+ traffic_selector_t *local, *remote;
+ sp_entry_t *sp;
+ DWORD res;
+ FWPM_FILTER0 filter = {
+ .displayData = {
+ .name = L"charon IPsec transport",
+ },
+ .action = {
+ .type = FWP_ACTION_CALLOUT_TERMINATING,
+ .calloutKey = inbound ? FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 :
+ FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4,
+ },
+ .layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V4 :
+ FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
+ };
+
+ enumerator = array_create_enumerator(entry->sps);
+ while (enumerator->enumerate(enumerator, &sp))
+ {
+ if (inbound)
+ {
+ if (sp->direction != POLICY_IN)
+ {
+ continue;
+ }
+ local = sp->dst;
+ remote = sp->src;
+ }
+ else
+ {
+ if (sp->direction != POLICY_OUT)
+ {
+ continue;
+ }
+ local = sp->src;
+ remote = sp->dst;
+ }
+
+ if (!ts2condition(local, TRUE, &conds, &count) ||
+ !ts2condition(remote, FALSE, &conds, &count))
+ {
+ free_conditions(conds, count);
+ 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 inbound FWP filter failed: 0x%08x", res);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Convert a chunk_t to a WFP FWP_BYTE_BLOB
+ */
+static inline FWP_BYTE_BLOB chunk2blob(chunk_t chunk)
+{
+ return (FWP_BYTE_BLOB){
+ .size = chunk.len,
+ .data = chunk.ptr,
+ };
+}
+
+/**
+ * Convert an integrity_algorithm_t to a WFP IPSEC_AUTH_TRANFORM_ID0
+ */
+static bool alg2auth(integrity_algorithm_t alg,
+ IPSEC_SA_AUTH_INFORMATION0 *info)
+{
+ struct {
+ integrity_algorithm_t alg;
+ IPSEC_AUTH_TRANSFORM_ID0 transform;
+ } map[] = {
+ { AUTH_HMAC_MD5_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_MD5_96 },
+ { AUTH_HMAC_SHA1_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96 },
+ { AUTH_HMAC_SHA2_256_128, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_256_128},
+ { AUTH_AES_128_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_128 },
+ { AUTH_AES_192_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_192 },
+ { AUTH_AES_256_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_256 },
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (map[i].alg == alg)
+ {
+ info->authTransform.authTransformId = map[i].transform;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Convert an encryption_algorithm_t to a WFP IPSEC_CIPHER_TRANFORM_ID0
+ */
+static bool alg2cipher(encryption_algorithm_t alg, int keylen,
+ IPSEC_SA_CIPHER_INFORMATION0 *info)
+{
+ struct {
+ encryption_algorithm_t alg;
+ int keylen;
+ IPSEC_CIPHER_TRANSFORM_ID0 transform;
+ } map[] = {
+ { ENCR_DES, 8, IPSEC_CIPHER_TRANSFORM_ID_CBC_DES },
+ { ENCR_3DES, 24, IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES },
+ { ENCR_AES_CBC, 16, IPSEC_CIPHER_TRANSFORM_ID_AES_128 },
+ { ENCR_AES_CBC, 24, IPSEC_CIPHER_TRANSFORM_ID_AES_192 },
+ { ENCR_AES_CBC, 32, IPSEC_CIPHER_TRANSFORM_ID_AES_256 },
+ { ENCR_AES_GCM_ICV16, 20, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_128 },
+ { ENCR_AES_GCM_ICV16, 28, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_192 },
+ { ENCR_AES_GCM_ICV16, 36, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_256 },
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (map[i].alg == alg && map[i].keylen == keylen)
+ {
+ info->cipherTransform.cipherTransformId = map[i].transform;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Get the integrity algorithm used for an AEAD transform
+ */
+static integrity_algorithm_t encr2integ(encryption_algorithm_t encr, int keylen)
+{
+ struct {
+ encryption_algorithm_t encr;
+ int keylen;
+ integrity_algorithm_t integ;
+ } map[] = {
+ { ENCR_NULL_AUTH_AES_GMAC, 20, AUTH_AES_128_GMAC },
+ { ENCR_NULL_AUTH_AES_GMAC, 28, AUTH_AES_192_GMAC },
+ { ENCR_NULL_AUTH_AES_GMAC, 36, AUTH_AES_256_GMAC },
+ { ENCR_AES_GCM_ICV16, 20, AUTH_AES_128_GMAC },
+ { ENCR_AES_GCM_ICV16, 28, AUTH_AES_192_GMAC },
+ { ENCR_AES_GCM_ICV16, 36, AUTH_AES_256_GMAC },
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (map[i].encr == encr && map[i].keylen == keylen)
+ {
+ return map[i].integ;
+ }
+ }
+ return AUTH_UNDEFINED;
+}
+
+/**
+ * Install a single transport mode SA
+ */
+static bool install_transport_sa(private_kernel_wfp_ipsec_t *this,
+ entry_t *entry, sa_entry_t *sa, FWP_IP_VERSION version)
+{
+ IPSEC_SA_AUTH_AND_CIPHER_INFORMATION0 info = {};
+ IPSEC_SA0 ipsec = {
+ .spi = ntohl(sa->spi),
+ };
+ IPSEC_SA_BUNDLE0 bundle = {
+ .saList = &ipsec,
+ .numSAs = 1,
+ .ipVersion = version,
+ };
+ struct {
+ u_int16_t alg;
+ chunk_t key;
+ } integ = {}, encr = {};
+ DWORD res;
+
+ switch (entry->protocol)
+ {
+ case IPPROTO_AH:
+ ipsec.saTransformType = IPSEC_TRANSFORM_AH;
+ ipsec.ahInformation = &info.saAuthInformation;
+ integ.key = sa->integ.key;
+ integ.alg = sa->integ.alg;
+ break;
+ case IPPROTO_ESP:
+ if (sa->encr.alg == ENCR_NULL ||
+ sa->encr.alg == ENCR_NULL_AUTH_AES_GMAC)
+ {
+ ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH;
+ ipsec.espAuthInformation = &info.saAuthInformation;
+ }
+ else
+ {
+ ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER;
+ ipsec.espAuthAndCipherInformation = &info;
+ encr.key = sa->encr.key;
+ encr.alg = sa->encr.alg;
+ }
+ if (encryption_algorithm_is_aead(sa->encr.alg))
+ {
+ integ.alg = encr2integ(sa->encr.alg, sa->encr.key.len);
+ integ.key = sa->encr.key;
+ }
+ else
+ {
+ integ.alg = sa->integ.alg;
+ integ.key = sa->integ.key;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (integ.alg)
+ {
+ info.saAuthInformation.authKey = chunk2blob(integ.key);
+ if (!alg2auth(integ.alg, &info.saAuthInformation))
+ {
+ DBG1(DBG_KNL, "integrity algorithm %N not supported by WFP",
+ integrity_algorithm_names, integ.alg);
+ return FALSE;
+ }
+ }
+ if (encr.alg)
+ {
+ info.saCipherInformation.cipherKey = chunk2blob(encr.key);
+ if (!alg2cipher(encr.alg, encr.key.len, &info.saCipherInformation))
+ {
+ DBG1(DBG_KNL, "encryption algorithm %N not supported by WFP",
+ encryption_algorithm_names, encr.alg);
+ return FALSE;
+ }
+ }
+
+ if (sa->inbound)
+ {
+ res = IPsecSaContextAddInbound0(this->handle, entry->sa_id, &bundle);
+ }
+ else
+ {
+ res = IPsecSaContextAddOutbound0(this->handle, entry->sa_id, &bundle);
+ }
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "adding %sbound WFP SA failed: 0x%08x",
+ sa->inbound ? "in" : "out", res);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Install transport mode SAs to the kernel
+ */
+static bool install_transport_sas(private_kernel_wfp_ipsec_t *this,
+ entry_t *entry)
+{
+ IPSEC_TRAFFIC0 traffic = {
+ .trafficType = IPSEC_TRAFFIC_TYPE_TRANSPORT,
+ };
+ IPSEC_GETSPI1 spi = {
+ .inboundIpsecTraffic = {
+ .trafficType = IPSEC_TRAFFIC_TYPE_TRANSPORT,
+ .ipsecFilterId = entry->policy_in,
+ },
+ };
+ sa_entry_t *sa;
+ IPSEC_SA_SPI inbound_spi = 0;
+ enumerator_t *enumerator;
+ DWORD res;
+
+ switch (entry->local->get_family(entry->local))
+ {
+ case AF_INET:
+ traffic.ipVersion = FWP_IP_VERSION_V4;
+ traffic.localV4Address =
+ untoh32(entry->local->get_address(entry->local).ptr);
+ traffic.remoteV4Address =
+ untoh32(entry->remote->get_address(entry->remote).ptr);
+ break;
+ case AF_INET6:
+ traffic.ipVersion = FWP_IP_VERSION_V6;
+ memcpy(&traffic.localV6Address,
+ entry->local->get_address(entry->local).ptr, 16);
+ memcpy(&traffic.remoteV6Address,
+ entry->remote->get_address(entry->remote).ptr, 16);
+ break;
+ default:
+ return FALSE;
+ }
+
+ traffic.ipsecFilterId = entry->policy_out;
+ res = IPsecSaContextCreate0(this->handle, &traffic, NULL, &entry->sa_id);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "creating WFP SA context failed: 0x%08x", res);
+ return FALSE;
+ }
+
+ enumerator = array_create_enumerator(entry->sas);
+ while (enumerator->enumerate(enumerator, &sa))
+ {
+ if (sa->inbound)
+ {
+ inbound_spi = ntohl(sa->spi);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ if (!inbound_spi)
+ {
+ return FALSE;
+ }
+
+ memcpy(spi.inboundIpsecTraffic.localV6Address, traffic.localV6Address,
+ sizeof(traffic.localV6Address));
+ memcpy(spi.inboundIpsecTraffic.remoteV6Address, traffic.remoteV6Address,
+ sizeof(traffic.remoteV6Address));
+ spi.ipVersion = traffic.ipVersion;
+
+ res = IPsecSaContextSetSpi0(this->handle, entry->sa_id, &spi, inbound_spi);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "setting WFP SA SPI failed: 0x%08x", res);
+ IPsecSaContextDeleteById0(this->handle, entry->sa_id);
+ entry->sa_id = 0;
+ return FALSE;
+ }
+
+ enumerator = array_create_enumerator(entry->sas);
+ while (enumerator->enumerate(enumerator, &sa))
+ {
+ if (!install_transport_sa(this, entry, sa, spi.ipVersion))
+ {
+ enumerator->destroy(enumerator);
+ IPsecSaContextDeleteById0(this->handle, entry->sa_id);
+ entry->sa_id = 0;
+ return FALSE;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return TRUE;
+}
+
+/**
+ * Install a transport mode SA/SP set to the kernel
+ */
+static bool install_transport(private_kernel_wfp_ipsec_t *this, entry_t *entry)
+{
+ if (install_transport_sp(this, entry, TRUE) &&
+ install_transport_sp(this, entry, FALSE) &&
+ install_transport_sas(this, entry))
+ {
+ return TRUE;
+ }
+ if (entry->policy_in)
+ {
+ FwpmFilterDeleteById0(this->handle, entry->policy_in);
+ entry->policy_in = 0;
+ }
+ if (entry->policy_out)
+ {
+ FwpmFilterDeleteById0(this->handle, entry->policy_out);
+ entry->policy_out = 0;
+ }
+ return FALSE;
+}
+
+/**
+ * Install a SA/SP set to the kernel
+ */
+static bool install(private_kernel_wfp_ipsec_t *this, entry_t *entry)
+{
+ switch (entry->mode)
+ {
+ case MODE_TRANSPORT:
+ return install_transport(this, entry);
+ case MODE_TUNNEL:
+ case MODE_BEET:
+ default:
+ return FALSE;
+ }
+}
+
METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
private_kernel_wfp_ipsec_t *this)
{
@@ -352,7 +1030,7 @@ METHOD(kernel_ipsec_t, del_sa, status_t,
(void*)(uintptr_t)entry->reqid);
if (entry)
{
- entry_destroy(entry);
+ entry_destroy(this, entry);
}
}
}
@@ -407,6 +1085,13 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
.direction = direction,
);
array_insert(entry->sps, -1, sp);
+ if (array_count(entry->sps) > 1)
+ {
+ if (!install(this, entry))
+ {
+ status = FAILED;
+ }
+ }
}
else
{
@@ -464,7 +1149,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
(void*)(uintptr_t)reqid);
if (entry)
{
- entry_destroy(entry);
+ entry_destroy(this, entry);
}
}
}
@@ -539,6 +1224,7 @@ kernel_wfp_ipsec_t *kernel_wfp_ipsec_create()
.destroy = _destroy,
},
},
+ .nextspi = htonl(0xc0000001),
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.entries = hashtable_create(hashtable_hash_ptr,
hashtable_equals_ptr, 4),