summaryrefslogtreecommitdiff
path: root/datapath-windows/ovsext/Conntrack-related.c
diff options
context:
space:
mode:
Diffstat (limited to 'datapath-windows/ovsext/Conntrack-related.c')
-rw-r--r--datapath-windows/ovsext/Conntrack-related.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/datapath-windows/ovsext/Conntrack-related.c b/datapath-windows/ovsext/Conntrack-related.c
new file mode 100644
index 000000000..2d95bc2f1
--- /dev/null
+++ b/datapath-windows/ovsext/Conntrack-related.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2016 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Conntrack.h"
+#include "Jhash.h"
+
+static PLIST_ENTRY ovsCtRelatedTable; /* Holds related entries */
+static UINT64 ctTotalRelatedEntries;
+static OVS_CT_THREAD_CTX ctRelThreadCtx;
+static PNDIS_RW_LOCK_EX ovsCtRelatedLockObj;
+extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
+
+static __inline UINT32
+OvsExtractCtRelatedKeyHash(OVS_CT_KEY *key)
+{
+ UINT32 hsrc, hdst,hash;
+ hsrc = OvsJhashBytes((UINT32*) &key->src, sizeof(key->src), 0);
+ hdst = OvsJhashBytes((UINT32*) &key->dst, sizeof(key->dst), 0);
+ hash = hsrc ^ hdst; /* TO identify reverse traffic */
+ return hash;
+}
+
+static __inline BOOLEAN
+OvsCtRelatedKeyAreSame(OVS_CT_KEY incomingKey, OVS_CT_KEY entryKey)
+{
+ /* FTP PASV - Client initiates the connection from unknown port */
+ if ((incomingKey.dst.addr.ipv4 == entryKey.src.addr.ipv4) &&
+ (incomingKey.dst.port == entryKey.src.port) &&
+ (incomingKey.src.addr.ipv4 == entryKey.dst.addr.ipv4) &&
+ (incomingKey.dl_type == entryKey.dl_type) &&
+ (incomingKey.nw_proto == entryKey.nw_proto)) {
+ return TRUE;
+ }
+
+ /* FTP ACTIVE - Server initiates the connection */
+ if ((incomingKey.src.addr.ipv4 == entryKey.src.addr.ipv4) &&
+ (incomingKey.src.port == entryKey.src.port) &&
+ (incomingKey.dst.addr.ipv4 == entryKey.dst.addr.ipv4) &&
+ (incomingKey.dst.port == entryKey.dst.port) &&
+ (incomingKey.dl_type == entryKey.dl_type) &&
+ (incomingKey.nw_proto == entryKey.nw_proto)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * OvsCtRelatedLookup
+ * Checks the related connections table for an entry that matches the
+ * incoming connection. If there is a matching entry, then it returns
+ * the pointer to the original control connection.
+ *
+ *---------------------------------------------------------------------------
+ */
+POVS_CT_ENTRY
+OvsCtRelatedLookup(OVS_CT_KEY key, UINT64 currentTime)
+{
+ PLIST_ENTRY link, next;
+ POVS_CT_REL_ENTRY entry;
+ LOCK_STATE_EX lockState;
+
+ NdisAcquireRWLockRead(ovsCtRelatedLockObj, &lockState, 0);
+
+ if (!ctTotalRelatedEntries) {
+ NdisReleaseRWLock(ovsCtRelatedLockObj, &lockState);
+ return NULL;
+ }
+
+ for (int i = 0; i < CT_HASH_TABLE_SIZE; i++) {
+ /* XXX - Scan the table based on the hash instead */
+ LIST_FORALL_SAFE(&ovsCtRelatedTable[i], link, next) {
+ entry = CONTAINING_RECORD(link, OVS_CT_REL_ENTRY, link);
+ if (entry->expiration > currentTime) {
+ if (OvsCtRelatedKeyAreSame(key, entry->key)) {
+ NdisReleaseRWLock(ovsCtRelatedLockObj, &lockState);
+ return entry->parent;
+ }
+ }
+ }
+ }
+ NdisReleaseRWLock(ovsCtRelatedLockObj, &lockState);
+ return NULL;
+}
+
+static __inline VOID
+OvsCtRelatedEntryDelete(POVS_CT_REL_ENTRY entry)
+{
+ RemoveEntryList(&entry->link);
+ OvsFreeMemoryWithTag(entry, OVS_CT_POOL_TAG);
+ ctTotalRelatedEntries--;
+}
+
+NDIS_STATUS
+OvsCtRelatedEntryCreate(UINT8 ipProto,
+ UINT16 dl_type,
+ UINT32 serverIp,
+ UINT32 clientIp,
+ UINT16 serverPort,
+ UINT16 clientPort,
+ UINT64 currentTime,
+ POVS_CT_ENTRY parent)
+{
+ LOCK_STATE_EX lockState;
+ POVS_CT_REL_ENTRY entry;
+ entry = OvsAllocateMemoryWithTag(sizeof(OVS_CT_REL_ENTRY),
+ OVS_CT_POOL_TAG);
+ if (!entry) {
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ RtlZeroMemory(entry, sizeof(struct OVS_CT_REL_ENTRY));
+ entry->expiration = currentTime + (CT_INTERVAL_SEC * 60);
+ entry->key.src.addr.ipv4 = serverIp;
+ entry->key.dst.addr.ipv4 = clientIp;
+ entry->key.nw_proto = ipProto;
+ entry->key.dl_type = dl_type;
+ entry->key.src.port = serverPort;
+ entry->key.dst.port = clientPort;
+ entry->parent = parent;
+
+ UINT32 hash = OvsExtractCtRelatedKeyHash(&entry->key);
+
+ NdisAcquireRWLockWrite(ovsCtRelatedLockObj, &lockState, 0);
+ InsertHeadList(&ovsCtRelatedTable[hash & CT_HASH_TABLE_MASK],
+ &entry->link);
+ ctTotalRelatedEntries++;
+ NdisReleaseRWLock(ovsCtRelatedLockObj, &lockState);
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+static __inline NDIS_STATUS
+OvsCtRelatedFlush()
+{
+ PLIST_ENTRY link, next;
+ POVS_CT_REL_ENTRY entry;
+
+ LOCK_STATE_EX lockState;
+ NdisAcquireRWLockWrite(ovsCtRelatedLockObj, &lockState, 0);
+
+ if (ctTotalRelatedEntries) {
+ for (int i = 0; i < CT_HASH_TABLE_SIZE; i++) {
+ LIST_FORALL_SAFE(&ovsCtRelatedTable[i], link, next) {
+ entry = CONTAINING_RECORD(link, OVS_CT_REL_ENTRY, link);
+ OvsCtRelatedEntryDelete(entry);
+ }
+ }
+ }
+
+ NdisReleaseRWLock(ovsCtRelatedLockObj, &lockState);
+ return NDIS_STATUS_SUCCESS;
+}
+
+/* XXX - Create a wrapper for managing Tables used by Connection Trackers */
+
+/*
+ *----------------------------------------------------------------------------
+ * ovsCtRelatedEntryCleaner
+ * Runs periodically and cleans up the related connections tracker
+ *----------------------------------------------------------------------------
+ */
+VOID
+ovsCtRelatedEntryCleaner(PVOID data)
+{
+ POVS_CT_THREAD_CTX context = (POVS_CT_THREAD_CTX)data;
+ PLIST_ENTRY link, next;
+ POVS_CT_REL_ENTRY entry;
+ BOOLEAN success = TRUE;
+
+ while (success) {
+ LOCK_STATE_EX lockState;
+ NdisAcquireRWLockWrite(ovsCtRelatedLockObj, &lockState, 0);
+ if (context->exit) {
+ NdisReleaseRWLock(ovsCtRelatedLockObj, &lockState);
+ break;
+ }
+
+ /* Set the timeout for the thread and cleanup */
+ UINT64 currentTime, threadSleepTimeout;
+ NdisGetCurrentSystemTime((LARGE_INTEGER *)&currentTime);
+ threadSleepTimeout = currentTime + CT_CLEANUP_INTERVAL;
+
+ if (ctTotalRelatedEntries) {
+ for (int i = 0; i < CT_HASH_TABLE_SIZE; i++) {
+ LIST_FORALL_SAFE(&ovsCtRelatedTable[i], link, next) {
+ entry = CONTAINING_RECORD(link, OVS_CT_REL_ENTRY, link);
+ if (entry->expiration < currentTime) {
+ OvsCtRelatedEntryDelete(entry);
+ }
+ }
+ }
+ }
+ NdisReleaseRWLock(ovsCtRelatedLockObj, &lockState);
+ KeWaitForSingleObject(&context->event, Executive, KernelMode,
+ FALSE, (LARGE_INTEGER *)&threadSleepTimeout);
+ }
+
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * OvsInitCtRelated
+ * Initialize the components used by Related Connections Tracker
+ *----------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsInitCtRelated(POVS_SWITCH_CONTEXT context)
+{
+ NTSTATUS status;
+ HANDLE threadHandle = NULL;
+ ctTotalRelatedEntries = 0;
+
+ /* Init the sync-lock */
+ ovsCtRelatedLockObj = NdisAllocateRWLock(context->NdisFilterHandle);
+ if (ovsCtRelatedLockObj == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Init the Hash Buffer */
+ ovsCtRelatedTable = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY)
+ * CT_HASH_TABLE_SIZE,
+ OVS_CT_POOL_TAG);
+ if (ovsCtRelatedTable == NULL) {
+ NdisFreeRWLock(ovsCtRelatedLockObj);
+ ovsCtRelatedLockObj = NULL;
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ for (int i = 0; i < CT_HASH_TABLE_SIZE; i++) {
+ InitializeListHead(&ovsCtRelatedTable[i]);
+ }
+
+ /* Init CT Cleaner Thread */
+ KeInitializeEvent(&ctRelThreadCtx.event, NotificationEvent, FALSE);
+ status = PsCreateSystemThread(&threadHandle, SYNCHRONIZE, NULL, NULL,
+ NULL, ovsCtRelatedEntryCleaner,
+ &ctRelThreadCtx);
+
+ if (status != STATUS_SUCCESS) {
+ NdisFreeRWLock(ovsCtRelatedLockObj);
+ ovsCtRelatedLockObj = NULL;
+
+ OvsFreeMemoryWithTag(ovsCtRelatedTable, OVS_CT_POOL_TAG);
+ ovsCtRelatedTable = NULL;
+
+ return status;
+ }
+
+ ObReferenceObjectByHandle(threadHandle, SYNCHRONIZE, NULL, KernelMode,
+ &ctRelThreadCtx.threadObject, NULL);
+ ZwClose(threadHandle);
+ threadHandle = NULL;
+ return STATUS_SUCCESS;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * OvsCleanupCtRelated
+ * Cleanup memory and thread that were spawned for tracking related entry
+ *----------------------------------------------------------------------------
+ */
+VOID
+OvsCleanupCtRelated(VOID)
+{
+ LOCK_STATE_EX lockState;
+ NdisAcquireRWLockWrite(ovsCtRelatedLockObj, &lockState, 0);
+ ctRelThreadCtx.exit = 1;
+ KeSetEvent(&ctRelThreadCtx.event, 0, FALSE);
+ NdisReleaseRWLock(ovsCtRelatedLockObj, &lockState);
+
+ KeWaitForSingleObject(ctRelThreadCtx.threadObject, Executive,
+ KernelMode, FALSE, NULL);
+ ObDereferenceObject(ctRelThreadCtx.threadObject);
+
+ if (ovsCtRelatedTable) {
+ OvsCtRelatedFlush();
+ OvsFreeMemoryWithTag(ovsCtRelatedTable, OVS_CT_POOL_TAG);
+ ovsCtRelatedTable = NULL;
+ }
+
+ NdisFreeRWLock(ovsCtRelatedLockObj);
+ ovsCtRelatedLockObj = NULL;
+}