[ovs-dev] [PATCH v7 2/4] datapath-windows: Add NAT module in conntrack
Sairam Venugopal
vsairam at vmware.com
Wed May 17 18:04:48 UTC 2017
Hi Yin,
Thanks for clarifying the comments. I will ack the next version.
@Alin - will you have sometime to review the changes?
Thanks,
Sairam
On 5/16/17, 5:11 PM, "Yin Lin" <linyi at vmware.com> wrote:
>Thanks Sai for the review! I fixed most of them and explained the remaining ones in the inline comments.
>
>-----Original Message-----
>From: Sairam Venugopal
>Sent: Tuesday, May 16, 2017 4:50 PM
>To: Yin Lin <linyi at vmware.com>; dev at openvswitch.org
>Subject: Re: [ovs-dev] [PATCH v7 2/4] datapath-windows: Add NAT module in conntrack
>
>Hi Yin,
>
>Thanks for the patch. Please find my comments inline.
>
>Thanks,
>Sairam
>
>
>
>
>On 5/9/17, 3:59 PM, "ovs-dev-bounces at openvswitch.org on behalf of Yin Lin" <ovs-dev-bounces at openvswitch.org on behalf of linyi at vmware.com> wrote:
>
>>Signed-off-by: Yin Lin <linyi at vmware.com>
>>---
>> datapath-windows/automake.mk | 2 +
>> datapath-windows/ovsext/Conntrack-nat.c | 424 ++++++++++++++++++++++++++++++++
>> datapath-windows/ovsext/Conntrack-nat.h | 39 +++
>> 3 files changed, 465 insertions(+)
>> create mode 100644 datapath-windows/ovsext/Conntrack-nat.c
>> create mode 100644 datapath-windows/ovsext/Conntrack-nat.h
>>
>>diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk
>>index 4f7b55a..7177630 100644
>>--- a/datapath-windows/automake.mk
>>+++ b/datapath-windows/automake.mk
>>@@ -16,7 +16,9 @@ EXTRA_DIST += \
>> datapath-windows/ovsext/Conntrack-icmp.c \
>> datapath-windows/ovsext/Conntrack-other.c \
>> datapath-windows/ovsext/Conntrack-related.c \
>>+ datapath-windows/ovsext/Conntrack-nat.c \
>> datapath-windows/ovsext/Conntrack-tcp.c \
>>+ datapath-windows/ovsext/Conntrack-nat.h \
>> datapath-windows/ovsext/Conntrack.c \
>> datapath-windows/ovsext/Conntrack.h \
>> datapath-windows/ovsext/Datapath.c \
>>diff --git a/datapath-windows/ovsext/Conntrack-nat.c b/datapath-windows/ovsext/Conntrack-nat.c
>>new file mode 100644
>>index 0000000..edf5f8f
>>--- /dev/null
>>+++ b/datapath-windows/ovsext/Conntrack-nat.c
>>@@ -0,0 +1,424 @@
>>+#include "Conntrack-nat.h"
>>+#include "Jhash.h"
>>+
>>+PLIST_ENTRY ovsNatTable = NULL;
>>+PLIST_ENTRY ovsUnNatTable = NULL;
>>+
>>+/*
>>+ *---------------------------------------------------------------------------
>>+ * OvsHashNatKey
>>+ * Hash NAT related fields in a Conntrack key.
>>+ *---------------------------------------------------------------------------
>>+ */
>>+static __inline UINT32
>>+OvsHashNatKey(const OVS_CT_KEY *key)
>>+{
>>+ UINT32 hash = 0;
>>+#define HASH_ADD(field) \
>>+ hash = OvsJhashBytes(&key->field, sizeof(key->field), hash)
>>+
>>+ HASH_ADD(src.addr.ipv4_aligned);
>>+ HASH_ADD(dst.addr.ipv4_aligned);
>>+ HASH_ADD(src.port);
>>+ HASH_ADD(dst.port);
>>+ HASH_ADD(zone);
>>+#undef HASH_ADD
>>+ return hash;
>>+}
>>+
>>+/*
>>+ *---------------------------------------------------------------------------
>>+ * OvsNatKeyAreSame
>>+ * Compare NAT related fields in a Conntrack key.
>>+ *---------------------------------------------------------------------------
>>+ */
>>+static __inline BOOLEAN
>>+OvsNatKeyAreSame(const OVS_CT_KEY *key1, const OVS_CT_KEY *key2)
>>+{
>>+ // XXX: Compare IPv6 key as well
>>+#define FIELD_COMPARE(field) \
>
>[Sai]: Nit: move return statement to next line.
>[Yin]: This is a MACRO and I don't want to spread it over lines. (Note that I don't even add a semicolon at the end to make it look like a function)
>
>>+ if (key1->field != key2->field) return FALSE
>>+
>>+ FIELD_COMPARE(src.addr.ipv4_aligned);
>>+ FIELD_COMPARE(dst.addr.ipv4_aligned);
>>+ FIELD_COMPARE(src.port);
>>+ FIELD_COMPARE(dst.port);
>>+ FIELD_COMPARE(zone);
>>+ return TRUE;
>>+#undef FIELD_COMPARE
>>+}
>>+
>>+/*
>>+ *---------------------------------------------------------------------------
>
>[Sai]: Nit - OvsNatGetBucket
>[Yin]: Fixed
>
>>+ * OvsNaGetBucket
>>+ * Returns the row of NAT table that has the same hash as the given NAT
>>+ * hash key. If isReverse is TRUE, returns the row of reverse NAT table
>>+ * instead.
>>+ *---------------------------------------------------------------------------
>>+ */
>>+static __inline PLIST_ENTRY
>>+OvsNatGetBucket(const OVS_CT_KEY *key, BOOLEAN isReverse)
>>+{
>>+ uint32_t hash = OvsHashNatKey(key);
>>+ if (isReverse) {
>>+ return &ovsUnNatTable[hash & NAT_HASH_TABLE_MASK];
>>+ } else {
>>+ return &ovsNatTable[hash & NAT_HASH_TABLE_MASK];
>>+ }
>>+}
>>+
>>+/*
>>+ *---------------------------------------------------------------------------
>>+ * OvsNatInit
>>+ * Initialize NAT related resources.
>>+ *---------------------------------------------------------------------------
>>+ */
>>+NTSTATUS OvsNatInit()
>>+{
>>+ ASSERT(ovsNatTable == NULL);
>>+
>>+ /* Init the Hash Buffer */
>>+ ovsNatTable = OvsAllocateMemoryWithTag(
>>+ sizeof(LIST_ENTRY) * NAT_HASH_TABLE_SIZE,
>>+ OVS_CT_POOL_TAG);
>>+ if (ovsNatTable == NULL) {
>>+ goto failNoMem;
>>+ }
>>+
>>+ ovsUnNatTable = OvsAllocateMemoryWithTag(
>>+ sizeof(LIST_ENTRY) * NAT_HASH_TABLE_SIZE,
>>+ OVS_CT_POOL_TAG);
>>+ if (ovsUnNatTable == NULL) {
>>+ goto freeNatTable;
>>+ }
>>+
>>+ for (int i = 0; i < NAT_HASH_TABLE_SIZE; i++) {
>>+ InitializeListHead(&ovsNatTable[i]);
>>+ InitializeListHead(&ovsUnNatTable[i]);
>>+ }
>>+ return STATUS_SUCCESS;
>>+
>>+freeNatTable:
>>+ OvsFreeMemoryWithTag(ovsNatTable, OVS_CT_POOL_TAG);
>>+failNoMem:
>>+ return STATUS_INSUFFICIENT_RESOURCES;
>>+}
>>+
>>+/*
>>+ *----------------------------------------------------------------------------
>>+ * OvsNatFlush
>>+ * Flushes out all NAT entries that match the given zone.
>>+ *----------------------------------------------------------------------------
>>+ */
>>+VOID OvsNatFlush(UINT16 zone)
>>+{
>>+ PLIST_ENTRY link, next;
>>+ for (int i = 0; i < NAT_HASH_TABLE_SIZE; i++) {
>>+ LIST_FORALL_SAFE(&ovsNatTable[i], link, next) {
>>+ POVS_NAT_ENTRY entry =
>>+ CONTAINING_RECORD(link, OVS_NAT_ENTRY, link);
>>+ /* zone is a non-zero value */
>>+ if (!zone || zone == entry->key.zone) {
>>+ OvsNatDeleteEntry(entry);
>>+ }
>>+ }
>>+ }
>>+}
>>+
>>+/*
>>+ *----------------------------------------------------------------------------
>>+ * OvsNatCleanup
>>+ * Releases all NAT related resources.
>>+ *----------------------------------------------------------------------------
>>+ */
>>+VOID OvsNatCleanup()
>>+{
>
>[Sai]: Nit: move return statement to next line.
>[Yin] Fixed and also added brackets around it according to coding standard.
>
>
>>+ if (ovsNatTable == NULL) return;
>>+ OvsFreeMemoryWithTag(ovsNatTable, OVS_CT_POOL_TAG);
>>+ OvsFreeMemoryWithTag(ovsUnNatTable, OVS_CT_POOL_TAG);
>>+ ovsNatTable = NULL;
>>+ ovsUnNatTable = NULL;
>>+}
>>+
>>+/*
>>+ *----------------------------------------------------------------------------
>>+ * OvsNatPacket
>>+ * Performs NAT operation on the packet by replacing the source/destinaton
>>+ * address/port based on natAction. If reverse is TRUE, perform unNAT
>>+ * instead.
>>+ *----------------------------------------------------------------------------
>>+ */
>>+VOID
>>+OvsNatPacket(OvsForwardingContext *ovsFwdCtx,
>>+ const OVS_CT_ENTRY *entry,
>>+ UINT16 natAction,
>>+ OvsFlowKey *key,
>>+ BOOLEAN reverse)
>>+{
>>+ UINT32 natFlag;
>>+ const struct ct_endpoint* endpoint;
>>+ /* When it is NAT, only entry->rev_key contains NATTED address;
>>+ When it is unNAT, only entry->key contains the UNNATTED address;*/
>>+ const OVS_CT_KEY *ctKey = reverse ? &entry->key : &entry->rev_key;
>>+ BOOLEAN isSrcNat;
>>+
>>+ if (!(natAction & (NAT_ACTION_SRC | NAT_ACTION_DST))) {
>>+ return;
>>+ }
>>+ isSrcNat = (((natAction & NAT_ACTION_SRC) && !reverse) ||
>>+ ((natAction & NAT_ACTION_DST) && reverse));
>>+
>>+ if (isSrcNat) {
>>+ /* Flag is set to SNAT for SNAT case and the reverse DNAT case */
>>+ natFlag = OVS_CS_F_SRC_NAT;
>>+ /* Note that ctKey is the key in the other direction, so
>>+ endpoint has to be reverted, i.e. ctKey->dst for SNAT
>>+ and ctKey->src for DNAT */
>>+ endpoint = &ctKey->dst;
>>+ } else {
>>+ natFlag = OVS_CS_F_DST_NAT;
>>+ endpoint = &ctKey->src;
>>+ }
>>+ key->ct.state |= natFlag;
>>+ if (ctKey->dl_type == htons(ETH_TYPE_IPV4)) {
>>+ OvsUpdateAddressAndPort(ovsFwdCtx,
>>+ endpoint->addr.ipv4_aligned,
>>+ endpoint->port, isSrcNat,
>>+ !reverse);
>>+ if (isSrcNat) {
>>+ key->ipKey.nwSrc = endpoint->addr.ipv4_aligned;
>>+ } else {
>>+ key->ipKey.nwDst = endpoint->addr.ipv4_aligned;
>>+ }
>>+ } else if (ctKey->dl_type == htons(ETH_TYPE_IPV6)){
>>+ // XXX: IPv6 packet not supported yet.
>>+ return;
>>+ }
>>+ if (natAction & (NAT_ACTION_SRC_PORT | NAT_ACTION_DST_PORT)) {
>>+ if (isSrcNat) {
>>+ if (key->ipKey.l4.tpSrc != 0) {
>>+ key->ipKey.l4.tpSrc = endpoint->port;
>>+ }
>>+ } else {
>>+ if (key->ipKey.l4.tpDst != 0) {
>>+ key->ipKey.l4.tpDst = endpoint->port;
>>+ }
>>+ }
>>+ }
>>+}
>>+
>>+
>>+/*
>>+ *----------------------------------------------------------------------------
>>+ * OvsNatHashRange
>>+ * Compute hash for a range of addresses specified in natInfo.
>>+ *----------------------------------------------------------------------------
>>+ */
>>+static UINT32 OvsNatHashRange(const OVS_CT_ENTRY *entry, UINT32 basis)
>>+{
>>+ UINT32 hash = basis;
>>+#define HASH_ADD(field) \
>>+ hash = OvsJhashBytes(&field, sizeof(field), hash)
>>+
>>+ HASH_ADD(entry->natInfo.minAddr);
>>+ HASH_ADD(entry->natInfo.maxAddr);
>>+ HASH_ADD(entry->key.dl_type);
>>+ HASH_ADD(entry->key.nw_proto);
>>+ HASH_ADD(entry->key.zone);
>>+#undef HASH_ADD
>>+ return hash;
>>+}
>>+
>>+/*
>>+ *----------------------------------------------------------------------------
>>+ * OvsNatAddEntry
>>+ * Add an entry to the NAT table. Also updates the reverse NAT lookup
>>+ * table.
>>+ *----------------------------------------------------------------------------
>>+ */
>>+VOID
>>+OvsNatAddEntry(OVS_NAT_ENTRY* entry)
>>+{
>>+ InsertHeadList(OvsNatGetBucket(&entry->key, FALSE),
>>+ &entry->link);
>>+ InsertHeadList(OvsNatGetBucket(&entry->value, TRUE),
>>+ &entry->reverseLink);
>>+}
>>+
>>+/*
>>+ *----------------------------------------------------------------------------
>>+ * OvsNatCtEntry
>>+ * Update an Conntrack entry with NAT information. Translated address and
>>+ * port will be generated and write back to the conntrack entry as a
>>+ * result.
>>+ *----------------------------------------------------------------------------
>>+ */
>[Sai] - Can you name this OvsNatUpdateCtEntry? Or something to imply what this function does.
>[Yin] - Update is a little bit confusing as it doesn't explain what's the update is about. I intended to mean the function NATs a CtEntry and NAT is a verb here. But I see what you are coming from and understand that the name is very confusing. Let me rename it OvsNatTranslateCtEntry as a compromise.
>
>>+BOOLEAN
>>+OvsNatCtEntry(OVS_CT_ENTRY *entry)
>>+{
>>+ const uint16_t MIN_NAT_EPHEMERAL_PORT = 1024;
>>+ const uint16_t MAX_NAT_EPHEMERAL_PORT = 65535;
>>+
>>+ uint16_t minPort;
>>+ uint16_t maxPort;
>>+ uint16_t firstPort;
>>+
>>+ uint32_t hash = OvsNatHashRange(entry, 0);
>>+
>>+ if ((entry->natInfo.natAction & NAT_ACTION_SRC) &&
>>+ (!(entry->natInfo.natAction & NAT_ACTION_SRC_PORT))) {
>>+ firstPort = minPort = maxPort = ntohs(entry->key.src.port);
>>+ } else if ((entry->natInfo.natAction & NAT_ACTION_DST) &&
>>+ (!(entry->natInfo.natAction & NAT_ACTION_DST_PORT))) {
>>+ firstPort = minPort = maxPort = ntohs(entry->key.dst.port);
>>+ } else {
>>+ uint16_t portDelta = entry->natInfo.maxPort - entry->natInfo.minPort;
>>+ uint16_t portIndex = (uint16_t) hash % (portDelta + 1);
>>+ firstPort = entry->natInfo.minPort + portIndex;
>>+ minPort = entry->natInfo.minPort;
>>+ maxPort = entry->natInfo.maxPort;
>>+ }
>>+
>
>[Sai] - Can you move these definitions to the top?
>[Yin] Fixed
>
>
>>+ uint32_t addrDelta = 0;
>>+ uint32_t addrIndex;
>>+ struct ct_addr ctAddr, maxCtAddr;
>>+ memset(&ctAddr, 0, sizeof ctAddr);
>>+ memset(&maxCtAddr, 0, sizeof maxCtAddr);
>>+ maxCtAddr = entry->natInfo.maxAddr;
>>+
>>+ if (entry->key.dl_type == htons(ETH_TYPE_IPV4)) {
>>+ addrDelta = ntohl(entry->natInfo.maxAddr.ipv4_aligned) -
>>+ ntohl(entry->natInfo.minAddr.ipv4_aligned);
>>+ addrIndex = hash % (addrDelta + 1);
>>+ ctAddr.ipv4_aligned = htonl(
>>+ ntohl(entry->natInfo.minAddr.ipv4_aligned) + addrIndex);
>>+ } else {
>>+ // XXX: IPv6 not supported
>>+ return FALSE;
>>+ }
>
>[Sai] - Can you move these definitions to the top?
>[Yin] Fixed
>
>
>>+
>
>>+ uint16_t port = firstPort;
>>+ BOOLEAN allPortsTried = FALSE;
>>+ BOOLEAN originalPortsTried = FALSE;
>>+ struct ct_addr firstAddr = ctAddr;
>>+ for (;;) {
>>+ if (entry->natInfo.natAction & NAT_ACTION_SRC) {
>>+ entry->rev_key.dst.addr = ctAddr;
>>+ entry->rev_key.dst.port = htons(port);
>>+ } else {
>>+ entry->rev_key.src.addr = ctAddr;
>>+ entry->rev_key.src.port = htons(port);
>>+ }
>>+
>>+ OVS_NAT_ENTRY *natEntry = OvsNatLookup(&entry->rev_key, TRUE);
>>+
>>+ if (!natEntry) {
>>+ natEntry = OvsAllocateMemoryWithTag(sizeof(*natEntry),
>>+ OVS_CT_POOL_TAG);
>
>[Sai]: Need to check if natEntry is not NULL and handle the Insufficient resources case.
>[Yin] Fixed.
>
>nit: NdisMoveMemory instead of memcpy
>
>>+ memcpy(&natEntry->key, &entry->key,
>>+ sizeof natEntry->key);
>>+ memcpy(&natEntry->value, &entry->rev_key,
>>+ sizeof natEntry->value);
>>+ natEntry->ctEntry = entry;
>>+ OvsNatAddEntry(natEntry);
>>+ return TRUE;
>>+ } else if (!allPortsTried) {
>>+ if (minPort == maxPort) {
>>+ allPortsTried = TRUE;
>>+ } else if (port == maxPort) {
>>+ port = minPort;
>>+ } else {
>>+ port++;
>>+ }
>>+ if (port == firstPort) {
>>+ allPortsTried = TRUE;
>>+ }
>>+ } else {
>>+ if (memcmp(&ctAddr, &maxCtAddr, sizeof ctAddr)) {
>>+ if (entry->key.dl_type == htons(ETH_TYPE_IPV4)) {
>>+ ctAddr.ipv4_aligned = htonl(
>>+ ntohl(ctAddr.ipv4_aligned) + 1);
>>+ } else {
>>+ // XXX: IPv6 not supported
>
>[Sai] - This can be done later. However, it will be good to return NDIS_STATUS
>
>and return NDIS_STATUS_INVALID. At the very minimum, we should add a log msg.
>
>[Yin] Shashank raised the similar concern earlier. We don't have IPv6 support anywhere and feels bad about
>spamming error message whenever an address is concerned. We don't have log rate control so if there is a IPv6 rule then the log
>is going to explode and prevent us from seeing the entries we are really interested in. If we have partial IPv6 support,
>then I agree that we should add logs in areas where the support is not extended yet.
>
>>+ return FALSE;
>>+ }
>>+ } else {
>>+ ctAddr = entry->natInfo.minAddr;
>>+ }
>>+ if (!memcmp(&ctAddr, &firstAddr, sizeof ctAddr)) {
>>+ if (!originalPortsTried) {
>>+ originalPortsTried = TRUE;
>>+ ctAddr = entry->natInfo.minAddr;
>>+ minPort = MIN_NAT_EPHEMERAL_PORT;
>>+ maxPort = MAX_NAT_EPHEMERAL_PORT;
>>+ } else {
>>+ break;
>>+ }
>>+ }
>>+ firstPort = minPort;
>>+ port = firstPort;
>>+ allPortsTried = FALSE;
>>+ }
>>+ }
>>+ return FALSE;
>>+}
>>+
>>+/*
>>+ *----------------------------------------------------------------------------
>>+ * OvsNatLookup
>>+ * Look up a NAT entry with the given key in the NAT table.
>>+ * If reverse is TRUE, look up a NAT entry with the given value instead.
>>+ *----------------------------------------------------------------------------
>>+ */
>>+POVS_NAT_ENTRY
>>+OvsNatLookup(const OVS_CT_KEY *ctKey, BOOLEAN reverse)
>>+{
>>+ PLIST_ENTRY link;
>>+ POVS_NAT_ENTRY entry;
>>+
>>+ LIST_FORALL(OvsNatGetBucket(ctKey, reverse), link) {
>>+ if (reverse) {
>>+ entry = CONTAINING_RECORD(link, OVS_NAT_ENTRY, reverseLink);
>>+
>>+ if (OvsNatKeyAreSame(ctKey, &entry->value)) {
>>+ return entry;
>>+ }
>>+ } else {
>>+ entry = CONTAINING_RECORD(link, OVS_NAT_ENTRY, link);
>>+
>>+ if (OvsNatKeyAreSame(ctKey, &entry->key)) {
>>+ return entry;
>>+ }
>>+ }
>>+ }
>>+ return NULL;
>>+}
>>+
>>+/*
>>+ *----------------------------------------------------------------------------
>>+ * OvsNatDeleteEntry
>>+ * Delete a NAT entry.
>>+ *----------------------------------------------------------------------------
>>+ */
>>+VOID
>>+OvsNatDeleteEntry(POVS_NAT_ENTRY entry)
>>+{
>>+ if (entry == NULL) {
>>+ return;
>>+ }
>>+ RemoveEntryList(&entry->link);
>>+ RemoveEntryList(&entry->reverseLink);
>>+ OvsFreeMemoryWithTag(entry, OVS_CT_POOL_TAG);
>>+}
>>+
>>+/*
>>+ *----------------------------------------------------------------------------
>>+ * OvsNatDeleteKey
>>+ * Delete a NAT entry with the given key.
>>+ *----------------------------------------------------------------------------
>>+ */
>>+VOID
>>+OvsNatDeleteKey(const OVS_CT_KEY *key)
>>+{
>>+ OvsNatDeleteEntry(OvsNatLookup(key, FALSE));
>>+}
>>diff --git a/datapath-windows/ovsext/Conntrack-nat.h b/datapath-windows/ovsext/Conntrack-nat.h
>>new file mode 100644
>>index 0000000..aaa8ceb
>>--- /dev/null
>>+++ b/datapath-windows/ovsext/Conntrack-nat.h
>>@@ -0,0 +1,39 @@
>>+#ifndef _CONNTRACK_NAT_H
>>+#define _CONNTRACK_NAT_H
>>+
>>+#include "precomp.h"
>>+#include "Flow.h"
>>+#include "Debug.h"
>>+#include <stddef.h>
>>+#include "Conntrack.h"
>>+
>>+#define NAT_HASH_TABLE_SIZE ((UINT32)1 << 10)
>>+#define NAT_HASH_TABLE_MASK (NAT_HASH_TABLE_SIZE - 1)
>>+
>>+typedef struct OVS_NAT_ENTRY {
>>+ LIST_ENTRY link;
>>+ LIST_ENTRY reverseLink;
>>+ OVS_CT_KEY key;
>>+ OVS_CT_KEY value;
>>+ POVS_CT_ENTRY ctEntry;
>>+} OVS_NAT_ENTRY, *POVS_NAT_ENTRY;
>>+
>>+__inline static BOOLEAN OvsIsForwardNat(UINT16 natAction) {
>
>[Sai] - What does the double !! do here?
>[Yin] - This is a bitwise masking operation on integer and the return value is a Boolean, so it needs double !! to ensure return value is either 0 or 1.
>
>
>>+ return !!(natAction & (NAT_ACTION_SRC | NAT_ACTION_DST));
>>+}
>>+
>>+NTSTATUS OvsNatInit();
>>+VOID OvsNatFlush(UINT16 zone);
>>+
>>+VOID OvsNatAddEntry(OVS_NAT_ENTRY* entry);
>>+
>>+VOID OvsNatDeleteEntry(POVS_NAT_ENTRY entry);
>>+VOID OvsNatDeleteKey(const OVS_CT_KEY *key);
>>+VOID OvsNatCleanup();
>>+
>>+POVS_NAT_ENTRY OvsNatLookup(const OVS_CT_KEY *ctKey, BOOLEAN reverse);
>>+BOOLEAN OvsNatCtEntry(OVS_CT_ENTRY *ctEntry);
>>+VOID OvsNatPacket(OvsForwardingContext *ovsFwdCtx, const OVS_CT_ENTRY *entry,
>>+ UINT16 natAction, OvsFlowKey *key, BOOLEAN reverse);
>>+
>>+#endif
>>--
>>2.10.2.windows.1
>>
>>_______________________________________________
>>dev mailing list
>>dev at openvswitch.org
>>https://urldefense.proofpoint.com/v2/url?u=https-3A__mail.openvswitch.org_mailman_listinfo_ovs-2Ddev&d=DwICAg&c=uilaK90D4TOVoH58JNXRgQ&r=Z6vowHUOjP5ysP_g372c49Nqc1vEKqHKNBkR5Q5Z7uo&m=veEopphpitZ1dkD8nov8CuLArrdnUU8WDczrz9UClwo&s=U88QV-uHXUzVrccSWR_EYp7F7ySpFOMRBlwZy34qfzA&e=
More information about the dev
mailing list