Enable support for UDP and ICMP in the connection tracking module on Hyper-V.
Signed-off-by: Sairam Venugopal <vsai...@vmware.com> --- datapath-windows/automake.mk | 1 + datapath-windows/ovsext/Conntrack-other.c | 78 +++++++++++++ datapath-windows/ovsext/Conntrack.c | 180 ++++++++++++++++++++++-------- datapath-windows/ovsext/Conntrack.h | 4 + datapath-windows/ovsext/ovsext.vcxproj | 1 + 5 files changed, 219 insertions(+), 45 deletions(-) create mode 100644 datapath-windows/ovsext/Conntrack-other.c diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk index c9af806..668cf2c 100644 --- a/datapath-windows/automake.mk +++ b/datapath-windows/automake.mk @@ -13,6 +13,7 @@ EXTRA_DIST += \ datapath-windows/ovsext/Atomic.h \ datapath-windows/ovsext/BufferMgmt.c \ datapath-windows/ovsext/BufferMgmt.h \ + datapath-windows/ovsext/Conntrack-other.c \ datapath-windows/ovsext/Conntrack-tcp.c \ datapath-windows/ovsext/Conntrack.c \ datapath-windows/ovsext/Conntrack.h \ diff --git a/datapath-windows/ovsext/Conntrack-other.c b/datapath-windows/ovsext/Conntrack-other.c new file mode 100644 index 0000000..bcd38d5 --- /dev/null +++ b/datapath-windows/ovsext/Conntrack-other.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, 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 <stddef.h> + +enum other_state { + OTHERS_FIRST, + OTHERS_MULTIPLE, + OTHERS_BIDIR, +}; + +struct conn_other { + struct OVS_CT_ENTRY up; + enum other_state state; +}; + +static const long long other_timeouts[] = { + [OTHERS_FIRST] = 60 * 10000000, + [OTHERS_MULTIPLE] = 60 * 10000000, + [OTHERS_BIDIR] = 30 * 10000000, +}; + +static __inline struct conn_other* +OvsCastConntrackEntryToOtherEntry(OVS_CT_ENTRY *conn) +{ + return CONTAINER_OF(conn, struct conn_other, up); +} + +static __inline VOID +OvsConntrackUpdateExpiration(struct conn_other *conn, long long now) +{ + conn->up.expiration = now + other_timeouts[conn->state]; +} + +enum ct_update_res +OvsConntrackUpdateOtherEntry(OVS_CT_ENTRY *conn_, + BOOLEAN reply, + UINT64 now) +{ + struct conn_other *conn = OvsCastConntrackEntryToOtherEntry(conn_); + + if (reply && conn->state != OTHERS_BIDIR) { + conn->state = OTHERS_BIDIR; + } else if (conn->state == OTHERS_FIRST) { + conn->state = OTHERS_MULTIPLE; + } + + OvsConntrackUpdateExpiration(conn, now); + + return CT_UPDATE_VALID; +} + +OVS_CT_ENTRY * +OvsConntrackCreateOtherEntry(UINT64 now) +{ + struct conn_other *conn; + conn = OvsAllocateMemoryWithTag(sizeof(struct conn_other), + OVS_CT_POOL_TAG); + /* XXX Handle memory allocation error*/ + conn->up = (OVS_CT_ENTRY) {0}; + conn->state = OTHERS_FIRST; + OvsConntrackUpdateExpiration(conn, now); + return &conn->up; +} \ No newline at end of file diff --git a/datapath-windows/ovsext/Conntrack.c b/datapath-windows/ovsext/Conntrack.c index 544fd51..e193e9f 100644 --- a/datapath-windows/ovsext/Conntrack.c +++ b/datapath-windows/ovsext/Conntrack.c @@ -146,9 +146,20 @@ OvsCtUpdateFlowKey(struct OvsFlowKey *key, } } +static __inline VOID +OvsCtAddEntry(POVS_CT_ENTRY entry, OvsConntrackKeyLookupCtx *ctx) +{ + NdisMoveMemory(&entry->key, &ctx->key, sizeof (OVS_CT_KEY)); + NdisMoveMemory(&entry->rev_key, &ctx->key, sizeof (OVS_CT_KEY)); + OvsCtKeyReverse(&entry->rev_key); + InsertHeadList(&ovsConntrackTable[ctx->hash & CT_HASH_TABLE_MASK], + &entry->link); +} + static __inline POVS_CT_ENTRY -OvsCtEntryCreate(const TCPHdr *tcp, - PNET_BUFFER_LIST curNbl, +OvsCtEntryCreate(PNET_BUFFER_LIST curNbl, + UINT8 ipProto, + UINT32 l4Offset, OvsConntrackKeyLookupCtx *ctx, OvsFlowKey *key, BOOLEAN commit, @@ -156,26 +167,71 @@ OvsCtEntryCreate(const TCPHdr *tcp, { POVS_CT_ENTRY entry = NULL; UINT32 state = 0; - if (!OvsConntrackValidateTcpPacket(tcp)) { - state |= OVS_CS_F_INVALID; - OvsCtUpdateFlowKey(key, state, ctx->key.zone, 0, NULL); - return entry; - } + switch (ipProto) + { + case IPPROTO_TCP: + { + TCPHdr tcpStorage; + const TCPHdr *tcp; + tcp = OvsGetTcp(curNbl, l4Offset, &tcpStorage); + if (!OvsConntrackValidateTcpPacket(tcp)) { + goto invalid; + } + + state |= OVS_CS_F_NEW; + if (commit) { + entry = OvsConntrackCreateTcpEntry(tcp, curNbl, currentTime); + OvsCtAddEntry(entry, ctx); + } + + OvsCtUpdateFlowKey(key, state, ctx->key.zone, 0, NULL); + return entry; + } + case IPPROTO_ICMP: + case IPPROTO_UDP: + state |= OVS_CS_F_NEW; + if (commit) { + entry = OvsConntrackCreateOtherEntry(currentTime); + OvsCtAddEntry(entry, ctx); + } - state |= OVS_CS_F_NEW; - if (commit) { - entry = OvsConntrackCreateTcpEntry(tcp, curNbl, currentTime); - NdisMoveMemory(&entry->key, &ctx->key, sizeof (OVS_CT_KEY)); - NdisMoveMemory(&entry->rev_key, &ctx->key, sizeof (OVS_CT_KEY)); - OvsCtKeyReverse(&entry->rev_key); - InsertHeadList(&ovsConntrackTable[ctx->hash & CT_HASH_TABLE_MASK], - &entry->link); + OvsCtUpdateFlowKey(key, state, ctx->key.zone, 0, NULL); + return entry; + default: + goto invalid; } +invalid: + state |= OVS_CS_F_INVALID; OvsCtUpdateFlowKey(key, state, ctx->key.zone, 0, NULL); return entry; } +static enum CT_UPDATE_RES +OvsCtUpdateEntry(OVS_CT_ENTRY* entry, + PNET_BUFFER_LIST nbl, + UINT8 ipProto, + UINT32 l4Offset, + BOOLEAN reply, + UINT64 now) +{ + switch (ipProto) + { + case IPPROTO_TCP: + { + TCPHdr tcpStorage; + const TCPHdr *tcp; + tcp = OvsGetTcp(nbl, l4Offset, &tcpStorage); + return OvsConntrackUpdateTcpEntry(entry, tcp, nbl, reply, now); + } + case IPPROTO_ICMP: + case IPPROTO_UDP: + return OvsConntrackUpdateOtherEntry(entry, reply, now); + default: + return CT_UPDATE_INVALID; + } +} + static __inline VOID OvsCtEntryDelete(POVS_CT_ENTRY entry) { @@ -204,10 +260,12 @@ OvsDetectCtPacket(OvsFlowKey *key) if (key->ipKey.nwFrag != OVS_FRAG_TYPE_NONE) { return NDIS_STATUS_NOT_SUPPORTED; } - if (key->ipKey.nwProto != IPPROTO_TCP) { - return NDIS_STATUS_NOT_SUPPORTED; + if (key->ipKey.nwProto == IPPROTO_TCP + || key->ipKey.nwProto == IPPROTO_UDP + || key->ipKey.nwProto == IPPROTO_ICMP) { + return NDIS_STATUS_SUCCESS; } - return NDIS_STATUS_SUCCESS; + return NDIS_STATUS_NOT_SUPPORTED; case ETH_TYPE_IPV6: return NDIS_STATUS_NOT_SUPPORTED; } @@ -265,16 +323,31 @@ OvsCtLookup(OvsConntrackKeyLookupCtx *ctx) return found; } -static __inline VOID -OvsCtSetupLookupCtx(OvsFlowKey *flowKey, - UINT16 zone, - OvsConntrackKeyLookupCtx *ctx) +static __inline UINT32 +OvsExtractLookupCtxHash(OvsConntrackKeyLookupCtx *ctx) { UINT32 hsrc, hdst,hash; + hsrc = OvsJhashBytes((UINT32*) &ctx->key.src, sizeof(ctx->key.src), 0); + hdst = OvsJhashBytes((UINT32*) &ctx->key.dst, sizeof(ctx->key.dst), 0); + hash = hsrc ^ hdst; /* TO identify reverse traffic */ + return OvsJhashBytes((uint32_t *) &ctx->key.dst + 1, + ((uint32_t *) (&ctx->key + 1) - + (uint32_t *) (&ctx->key.dst + 1)), + hash); +} +static __inline NDIS_STATUS +OvsCtSetupLookupCtx(OvsFlowKey *flowKey, + UINT16 zone, + OvsConntrackKeyLookupCtx *ctx, + PNET_BUFFER_LIST curNbl, + UINT32 l4Offset) +{ ctx->key.zone = zone; ctx->key.dl_type = flowKey->l2.dlType; + ctx->related = FALSE; + /* Extract L3 and L4*/ if (flowKey->l2.dlType == htons(ETH_TYPE_IPV4)) { ctx->key.src.addr.ipv4 = flowKey->ipKey.nwSrc; ctx->key.dst.addr.ipv4 = flowKey->ipKey.nwDst; @@ -282,6 +355,26 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey, ctx->key.src.port = flowKey->ipKey.l4.tpSrc; ctx->key.dst.port = flowKey->ipKey.l4.tpDst; + if (flowKey->ipKey.nwProto == IPPROTO_ICMP) { + ICMPHdr icmpStorage; + const ICMPHdr *icmp; + icmp = OvsGetIcmp(curNbl, l4Offset, &icmpStorage); + 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_DEST_UNREACH: + case ICMP4_TIME_EXCEEDED: + case ICMP4_PARAM_PROB: + case ICMP4_SOURCE_QUENCH: + case ICMP4_REDIRECT: { + ctx->related = TRUE; + } + default: + ctx->related = FALSE; + } + } } else if (flowKey->l2.dlType == htons(ETH_TYPE_IPV6)) { ctx->key.src.addr.ipv6 = flowKey->ipv6Key.ipv6Src; ctx->key.dst.addr.ipv6 = flowKey->ipv6Key.ipv6Dst; @@ -289,18 +382,13 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey, ctx->key.src.port = flowKey->ipv6Key.l4.tpSrc; ctx->key.dst.port = flowKey->ipv6Key.l4.tpDst; + /* XXX Handle ICMPv6 errors*/ + } else { + return NDIS_STATUS_INVALID_PACKET; } - /* Related bit is set for ICMP and FTP (Not supported)*/ - ctx->related = FALSE; - - hsrc = OvsJhashBytes((UINT32*) &ctx->key.src, sizeof(ctx->key.src), 0); - hdst = OvsJhashBytes((UINT32*) &ctx->key.dst, sizeof(ctx->key.dst), 0); - hash = hsrc ^ hdst; /* TO identify reverse traffic */ - ctx->hash = OvsJhashBytes((uint32_t *) &ctx->key.dst + 1, - ((uint32_t *) (&ctx->key + 1) - - (uint32_t *) (&ctx->key.dst + 1)), - hash); + ctx->hash = OvsExtractLookupCtxHash(ctx); + return NDIS_STATUS_SUCCESS; } /* @@ -311,7 +399,7 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey, */ static __inline POVS_CT_ENTRY OvsProcessConntrackEntry(PNET_BUFFER_LIST curNbl, - const TCPHdr *tcp, + UINT32 l4Offset, OvsConntrackKeyLookupCtx *ctx, OvsFlowKey *key, UINT16 zone, @@ -329,8 +417,8 @@ OvsProcessConntrackEntry(PNET_BUFFER_LIST curNbl, } } else { CT_UPDATE_RES result; - result = OvsConntrackUpdateTcpEntry(entry, tcp, curNbl, - ctx->reply, currentTime); + result = OvsCtUpdateEntry(entry, curNbl, key->ipKey.nwProto, + l4Offset, ctx->reply, currentTime); switch (result) { case CT_UPDATE_VALID: state |= OVS_CS_F_ESTABLISHED; @@ -345,14 +433,18 @@ OvsProcessConntrackEntry(PNET_BUFFER_LIST curNbl, //Delete and update the Conntrack OvsCtEntryDelete(ctx->entry); ctx->entry = NULL; - entry = OvsCtEntryCreate(tcp, curNbl, ctx, key, - commit, currentTime); + entry = OvsCtEntryCreate(curNbl, key->ipKey.nwProto, l4Offset, + ctx, key, commit, currentTime); break; } } /* Copy mark and label from entry into flowKey. If actions specify different mark and label, update the flowKey. */ - OvsCtUpdateFlowKey(key, state, zone, entry->mark, &entry->labels); + if (entry) { + OvsCtUpdateFlowKey(key, state, zone, entry->mark, &entry->labels); + } else { + OvsCtUpdateFlowKey(key, state, zone, 0, NULL); + } return entry; } @@ -401,15 +493,12 @@ OvsCtExecute_(PNET_BUFFER_LIST curNbl, NDIS_STATUS status = NDIS_STATUS_SUCCESS; POVS_CT_ENTRY entry = NULL; OvsConntrackKeyLookupCtx ctx = { 0 }; - TCPHdr tcpStorage; - UINT64 currentTime; LOCK_STATE_EX lockState; - const TCPHdr *tcp; - tcp = OvsGetTcp(curNbl, layers->l4Offset, &tcpStorage); + UINT64 currentTime; NdisGetCurrentSystemTime((LARGE_INTEGER *) ¤tTime); /* Retrieve the Conntrack Key related fields from packet */ - OvsCtSetupLookupCtx(key, zone, &ctx); + OvsCtSetupLookupCtx(key, zone, &ctx, curNbl, layers->l4Offset); NdisAcquireRWLockWrite(ovsConntrackLockObj, &lockState, 0); @@ -418,11 +507,12 @@ OvsCtExecute_(PNET_BUFFER_LIST curNbl, if (!entry) { /* If no matching entry was found, create one and add New state */ - entry = OvsCtEntryCreate(tcp, curNbl, &ctx, + entry = OvsCtEntryCreate(curNbl, key->ipKey.nwProto, + layers->l4Offset, &ctx, key, commit, currentTime); } else { /* Process the entry and update CT flags */ - entry = OvsProcessConntrackEntry(curNbl, tcp, &ctx, key, + entry = OvsProcessConntrackEntry(curNbl, layers->l4Offset, &ctx, key, zone, commit, currentTime); } diff --git a/datapath-windows/ovsext/Conntrack.h b/datapath-windows/ovsext/Conntrack.h index a754544..37dd0a6 100644 --- a/datapath-windows/ovsext/Conntrack.h +++ b/datapath-windows/ovsext/Conntrack.h @@ -99,9 +99,13 @@ BOOLEAN OvsConntrackValidateTcpPacket(const TCPHdr *tcp); OVS_CT_ENTRY * OvsConntrackCreateTcpEntry(const TCPHdr *tcp, PNET_BUFFER_LIST nbl, UINT64 now); +OVS_CT_ENTRY * OvsConntrackCreateOtherEntry(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_, + BOOLEAN reply, + UINT64 now); #endif /* __OVS_CONNTRACK_H_ */ \ No newline at end of file diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj index 0356ddf..0ad4c58 100644 --- a/datapath-windows/ovsext/ovsext.vcxproj +++ b/datapath-windows/ovsext/ovsext.vcxproj @@ -176,6 +176,7 @@ <ItemGroup> <ClCompile Include="Actions.c" /> <ClCompile Include="BufferMgmt.c" /> + <ClCompile Include="Conntrack-other.c" /> <ClCompile Include="Conntrack-tcp.c" /> <ClCompile Include="Conntrack.c" /> <ClCompile Include="Debug.c" /> -- 2.5.0.windows.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev