diff mbox

[ovs-dev,v3,3/4] datapath-windows: Conntrack - Introduce support for tracking related connections

Message ID 20161216222812.128904-4-vsairam@vmware.com
State Accepted
Headers show

Commit Message

Sairam Venugopal Dec. 16, 2016, 10:28 p.m. UTC
Introduce a new table to track related connections. This table will be
used to track FTP data connections based on the control connection. There
is a new Conntrack-ftp.c to parse incoming FTP messages to determine the
related data ports. It creates a new entry in the related connections
tracker table. If there is a matching FTP data connection, then the state
for that connection is marked as RELATED.

Signed-off-by: Sairam Venugopal <vsairam@vmware.com>
---
 datapath-windows/automake.mk                |   2 +
 datapath-windows/ovsext/Conntrack-ftp.c     | 237 ++++++++++++++++++++++
 datapath-windows/ovsext/Conntrack-related.c | 299 ++++++++++++++++++++++++++++
 datapath-windows/ovsext/ovsext.vcxproj      |   2 +
 4 files changed, 540 insertions(+)
 create mode 100644 datapath-windows/ovsext/Conntrack-ftp.c
 create mode 100644 datapath-windows/ovsext/Conntrack-related.c

Comments

Alin Serdean Dec. 20, 2016, 5:06 p.m. UTC | #1
Acked-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com>


> -----Original Message-----
> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-
> bounces@openvswitch.org] On Behalf Of Sairam Venugopal
> Sent: Saturday, December 17, 2016 12:28 AM
> To: dev@openvswitch.org
> Subject: [ovs-dev] [PATCH v3 3/4] datapath-windows: Conntrack - Introduce
> support for tracking related connections
> 
> Introduce a new table to track related connections. This table will be used to
> track FTP data connections based on the control connection. There is a new
> Conntrack-ftp.c to parse incoming FTP messages to determine the related
> data ports. It creates a new entry in the related connections tracker table. If
> there is a matching FTP data connection, then the state for that connection is
> marked as RELATED.
> 
> Signed-off-by: Sairam Venugopal <vsairam@vmware.com>
> ---
>  datapath-windows/automake.mk                |   2 +
>  datapath-windows/ovsext/Conntrack-ftp.c     | 237
> ++++++++++++++++++++++
>  datapath-windows/ovsext/Conntrack-related.c | 299
> ++++++++++++++++++++++++++++
>  datapath-windows/ovsext/ovsext.vcxproj      |   2 +
>  4 files changed, 540 insertions(+)
>  create mode 100644 datapath-windows/ovsext/Conntrack-ftp.c
>  create mode 100644 datapath-windows/ovsext/Conntrack-related.c
> 
> diff --git a/datapath-windows/automake.mk b/datapath-
> windows/automake.mk index 88aa50a..53983ae 100644
> --- a/datapath-windows/automake.mk
> +++ b/datapath-windows/automake.mk
> @@ -12,8 +12,10 @@ EXTRA_DIST += \
>  	datapath-windows/ovsext/Atomic.h \
>  	datapath-windows/ovsext/BufferMgmt.c \
>  	datapath-windows/ovsext/BufferMgmt.h \
> +	datapath-windows/ovsext/Conntrack-ftp.c \
>  	datapath-windows/ovsext/Conntrack-icmp.c \
>  	datapath-windows/ovsext/Conntrack-other.c \
> +	datapath-windows/ovsext/Conntrack-related.c \
>  	datapath-windows/ovsext/Conntrack-tcp.c \
>  	datapath-windows/ovsext/Conntrack.c \
>  	datapath-windows/ovsext/Conntrack.h \
> diff --git a/datapath-windows/ovsext/Conntrack-ftp.c b/datapath-
> windows/ovsext/Conntrack-ftp.c
> new file mode 100644
> index 0000000..6830dfa
> --- /dev/null
> +++ b/datapath-windows/ovsext/Conntrack-ftp.c
> @@ -0,0 +1,237 @@
> +/*
> + * 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 "PacketParser.h"
> +
> +/* Eg: 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)*/ #define
> +FTP_PASV_RSP_PREFIX "227"
> +
> +typedef enum FTP_TYPE {
> +    FTP_TYPE_PASV = 1,
> +    FTP_TYPE_ACTIVE
> +} FTP_TYPE;
> +
> +static __inline UINT32
> +OvsStrncmp(const char *s1, const char *s2, size_t n) {
> +    if (!s1 || !s2) {
> +        return 0;
> +    }
> +
> +    const char *s2end = s2 + n;
> +    while (s2 < s2end && *s2 != '\0' && toupper(*s1) == toupper(*s2)) {
> +        s1++, s2++;
> +    }
> +
> +    if (s2end == s2) {
> +        return 0;
> +    }
> +
> +    return (UINT32)(toupper(*s1) - toupper(*s2)); }
> +
> +static __inline VOID
> +OvsStrlcpy(char *dest, const char *src, size_t size) {
> +    /* XXX Replace ret with strlen(src) instead. */
> +    size_t ret = size;
> +    if (size) {
> +       size_t len = (ret >= size) ? size - 1 : ret;
> +       memcpy(dest, src, len);
> +       dest[len] = '\0';
> +   }
> +}
> +
> +/*
> +
> +*----------------------------------------------------------------------
> +-----
> + * OvsCtExtractNumbers
> + * Returns an array of numbers after parsing the string.
> + *    Eg: PASV: 192,168,0,1,5,6 -> {192,168,0,1,5,6}
> + *        EPRT: 192.168.0.1 -> {192,168,0,1}
> + *
> +
> +*----------------------------------------------------------------------
> +-----
> + */
> +static __inline NDIS_STATUS
> +OvsCtExtractNumbers(char *buf,
> +                    UINT32 bufLen,
> +                    UINT32 arr[],
> +                    UINT32 arrLen,
> +                    char delimiter)
> +{
> +    if (!buf) {
> +        return  NDIS_STATUS_INVALID_PACKET;
> +    }
> +
> +    UINT32 i = 0;
> +
> +    while (*buf != '\0') {
> +        if (i >= bufLen || i >= arrLen) {
> +            /* Non-standard FTP command */
> +            return NDIS_STATUS_INVALID_PARAMETER;
> +        }
> +
> +        /* Parse the number */
> +        if (*buf >= '0' && *buf <= '9') {
> +            arr[i] = arr[i] * 10 + *buf - '0';
> +        } else if (*buf == delimiter) {
> +            i++;
> +        } else {
> +            /* End of FTP response is either ) or \r\n */
> +            if (*buf == ')' || *buf == '\r' || *buf == '\n') {
> +                return NDIS_STATUS_SUCCESS;
> +            }
> +            /* Could be non-numerals or space */
> +        }
> +        buf++;
> +    }
> +
> +    /* Parsing ended without the correct format */
> +    return NDIS_STATUS_INVALID_PARAMETER; }
> +
> +/*
> +
> +*----------------------------------------------------------------------
> +------
> + * OvsCtHandleFtp
> + *     Extract the FTP control data from the packet and created a related
> + *     entry if it's a valid connection. This method doesn't support extended
> + *     FTP yet. Supports PORT and PASV commands.
> + *     Eg:
> + *     'PORT 192,168,137,103,192,22\r\n' -> '192.168.137.103' and 49174
> + *     '227 Entering Passive Mode (192,168,137,104,194,14)\r\n' gets
> extracted
> + *      to '192.168.137.104' and 49678
> +
> +*----------------------------------------------------------------------
> +------
> + */
> +NDIS_STATUS
> +OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
> +               OvsFlowKey *key,
> +               OVS_PACKET_HDR_INFO *layers,
> +               UINT64 currentTime,
> +               POVS_CT_ENTRY entry,
> +               BOOLEAN request)
> +{
> +    NDIS_STATUS status;
> +    FTP_TYPE ftpType = 0;
> +    const char *buf;
> +    char temp[256] = { 0 };
> +    char ftpMsg[256] = { 0 };
> +
> +    TCPHdr tcpStorage;
> +    const TCPHdr *tcp;
> +    tcp = OvsGetTcp(curNbl, layers->l4Offset, &tcpStorage);
> +    if (!tcp) {
> +        return NDIS_STATUS_INVALID_PACKET;
> +    }
> +
> +    UINT32 len = OvsGetTcpPayloadLength(curNbl);
> +    if (len > sizeof(temp)) {
> +        /* We only care up to 256 */
> +        len = sizeof(temp);
> +    }
> +
> +    buf = OvsGetPacketBytes(curNbl, len,
> +                            layers->l4Offset + TCP_HDR_LEN(tcp),
> +                            temp);
> +    if (buf == NULL) {
> +        return NDIS_STATUS_INVALID_PACKET;
> +    }
> +
> +    OvsStrlcpy((char *)ftpMsg, (char *)buf, min(len, sizeof(ftpMsg)));
> +    char *req = NULL;
> +
> +    if (request) {
> +        if ((len >= 5) && (OvsStrncmp("PORT", ftpMsg, 4) == 0)) {
> +            ftpType = FTP_TYPE_ACTIVE;
> +            req = ftpMsg + 4;
> +        }
> +    } else {
> +        if ((len >= 4) && (OvsStrncmp(FTP_PASV_RSP_PREFIX, ftpMsg, 3) == 0))
> {
> +            ftpType = FTP_TYPE_PASV;
> +            /* There are various formats for PASV command. We try to support
> +             * some of them. This has been addressed by RFC 2428 - EPSV.
> +             * Eg:
> +             *    227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
> +             *    227 Entering Passive Mode (h1,h2,h3,h4,p1,p2
> +             *    227 Entering Passive Mode. h1,h2,h3,h4,p1,p2
> +             *    227 =h1,h2,h3,h4,p1,p2
> +             */
> +            char *paren;
> +            paren = strchr(ftpMsg, '(');
> +            if (paren) {
> +                req = paren + 1;
> +            } else {
> +                /* PASV command without ( */
> +                req = ftpMsg + 3;
> +            }
> +        }
> +    }
> +
> +    if (req == NULL) {
> +        /* Not a PORT/PASV control packet */
> +        return NDIS_STATUS_SUCCESS;
> +    }
> +
> +    UINT32 arr[6] = {0};
> +    status = OvsCtExtractNumbers(req, len, arr, 6, ',');
> +
> +    if (status != NDIS_STATUS_SUCCESS) {
> +        return status;
> +    }
> +
> +    UINT32 ip = ntohl((arr[0] << 24) | (arr[1] << 16) |
> +                      (arr[2] << 8) | arr[3]);
> +    UINT16 port = ntohs(((arr[4] << 8) | arr[5]));
> +
> +    switch (ftpType) {
> +    case FTP_TYPE_PASV:
> +        /* Ensure that the command states Server's IP address */
> +        ASSERT(ip == key->ipKey.nwSrc);
> +
> +        OvsCtRelatedEntryCreate(key->ipKey.nwProto,
> +                                key->l2.dlType,
> +                                /* Server's IP */
> +                                ip,
> +                                /* Use intended client's IP */
> +                                key->ipKey.nwDst,
> +                                /* Dynamic port opened on server */
> +                                port,
> +                                /* We don't know the client port */
> +                                0,
> +                                currentTime,
> +                                entry);
> +        break;
> +    case FTP_TYPE_ACTIVE:
> +        OvsCtRelatedEntryCreate(key->ipKey.nwProto,
> +                                key->l2.dlType,
> +                                /* Server's default IP address */
> +                                key->ipKey.nwDst,
> +                                /* Client's IP address */
> +                                ip,
> +                                /* FTP Data Port is 20 */
> +                                ntohs(IPPORT_FTP_DATA),
> +                                /* Port opened up on Client */
> +                                port,
> +                                currentTime,
> +                                entry);
> +        break;
> +    default:
> +        OVS_LOG_ERROR("invalid ftp type:%d", ftpType);
> +        status = NDIS_STATUS_INVALID_PARAMETER;
> +        break;
> +    }
> +
> +    return status;
> +}
> diff --git a/datapath-windows/ovsext/Conntrack-related.c b/datapath-
> windows/ovsext/Conntrack-related.c
> new file mode 100644
> index 0000000..2d95bc2
> --- /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;
> +}
> diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-
> windows/ovsext/ovsext.vcxproj
> index 77530fd..e311a09 100644
> --- a/datapath-windows/ovsext/ovsext.vcxproj
> +++ b/datapath-windows/ovsext/ovsext.vcxproj
> @@ -178,6 +178,8 @@
>    <ItemGroup>
>      <ClCompile Include="Actions.c" />
>      <ClCompile Include="BufferMgmt.c" />
> +    <ClCompile Include="Conntrack-related.c" />
> +    <ClCompile Include="Conntrack-ftp.c" />
>      <ClCompile Include="Conntrack-icmp.c" />
>      <ClCompile Include="Conntrack-other.c" />
>      <ClCompile Include="Conntrack-tcp.c" />
> --
> 2.9.0.windows.1
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
diff mbox

Patch

diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk
index 88aa50a..53983ae 100644
--- a/datapath-windows/automake.mk
+++ b/datapath-windows/automake.mk
@@ -12,8 +12,10 @@  EXTRA_DIST += \
 	datapath-windows/ovsext/Atomic.h \
 	datapath-windows/ovsext/BufferMgmt.c \
 	datapath-windows/ovsext/BufferMgmt.h \
+	datapath-windows/ovsext/Conntrack-ftp.c \
 	datapath-windows/ovsext/Conntrack-icmp.c \
 	datapath-windows/ovsext/Conntrack-other.c \
+	datapath-windows/ovsext/Conntrack-related.c \
 	datapath-windows/ovsext/Conntrack-tcp.c \
 	datapath-windows/ovsext/Conntrack.c \
 	datapath-windows/ovsext/Conntrack.h \
diff --git a/datapath-windows/ovsext/Conntrack-ftp.c b/datapath-windows/ovsext/Conntrack-ftp.c
new file mode 100644
index 0000000..6830dfa
--- /dev/null
+++ b/datapath-windows/ovsext/Conntrack-ftp.c
@@ -0,0 +1,237 @@ 
+/*
+ * 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 "PacketParser.h"
+
+/* Eg: 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)*/
+#define FTP_PASV_RSP_PREFIX "227"
+
+typedef enum FTP_TYPE {
+    FTP_TYPE_PASV = 1,
+    FTP_TYPE_ACTIVE
+} FTP_TYPE;
+
+static __inline UINT32
+OvsStrncmp(const char *s1, const char *s2, size_t n)
+{
+    if (!s1 || !s2) {
+        return 0;
+    }
+
+    const char *s2end = s2 + n;
+    while (s2 < s2end && *s2 != '\0' && toupper(*s1) == toupper(*s2)) {
+        s1++, s2++;
+    }
+
+    if (s2end == s2) {
+        return 0;
+    }
+
+    return (UINT32)(toupper(*s1) - toupper(*s2));
+}
+
+static __inline VOID
+OvsStrlcpy(char *dest, const char *src, size_t size)
+{
+    /* XXX Replace ret with strlen(src) instead. */
+    size_t ret = size;
+    if (size) {
+       size_t len = (ret >= size) ? size - 1 : ret;
+       memcpy(dest, src, len);
+       dest[len] = '\0';
+   }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * OvsCtExtractNumbers
+ * Returns an array of numbers after parsing the string.
+ *    Eg: PASV: 192,168,0,1,5,6 -> {192,168,0,1,5,6}
+ *        EPRT: 192.168.0.1 -> {192,168,0,1}
+ *
+ *---------------------------------------------------------------------------
+ */
+static __inline NDIS_STATUS
+OvsCtExtractNumbers(char *buf,
+                    UINT32 bufLen,
+                    UINT32 arr[],
+                    UINT32 arrLen,
+                    char delimiter)
+{
+    if (!buf) {
+        return  NDIS_STATUS_INVALID_PACKET;
+    }
+
+    UINT32 i = 0;
+
+    while (*buf != '\0') {
+        if (i >= bufLen || i >= arrLen) {
+            /* Non-standard FTP command */
+            return NDIS_STATUS_INVALID_PARAMETER;
+        }
+
+        /* Parse the number */
+        if (*buf >= '0' && *buf <= '9') {
+            arr[i] = arr[i] * 10 + *buf - '0';
+        } else if (*buf == delimiter) {
+            i++;
+        } else {
+            /* End of FTP response is either ) or \r\n */
+            if (*buf == ')' || *buf == '\r' || *buf == '\n') {
+                return NDIS_STATUS_SUCCESS;
+            }
+            /* Could be non-numerals or space */
+        }
+        buf++;
+    }
+
+    /* Parsing ended without the correct format */
+    return NDIS_STATUS_INVALID_PARAMETER;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * OvsCtHandleFtp
+ *     Extract the FTP control data from the packet and created a related
+ *     entry if it's a valid connection. This method doesn't support extended
+ *     FTP yet. Supports PORT and PASV commands.
+ *     Eg:
+ *     'PORT 192,168,137,103,192,22\r\n' -> '192.168.137.103' and 49174
+ *     '227 Entering Passive Mode (192,168,137,104,194,14)\r\n' gets extracted
+ *      to '192.168.137.104' and 49678
+ *----------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
+               OvsFlowKey *key,
+               OVS_PACKET_HDR_INFO *layers,
+               UINT64 currentTime,
+               POVS_CT_ENTRY entry,
+               BOOLEAN request)
+{
+    NDIS_STATUS status;
+    FTP_TYPE ftpType = 0;
+    const char *buf;
+    char temp[256] = { 0 };
+    char ftpMsg[256] = { 0 };
+
+    TCPHdr tcpStorage;
+    const TCPHdr *tcp;
+    tcp = OvsGetTcp(curNbl, layers->l4Offset, &tcpStorage);
+    if (!tcp) {
+        return NDIS_STATUS_INVALID_PACKET;
+    }
+
+    UINT32 len = OvsGetTcpPayloadLength(curNbl);
+    if (len > sizeof(temp)) {
+        /* We only care up to 256 */
+        len = sizeof(temp);
+    }
+
+    buf = OvsGetPacketBytes(curNbl, len,
+                            layers->l4Offset + TCP_HDR_LEN(tcp),
+                            temp);
+    if (buf == NULL) {
+        return NDIS_STATUS_INVALID_PACKET;
+    }
+
+    OvsStrlcpy((char *)ftpMsg, (char *)buf, min(len, sizeof(ftpMsg)));
+    char *req = NULL;
+
+    if (request) {
+        if ((len >= 5) && (OvsStrncmp("PORT", ftpMsg, 4) == 0)) {
+            ftpType = FTP_TYPE_ACTIVE;
+            req = ftpMsg + 4;
+        }
+    } else {
+        if ((len >= 4) && (OvsStrncmp(FTP_PASV_RSP_PREFIX, ftpMsg, 3) == 0)) {
+            ftpType = FTP_TYPE_PASV;
+            /* There are various formats for PASV command. We try to support
+             * some of them. This has been addressed by RFC 2428 - EPSV.
+             * Eg:
+             *    227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
+             *    227 Entering Passive Mode (h1,h2,h3,h4,p1,p2
+             *    227 Entering Passive Mode. h1,h2,h3,h4,p1,p2
+             *    227 =h1,h2,h3,h4,p1,p2
+             */
+            char *paren;
+            paren = strchr(ftpMsg, '(');
+            if (paren) {
+                req = paren + 1;
+            } else {
+                /* PASV command without ( */
+                req = ftpMsg + 3;
+            }
+        }
+    }
+
+    if (req == NULL) {
+        /* Not a PORT/PASV control packet */
+        return NDIS_STATUS_SUCCESS;
+    }
+
+    UINT32 arr[6] = {0};
+    status = OvsCtExtractNumbers(req, len, arr, 6, ',');
+
+    if (status != NDIS_STATUS_SUCCESS) {
+        return status;
+    }
+
+    UINT32 ip = ntohl((arr[0] << 24) | (arr[1] << 16) |
+                      (arr[2] << 8) | arr[3]);
+    UINT16 port = ntohs(((arr[4] << 8) | arr[5]));
+
+    switch (ftpType) {
+    case FTP_TYPE_PASV:
+        /* Ensure that the command states Server's IP address */
+        ASSERT(ip == key->ipKey.nwSrc);
+
+        OvsCtRelatedEntryCreate(key->ipKey.nwProto,
+                                key->l2.dlType,
+                                /* Server's IP */
+                                ip,
+                                /* Use intended client's IP */
+                                key->ipKey.nwDst,
+                                /* Dynamic port opened on server */
+                                port,
+                                /* We don't know the client port */
+                                0,
+                                currentTime,
+                                entry);
+        break;
+    case FTP_TYPE_ACTIVE:
+        OvsCtRelatedEntryCreate(key->ipKey.nwProto,
+                                key->l2.dlType,
+                                /* Server's default IP address */
+                                key->ipKey.nwDst,
+                                /* Client's IP address */
+                                ip,
+                                /* FTP Data Port is 20 */
+                                ntohs(IPPORT_FTP_DATA),
+                                /* Port opened up on Client */
+                                port,
+                                currentTime,
+                                entry);
+        break;
+    default:
+        OVS_LOG_ERROR("invalid ftp type:%d", ftpType);
+        status = NDIS_STATUS_INVALID_PARAMETER;
+        break;
+    }
+
+    return status;
+}
diff --git a/datapath-windows/ovsext/Conntrack-related.c b/datapath-windows/ovsext/Conntrack-related.c
new file mode 100644
index 0000000..2d95bc2
--- /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;
+}
diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj
index 77530fd..e311a09 100644
--- a/datapath-windows/ovsext/ovsext.vcxproj
+++ b/datapath-windows/ovsext/ovsext.vcxproj
@@ -178,6 +178,8 @@ 
   <ItemGroup>
     <ClCompile Include="Actions.c" />
     <ClCompile Include="BufferMgmt.c" />
+    <ClCompile Include="Conntrack-related.c" />
+    <ClCompile Include="Conntrack-ftp.c" />
     <ClCompile Include="Conntrack-icmp.c" />
     <ClCompile Include="Conntrack-other.c" />
     <ClCompile Include="Conntrack-tcp.c" />