+ 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;
+