[ovs-dev] [PATCH] datapath-windows: Enable support for tracking ICMP code and type

Sairam Venugopal vsairam at vmware.com
Wed Nov 2 23:59:54 UTC 2016


Add support for tracking ICMP code and Type in the Hyper-V Conntrack
module. This code is similar to the userspace connection tracker.

Signed-off-by: Sairam Venugopal <vsairam at vmware.com>
---
 datapath-windows/ovsext/Conntrack-icmp.c  | 80 +++++++++++++++++++++++++++++++
 datapath-windows/ovsext/Conntrack-other.c | 15 ++----
 datapath-windows/ovsext/Conntrack-tcp.c   | 25 +++++-----
 datapath-windows/ovsext/Conntrack.c       | 72 +++++++++++++++++++++++++---
 datapath-windows/ovsext/Conntrack.h       | 24 +++++++++-
 datapath-windows/ovsext/ovsext.vcxproj    |  1 +
 6 files changed, 185 insertions(+), 32 deletions(-)
 create mode 100644 datapath-windows/ovsext/Conntrack-icmp.c

diff --git a/datapath-windows/ovsext/Conntrack-icmp.c b/datapath-windows/ovsext/Conntrack-icmp.c
new file mode 100644
index 0000000..ffcd2df
--- /dev/null
+++ b/datapath-windows/ovsext/Conntrack-icmp.c
@@ -0,0 +1,80 @@
+/*
+ * 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 "NetProto.h"
+#include "Conntrack.h"
+#include <stddef.h>
+
+enum icmp_state {
+    ICMPS_FIRST,
+    ICMPS_REPLY,
+};
+
+struct conn_icmp {
+    struct OVS_CT_ENTRY up;
+    enum icmp_state state;
+};
+
+static const enum ct_timeout icmp_timeouts[] = {
+    [ICMPS_FIRST] = 60 * CT_INTERVAL_SEC,
+    [ICMPS_REPLY] = 30 * CT_INTERVAL_SEC,
+};
+
+static __inline struct conn_icmp *
+OvsCastConntrackEntryToIcmpEntry(OVS_CT_ENTRY* conn)
+{
+    return CONTAINER_OF(conn, struct conn_icmp, up);
+}
+
+enum CT_UPDATE_RES
+OvsConntrackUpdateIcmpEntry(OVS_CT_ENTRY* conn_,
+                            BOOLEAN reply,
+                            UINT64 now)
+{
+    struct conn_icmp *conn = OvsCastConntrackEntryToIcmpEntry(conn_);
+
+    if (reply && conn->state != ICMPS_REPLY) {
+        conn->state = ICMPS_REPLY;
+    }
+
+    OvsConntrackUpdateExpiration(&conn->up, now,
+                                 icmp_timeouts[conn->state]);
+
+    return CT_UPDATE_VALID;
+}
+
+BOOLEAN
+OvsConntrackValidateIcmpPacket(const ICMPHdr *icmp)
+{
+    return icmp->type == ICMP4_ECHO_REQUEST
+           || icmp->type == ICMP4_INFO_REQUEST
+           || icmp->type == ICMP4_TIMESTAMP_REQUEST;
+}
+
+OVS_CT_ENTRY *
+OvsConntrackCreateIcmpEntry(UINT64 now)
+{
+    struct conn_icmp *conn;
+
+    conn = OvsAllocateMemoryWithTag(sizeof(struct conn_icmp),
+                                    OVS_CT_POOL_TAG);
+    conn->state = ICMPS_FIRST;
+
+    OvsConntrackUpdateExpiration(&conn->up, now,
+                                 icmp_timeouts[conn->state]);
+
+    return &conn->up;
+}
diff --git a/datapath-windows/ovsext/Conntrack-other.c b/datapath-windows/ovsext/Conntrack-other.c
index b853020..6c68ba8 100644
--- a/datapath-windows/ovsext/Conntrack-other.c
+++ b/datapath-windows/ovsext/Conntrack-other.c
@@ -41,14 +41,7 @@ OvsCastConntrackEntryToOtherEntry(OVS_CT_ENTRY *conn)
     return CONTAINER_OF(conn, struct conn_other, up);
 }
 
-static __inline VOID
-OvsConntrackUpdateExpiration(struct conn_other *conn, long long now)
-{
-    ASSERT(conn);
-    conn->up.expiration = now + other_timeouts[conn->state];
-}
-
-enum ct_update_res
+enum CT_UPDATE_RES
 OvsConntrackUpdateOtherEntry(OVS_CT_ENTRY *conn_,
                              BOOLEAN reply,
                              UINT64 now)
@@ -62,7 +55,8 @@ OvsConntrackUpdateOtherEntry(OVS_CT_ENTRY *conn_,
         conn->state = OTHERS_MULTIPLE;
     }
 
-    OvsConntrackUpdateExpiration(conn, now);
+    OvsConntrackUpdateExpiration(&conn->up, now,
+                                 other_timeouts[conn->state]);
 
     return CT_UPDATE_VALID;
 }
@@ -78,6 +72,7 @@ OvsConntrackCreateOtherEntry(UINT64 now)
     }
     conn->up = (OVS_CT_ENTRY) {0};
     conn->state = OTHERS_FIRST;
-    OvsConntrackUpdateExpiration(conn, now);
+    OvsConntrackUpdateExpiration(&conn->up, now,
+                                 other_timeouts[conn->state]);
     return &conn->up;
 }
diff --git a/datapath-windows/ovsext/Conntrack-tcp.c b/datapath-windows/ovsext/Conntrack-tcp.c
index 6adf490..c7fcfa8 100644
--- a/datapath-windows/ovsext/Conntrack-tcp.c
+++ b/datapath-windows/ovsext/Conntrack-tcp.c
@@ -199,14 +199,6 @@ OvsGetTcpPayloadLength(PNET_BUFFER_LIST nbl)
                         - (sizeof * tcp);
 }
 
-static __inline void
-OvsConntrackUpdateExpiration(struct conn_tcp *conn,
-                             long long now,
-                             long long interval)
-{
-    conn->up.expiration = now + interval;
-}
-
 static __inline struct conn_tcp*
 OvsCastConntrackEntryToTcpEntry(OVS_CT_ENTRY* conn)
 {
@@ -383,18 +375,23 @@ OvsConntrackUpdateTcpEntry(OVS_CT_ENTRY* conn_,
 
         if (src->state >= CT_DPIF_TCPS_FIN_WAIT_2
             && dst->state >= CT_DPIF_TCPS_FIN_WAIT_2) {
-            OvsConntrackUpdateExpiration(conn, now, 30 * CT_INTERVAL_SEC);
+            OvsConntrackUpdateExpiration(&conn->up, now,
+                                         30 * CT_INTERVAL_SEC);
         } else if (src->state >= CT_DPIF_TCPS_CLOSING
                    && dst->state >= CT_DPIF_TCPS_CLOSING) {
-            OvsConntrackUpdateExpiration(conn, now, 45 * CT_INTERVAL_SEC);
+            OvsConntrackUpdateExpiration(&conn->up, now,
+                                         45 * CT_INTERVAL_SEC);
         } else if (src->state < CT_DPIF_TCPS_ESTABLISHED
                    || dst->state < CT_DPIF_TCPS_ESTABLISHED) {
-            OvsConntrackUpdateExpiration(conn, now, 30 * CT_INTERVAL_SEC);
+            OvsConntrackUpdateExpiration(&conn->up, now,
+                                         30 * CT_INTERVAL_SEC);
         } else if (src->state >= CT_DPIF_TCPS_CLOSING
                    || dst->state >= CT_DPIF_TCPS_CLOSING) {
-            OvsConntrackUpdateExpiration(conn, now, 15 * 60 * CT_INTERVAL_SEC);
+            OvsConntrackUpdateExpiration(&conn->up, now,
+                                         15 * 60 * CT_INTERVAL_SEC);
         } else {
-            OvsConntrackUpdateExpiration(conn, now, 24 * 60 * 60 * CT_INTERVAL_SEC);
+            OvsConntrackUpdateExpiration(&conn->up, now,
+                                         24 * 60 * 60 * CT_INTERVAL_SEC);
         }
     } else if ((dst->state < CT_DPIF_TCPS_SYN_SENT
                 || dst->state >= CT_DPIF_TCPS_FIN_WAIT_2
@@ -518,7 +515,7 @@ OvsConntrackCreateTcpEntry(const TCPHdr *tcp,
     src->state = CT_DPIF_TCPS_SYN_SENT;
     dst->state = CT_DPIF_TCPS_CLOSED;
 
-    OvsConntrackUpdateExpiration(newconn, now, CT_ENTRY_TIMEOUT);
+    OvsConntrackUpdateExpiration(&newconn->up, now, CT_ENTRY_TIMEOUT);
 
     return &newconn->up;
 }
diff --git a/datapath-windows/ovsext/Conntrack.c b/datapath-windows/ovsext/Conntrack.c
index 74fb38c..ce18f29 100644
--- a/datapath-windows/ovsext/Conntrack.c
+++ b/datapath-windows/ovsext/Conntrack.c
@@ -211,7 +211,28 @@ OvsCtEntryCreate(PNET_BUFFER_LIST curNbl,
             return entry;
         }
         case IPPROTO_ICMP:
+        {
+            ICMPHdr storage;
+            const ICMPHdr *icmp;
+            icmp = OvsGetIcmp(curNbl, l4Offset, &storage);
+            if (!OvsConntrackValidateIcmpPacket(icmp)) {
+                goto invalid;
+            }
+
+            state |= OVS_CS_F_NEW;
+            if (commit) {
+                entry = OvsConntrackCreateIcmpEntry(currentTime);
+                if (!entry) {
+                    return NULL;
+                }
+                OvsCtAddEntry(entry, ctx, currentTime);
+            }
+
+            OvsCtUpdateFlowKey(key, state, ctx->key.zone, 0, NULL);
+            return entry;
+        }
         case IPPROTO_UDP:
+        {
             state |= OVS_CS_F_NEW;
             if (commit) {
                 entry = OvsConntrackCreateOtherEntry(currentTime);
@@ -223,6 +244,7 @@ OvsCtEntryCreate(PNET_BUFFER_LIST curNbl,
 
             OvsCtUpdateFlowKey(key, state, ctx->key.zone, 0, NULL);
             return entry;
+        }
         default:
             goto invalid;
     }
@@ -254,6 +276,7 @@ OvsCtUpdateEntry(OVS_CT_ENTRY* entry,
             return OvsConntrackUpdateTcpEntry(entry, tcp, nbl, reply, now);
         }
         case IPPROTO_ICMP:
+            return OvsConntrackUpdateIcmpEntry(entry, reply, now);
         case IPPROTO_UDP:
             return OvsConntrackUpdateOtherEntry(entry, reply, now);
         default:
@@ -338,8 +361,7 @@ OvsCtLookup(OvsConntrackKeyLookupCtx *ctx)
     BOOLEAN reply = FALSE;
     POVS_CT_ENTRY found = NULL;
 
-    if (!ctTotalEntries)
-    {
+    if (!ctTotalEntries) {
         return found;
     }
 
@@ -384,6 +406,27 @@ OvsExtractLookupCtxHash(OvsConntrackKeyLookupCtx *ctx)
                          hash);
 }
 
+static UINT8
+OvsReverseIcmpType(UINT8 type)
+{
+    switch (type) {
+    case ICMP4_ECHO_REQUEST:
+        return ICMP4_ECHO_REPLY;
+    case ICMP4_ECHO_REPLY:
+        return ICMP4_ECHO_REQUEST;
+    case ICMP4_TIMESTAMP_REQUEST:
+        return ICMP4_TIMESTAMP_REPLY;
+    case ICMP4_TIMESTAMP_REPLY:
+        return ICMP4_TIMESTAMP_REQUEST;
+    case ICMP4_INFO_REQUEST:
+        return ICMP4_INFO_REPLY;
+    case ICMP4_INFO_REPLY:
+        return ICMP4_INFO_REQUEST;
+    default:
+        return 0;
+    }
+}
+
 static __inline NDIS_STATUS
 OvsCtSetupLookupCtx(OvsFlowKey *flowKey,
                     UINT16 zone,
@@ -408,11 +451,25 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey,
             const ICMPHdr *icmp;
             icmp = OvsGetIcmp(curNbl, l4Offset, &icmpStorage);
             ASSERT(icmp);
-            ctx->key.src.port = ctx->key.dst.port = icmp->fields.echo.id;
 
             /* Related bit is set when ICMP has an error */
             /* XXX parse out the appropriate src and dst from inner pkt */
             switch (icmp->type) {
+               case ICMP4_ECHO_REQUEST:
+               case ICMP4_ECHO_REPLY:
+               case ICMP4_TIMESTAMP_REQUEST:
+               case ICMP4_TIMESTAMP_REPLY:
+               case ICMP4_INFO_REQUEST:
+               case ICMP4_INFO_REPLY:
+                   if (icmp->code != 0) {
+                       return NDIS_STATUS_INVALID_PACKET;
+                   }
+                   /* Separate ICMP connection: identified using id */
+                   ctx->key.dst.icmp_id = icmp->fields.echo.id;
+                   ctx->key.src.icmp_id = icmp->fields.echo.id;
+                   ctx->key.src.icmp_type = icmp->type;
+                   ctx->key.dst.icmp_type = OvsReverseIcmpType(icmp->type);
+                   break;
                case ICMP4_DEST_UNREACH:
                case ICMP4_TIME_EXCEEDED:
                case ICMP4_PARAM_PROB:
@@ -830,15 +887,18 @@ MapProtoTupleToNl(PNL_BUFFER nlBuf, OVS_CT_KEY *key)
         || key->dl_type == ntohs(ETH_TYPE_IPV6)) {
         /* ICMP and ICMPv6 Type, Code and ID are currently not tracked */
         if (key->nw_proto == IPPROTO_ICMP) {
-            if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_ICMP_ID, 0)) {
+            if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_ICMP_ID,
+                                 htons(key->src.icmp_id))) {
                 status = NDIS_STATUS_FAILURE;
                 goto done;
             }
-            if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMP_TYPE, 0)) {
+            if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMP_TYPE,
+                                key->src.icmp_type)) {
                 status = NDIS_STATUS_FAILURE;
                 goto done;
             }
-            if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMP_CODE, 0)) {
+            if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMP_CODE,
+                                key->src.icmp_code)) {
                 status = NDIS_STATUS_FAILURE;
                 goto done;
             }
diff --git a/datapath-windows/ovsext/Conntrack.h b/datapath-windows/ovsext/Conntrack.h
index 4995ff4..270e2dd 100644
--- a/datapath-windows/ovsext/Conntrack.h
+++ b/datapath-windows/ovsext/Conntrack.h
@@ -31,7 +31,14 @@ struct ct_addr {
 
 struct ct_endpoint {
     struct ct_addr addr;
-    ovs_be16 port;
+    union {
+        ovs_be16 port;
+        struct {
+            ovs_be16 icmp_id;
+            uint8_t icmp_type;
+            uint8_t icmp_code;
+        };
+    };
     UINT16 pad;
 };
 
@@ -94,6 +101,14 @@ typedef struct OvsConntrackKeyLookupCtx {
         ((STRUCT *) (void *) ((char *) (POINTER) - \
          offsetof (STRUCT, MEMBER)))
 
+static __inline void
+OvsConntrackUpdateExpiration(OVS_CT_ENTRY *ctEntry,
+                             long long now,
+                             long long interval)
+{
+    ctEntry->expiration = now + interval;
+}
+
 VOID OvsCleanupConntrack(VOID);
 NTSTATUS OvsInitConntrack(POVS_SWITCH_CONTEXT context);
 
@@ -102,20 +117,25 @@ NDIS_STATUS OvsExecuteConntrackAction(PNET_BUFFER_LIST curNbl,
                                       OvsFlowKey *key,
                                       const PNL_ATTR a);
 BOOLEAN OvsConntrackValidateTcpPacket(const TCPHdr *tcp);
+BOOLEAN OvsConntrackValidateIcmpPacket(const ICMPHdr *icmp);
 OVS_CT_ENTRY * OvsConntrackCreateTcpEntry(const TCPHdr *tcp,
                                           PNET_BUFFER_LIST nbl,
                                           UINT64 now);
 NDIS_STATUS OvsCtMapTcpProtoInfoToNl(PNL_BUFFER nlBuf,
                                      OVS_CT_ENTRY *conn_);
 OVS_CT_ENTRY * OvsConntrackCreateOtherEntry(UINT64 now);
+OVS_CT_ENTRY * OvsConntrackCreateIcmpEntry(UINT64 now);
 enum CT_UPDATE_RES OvsConntrackUpdateTcpEntry(OVS_CT_ENTRY* conn_,
                                               const TCPHdr *tcp,
                                               PNET_BUFFER_LIST nbl,
                                               BOOLEAN reply,
                                               UINT64 now);
-enum ct_update_res OvsConntrackUpdateOtherEntry(OVS_CT_ENTRY *conn_,
+enum CT_UPDATE_RES OvsConntrackUpdateOtherEntry(OVS_CT_ENTRY *conn_,
                                                 BOOLEAN reply,
                                                 UINT64 now);
+enum CT_UPDATE_RES OvsConntrackUpdateIcmpEntry(OVS_CT_ENTRY* conn_,
+                                               BOOLEAN reply,
+                                               UINT64 now);
 NTSTATUS
 OvsCreateNlMsgFromCtEntry(POVS_CT_ENTRY entry,
                           PVOID outBuffer,
diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj
index a00deb0..77530fd 100644
--- a/datapath-windows/ovsext/ovsext.vcxproj
+++ b/datapath-windows/ovsext/ovsext.vcxproj
@@ -178,6 +178,7 @@
   <ItemGroup>
     <ClCompile Include="Actions.c" />
     <ClCompile Include="BufferMgmt.c" />
+    <ClCompile Include="Conntrack-icmp.c" />
     <ClCompile Include="Conntrack-other.c" />
     <ClCompile Include="Conntrack-tcp.c" />
     <ClCompile Include="Conntrack.c" />
-- 
2.9.0.windows.1




More information about the dev mailing list