Solved datapath-windows: BSOD when initializing switch context
[cascardo/ovs.git] / datapath-windows / ovsext / Flow.c
index 61a1eda..69b546a 100644 (file)
@@ -31,7 +31,6 @@
 #pragma warning( push )
 #pragma warning( disable:4127 )
 
-extern PNDIS_SPIN_LOCK gOvsCtrlLock;
 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
 extern UINT64 ovsTimeIncrementPerTick;
 
@@ -68,10 +67,6 @@ static NTSTATUS _FlowNlDumpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                                       UINT32 *replyLen);
 static NTSTATUS _MapFlowInfoToNl(PNL_BUFFER nlBuf,
                                  OvsFlowInfo *flowInfo);
-static NTSTATUS _MapFlowKeyToNlKey(PNL_BUFFER nlBuf,
-                                   OvsFlowKey *flowKey);
-static NTSTATUS _MapFlowTunKeyToNlKey(PNL_BUFFER nlBuf,
-                                      OvsIPv4TunnelKey *tunKey);
 static NTSTATUS _MapFlowStatsToNlStats(PNL_BUFFER nlBuf,
                                        OvsFlowStats *flowStats);
 static NTSTATUS _MapFlowActionToNlAction(PNL_BUFFER nlBuf,
@@ -98,7 +93,7 @@ static NTSTATUS OvsDoDumpFlows(OvsFlowDumpInput *dumpInput,
 /* Flow family related netlink policies */
 
 /* For Parsing attributes in FLOW_* commands */
-static const NL_POLICY nlFlowPolicy[] = {
+const NL_POLICY nlFlowPolicy[] = {
     [OVS_FLOW_ATTR_KEY] = {.type = NL_A_NESTED, .optional = FALSE},
     [OVS_FLOW_ATTR_MASK] = {.type = NL_A_NESTED, .optional = TRUE},
     [OVS_FLOW_ATTR_ACTIONS] = {.type = NL_A_NESTED, .optional = TRUE},
@@ -114,7 +109,7 @@ static const NL_POLICY nlFlowPolicy[] = {
  * Some of the attributes like OVS_KEY_ATTR_RECIRC_ID
  * & OVS_KEY_ATTR_MPLS are not supported yet. */
 
-static const NL_POLICY nlFlowKeyPolicy[] = {
+const NL_POLICY nlFlowKeyPolicy[] = {
     [OVS_KEY_ATTR_ENCAP] = {.type = NL_A_VAR_LEN, .optional = TRUE},
     [OVS_KEY_ATTR_PRIORITY] = {.type = NL_A_UNSPEC, .minLen = 4,
                                .maxLen = 4, .optional = TRUE},
@@ -123,7 +118,7 @@ static const NL_POLICY nlFlowKeyPolicy[] = {
     [OVS_KEY_ATTR_ETHERNET] = {.type = NL_A_UNSPEC,
                                .minLen = sizeof(struct ovs_key_ethernet),
                                .maxLen = sizeof(struct ovs_key_ethernet),
-                               .optional = FALSE},
+                               .optional = TRUE},
     [OVS_KEY_ATTR_VLAN] = {.type = NL_A_UNSPEC, .minLen = 2,
                            .maxLen = 2, .optional = TRUE},
     [OVS_KEY_ATTR_ETHERTYPE] = {.type = NL_A_UNSPEC, .minLen = 2,
@@ -178,7 +173,7 @@ static const NL_POLICY nlFlowKeyPolicy[] = {
 };
 
 /* For Parsing nested OVS_KEY_ATTR_TUNNEL attributes */
-static const NL_POLICY nlFlowTunnelKeyPolicy[] = {
+const NL_POLICY nlFlowTunnelKeyPolicy[] = {
     [OVS_TUNNEL_KEY_ATTR_ID] = {.type = NL_A_UNSPEC, .minLen = 8,
                                 .maxLen = 8, .optional = TRUE},
     [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = {.type = NL_A_UNSPEC, .minLen = 4,
@@ -200,7 +195,7 @@ static const NL_POLICY nlFlowTunnelKeyPolicy[] = {
 };
 
 /* For Parsing nested OVS_FLOW_ATTR_ACTIONS attributes */
-static const NL_POLICY nlFlowActionPolicy[] = {
+const NL_POLICY nlFlowActionPolicy[] = {
     [OVS_ACTION_ATTR_OUTPUT] = {.type = NL_A_UNSPEC, .minLen = sizeof(UINT32),
                                 .maxLen = sizeof(UINT32), .optional = TRUE},
     [OVS_ACTION_ATTR_USERSPACE] = {.type = NL_A_VAR_LEN, .optional = TRUE},
@@ -251,6 +246,7 @@ OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                     UINT32 *replyLen)
 {
     NTSTATUS rc = STATUS_SUCCESS;
+    BOOLEAN ok;
     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
     PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
@@ -262,15 +258,12 @@ OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     OvsFlowStats stats;
     struct ovs_flow_stats replyStats;
     NL_ERROR nlError = NL_ERROR_SUCCESS;
-
     NL_BUFFER nlBuf;
 
     RtlZeroMemory(&mappedFlow, sizeof(OvsFlowPut));
     RtlZeroMemory(&stats, sizeof(stats));
     RtlZeroMemory(&replyStats, sizeof(replyStats));
 
-    *replyLen = 0;
-
     if (!(usrParamsCtx->outputBuffer)) {
         /* No output buffer */
         rc = STATUS_INVALID_BUFFER_SIZE;
@@ -290,8 +283,28 @@ OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     /* FLOW_DEL command w/o any key input is a flush case. */
     if ((genlMsgHdr->cmd == OVS_FLOW_CMD_DEL) &&
         (!(nlAttrs[OVS_FLOW_ATTR_KEY]))) {
+
         rc = OvsFlushFlowIoctl(ovsHdr->dp_ifindex);
-        goto done;
+
+       if (rc == STATUS_SUCCESS) {
+            /* XXX: refactor this code. */
+            /* So far so good. Prepare the reply for userspace */
+            NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
+                      usrParamsCtx->outputLength);
+
+            /* Prepare nl Msg headers */
+            ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
+                              nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
+                              genlMsgHdr->cmd, OVS_FLOW_VERSION,
+                              ovsHdr->dp_ifindex);
+            if (ok) {
+                *replyLen = msgOut->nlMsg.nlmsgLen;
+            } else {
+                rc = STATUS_INVALID_BUFFER_SIZE;
+            }
+       }
+
+       goto done;
     }
 
     if ((rc = _MapNlToFlowPut(msgIn, nlAttrs[OVS_FLOW_ATTR_KEY],
@@ -305,7 +318,7 @@ OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     rc = OvsPutFlowIoctl(&mappedFlow, sizeof (struct OvsFlowPut),
                          &stats);
     if (rc != STATUS_SUCCESS) {
-        OVS_LOG_ERROR("OvsFlowPut failed.");
+        OVS_LOG_ERROR("OvsPutFlowIoctl failed.");
         goto done;
     }
 
@@ -317,11 +330,16 @@ OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
               usrParamsCtx->outputLength);
 
     /* Prepare nl Msg headers */
-    rc = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
+    ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0,
                       nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
                       genlMsgHdr->cmd, OVS_FLOW_VERSION,
                       ovsHdr->dp_ifindex);
-    ASSERT(rc);
+    if (!ok) {
+        rc = STATUS_INVALID_BUFFER_SIZE;
+        goto done;
+    } else {
+        rc = STATUS_SUCCESS;
+    }
 
     /* Append OVS_FLOW_ATTR_STATS attribute */
     if (!NlMsgPutTailUnspec(&nlBuf, OVS_FLOW_ATTR_STATS,
@@ -336,12 +354,12 @@ OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
 
 done:
 
-    if ((nlError != NL_ERROR_SUCCESS) &&
-        (usrParamsCtx->outputBuffer)) {
+    if (nlError != NL_ERROR_SUCCESS) {
         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
                                        usrParamsCtx->outputBuffer;
-        BuildErrorMsg(msgIn, msgError, nlError);
+        NlBuildErrorMsg(msgIn, msgError, nlError);
         *replyLen = msgError->nlMsg.nlmsgLen;
+        rc = STATUS_SUCCESS;
     }
 
     return rc;
@@ -357,34 +375,31 @@ NTSTATUS
 OvsFlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                        UINT32 *replyLen)
 {
-    NTSTATUS rc = STATUS_SUCCESS;
+    NTSTATUS status = STATUS_SUCCESS;
     NL_ERROR nlError = NL_ERROR_SUCCESS;
     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
 
-    *replyLen = 0;
-
-    if (!(usrParamsCtx->outputBuffer)) {
-        /* No output buffer */
-        rc = STATUS_INVALID_BUFFER_SIZE;
-        goto done;
-    }
-
     if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
-        rc = _FlowNlGetCmdHandler(usrParamsCtx, replyLen);
+        status = _FlowNlGetCmdHandler(usrParamsCtx, replyLen);
+
+        /* No trasanctional errors as of now.
+         * If we have something then we need to convert rc to
+         * nlError. */
+        if ((nlError != NL_ERROR_SUCCESS) &&
+            (usrParamsCtx->outputBuffer)) {
+            POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+                                           usrParamsCtx->outputBuffer;
+            NlBuildErrorMsg(msgIn, msgError, nlError);
+            *replyLen = msgError->nlMsg.nlmsgLen;
+            status = STATUS_SUCCESS;
+            goto done;
+        }
     } else {
-        rc = _FlowNlDumpCmdHandler(usrParamsCtx, replyLen);
-    }
-
-    if ((nlError != NL_ERROR_SUCCESS) &&
-        (usrParamsCtx->outputBuffer)) {
-        POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
-                                       usrParamsCtx->outputBuffer;
-        BuildErrorMsg(msgIn, msgError, nlError);
-        *replyLen = msgError->nlMsg.nlmsgLen;
+        status = _FlowNlDumpCmdHandler(usrParamsCtx, replyLen);
     }
 
 done:
-    return rc;
+    return status;
 }
 
 /*
@@ -398,9 +413,7 @@ _FlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                      UINT32 *replyLen)
 {
     NTSTATUS rc = STATUS_SUCCESS;
-    POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)
-                                  (usrParamsCtx->ovsInstance);
-    POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
+    POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
     PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
     POVS_HDR ovsHdr = &(msgIn->ovsHdr);
     PNL_MSG_HDR nlMsgOutHdr = NULL;
@@ -419,8 +432,7 @@ _FlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     RtlZeroMemory(&getOutput, sizeof(OvsFlowGetOutput));
     UINT32 keyAttrOffset = 0;
     UINT32 tunnelKeyAttrOffset = 0;
-
-    *replyLen = 0;
+    BOOLEAN ok;
 
     if (usrParamsCtx->inputLength > usrParamsCtx->outputLength) {
         /* Should not be the case.
@@ -494,8 +506,12 @@ _FlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
 
     /* Input already has all the attributes for the flow key.
      * Lets copy the values back. */
-    RtlCopyMemory(usrParamsCtx->outputBuffer, usrParamsCtx->inputBuffer,
-                  usrParamsCtx->inputLength);
+    ok = NlMsgPutTail(&nlBuf, (PCHAR)(usrParamsCtx->inputBuffer),
+                      usrParamsCtx->inputLength);
+    if (!ok) {
+        OVS_LOG_ERROR("Could not copy the data to the buffer tail");
+        goto done;
+    }
 
     rc = _MapFlowStatsToNlStats(&nlBuf, &((getOutput.info).stats));
     if (rc != STATUS_SUCCESS) {
@@ -521,7 +537,7 @@ done:
 /*
  *----------------------------------------------------------------------------
  *  _FlowNlDumpCmdHandler --
- *    Handler for OVS_FLOW_CMD_GET command.
+ *    Handler for OVS_FLOW_CMD_DUMP command.
  *----------------------------------------------------------------------------
  */
 NTSTATUS
@@ -577,15 +593,20 @@ _FlowNlDumpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
 
         /* Done with Dump, send NLMSG_DONE */
         if (!(dumpOutput.n)) {
+            BOOLEAN ok;
+
             OVS_LOG_INFO("Dump Done");
 
             nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, NlBufSize(&nlBuf), 0));
-            rc = NlFillNlHdr(&nlBuf, NLMSG_DONE, NLM_F_MULTI,
+            ok = NlFillNlHdr(&nlBuf, NLMSG_DONE, NLM_F_MULTI,
                              nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid);
 
-            if (rc != STATUS_SUCCESS) {
+            if (!ok) {
+                rc = STATUS_INVALID_BUFFER_SIZE;
                 OVS_LOG_ERROR("Unable to prepare DUMP_DONE reply.");
                 break;
+            } else {
+                rc = STATUS_SUCCESS;
             }
 
             NlMsgAlignSize(nlMsgOutHdr);
@@ -594,17 +615,18 @@ _FlowNlDumpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
             FreeUserDumpState(instance);
             break;
         } else {
+            BOOLEAN ok;
 
             hdrOffset = NlBufSize(&nlBuf);
             nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, hdrOffset, 0));
 
             /* Netlink header */
-            rc = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, NLM_F_MULTI,
+            ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, NLM_F_MULTI,
                               nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid,
                               genlMsgHdr->cmd, genlMsgHdr->version,
                               ovsHdr->dp_ifindex);
 
-            if (rc != STATUS_SUCCESS) {
+            if (!ok) {
                 /* Reset rc to success so that we can
                  * send already added messages to user space. */
                 rc = STATUS_SUCCESS;
@@ -649,7 +671,8 @@ _MapFlowInfoToNl(PNL_BUFFER nlBuf, OvsFlowInfo *flowInfo)
 {
     NTSTATUS rc = STATUS_SUCCESS;
 
-    rc = _MapFlowKeyToNlKey(nlBuf, &(flowInfo->key));
+    rc = MapFlowKeyToNlKey(nlBuf, &(flowInfo->key), OVS_FLOW_ATTR_KEY,
+                           OVS_KEY_ATTR_TUNNEL);
     if (rc != STATUS_SUCCESS) {
         goto done;
     }
@@ -740,18 +763,21 @@ error_nested_start:
 
 /*
  *----------------------------------------------------------------------------
- *  _MapFlowKeyToNlKey --
- *    Maps OvsFlowKey to OVS_FLOW_ATTR_KEY attribute.
+ *  MapFlowKeyToNlKey --
+ *   Maps OvsFlowKey to OVS_FLOW_ATTR_KEY attribute.
  *----------------------------------------------------------------------------
  */
-static NTSTATUS
-_MapFlowKeyToNlKey(PNL_BUFFER nlBuf, OvsFlowKey *flowKey)
+NTSTATUS
+MapFlowKeyToNlKey(PNL_BUFFER nlBuf,
+                  OvsFlowKey *flowKey,
+                  UINT16 keyType,
+                  UINT16 tunKeyType)
 {
     NTSTATUS rc = STATUS_SUCCESS;
     struct ovs_key_ethernet ethKey;
     UINT32 offset = 0;
 
-    offset = NlMsgStartNested(nlBuf, OVS_FLOW_ATTR_KEY);
+    offset = NlMsgStartNested(nlBuf, keyType);
     if (!offset) {
         /* Starting the nested attribute failed. */
         rc = STATUS_UNSUCCESSFUL;
@@ -776,7 +802,7 @@ _MapFlowKeyToNlKey(PNL_BUFFER nlBuf, OvsFlowKey *flowKey)
     }
 
     if (!NlMsgPutTailU16(nlBuf, OVS_KEY_ATTR_ETHERTYPE,
-                         htons(flowKey->l2.dlType))) {
+                         flowKey->l2.dlType)) {
         rc = STATUS_UNSUCCESSFUL;
         goto done;
     }
@@ -790,7 +816,7 @@ _MapFlowKeyToNlKey(PNL_BUFFER nlBuf, OvsFlowKey *flowKey)
     }
 
     /* ==== L3 + L4 ==== */
-    switch (flowKey->l2.dlType) {
+    switch (ntohs(flowKey->l2.dlType)) {
         case ETH_TYPE_IPV4: {
         IpKey *ipv4FlowPutKey = &(flowKey->ipKey);
         rc = _MapFlowIpv4KeyToNlKey(nlBuf, ipv4FlowPutKey);
@@ -821,7 +847,8 @@ _MapFlowKeyToNlKey(PNL_BUFFER nlBuf, OvsFlowKey *flowKey)
     }
 
     if (flowKey->tunKey.dst) {
-        rc = _MapFlowTunKeyToNlKey(nlBuf, &(flowKey->tunKey));
+        rc = MapFlowTunKeyToNlKey(nlBuf, &(flowKey->tunKey),
+                                  tunKeyType);
         if (rc != STATUS_SUCCESS) {
             goto done;
         }
@@ -835,17 +862,19 @@ error_nested_start:
 
 /*
  *----------------------------------------------------------------------------
- *  _MapFlowTunKeyToNlKey --
- *    Maps OvsIPv4TunnelKey to OVS_TUNNEL_KEY_ATTR_ID attribute.
+ *  MapFlowTunKeyToNlKey --
+ *   Maps OvsIPv4TunnelKey to OVS_TUNNEL_KEY_ATTR_ID attribute.
  *----------------------------------------------------------------------------
  */
-static NTSTATUS
-_MapFlowTunKeyToNlKey(PNL_BUFFER nlBuf, OvsIPv4TunnelKey *tunKey)
+NTSTATUS
+MapFlowTunKeyToNlKey(PNL_BUFFER nlBuf,
+                     OvsIPv4TunnelKey *tunKey,
+                     UINT16 tunKeyType)
 {
     NTSTATUS rc = STATUS_SUCCESS;
     UINT32 offset = 0;
 
-    offset = NlMsgStartNested(nlBuf, OVS_KEY_ATTR_TUNNEL);
+    offset = NlMsgStartNested(nlBuf, tunKeyType);
     if (!offset) {
         /* Starting the nested attribute failed. */
         rc = STATUS_UNSUCCESSFUL;
@@ -1105,7 +1134,18 @@ _MapFlowArpKeyToNlKey(PNL_BUFFER nlBuf, ArpKey *arpFlowPutKey)
     RtlCopyMemory(&(arpKey.arp_sha), arpFlowPutKey->arpSha, ETH_ADDR_LEN);
     RtlCopyMemory(&(arpKey.arp_tha), arpFlowPutKey->arpTha, ETH_ADDR_LEN);
 
-    arpKey.arp_op = arpFlowPutKey->nwProto;
+    /*
+     * Flow_Extract() stores 'nwProto' in host order for ARP since 'nwProto' is
+     * 1 byte field and the ARP opcode is 2 bytes, and all of the kernel code
+     * understand this while looking at an ARP key.
+     * While we pass up the ARP key to userspace, convert from host order to
+     * network order. Likewise, when processing an ARP key from userspace,
+     * convert from network order to host order.
+     *
+     * It is important to note that the flow table stores the ARP opcode field
+     * in host order.
+     */
+    arpKey.arp_op = htons(arpFlowPutKey->nwProto);
 
     if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_ARP,
                            (PCHAR)(&arpKey),
@@ -1155,8 +1195,6 @@ _MapNlToFlowPut(POVS_MESSAGE msgIn, PNL_ATTR keyAttr,
                               (keyAttrs[OVS_KEY_ATTR_TUNNEL])
                               - (PCHAR)nlMsgHdr);
 
-        OVS_LOG_ERROR("Parse Flow Tunnel Key Policy");
-
         /* Get tunnel keys attributes */
         if ((NlAttrParseNested(nlMsgHdr, tunnelKeyAttrOffset,
                                NlAttrLen(keyAttrs[OVS_KEY_ATTR_TUNNEL]),
@@ -1232,29 +1270,36 @@ _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs,
                      PNL_ATTR *tunnelAttrs,
                      OvsFlowKey *destKey)
 {
-    const struct ovs_key_ethernet *eth_key;
-
     _MapTunAttrToFlowPut(keyAttrs, tunnelAttrs, destKey);
 
     /* ===== L2 headers ===== */
     destKey->l2.inPort = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_IN_PORT]);
-    eth_key = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ETHERNET]);
-    RtlCopyMemory(destKey->l2.dlSrc, eth_key->eth_src, ETH_ADDR_LEN);
-    RtlCopyMemory(destKey->l2.dlDst, eth_key->eth_dst, ETH_ADDR_LEN);
 
-    destKey->l2.dlType = ntohs((NlAttrGetU16(keyAttrs
-                          [OVS_KEY_ATTR_ETHERTYPE])));
+    if (keyAttrs[OVS_KEY_ATTR_ETHERNET]) {
+        const struct ovs_key_ethernet *eth_key;
+        eth_key = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ETHERNET]);
+        RtlCopyMemory(destKey->l2.dlSrc, eth_key->eth_src, ETH_ADDR_LEN);
+        RtlCopyMemory(destKey->l2.dlDst, eth_key->eth_dst, ETH_ADDR_LEN);
+    }
+
+    /* TODO: Ideally ETHERTYPE should not be optional.
+     * But during vswitchd bootup we are seeing FLOW_ADD
+     * requests with no ETHERTYPE attributes.
+     * Need to verify this. */
+    if (keyAttrs[OVS_KEY_ATTR_ETHERTYPE]) {
+        destKey->l2.dlType = (NlAttrGetU16(keyAttrs
+                                        [OVS_KEY_ATTR_ETHERTYPE]));
+    }
 
     if (keyAttrs[OVS_KEY_ATTR_VLAN]) {
-        destKey->l2.vlanTci = NlAttrGetU16(keyAttrs
-                              [OVS_KEY_ATTR_VLAN]);
+        destKey->l2.vlanTci = NlAttrGetU16(keyAttrs[OVS_KEY_ATTR_VLAN]);
     }
 
     /* ==== L3 + L4. ==== */
     destKey->l2.keyLen = OVS_WIN_TUNNEL_KEY_SIZE + OVS_L2_KEY_SIZE
                          - destKey->l2.offset;
 
-    switch (destKey->l2.dlType) {
+    switch (ntohs(destKey->l2.dlType)) {
     case ETH_TYPE_IPV4: {
 
         if (keyAttrs[OVS_KEY_ATTR_IPV4]) {
@@ -1369,22 +1414,27 @@ _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs,
     }
     case ETH_TYPE_ARP:
     case ETH_TYPE_RARP: {
-        ArpKey *arpFlowPutKey = &destKey->arpKey;
-        const struct ovs_key_arp *arpKey;
 
-        arpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ARP]);
+        if (keyAttrs[OVS_KEY_ATTR_ARP]) {
+            ArpKey *arpFlowPutKey = &destKey->arpKey;
+            const struct ovs_key_arp *arpKey;
 
-        arpFlowPutKey->nwSrc = arpKey->arp_sip;
-        arpFlowPutKey->nwDst = arpKey->arp_tip;
+            arpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ARP]);
 
-        RtlCopyMemory(arpFlowPutKey->arpSha, arpKey->arp_sha, ETH_ADDR_LEN);
-        RtlCopyMemory(arpFlowPutKey->arpTha, arpKey->arp_tha, ETH_ADDR_LEN);
-        arpFlowPutKey->nwProto = (UINT8)(arpKey->arp_op);
-        arpFlowPutKey->pad[0] = 0;
-        arpFlowPutKey->pad[1] = 0;
-        arpFlowPutKey->pad[2] = 0;
-        destKey->l2.keyLen += OVS_ARP_KEY_SIZE;
-        break;
+            arpFlowPutKey->nwSrc = arpKey->arp_sip;
+            arpFlowPutKey->nwDst = arpKey->arp_tip;
+
+            RtlCopyMemory(arpFlowPutKey->arpSha, arpKey->arp_sha, ETH_ADDR_LEN);
+            RtlCopyMemory(arpFlowPutKey->arpTha, arpKey->arp_tha, ETH_ADDR_LEN);
+            /* Kernel datapath assumes 'arpFlowPutKey->nwProto' to be in host
+             * order. */
+            arpFlowPutKey->nwProto = (UINT8)ntohs((arpKey->arp_op));
+            arpFlowPutKey->pad[0] = 0;
+            arpFlowPutKey->pad[1] = 0;
+            arpFlowPutKey->pad[2] = 0;
+            destKey->l2.keyLen += OVS_ARP_KEY_SIZE;
+            break;
+        }
     }
     }
 }
@@ -1461,8 +1511,13 @@ OvsDeleteFlowTable(OVS_DATAPATH *datapath)
     }
 
     DeleteAllFlows(datapath);
-    OvsFreeMemory(datapath->flowTable);
+    OvsFreeMemoryWithTag(datapath->flowTable, OVS_FLOW_POOL_TAG);
     datapath->flowTable = NULL;
+
+    if (datapath->lock == NULL) {
+        return NDIS_STATUS_SUCCESS;
+    }
+
     NdisFreeRWLock(datapath->lock);
 
     return NDIS_STATUS_SUCCESS;
@@ -1483,8 +1538,8 @@ OvsAllocateFlowTable(OVS_DATAPATH *datapath,
     PLIST_ENTRY bucket;
     int i;
 
-    datapath->flowTable = OvsAllocateMemory(OVS_FLOW_TABLE_SIZE *
-                                            sizeof (LIST_ENTRY));
+    datapath->flowTable = OvsAllocateMemoryWithTag(
+        OVS_FLOW_TABLE_SIZE * sizeof(LIST_ENTRY), OVS_FLOW_POOL_TAG);
     if (!datapath->flowTable) {
         return NDIS_STATUS_RESOURCES;
     }
@@ -1494,6 +1549,10 @@ OvsAllocateFlowTable(OVS_DATAPATH *datapath,
     }
     datapath->lock = NdisAllocateRWLock(switchContext->NdisFilterHandle);
 
+    if (!datapath->lock) {
+        return NDIS_STATUS_RESOURCES;
+    }
+
     return NDIS_STATUS_SUCCESS;
 }
 
@@ -1836,7 +1895,6 @@ RemoveFlow(OVS_DATAPATH *datapath,
 {
     OvsFlow *f = *flow;
     *flow = NULL;
-    UNREFERENCED_PARAMETER(datapath);
 
     ASSERT(datapath->nFlows);
     datapath->nFlows--;
@@ -1926,7 +1984,7 @@ VOID
 FreeFlow(OvsFlow *flow)
 {
     ASSERT(flow);
-    OvsFreeMemory(flow);
+    OvsFreeMemoryWithTag(flow, OVS_FLOW_POOL_TAG);
 }
 
 NTSTATUS
@@ -1945,26 +2003,23 @@ OvsDoDumpFlows(OvsFlowDumpInput *dumpInput,
     BOOLEAN findNextNonEmpty = FALSE;
 
     dpNo = dumpInput->dpNo;
-    NdisAcquireSpinLock(gOvsCtrlLock);
-    if (gOvsSwitchContext == NULL ||
-        gOvsSwitchContext->dpNo != dpNo) {
+    if (gOvsSwitchContext->dpNo != dpNo) {
         status = STATUS_INVALID_PARAMETER;
-        goto unlock;
+        goto exit;
     }
 
     rowIndex = dumpInput->position[0];
     if (rowIndex >= OVS_FLOW_TABLE_SIZE) {
         dumpOutput->n = 0;
         *replyLen = sizeof(*dumpOutput);
-        goto unlock;
+        goto exit;
     }
 
     columnIndex = dumpInput->position[1];
 
     datapath = &gOvsSwitchContext->datapath;
     ASSERT(datapath);
-    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
-    OvsAcquireDatapathRead(datapath, &dpLockState, TRUE);
+    OvsAcquireDatapathRead(datapath, &dpLockState, FALSE);
 
     head = &datapath->flowTable[rowIndex];
     node = head->Flink;
@@ -2013,33 +2068,10 @@ OvsDoDumpFlows(OvsFlowDumpInput *dumpInput,
 dp_unlock:
     OvsReleaseDatapath(datapath, &dpLockState);
 
-unlock:
-    NdisReleaseSpinLock(gOvsCtrlLock);
+exit:
     return status;
 }
 
-NTSTATUS
-OvsDumpFlowIoctl(PVOID inputBuffer,
-                 UINT32 inputLength,
-                 PVOID outputBuffer,
-                 UINT32 outputLength,
-                 UINT32 *replyLen)
-{
-    OvsFlowDumpOutput *dumpOutput = (OvsFlowDumpOutput *)outputBuffer;
-    OvsFlowDumpInput *dumpInput = (OvsFlowDumpInput *)inputBuffer;
-
-    if (inputBuffer == NULL || outputBuffer == NULL) {
-        return STATUS_INVALID_PARAMETER;
-    }
-
-    if ((inputLength != sizeof(OvsFlowDumpInput))
-        || (outputLength != sizeof *dumpOutput + dumpInput->actionsLen)) {
-        return STATUS_INFO_LENGTH_MISMATCH;
-    }
-
-    return OvsDoDumpFlows(dumpInput, dumpOutput, replyLen);
-}
-
 static NTSTATUS
 ReportFlowInfo(OvsFlow *flow,
                UINT32 getFlags,
@@ -2097,22 +2129,18 @@ OvsPutFlowIoctl(PVOID inputBuffer,
     }
 
     dpNo = put->dpNo;
-    NdisAcquireSpinLock(gOvsCtrlLock);
-    if (gOvsSwitchContext == NULL ||
-        gOvsSwitchContext->dpNo != dpNo) {
+    if (gOvsSwitchContext->dpNo != dpNo) {
         status = STATUS_INVALID_PARAMETER;
-        goto unlock;
+        goto exit;
     }
 
     datapath = &gOvsSwitchContext->datapath;
     ASSERT(datapath);
-    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
-    OvsAcquireDatapathWrite(datapath, &dpLockState, TRUE);
+    OvsAcquireDatapathWrite(datapath, &dpLockState, FALSE);
     status = HandleFlowPut(put, datapath, stats);
     OvsReleaseDatapath(datapath, &dpLockState);
 
-unlock:
-    NdisReleaseSpinLock(gOvsCtrlLock);
+exit:
     return status;
 }
 
@@ -2144,7 +2172,6 @@ HandleFlowPut(OvsFlowPut *put,
 
         status = OvsPrepareFlow(&KernelFlow, put, hash);
         if (status != STATUS_SUCCESS) {
-            FreeFlow(KernelFlow);
             return STATUS_UNSUCCESSFUL;
         }
 
@@ -2214,7 +2241,9 @@ HandleFlowPut(OvsFlowPut *put,
                     RemoveFlow(datapath, &KernelFlow);
                 }
             } else {
-                return STATUS_UNSUCCESSFUL;
+                /* Return success if an identical flow already exists. */
+                /* XXX: should we return EEXIST in a netlink error? */
+                return STATUS_SUCCESS;
             }
         }
     }
@@ -2231,7 +2260,8 @@ OvsPrepareFlow(OvsFlow **flow,
 
     do {
         *flow = localFlow =
-            OvsAllocateMemory(sizeof(OvsFlow) + put->actionsLen);
+            OvsAllocateMemoryWithTag(sizeof(OvsFlow) + put->actionsLen,
+                                     OVS_FLOW_POOL_TAG);
         if (localFlow == NULL) {
             status = STATUS_NO_MEMORY;
             break;
@@ -2277,17 +2307,14 @@ OvsGetFlowIoctl(PVOID inputBuffer,
     }
 
     dpNo = getInput->dpNo;
-    NdisAcquireSpinLock(gOvsCtrlLock);
-    if (gOvsSwitchContext == NULL ||
-        gOvsSwitchContext->dpNo != dpNo) {
+    if (gOvsSwitchContext->dpNo != dpNo) {
         status = STATUS_INVALID_PARAMETER;
-        goto unlock;
+        goto exit;
     }
 
     datapath = &gOvsSwitchContext->datapath;
     ASSERT(datapath);
-    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
-    OvsAcquireDatapathRead(datapath, &dpLockState, TRUE);
+    OvsAcquireDatapathRead(datapath, &dpLockState, FALSE);
     flow = OvsLookupFlow(datapath, &getInput->key, &hash, FALSE);
     if (!flow) {
         status = STATUS_INVALID_PARAMETER;
@@ -2299,8 +2326,7 @@ OvsGetFlowIoctl(PVOID inputBuffer,
 
 dp_unlock:
     OvsReleaseDatapath(datapath, &dpLockState);
-unlock:
-    NdisReleaseSpinLock(gOvsCtrlLock);
+exit:
     return status;
 }
 
@@ -2311,23 +2337,58 @@ OvsFlushFlowIoctl(UINT32 dpNo)
     OVS_DATAPATH *datapath = NULL;
     LOCK_STATE_EX dpLockState;
 
-    NdisAcquireSpinLock(gOvsCtrlLock);
-    if (gOvsSwitchContext == NULL ||
-        gOvsSwitchContext->dpNo != dpNo) {
+    if (gOvsSwitchContext->dpNo != dpNo) {
         status = STATUS_INVALID_PARAMETER;
-        goto unlock;
+        goto exit;
     }
 
     datapath = &gOvsSwitchContext->datapath;
     ASSERT(datapath);
-    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
-    OvsAcquireDatapathWrite(datapath, &dpLockState, TRUE);
+    OvsAcquireDatapathWrite(datapath, &dpLockState, FALSE);
     DeleteAllFlows(datapath);
     OvsReleaseDatapath(datapath, &dpLockState);
 
-unlock:
-    NdisReleaseSpinLock(gOvsCtrlLock);
+exit:
     return status;
 }
 
+UINT32
+OvsFlowKeyAttrSize(void)
+{
+    return NlAttrTotalSize(4)   /* OVS_KEY_ATTR_PRIORITY */
+         + NlAttrTotalSize(0)   /* OVS_KEY_ATTR_TUNNEL */
+         + OvsTunKeyAttrSize()
+         + NlAttrTotalSize(4)   /* OVS_KEY_ATTR_IN_PORT */
+         + NlAttrTotalSize(4)   /* OVS_KEY_ATTR_SKB_MARK */
+         + NlAttrTotalSize(4)   /* OVS_KEY_ATTR_DP_HASH */
+         + NlAttrTotalSize(4)   /* OVS_KEY_ATTR_RECIRC_ID */
+         + NlAttrTotalSize(12)  /* OVS_KEY_ATTR_ETHERNET */
+         + NlAttrTotalSize(2)   /* OVS_KEY_ATTR_ETHERTYPE */
+         + NlAttrTotalSize(4)   /* OVS_KEY_ATTR_VLAN */
+         + NlAttrTotalSize(0)   /* OVS_KEY_ATTR_ENCAP */
+         + NlAttrTotalSize(2)   /* OVS_KEY_ATTR_ETHERTYPE */
+         + NlAttrTotalSize(40)  /* OVS_KEY_ATTR_IPV6 */
+         + NlAttrTotalSize(2)   /* OVS_KEY_ATTR_ICMPV6 */
+         + NlAttrTotalSize(28); /* OVS_KEY_ATTR_ND */
+}
+
+UINT32
+OvsTunKeyAttrSize(void)
+{
+    /* Whenever adding new OVS_TUNNEL_KEY_ FIELDS, we should consider
+     * updating this function.
+     */
+    return NlAttrTotalSize(8)    /* OVS_TUNNEL_KEY_ATTR_ID */
+         + NlAttrTotalSize(4)    /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */
+         + NlAttrTotalSize(4)    /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */
+         + NlAttrTotalSize(1)    /* OVS_TUNNEL_KEY_ATTR_TOS */
+         + NlAttrTotalSize(1)    /* OVS_TUNNEL_KEY_ATTR_TTL */
+         + NlAttrTotalSize(0)    /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
+         + NlAttrTotalSize(0)    /* OVS_TUNNEL_KEY_ATTR_CSUM */
+         + NlAttrTotalSize(0)    /* OVS_TUNNEL_KEY_ATTR_OAM */
+         + NlAttrTotalSize(256)  /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */
+         + NlAttrTotalSize(2)    /* OVS_TUNNEL_KEY_ATTR_TP_SRC */
+         + NlAttrTotalSize(2);   /* OVS_TUNNEL_KEY_ATTR_TP_DST */
+}
+
 #pragma warning( pop )