/*
- * Copyright (c) 2014 VMware, Inc.
+ * Copyright (c) 2014, 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.
#include "precomp.h"
-#include "Switch.h"
-#include "Vport.h"
+#include "Debug.h"
#include "Event.h"
-#include "User.h"
-#include "NetProto.h"
#include "Flow.h"
-#include "Vxlan.h"
-#include "Stt.h"
-#include "Checksum.h"
+#include "Gre.h"
+#include "Mpls.h"
+#include "NetProto.h"
+#include "Offload.h"
#include "PacketIO.h"
+#include "Stt.h"
+#include "Switch.h"
+#include "User.h"
+#include "Vport.h"
+#include "Vxlan.h"
#ifdef OVS_DBG_MOD
#undef OVS_DBG_MOD
#endif
#define OVS_DBG_MOD OVS_DBG_ACTION
-#include "Debug.h"
typedef struct _OVS_ACTION_STATS {
+ UINT64 rxGre;
+ UINT64 txGre;
UINT64 rxVxlan;
UINT64 txVxlan;
UINT64 rxStt;
/* XXX: we should also check for the length of the UDP payload to pick
* packets only if they are at least VXLAN header size.
*/
- if (!flowKey->ipKey.nwFrag &&
- flowKey->ipKey.nwProto == IPPROTO_UDP) {
+ if (!flowKey->ipKey.nwFrag) {
UINT16 dstPort = htons(flowKey->ipKey.l4.tpDst);
- tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
- dstPort,
- OVS_VPORT_TYPE_VXLAN);
- if (tunnelVport) {
- ovsActionStats.rxVxlan++;
- }
- } else if (!flowKey->ipKey.nwFrag &&
- flowKey->ipKey.nwProto == IPPROTO_TCP) {
- UINT16 dstPort = htons(flowKey->ipKey.l4.tpDst);
- tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
- dstPort,
- OVS_VPORT_TYPE_STT);
- if (tunnelVport) {
- ovsActionStats.rxStt++;
+ switch (flowKey->ipKey.nwProto) {
+ case IPPROTO_GRE:
+ tunnelVport = OvsFindTunnelVportByPortType(ovsFwdCtx->switchContext,
+ OVS_VPORT_TYPE_GRE);
+ if (tunnelVport) {
+ ovsActionStats.rxGre++;
+ }
+ break;
+ case IPPROTO_TCP:
+ tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
+ dstPort,
+ OVS_VPORT_TYPE_STT);
+ if (tunnelVport) {
+ ovsActionStats.rxStt++;
+ }
+ break;
+ case IPPROTO_UDP:
+ tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
+ dstPort,
+ OVS_VPORT_TYPE_VXLAN);
+ if (tunnelVport) {
+ ovsActionStats.rxVxlan++;
+ }
+ break;
}
}
-
// We might get tunnel packets even before the tunnel gets initialized.
if (tunnelVport) {
ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
* If the packet will not be encapsulated, consume the tunnel context
* by clearing it.
*/
- if (ovsFwdCtx->srcVportNo != OVS_DEFAULT_PORT_NO) {
+ if (ovsFwdCtx->srcVportNo != OVS_DPPORT_NUMBER_INVALID) {
POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(
ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
/* Tunnel the packet only if tunnel context is set. */
if (ovsFwdCtx->tunKey.dst != 0) {
switch(dstVport->ovsType) {
+ case OVS_VPORT_TYPE_GRE:
+ ovsActionStats.txGre++;
+ break;
case OVS_VPORT_TYPE_VXLAN:
ovsActionStats.txVxlan++;
break;
ovsActionStats.txStt++;
break;
}
- ovsActionStats.txVxlan++;
ovsFwdCtx->tunnelTxNic = dstVport;
}
POVS_VPORT_ENTRY vport =
OvsFindVportByPortNo(ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
- ASSERT(FALSE); // XXX: let's catch this for now
OvsCompleteNBLForwardingCtx(ovsFwdCtx,
L"OVS-Dropped due to internal/tunnel port removal");
ovsActionStats.noVport++;
/* Assert that in the Rx direction, key is always setup. */
ASSERT(ovsFwdCtx->tunnelRxNic == NULL || ovsFwdCtx->tunKey.dst != 0);
- status = OvsExtractFlow(ovsFwdCtx->curNbl, ovsFwdCtx->srcVportNo,
- &key, &ovsFwdCtx->layers, ovsFwdCtx->tunKey.dst != 0 ?
- &ovsFwdCtx->tunKey : NULL);
+ status =
+ OvsExtractFlow(ovsFwdCtx->curNbl, ovsFwdCtx->srcVportNo,
+ &key, &ovsFwdCtx->layers,
+ ovsFwdCtx->tunKey.dst != 0 ? &ovsFwdCtx->tunKey : NULL);
if (status != NDIS_STATUS_SUCCESS) {
OvsCompleteNBLForwardingCtx(ovsFwdCtx,
L"OVS-Flow extract failed");
OvsFlowUsed(flow, ovsFwdCtx->curNbl, &ovsFwdCtx->layers);
ovsFwdCtx->switchContext->datapath.hits++;
status = OvsActionsExecute(ovsFwdCtx->switchContext,
- ovsFwdCtx->completionList, ovsFwdCtx->curNbl,
- ovsFwdCtx->srcVportNo, ovsFwdCtx->sendFlags,
- &key, &hash, &ovsFwdCtx->layers,
- flow->actions, flow->actionsLen);
+ ovsFwdCtx->completionList, ovsFwdCtx->curNbl,
+ ovsFwdCtx->srcVportNo, ovsFwdCtx->sendFlags,
+ &key, &hash, &ovsFwdCtx->layers,
+ flow->actions, flow->actionsLen);
ovsFwdCtx->curNbl = NULL;
} else {
LIST_ENTRY missedPackets;
UINT32 num = 0;
ovsFwdCtx->switchContext->datapath.misses++;
InitializeListHead(&missedPackets);
- status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS,
- ovsFwdCtx->srcVportNo,
+ status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS, vport,
&key,ovsFwdCtx->curNbl,
- ovsFwdCtx->tunnelRxNic != NULL, &ovsFwdCtx->layers,
+ FALSE, &ovsFwdCtx->layers,
ovsFwdCtx->switchContext, &missedPackets, &num);
if (num) {
OvsQueuePackets(&missedPackets, num);
/* Do the encap. Encap function does not consume the NBL. */
switch(ovsFwdCtx->tunnelTxNic->ovsType) {
+ case OVS_VPORT_TYPE_GRE:
+ status = OvsEncapGre(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
+ &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
+ &ovsFwdCtx->layers, &newNbl);
+ break;
case OVS_VPORT_TYPE_VXLAN:
- status = OvsEncapVxlan(ovsFwdCtx->curNbl, &ovsFwdCtx->tunKey,
- ovsFwdCtx->switchContext,
+ status = OvsEncapVxlan(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
+ &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
&ovsFwdCtx->layers, &newNbl);
break;
case OVS_VPORT_TYPE_STT:
status = OvsEncapStt(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
- &ovsFwdCtx->tunKey,
- ovsFwdCtx->switchContext,
+ &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
&ovsFwdCtx->layers, &newNbl);
break;
default:
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
PNET_BUFFER_LIST newNbl = NULL;
POVS_VPORT_ENTRY tunnelRxVport = ovsFwdCtx->tunnelRxNic;
+ PCWSTR dropReason = L"OVS-dropped due to new decap packet";
if (OvsValidateIPChecksum(ovsFwdCtx->curNbl, &ovsFwdCtx->layers)
!= NDIS_STATUS_SUCCESS) {
*/
switch(tunnelRxVport->ovsType) {
+ case OVS_VPORT_TYPE_GRE:
+ status = OvsDecapGre(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
+ &ovsFwdCtx->tunKey, &newNbl);
+ break;
case OVS_VPORT_TYPE_VXLAN:
status = OvsDecapVxlan(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
&ovsFwdCtx->tunKey, &newNbl);
case OVS_VPORT_TYPE_STT:
status = OvsDecapStt(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
&ovsFwdCtx->tunKey, &newNbl);
+ if (status == NDIS_STATUS_SUCCESS && newNbl == NULL) {
+ /* This was an STT-LSO Fragment */
+ dropReason = L"OVS-STT segment is cached";
+ }
break;
default:
OVS_LOG_ERROR("Rx: Unhandled tunnel type: %d\n",
* tunnelRxNic and other fields will be cleared, re-init the context
* before usage.
*/
- OvsCompleteNBLForwardingCtx(ovsFwdCtx,
- L"OVS-dropped due to new decap packet");
+ OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason);
- /* Decapsulated packet is in a new NBL */
- ovsFwdCtx->tunnelRxNic = tunnelRxVport;
- OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
- newNbl, tunnelRxVport->portNo, 0,
- NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
- ovsFwdCtx->completionList,
- &ovsFwdCtx->layers, FALSE);
+ if (newNbl) {
+ /* Decapsulated packet is in a new NBL */
+ ovsFwdCtx->tunnelRxNic = tunnelRxVport;
+ OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
+ newNbl, tunnelRxVport->portNo, 0,
+ NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
+ ovsFwdCtx->completionList,
+ &ovsFwdCtx->layers, FALSE);
- /*
- * Set the NBL's SourcePortId and SourceNicIndex to default values to
- * keep NDIS happy when we forward the packet.
- */
- ovsFwdCtx->fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
- ovsFwdCtx->fwdDetail->SourceNicIndex = 0;
+ /*
+ * Set the NBL's SourcePortId and SourceNicIndex to default values to
+ * keep NDIS happy when we forward the packet.
+ */
+ ovsFwdCtx->fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
+ ovsFwdCtx->fwdDetail->SourceNicIndex = 0;
- status = OvsDoFlowLookupOutput(ovsFwdCtx);
+ status = OvsDoFlowLookupOutput(ovsFwdCtx);
+ }
ASSERT(ovsFwdCtx->curNbl == NULL);
OvsClearTunRxCtx(ovsFwdCtx);
{
PNET_BUFFER_LIST newNbl;
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
- PNET_BUFFER nb;
/*
* Create a copy and work on the copy after this point. The original NBL is
* XXX Head room needs to include the additional encap.
* XXX copySize check is not considering multiple NBs.
*/
- nb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
0, 0, TRUE /*copy NBL info*/);
ASSERT(ovsFwdCtx->destPortsSizeOut > 0 ||
ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL);
- /* Send the original packet out */
+ /* Send the original packet out and save the original source port number */
+ UINT32 tempVportNo = ovsFwdCtx->srcVportNo;
status = OvsOutputForwardingCtx(ovsFwdCtx);
ASSERT(ovsFwdCtx->curNbl == NULL);
ASSERT(ovsFwdCtx->destPortsSizeOut == 0);
OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
} else {
status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
- newNbl, ovsFwdCtx->srcVportNo, 0,
+ newNbl, tempVportNo, 0,
NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
ovsFwdCtx->completionList,
&ovsFwdCtx->layers, FALSE);
/*
* --------------------------------------------------------------------------
- * OvsPopVlanInPktBuf --
- * Function to pop a VLAN tag when the tag is in the packet buffer.
+ * OvsPopFieldInPacketBuf --
+ * Function to pop a specified field of length 'shiftLength' located at
+ * 'shiftOffset' from the ethernet header. The data on the left of the
+ * 'shiftOffset' is right shifted.
+ *
+ * Returns a pointer to the new start in 'bufferData'.
* --------------------------------------------------------------------------
*/
static __inline NDIS_STATUS
-OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
+OvsPopFieldInPacketBuf(OvsForwardingContext *ovsFwdCtx,
+ UINT32 shiftOffset,
+ UINT32 shiftLength,
+ PUINT8 *bufferData)
{
PNET_BUFFER curNb;
PMDL curMdl;
PUINT8 bufferStart;
- ULONG dataLength = sizeof (DL_EUI48) + sizeof (DL_EUI48);
UINT32 packetLen, mdlLen;
PNET_BUFFER_LIST newNbl;
NDIS_STATUS status;
+ PUINT8 tempBuffer[ETH_HEADER_LENGTH];
- /*
- * Declare a dummy vlanTag structure since we need to compute the size
- * of shiftLength. The NDIS one is a unionized structure.
- */
- NDIS_PACKET_8021Q_INFO vlanTag = {0};
- ULONG shiftLength = sizeof (vlanTag.TagHeader);
- PUINT8 tempBuffer[sizeof (DL_EUI48) + sizeof (DL_EUI48)];
+ ASSERT(shiftOffset > ETH_ADDR_LENGTH);
newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
0, 0, TRUE /* copy NBL info */);
/* Complete the original NBL and create a copy to modify. */
OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to copy");
- status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
- newNbl, ovsFwdCtx->srcVportNo, 0,
+ status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext, newNbl,
+ ovsFwdCtx->srcVportNo, 0,
NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
NULL, &ovsFwdCtx->layers, FALSE);
if (status != NDIS_STATUS_SUCCESS) {
return NDIS_STATUS_RESOURCES;
}
mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
- /* Bail out if L2 + VLAN header is not contiguous in the first buffer. */
- if (MIN(packetLen, mdlLen) < sizeof (EthHdr) + shiftLength) {
+ /* Bail out if L2 + shiftLength is not contiguous in the first buffer. */
+ if (MIN(packetLen, mdlLen) < sizeof(EthHdr) + shiftLength) {
ASSERT(FALSE);
return NDIS_STATUS_FAILURE;
}
bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
- RtlCopyMemory(tempBuffer, bufferStart, dataLength);
- RtlCopyMemory(bufferStart + shiftLength, tempBuffer, dataLength);
+ RtlCopyMemory(tempBuffer, bufferStart, shiftOffset);
+ RtlCopyMemory(bufferStart + shiftLength, tempBuffer, shiftOffset);
NdisAdvanceNetBufferDataStart(curNb, shiftLength, FALSE, NULL);
+ if (bufferData) {
+ *bufferData = bufferStart + shiftLength;
+ }
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsPopVlanInPktBuf --
+ * Function to pop a VLAN tag when the tag is in the packet buffer.
+ * --------------------------------------------------------------------------
+ */
+static __inline NDIS_STATUS
+OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
+{
+ /*
+ * Declare a dummy vlanTag structure since we need to compute the size
+ * of shiftLength. The NDIS one is a unionized structure.
+ */
+ NDIS_PACKET_8021Q_INFO vlanTag = {0};
+ UINT32 shiftLength = sizeof(vlanTag.TagHeader);
+ UINT32 shiftOffset = sizeof(DL_EUI48) + sizeof(DL_EUI48);
+
+ return OvsPopFieldInPacketBuf(ovsFwdCtx, shiftOffset, shiftLength, NULL);
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsActionMplsPop --
+ * Function to pop the first MPLS label from the current packet.
+ * --------------------------------------------------------------------------
+ */
+static __inline NDIS_STATUS
+OvsActionMplsPop(OvsForwardingContext *ovsFwdCtx,
+ ovs_be16 ethertype)
+{
+ NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+ OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
+ EthHdr *ethHdr = NULL;
+
+ status = OvsPopFieldInPacketBuf(ovsFwdCtx, sizeof(*ethHdr),
+ MPLS_HLEN, (PUINT8*)ðHdr);
+ if (status == NDIS_STATUS_SUCCESS) {
+ if (ethHdr && OvsEthertypeIsMpls(ethHdr->Type)) {
+ ethHdr->Type = ethertype;
+ }
+
+ layers->l3Offset -= MPLS_HLEN;
+ layers->l4Offset -= MPLS_HLEN;
+ }
+
+ return status;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsActionMplsPush --
+ * Function to push the MPLS label into the current packet.
+ * --------------------------------------------------------------------------
+ */
+static __inline NDIS_STATUS
+OvsActionMplsPush(OvsForwardingContext *ovsFwdCtx,
+ const struct ovs_action_push_mpls *mpls)
+{
+ NDIS_STATUS status;
+ PNET_BUFFER curNb = NULL;
+ PMDL curMdl = NULL;
+ PUINT8 bufferStart = NULL;
+ OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
+ EthHdr *ethHdr = NULL;
+ MPLSHdr *mplsHdr = NULL;
+ UINT32 mdlLen = 0, curMdlOffset = 0;
+ PNET_BUFFER_LIST newNbl;
+
+ newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
+ layers->l3Offset, MPLS_HLEN, TRUE);
+ if (!newNbl) {
+ ovsActionStats.noCopiedNbl++;
+ return NDIS_STATUS_RESOURCES;
+ }
+ OvsCompleteNBLForwardingCtx(ovsFwdCtx,
+ L"Complete after partial copy.");
+
+ status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
+ newNbl, ovsFwdCtx->srcVportNo, 0,
+ NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
+ NULL, &ovsFwdCtx->layers, FALSE);
+ if (status != NDIS_STATUS_SUCCESS) {
+ OvsCompleteNBLForwardingCtx(ovsFwdCtx,
+ L"OVS-Dropped due to resources");
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
+ ASSERT(curNb->Next == NULL);
+
+ status = NdisRetreatNetBufferDataStart(curNb, MPLS_HLEN, 0, NULL);
+ if (status != NDIS_STATUS_SUCCESS) {
+ return status;
+ }
+
+ curMdl = NET_BUFFER_CURRENT_MDL(curNb);
+ NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
+ if (!curMdl) {
+ ovsActionStats.noResource++;
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
+ mdlLen -= curMdlOffset;
+ ASSERT(mdlLen >= MPLS_HLEN);
+
+ ethHdr = (EthHdr *)(bufferStart + curMdlOffset);
+ RtlMoveMemory(ethHdr, (UINT8*)ethHdr + MPLS_HLEN, sizeof(*ethHdr));
+ ethHdr->Type = mpls->mpls_ethertype;
+
+ mplsHdr = (MPLSHdr *)(ethHdr + 1);
+ mplsHdr->lse = mpls->mpls_lse;
+
+ layers->l3Offset += MPLS_HLEN;
+ layers->l4Offset += MPLS_HLEN;
+
return NDIS_STATUS_SUCCESS;
}
RtlCopyMemory(&ovsFwdCtx->tunKey, &tunKey, sizeof ovsFwdCtx->tunKey);
break;
}
- case OVS_KEY_ATTR_SKB_MARK:
- /* XXX: Not relevant to Hyper-V. Return OK */
- break;
- case OVS_KEY_ATTR_UNSPEC:
- case OVS_KEY_ATTR_ENCAP:
- case OVS_KEY_ATTR_ETHERTYPE:
- case OVS_KEY_ATTR_IN_PORT:
- case OVS_KEY_ATTR_VLAN:
- case OVS_KEY_ATTR_ICMP:
- case OVS_KEY_ATTR_ICMPV6:
- case OVS_KEY_ATTR_ARP:
- case OVS_KEY_ATTR_ND:
- case __OVS_KEY_ATTR_MAX:
+
default:
- OVS_LOG_INFO("Unhandled attribute %#x", type);
- ASSERT(FALSE);
+ OVS_LOG_INFO("Unhandled attribute %#x", type);
+ break;
}
return status;
}
break;
}
+ case OVS_ACTION_ATTR_PUSH_MPLS:
+ {
+ if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
+ || ovsFwdCtx.tunnelRxNic != NULL) {
+ status = OvsOutputBeforeSetAction(&ovsFwdCtx);
+ if (status != NDIS_STATUS_SUCCESS) {
+ dropReason = L"OVS-adding destination failed";
+ goto dropit;
+ }
+ }
+
+ status = OvsActionMplsPush(&ovsFwdCtx,
+ (struct ovs_action_push_mpls *)NlAttrGet
+ ((const PNL_ATTR)a));
+ if (status != NDIS_STATUS_SUCCESS) {
+ dropReason = L"OVS-push MPLS action failed";
+ goto dropit;
+ }
+ layers->l3Offset += MPLS_HLEN;
+ layers->l4Offset += MPLS_HLEN;
+ break;
+ }
+
+ case OVS_ACTION_ATTR_POP_MPLS:
+ {
+ if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
+ || ovsFwdCtx.tunnelRxNic != NULL) {
+ status = OvsOutputBeforeSetAction(&ovsFwdCtx);
+ if (status != NDIS_STATUS_SUCCESS) {
+ dropReason = L"OVS-adding destination failed";
+ goto dropit;
+ }
+ }
+
+ status = OvsActionMplsPop(&ovsFwdCtx, NlAttrGetBe16(a));
+ if (status != NDIS_STATUS_SUCCESS) {
+ dropReason = L"OVS-pop MPLS action failed";
+ goto dropit;
+ }
+ layers->l3Offset -= MPLS_HLEN;
+ layers->l4Offset -= MPLS_HLEN;
+ break;
+ }
+
case OVS_ACTION_ATTR_USERSPACE:
{
PNL_ATTR userdataAttr;
elem = OvsCreateQueueNlPacket((PVOID)userdataAttr,
userdataAttr->nlaLen,
OVS_PACKET_CMD_ACTION,
- portNo, key,ovsFwdCtx.curNbl,
+ vport, key, ovsFwdCtx.curNbl,
NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx.curNbl),
isRecv,
layers);