kernel-wfp: Implement update_sa()
authorMartin Willi <martin@revosec.ch>
Fri, 13 Dec 2013 16:14:26 +0000 (17:14 +0100)
committerMartin Willi <martin@revosec.ch>
Wed, 4 Jun 2014 14:32:09 +0000 (16:32 +0200)
src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.c
src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.h
src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c

index a687c43..6b32f31 100644 (file)
@@ -90,3 +90,9 @@ STUB(fwpuclnt, DWORD, IPsecSaContextCreate1, 40,
 
 STUB(fwpuclnt, DWORD, IPsecSaContextSetSpi0, 32,
        HANDLE engineHandle, UINT64 id, const void *getSpi, UINT32 inboundSpi)
+
+STUB(fwpuclnt, DWORD, IPsecSaContextGetById1, 24,
+       HANDLE engineHandle, UINT64 id, void **saContext)
+
+STUB(fwpuclnt, DWORD, IPsecSaContextUpdate0, 24,
+       HANDLE engineHandle, UINT32 flags, const void *newValues)
index efccd4c..76a6c85 100644 (file)
@@ -51,6 +51,17 @@ enum {
        FWPM_TUNNEL_FLAG_ENABLE_VIRTUAL_IF_TUNNELING =                  (1<<1),
 };
 
+/* missing in MinGW */
+enum {
+       IPSEC_SA_DETAILS_UPDATE_TRAFFIC =                                               (1<<0),
+       IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION =                             (1<<1),
+       IPSEC_SA_BUNDLE_UPDATE_FLAGS =                                                  (1<<2),
+       IPSEC_SA_BUNDLE_UPDATE_NAP_CONTEXT =                                    (1<<3),
+       IPSEC_SA_BUNDLE_UPDATE_KEY_MODULE_STATE =                               (1<<4),
+       IPSEC_SA_BUNDLE_UPDATE_PEER_V4_PRIVATE_ADDRESS =                (1<<5),
+       IPSEC_SA_BUNDLE_UPDATE_MM_SA_ID =                                               (1<<6),
+};
+
 DWORD WINAPI FwpmIPsecTunnelAdd0(HANDLE, UINT32,
        const FWPM_PROVIDER_CONTEXT0*, const FWPM_PROVIDER_CONTEXT0*, UINT32,
        const FWPM_FILTER_CONDITION0*, PSECURITY_DESCRIPTOR);
index f26b60a..3dbbb30 100644 (file)
@@ -1509,7 +1509,103 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
        u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst,
        bool encap, bool new_encap, mark_t mark)
 {
-       return NOT_SUPPORTED;
+       entry_t *entry;
+       sa_entry_t key = {
+               .dst = dst,
+               .spi = spi,
+       };
+       UINT64 sa_id = 0;
+       IPSEC_SA_CONTEXT1 *ctx;
+       IPSEC_V4_UDP_ENCAPSULATION0 ports;
+       UINT32 flags = IPSEC_SA_DETAILS_UPDATE_TRAFFIC;
+       DWORD res;
+
+       this->mutex->lock(this->mutex);
+       entry = this->osas->get(this->osas, &key);
+       this->mutex->unlock(this->mutex);
+
+       if (entry)
+       {
+               /* outbound entry, nothing to do */
+               return SUCCESS;
+       }
+
+       this->mutex->lock(this->mutex);
+       entry = this->isas->get(this->isas, &key);
+       if (entry)
+       {
+               /* inbound entry, do update */
+               sa_id = entry->sa_id;
+               ports.localUdpEncapPort = entry->local->get_port(entry->local);
+               ports.remoteUdpEncapPort = entry->remote->get_port(entry->remote);
+       }
+       this->mutex->unlock(this->mutex);
+
+       if (!sa_id)
+       {
+               return NOT_FOUND;
+       }
+
+       res = IPsecSaContextGetById1(this->handle, sa_id, &ctx);
+       if (res != ERROR_SUCCESS)
+       {
+               DBG1(DBG_KNL, "getting WFP SA context for updated failed: 0x%08x", res);
+               return FAILED;
+       }
+       if (!hosts2traffic(this, new_dst, new_src, &ctx->inboundSa->traffic) ||
+               !hosts2traffic(this, new_dst, new_src, &ctx->outboundSa->traffic))
+       {
+               FwpmFreeMemory0((void**)&ctx);
+               return FAILED;
+       }
+
+       if (new_encap != encap)
+       {
+               if (new_encap)
+               {
+                       ctx->inboundSa->udpEncapsulation = &ports;
+                       ctx->outboundSa->udpEncapsulation = &ports;
+               }
+               else
+               {
+                       ctx->inboundSa->udpEncapsulation = NULL;
+                       ctx->outboundSa->udpEncapsulation = NULL;
+               }
+               flags |= IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION;
+       }
+
+       res = IPsecSaContextUpdate0(this->handle, flags, ctx);
+       FwpmFreeMemory0((void**)&ctx);
+       if (res != ERROR_SUCCESS)
+       {
+               DBG1(DBG_KNL, "updating WFP SA context failed: 0x%08x", res);
+               return FAILED;
+       }
+
+       this->mutex->lock(this->mutex);
+       entry = this->isas->remove(this->isas, &key);
+       if (entry)
+       {
+               key.spi = entry->osa.spi;
+               key.dst = entry->osa.dst;
+               this->osas->remove(this->osas, &key);
+
+               entry->local->destroy(entry->local);
+               entry->remote->destroy(entry->remote);
+               entry->local = new_dst->clone(new_dst);
+               entry->remote = new_src->clone(new_src);
+               entry->isa.dst = entry->local;
+               entry->osa.dst = entry->remote;
+
+               this->isas->put(this->isas, &entry->isa, entry);
+               this->osas->put(this->osas, &entry->osa, entry);
+
+               manage_routes(this, entry, FALSE);
+               manage_routes(this, entry, TRUE);
+       }
+       this->mutex->unlock(this->mutex);
+
+       return SUCCESS;
 }
 
 METHOD(kernel_ipsec_t, query_sa, status_t,