summaryrefslogtreecommitdiff
path: root/datapath-windows/ovsext/Conntrack-ftp.c
diff options
context:
space:
mode:
authorSairam Venugopal <vsairam@vmware.com>2016-12-16 14:28:11 -0800
committerGurucharan Shetty <guru@ovn.org>2016-12-20 09:33:03 -0800
commit7fd123b627f35a7da6aa534276760405d66ccbbe (patch)
treeae8a3f402ee6073e646c64587f363c7deb8b84eb /datapath-windows/ovsext/Conntrack-ftp.c
parent5e422c9ef2c4bf60a7856ae19a3686e9f63f3e62 (diff)
downloadopenvswitch-7fd123b627f35a7da6aa534276760405d66ccbbe.tar.gz
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> Acked-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com> Signed-off-by: Gurucharan Shetty <guru@ovn.org>
Diffstat (limited to 'datapath-windows/ovsext/Conntrack-ftp.c')
-rw-r--r--datapath-windows/ovsext/Conntrack-ftp.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/datapath-windows/ovsext/Conntrack-ftp.c b/datapath-windows/ovsext/Conntrack-ftp.c
new file mode 100644
index 000000000..6830dfafa
--- /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;
+}