static PNET_BUFFER_LIST OvsCopySinglePacketNBL(PVOID ovsContext, PNET_BUFFER_LIST nbl, PNET_BUFFER nb, UINT32 headRoom, BOOLEAN copyNblInfo) { UINT32 size; ULONG copiedSize; POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; PNET_BUFFER_LIST newNbl; PNET_BUFFER newNb; NDIS_STATUS status; POVS_BUFFER_CONTEXT srcCtx, dstCtx; size = NET_BUFFER_DATA_LENGTH(nb); if ((size + headRoom) <= OVS_FIX_NBL_DATA_SIZE) { newNbl = OvsAllocateFixSizeNBL(context, size, headRoom); } else { newNbl = OvsAllocateVariableSizeNBL(context, size, headRoom); } if (newNbl == NULL) { return NULL; } newNb = NET_BUFFER_LIST_FIRST_NB(newNbl); status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, size, nb, 0, &copiedSize); srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); if (status == NDIS_STATUS_SUCCESS) { status = OvsCopyNBLInfo(nbl, newNbl, srcCtx, copiedSize, copyNblInfo); } if (status != NDIS_STATUS_SUCCESS || copiedSize != size) { OvsCompleteNBL(context, newNbl, TRUE); return NULL; } dstCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl); ASSERT(dstCtx && srcCtx); ASSERT(srcCtx->magic == OVS_CTX_MAGIC && dstCtx->magic == OVS_CTX_MAGIC); dstCtx->flags |= srcCtx->flags & (OVS_BUFFER_RECV_BUFFER | OVS_BUFFER_SEND_BUFFER); #ifdef DBG OvsDumpNetBufferList(newNbl); OvsDumpForwardingDetails(newNbl); #endif OVS_LOG_LOUD("Copy single nb to new NBL: %p", newNbl); return newNbl; }
/* * -------------------------------------------------------------------------- * OvsFullCopyToMultipleNBLs -- * * Copy NBL to multiple NBLs, each NB will have its own NBL * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsFullCopyToMultipleNBLs(PVOID ovsContext, PNET_BUFFER_LIST nbl, UINT32 headRoom, BOOLEAN copyNblInfo) { POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; PNET_BUFFER_LIST firstNbl, currNbl, newNbl; PNET_BUFFER nb; POVS_BUFFER_CONTEXT srcCtx; srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) { OVS_LOG_INFO("src nbl must have ctx initialized"); ASSERT(srcCtx && srcCtx->magic == OVS_CTX_MAGIC); return NULL; } nb = NET_BUFFER_LIST_FIRST_NB(nbl); newNbl = OvsCopySinglePacketNBL(context, nbl, nb, headRoom, copyNblInfo); if (newNbl == NULL || NET_BUFFER_NEXT_NB(nb) == NULL) { return newNbl; } else { firstNbl = newNbl; currNbl = newNbl; } while (nb) { newNbl = OvsCopySinglePacketNBL(context, nbl, nb, headRoom, copyNblInfo); if (newNbl == NULL) { goto copymultiple_error; } NET_BUFFER_LIST_NEXT_NBL(currNbl) = newNbl; currNbl = newNbl; nb = NET_BUFFER_NEXT_NB(nb); } return firstNbl; copymultiple_error: while (firstNbl) { currNbl = firstNbl; firstNbl = NET_BUFFER_LIST_NEXT_NBL(firstNbl); NET_BUFFER_LIST_NEXT_NBL(currNbl) = NULL; OvsCompleteNBL(context, currNbl, TRUE); } return NULL; }
static NTSTATUS OvsTunnelAnalyzePacket(OVS_TUNNEL_PENDED_PACKET *packet) { NTSTATUS status = STATUS_SUCCESS; UINT32 packetLength = 0; ULONG bytesCopied = 0; NET_BUFFER_LIST *copiedNBL = NULL; NET_BUFFER *netBuffer; NDIS_STATUS ndisStatus; /* * For inbound net buffer list, we can assume it contains only one * net buffer (unless it was an re-assembeled fragments). in both cases * the first net buffer should include all headers, we assert if the retreat fails */ netBuffer = NET_BUFFER_LIST_FIRST_NB(packet->netBufferList); /* Drop the packet from the host stack */ packet->classifyOut->actionType = FWP_ACTION_BLOCK; packet->classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; /* Adjust the net buffer list offset to the start of the IP header */ ndisStatus = NdisRetreatNetBufferDataStart(netBuffer, packet->ipHeaderSize + packet->transportHeaderSize, 0, NULL); ASSERT(ndisStatus == NDIS_STATUS_SUCCESS); /* Single NBL element for WFP */ ASSERT(packet->netBufferList->Next == NULL); /* Note that the copy will inherit the original net buffer list's offset */ packetLength = NET_BUFFER_DATA_LENGTH(netBuffer); copiedNBL = OvsAllocateVariableSizeNBL(gOvsSwitchContext, packetLength, OVS_DEFAULT_HEADROOM_SIZE); if (copiedNBL == NULL) { goto analyzeDone; } status = NdisCopyFromNetBufferToNetBuffer(NET_BUFFER_LIST_FIRST_NB(copiedNBL), 0, packetLength, netBuffer, 0, &bytesCopied); if (status != NDIS_STATUS_SUCCESS || packetLength != bytesCopied) { goto analyzeFreeNBL; } status = OvsInjectPacketThroughActions(copiedNBL, packet); goto analyzeDone; /* Undo the adjustment on the original net buffer list */ analyzeFreeNBL: OvsCompleteNBL(gOvsSwitchContext, copiedNBL, TRUE); analyzeDone: NdisAdvanceNetBufferDataStart(netBuffer, packet->transportHeaderSize + packet->ipHeaderSize, FALSE, NULL); return status; }
static NTSTATUS OvsInjectPacketThroughActions(PNET_BUFFER_LIST pNbl, OVS_TUNNEL_PENDED_PACKET *packet) { NTSTATUS status = STATUS_SUCCESS; OvsIPv4TunnelKey tunnelKey; NET_BUFFER *pNb; ULONG sendCompleteFlags = 0; BOOLEAN dispatch; PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; LOCK_STATE_EX lockState, dpLockState; LIST_ENTRY missedPackets; OvsCompletionList completionList; KIRQL irql; ULONG SendFlags = NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP; OVS_DATAPATH *datapath = NULL; ASSERT(gOvsSwitchContext); datapath = &gOvsSwitchContext->datapath; /* Fill the tunnel key */ status = OvsSlowPathDecapVxlan(pNbl, &tunnelKey); if(!NT_SUCCESS(status)) { goto dropit; } pNb = NET_BUFFER_LIST_FIRST_NB(pNbl); NdisAdvanceNetBufferDataStart(pNb, packet->transportHeaderSize + packet->ipHeaderSize + sizeof(VXLANHdr), FALSE, NULL); /* Most likely (always) dispatch irql */ irql = KeGetCurrentIrql(); /* dispatch is used for datapath lock as well */ dispatch = (irql == DISPATCH_LEVEL) ? NDIS_RWL_AT_DISPATCH_LEVEL : 0; if (dispatch) { sendCompleteFlags |= NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL; } InitializeListHead(&missedPackets); OvsInitCompletionList(&completionList, gOvsSwitchContext, sendCompleteFlags); { POVS_VPORT_ENTRY vport; UINT32 portNo; OVS_PACKET_HDR_INFO layers; OvsFlowKey key; UINT64 hash; PNET_BUFFER curNb; OvsFlow *flow; fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(pNbl); /* * XXX WFP packets contain a single NBL structure. * Reassembeled packet "may" have multiple NBs, however, a simple test shows * that the packet still has a single NB (after reassemble) * We still need to check if the Ethernet header of the innet packet is in a single MD */ curNb = NET_BUFFER_LIST_FIRST_NB(pNbl); ASSERT(curNb->Next == NULL); NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, dispatch); /* Lock the flowtable for the duration of accessing the flow */ OvsAcquireDatapathRead(datapath, &dpLockState, NDIS_RWL_AT_DISPATCH_LEVEL); SendFlags |= NDIS_SEND_FLAGS_DISPATCH_LEVEL; vport = OvsFindTunnelVportByDstPort(gOvsSwitchContext, htons(tunnelKey.dst_port), OVS_VPORT_TYPE_VXLAN); if (vport == NULL){ status = STATUS_UNSUCCESSFUL; goto unlockAndDrop; } ASSERT(vport->ovsType == OVS_VPORT_TYPE_VXLAN); portNo = vport->portNo; status = OvsExtractFlow(pNbl, portNo, &key, &layers, &tunnelKey); if (status != NDIS_STATUS_SUCCESS) { goto unlockAndDrop; } flow = OvsLookupFlow(datapath, &key, &hash, FALSE); if (flow) { OvsFlowUsed(flow, pNbl, &layers); datapath->hits++; OvsActionsExecute(gOvsSwitchContext, &completionList, pNbl, portNo, SendFlags, &key, &hash, &layers, flow->actions, flow->actionsLen); OvsReleaseDatapath(datapath, &dpLockState); } else { POVS_PACKET_QUEUE_ELEM elem; datapath->misses++; elem = OvsCreateQueueNlPacket(NULL, 0, OVS_PACKET_CMD_MISS, vport, &key, pNbl, curNb, TRUE, &layers); if (elem) { /* Complete the packet since it was copied to user buffer. */ InsertTailList(&missedPackets, &elem->link); OvsQueuePackets(&missedPackets, 1); } else { status = STATUS_INSUFFICIENT_RESOURCES; } goto unlockAndDrop; } NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); } return status; unlockAndDrop: OvsReleaseDatapath(datapath, &dpLockState); NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); dropit: pNbl = OvsCompleteNBL(gOvsSwitchContext, pNbl, TRUE); ASSERT(pNbl == NULL); return status; }
/* * -------------------------------------------------------------------------- * OvsCompleteNBL -- * * This function tries to free the NBL allocated by OVS buffer * management module. If it trigger the completion of the parent * NBL, it will recursively call itself. If it trigger the completion * of external NBL, it will be returned to the caller. The caller * is responsible to call API to return to upper layer. * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsCompleteNBL(POVS_SWITCH_CONTEXT context, PNET_BUFFER_LIST nbl, BOOLEAN updateRef) { POVS_BUFFER_CONTEXT ctx; UINT16 flags; PNET_BUFFER_LIST parent; NDIS_STATUS status; NDIS_HANDLE poolHandle; LONG value; POVS_NBL_POOL ovsPool = &context->ovsPool; PNET_BUFFER nb; ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC); OVS_LOG_TRACE("Enter: nbl: %p, ctx: %p, refCount: %d, updateRef:%d", nbl, ctx, ctx->refCount, updateRef); if (updateRef) { value = InterlockedDecrement((LONG volatile *)&ctx->refCount); if (value != 0) { return NULL; } } else { /* * This is a special case, the refCount must be zero */ ASSERT(ctx->refCount == 0); } nb = NET_BUFFER_LIST_FIRST_NB(nbl); flags = ctx->flags; if (!(flags & OVS_BUFFER_FRAGMENT) && NET_BUFFER_DATA_LENGTH(nb) != ctx->origDataLength) { UINT32 diff; if (NET_BUFFER_DATA_LENGTH(nb) < ctx->origDataLength) { diff = ctx->origDataLength -NET_BUFFER_DATA_LENGTH(nb); status = NdisRetreatNetBufferListDataStart(nbl, diff, 0, NULL, NULL); ASSERT(status == NDIS_STATUS_SUCCESS); } else { diff = NET_BUFFER_DATA_LENGTH(nb) - ctx->origDataLength; NdisAdvanceNetBufferListDataStart(nbl, diff, TRUE, NULL); } } if (ctx->flags & OVS_BUFFER_PRIVATE_CONTEXT) { NdisFreeNetBufferListContext(nbl, sizeof (OVS_BUFFER_CONTEXT)); } if (flags & OVS_BUFFER_NEED_COMPLETE) { /* * return to caller for completion */ #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->sysNBLCount); #endif return nbl; } if (flags & OVS_BUFFER_PRIVATE_FORWARD_CONTEXT) { context->NdisSwitchHandlers. FreeNetBufferListForwardingContext(ovsPool->ndisContext, nbl); } if (flags & (OVS_BUFFER_PRIVATE_MDL | OVS_BUFFER_PRIVATE_DATA)) { PNET_BUFFER nb = NET_BUFFER_LIST_FIRST_NB(nbl); while (nb) { PMDL mdl = NET_BUFFER_FIRST_MDL(nb); NET_BUFFER_FIRST_MDL(nb) = NULL; ASSERT(mdl->Next == NULL); OvsFreeMDLAndData(mdl); nb = NET_BUFFER_NEXT_NB(nb); } } if (flags & OVS_BUFFER_PRIVATE_NET_BUFFER) { PNET_BUFFER nb, nextNb; nb = NET_BUFFER_LIST_FIRST_NB(nbl); while (nb) { nextNb = NET_BUFFER_NEXT_NB(nb); NdisFreeNetBuffer(nb); #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->nbCount); #endif nb = nextNb; } NET_BUFFER_LIST_FIRST_NB(nbl) = NULL; } parent = nbl->ParentNetBufferList; poolHandle = NdisGetPoolFromNetBufferList(nbl); if (flags & OVS_BUFFER_FROM_FIX_SIZE_POOL) { ASSERT(poolHandle == ovsPool->fixSizePool); #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->fixNBLCount); #endif NdisFreeNetBufferList(nbl); } else if (flags & OVS_BUFFER_FROM_ZERO_SIZE_POOL) { ASSERT(poolHandle == ovsPool->zeroSizePool); #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->zeroNBLCount); #endif NdisFreeNetBufferList(nbl); } else if (flags & OVS_BUFFER_FROM_NBL_ONLY_POOL) { ASSERT(poolHandle == ovsPool->nblOnlyPool); #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->nblOnlyCount); #endif NdisFreeCloneNetBufferList(nbl, 0); } else if (flags & OVS_BUFFER_FRAGMENT) { OVS_LOG_TRACE("Free fragment %p parent %p", nbl, parent); #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->fragNBLCount); #endif NdisFreeFragmentNetBufferList(nbl, ctx->dataOffsetDelta, 0); } if (parent != NULL) { ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(parent); ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC); value = InterlockedDecrement((LONG volatile *)&ctx->refCount); if (value == 0) { return OvsCompleteNBL(context, parent, FALSE); } } return NULL; }
/* *---------------------------------------------------------------------------- * OvsDoEncapVxlan * Encapsulates the packet. *---------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsDoEncapVxlan(PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, POVS_FWD_INFO fwdInfo, POVS_PACKET_HDR_INFO layers, POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST *newNbl) { NDIS_STATUS status; PNET_BUFFER curNb; PMDL curMdl; PUINT8 bufferStart; EthHdr *ethHdr; IPHdr *ipHdr; UDPHdr *udpHdr; VXLANHdr *vxlanHdr; UINT32 headRoom = OvsGetVxlanTunHdrSize(); UINT32 packetLength; /* * XXX: the assumption currently is that the NBL is owned by OVS, and * headroom has already been allocated as part of allocating the NBL and * MDL. */ curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); packetLength = NET_BUFFER_DATA_LENGTH(curNb); if (layers->isTcp) { NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO tsoInfo; tsoInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpLargeSendNetBufferListInfo); OVS_LOG_TRACE("MSS %u packet len %u", tsoInfo.LsoV1Transmit.MSS, packetLength); if (tsoInfo.LsoV1Transmit.MSS) { OVS_LOG_TRACE("l4Offset %d", layers->l4Offset); *newNbl = OvsTcpSegmentNBL(switchContext, curNbl, layers, tsoInfo.LsoV1Transmit.MSS, headRoom); if (*newNbl == NULL) { OVS_LOG_ERROR("Unable to segment NBL"); return NDIS_STATUS_FAILURE; } } } /* If we didn't split the packet above, make a copy now */ if (*newNbl == NULL) { *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, headRoom, FALSE /*NBL info*/); if (*newNbl == NULL) { OVS_LOG_ERROR("Unable to copy NBL"); return NDIS_STATUS_FAILURE; } } curNbl = *newNbl; for (curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curNb != NULL; curNb = curNb->Next) { status = NdisRetreatNetBufferDataStart(curNb, headRoom, 0, NULL); if (status != NDIS_STATUS_SUCCESS) { goto ret_error; } curMdl = NET_BUFFER_CURRENT_MDL(curNb); bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority); if (!bufferStart) { status = NDIS_STATUS_RESOURCES; goto ret_error; } bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb); if (NET_BUFFER_NEXT_NB(curNb)) { OVS_LOG_TRACE("nb length %u next %u", NET_BUFFER_DATA_LENGTH(curNb), NET_BUFFER_DATA_LENGTH(curNb->Next)); } /* L2 header */ ethHdr = (EthHdr *)bufferStart; NdisMoveMemory(ethHdr->Destination, fwdInfo->dstMacAddr, sizeof ethHdr->Destination + sizeof ethHdr->Source); ASSERT(((PCHAR)&fwdInfo->dstMacAddr + sizeof fwdInfo->dstMacAddr) == (PCHAR)&fwdInfo->srcMacAddr); ethHdr->Type = htons(ETH_TYPE_IPV4); // XXX: question: there are fields in the OvsIPv4TunnelKey for ttl and such, // should we use those values instead? or will they end up being // uninitialized; /* IP header */ ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr); ipHdr->ihl = sizeof *ipHdr / 4; ipHdr->version = IPV4; ipHdr->tos = 0; ipHdr->tot_len = htons(NET_BUFFER_DATA_LENGTH(curNb) - sizeof *ethHdr); ipHdr->id = 0; ipHdr->frag_off = IP_DF_NBO; ipHdr->ttl = tunKey->ttl ? tunKey->ttl : VXLAN_DEFAULT_TTL; ipHdr->protocol = IPPROTO_UDP; ASSERT(tunKey->dst == fwdInfo->dstIpAddr); ASSERT(tunKey->src == fwdInfo->srcIpAddr || tunKey->src == 0); ipHdr->saddr = fwdInfo->srcIpAddr; ipHdr->daddr = fwdInfo->dstIpAddr; ipHdr->check = 0; ipHdr->check = IPChecksum((UINT8 *)ipHdr, sizeof *ipHdr, 0); /* UDP header */ udpHdr = (UDPHdr *)((PCHAR)ipHdr + sizeof *ipHdr); udpHdr->source = htons(tunKey->flow_hash | 32768); udpHdr->dest = VXLAN_UDP_PORT_NBO; udpHdr->len = htons(NET_BUFFER_DATA_LENGTH(curNb) - headRoom + sizeof *udpHdr + sizeof *vxlanHdr); udpHdr->check = 0; /* VXLAN header */ vxlanHdr = (VXLANHdr *)((PCHAR)udpHdr + sizeof *udpHdr); vxlanHdr->flags1 = 0; vxlanHdr->locallyReplicate = 0; vxlanHdr->flags2 = 0; vxlanHdr->reserved1 = 0; if (tunKey->flags | OVS_TNL_F_KEY) { vxlanHdr->vxlanID = VXLAN_TUNNELID_TO_VNI(tunKey->tunnelId); vxlanHdr->instanceID = 1; } vxlanHdr->reserved2 = 0; } return STATUS_SUCCESS; ret_error: OvsCompleteNBL(switchContext, *newNbl, TRUE); *newNbl = NULL; return status; }
/* *---------------------------------------------------------------------------- * OvsDoDecapVxlan * Decapsulates to tunnel header in 'curNbl' and puts into 'tunKey'. *---------------------------------------------------------------------------- */ NDIS_STATUS OvsDoDecapVxlan(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, PNET_BUFFER_LIST *newNbl) { PNET_BUFFER curNb; PMDL curMdl; EthHdr *ethHdr; IPHdr *ipHdr; UDPHdr *udpHdr; VXLANHdr *vxlanHdr; UINT32 tunnelSize = 0, packetLength = 0; PUINT8 bufferStart; NDIS_STATUS status; /* Check the the length of the UDP payload */ curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); packetLength = NET_BUFFER_DATA_LENGTH(curNb); tunnelSize = OvsGetVxlanTunHdrSize(); if (packetLength <= tunnelSize) { return NDIS_STATUS_INVALID_LENGTH; } /* * Create a copy of the NBL so that we have all the headers in one MDL. */ *newNbl = OvsPartialCopyNBL(switchContext, curNbl, tunnelSize + OVS_DEFAULT_COPY_SIZE, 0, TRUE /*copy NBL info */); if (*newNbl == NULL) { return NDIS_STATUS_RESOURCES; } /* XXX: Handle VLAN header. */ curNbl = *newNbl; curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curMdl = NET_BUFFER_CURRENT_MDL(curNb); bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority) + NET_BUFFER_CURRENT_MDL_OFFSET(curNb); if (!bufferStart) { status = NDIS_STATUS_RESOURCES; goto dropNbl; } ethHdr = (EthHdr *)bufferStart; /* XXX: Handle IP options. */ ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr); tunKey->src = ipHdr->saddr; tunKey->dst = ipHdr->daddr; tunKey->tos = ipHdr->tos; tunKey->ttl = ipHdr->ttl; tunKey->pad = 0; udpHdr = (UDPHdr *)((PCHAR)ipHdr + sizeof *ipHdr); /* Validate if NIC has indicated checksum failure. */ status = OvsValidateUDPChecksum(curNbl, udpHdr->check == 0); if (status != NDIS_STATUS_SUCCESS) { goto dropNbl; } /* Calculate and verify UDP checksum if NIC didn't do it. */ if (udpHdr->check != 0) { status = OvsCalculateUDPChecksum(curNbl, curNb, ipHdr, udpHdr, packetLength); if (status != NDIS_STATUS_SUCCESS) { goto dropNbl; } } vxlanHdr = (VXLANHdr *)((PCHAR)udpHdr + sizeof *udpHdr); if (vxlanHdr->instanceID) { tunKey->flags = OVS_TNL_F_KEY; tunKey->tunnelId = VXLAN_VNI_TO_TUNNELID(vxlanHdr->vxlanID); } else { tunKey->flags = 0; tunKey->tunnelId = 0; } /* Clear out the receive flag for the inner packet. */ NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = 0; NdisAdvanceNetBufferDataStart(curNb, tunnelSize, FALSE, NULL); return NDIS_STATUS_SUCCESS; dropNbl: OvsCompleteNBL(switchContext, *newNbl, TRUE); *newNbl = NULL; return status; }