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 <vsai...@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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev