// NDIS packet transmission completion notification procedure void SlNdisSendNetBufferListsCompleteProc(NDIS_HANDLE protocol_binding_context, NET_BUFFER_LIST *net_buffer_lists, ULONG send_complete_flags) { NET_BUFFER_LIST *nbl; nbl = net_buffer_lists; while (nbl != NULL) { NET_BUFFER_LIST *current_nbl = nbl; SL_FILE *f; NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); if (nb != NULL) { UINT size = NET_BUFFER_DATA_LENGTH(nb); NdisAdvanceNetBufferDataStart(nb, size, false, NULL); } // Get a file context f = *((void **)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl)); nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); NET_BUFFER_LIST_NEXT_NBL(current_nbl) = NULL; // Release the NET_BUFFER_LIST NdisFreeNetBufferList(current_nbl); // Reduce the number of packets being sent by 1 InterlockedExchangeAdd(&f->NumSendingPacketets, (LONG)-1); InterlockedExchangeAdd(&f->Adapter->NumPendingSendPackets, (LONG)-1); } }
// Process the received packet void NeoWrite(void *buf) { UINT num, i, size; UCHAR *packet_buf; NET_BUFFER_LIST *nbl_chain = NULL; NET_BUFFER_LIST *nbl_tail = NULL; UINT num_nbl_chain = 0; // Validate arguments if (buf == NULL) { return; } // Number of packets num = NEO_NUM_PACKET(buf); if (num > NEO_MAX_PACKET_EXCHANGE) { // Number of packets is too many return; } if (num == 0) { // No packet return; } if (ctx->Halting != FALSE) { // Stopping return; } if (ctx->Paused) { // Paused return; } if (ctx->Opened == FALSE) { // Not connected return; } for (i = 0;i < num;i++) { PACKET_BUFFER *p = ctx->PacketBuffer[i]; void *dst; NET_BUFFER_LIST *nbl = ctx->PacketBuffer[i]->NetBufferList; NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); nbl->SourceHandle = ctx->NdisMiniport; NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; size = NEO_SIZE_OF_PACKET(buf, i); if (size > NEO_MAX_PACKET_SIZE) { size = NEO_MAX_PACKET_SIZE; } if (size < NEO_PACKET_HEADER_SIZE) { size = NEO_PACKET_HEADER_SIZE; } packet_buf = (UCHAR *)(NEO_ADDR_OF_PACKET(buf, i)); if (OK(NdisRetreatNetBufferDataStart(nb, size, 0, NULL))) { // Buffer copy dst = NdisGetDataBuffer(nb, size, NULL, 1, 0); if (dst != NULL) { NeoCopy(dst, packet_buf, size); if (nbl_chain == NULL) { nbl_chain = nbl; } if (nbl_tail != NULL) { NET_BUFFER_LIST_NEXT_NBL(nbl_tail) = nbl; } nbl_tail = nbl; num_nbl_chain++; } } nbl->Status = NDIS_STATUS_RESOURCES; ctx->Status.Int64BytesRecvTotal += (UINT64)size; if (packet_buf[0] & 0x40) { ctx->Status.Int64NumRecvBroadcast++; ctx->Status.Int64BytesRecvBroadcast += (UINT64)size; } else { ctx->Status.Int64NumRecvUnicast++; ctx->Status.Int64BytesRecvUnicast += (UINT64)size; } } if (nbl_chain == NULL) { return; } // Notify that it has received ctx->Status.NumPacketRecv += num_nbl_chain; NdisMIndicateReceiveNetBufferLists(ctx->NdisMiniport, nbl_chain, 0, num_nbl_chain, NDIS_RECEIVE_FLAGS_RESOURCES); if (true) { // Restore the packet buffer NET_BUFFER_LIST *nbl = nbl_chain; while (nbl != NULL) { NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); if (nb != NULL) { UINT size = NET_BUFFER_DATA_LENGTH(nb); NdisAdvanceNetBufferDataStart(nb, size, false, NULL); } nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); } } }
NTSTATUS TLInspectCloneReinjectInbound( IN OUT TL_INSPECT_PENDED_PACKET* packet ) /* ++ This function clones the inbound net buffer list and, if needed, rebuild the IP header to remove the IpSec headers and receive-injects the clone back to the tcpip stack. -- */ { NTSTATUS status = STATUS_SUCCESS; NET_BUFFER_LIST* clonedNetBufferList = NULL; NET_BUFFER* netBuffer; ULONG nblOffset; // // For inbound net buffer list, we can assume it contains only one // net buffer. // netBuffer = NET_BUFFER_LIST_FIRST_NB(packet->netBufferList); nblOffset = NET_BUFFER_DATA_OFFSET(netBuffer); // // The TCP/IP stack could have retreated the net buffer list by the // transportHeaderSize amount; detect the condition here to avoid // retreating twice. // if (nblOffset != packet->nblOffset) { ASSERT(packet->nblOffset - nblOffset == packet->transportHeaderSize); packet->transportHeaderSize = 0; } // // Adjust the net buffer list offset to the start of the IP header. // NdisRetreatNetBufferDataStart( netBuffer, packet->ipHeaderSize + packet->transportHeaderSize, 0, NULL ); // // Note that the clone will inherit the original net buffer list's offset. // status = FwpsAllocateCloneNetBufferList0( packet->netBufferList, NULL, NULL, 0, &clonedNetBufferList ); // // Undo the adjustment on the original net buffer list. // NdisAdvanceNetBufferDataStart( netBuffer, packet->ipHeaderSize + packet->transportHeaderSize, FALSE, NULL ); if (!NT_SUCCESS(status)) { goto Exit; } if (packet->ipSecProtected) { // // When an IpSec protected packet is indicated to AUTH_RECV_ACCEPT or // INBOUND_TRANSPORT layers, for performance reasons the tcpip stack // does not remove the AH/ESP header from the packet. And such // packets cannot be recv-injected back to the stack w/o removing the // AH/ESP header. Therefore before re-injection we need to "re-build" // the cloned packet. // #if (NTDDI_VERSION >= NTDDI_WIN6SP1) status = FwpsConstructIpHeaderForTransportPacket0( clonedNetBufferList, packet->ipHeaderSize, packet->addressFamily, (UINT8*)&packet->remoteAddr, (UINT8*)&packet->localAddr, packet->protocol, 0, NULL, 0, 0, NULL, 0, 0 ); #else ASSERT(FALSE); // Prior to Vista SP1, IP address needs to be updated // manually (including updating IP checksum). status = STATUS_NOT_IMPLEMENTED; #endif if (!NT_SUCCESS(status)) { goto Exit; } } if (packet->completionContext != NULL) { ASSERT(packet->type == TL_INSPECT_CONNECT_PACKET); FwpsCompleteOperation0( packet->completionContext, clonedNetBufferList ); packet->completionContext = NULL; } status = FwpsInjectTransportReceiveAsync0( gInjectionHandle, NULL, NULL, 0, packet->addressFamily, packet->compartmentId, packet->interfaceIndex, packet->subInterfaceIndex, clonedNetBufferList, TLInspectInjectComplete, packet ); if (!NT_SUCCESS(status)) { goto Exit; } clonedNetBufferList = NULL; // ownership transferred to the // completion function. Exit: if (clonedNetBufferList != NULL) { FwpsFreeCloneNetBufferList0(clonedNetBufferList, 0); } return status; }
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; }
NTSTATUS PrvCloneAuthorizedNBLAndInject(_Inout_ CLASSIFY_DATA** ppClassifyData, _Inout_ INJECTION_DATA** ppInjectionData) { #if DBG DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_INFO_LEVEL, " ---> PrvCloneAuthorizedNBLAndInject()\n"); #endif /// DBG NT_ASSERT(ppClassifyData); NT_ASSERT(ppInjectionData); NT_ASSERT(*ppClassifyData); NT_ASSERT(*ppInjectionData); NTSTATUS status = STATUS_SUCCESS; FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; UINT32 bytesRetreated = 0; COMPARTMENT_ID compartmentID = UNSPECIFIED_COMPARTMENT_ID; BOOLEAN isInbound = FALSE; NET_BUFFER_LIST* pNetBufferList = 0; PEND_AUTHORIZATION_COMPLETION_DATA* pCompletionData = 0; FWPS_TRANSPORT_SEND_PARAMS* pSendParams = 0; BYTE* pRemoteAddress = 0; #pragma warning(push) #pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using PendAuthorizationCompletionDataDestroy HLPR_NEW(pCompletionData, PEND_AUTHORIZATION_COMPLETION_DATA, WFPSAMPLER_CALLOUT_DRIVER_TAG); HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, status); HLPR_NEW(pSendParams, FWPS_TRANSPORT_SEND_PARAMS, WFPSAMPLER_CALLOUT_DRIVER_TAG); HLPR_BAIL_ON_ALLOC_FAILURE(pSendParams, status); #pragma warning(pop) KeInitializeSpinLock(&(pCompletionData->spinLock)); pCompletionData->performedInline = FALSE; pCompletionData->pClassifyData = *ppClassifyData; pCompletionData->pInjectionData = *ppInjectionData; pCompletionData->pSendParams = pSendParams; /// Responsibility for freeing this memory has been transferred to the pCompletionData *ppClassifyData = 0; *ppInjectionData = 0; pSendParams = 0; if(pCompletionData->pInjectionData->direction == FWP_DIRECTION_INBOUND) isInbound = TRUE; if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, FWPS_METADATA_FIELD_COMPARTMENT_ID)) compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, FWPS_METADATA_FIELD_IP_HEADER_SIZE)) bytesRetreated = pMetadata->ipHeaderSize; if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) bytesRetreated += pMetadata->transportHeaderSize; if(pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6) { if(isInbound) /// NBL offset is at the start of the transport header ... { if(bytesRetreated) { /// so retreat (size of IP Header) to clone the whole NBL status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), bytesRetreated, 0, 0); if(status != STATUS_SUCCESS) { DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, " !!!! PrvCloneAuthorizedNBLAndInject: NdisRetreatNetBufferDataStart() [status: %#x]\n", status); HLPR_BAIL; } } } } else if(pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6) { /// NBL offset is at the Transport Header, and no IP Header is present yet bytesRetreated = 0; isInbound = FALSE; } else { status = STATUS_FWP_INCOMPATIBLE_LAYER; HLPR_BAIL; } status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)(pCompletionData->pClassifyData->pPacket), g_pNDISPoolData->nblPoolHandle, g_pNDISPoolData->nbPoolHandle, 0, &pNetBufferList); if(bytesRetreated) { /// Advance the NBL offset so we are back at the expected position in the NET_BUFFER_LIST NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), bytesRetreated, FALSE, 0); } if(status != STATUS_SUCCESS) { DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, " !!!! PrvCloneAuthorizedNBLAndInject : FwpsAllocateCloneNetBufferList() [status: %#x]\n", status); HLPR_BAIL; } if(isInbound) { FWP_VALUE* pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, &FWPM_CONDITION_INTERFACE_INDEX); FWP_VALUE* pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, &FWPM_CONDITION_SUB_INTERFACE_INDEX); IF_INDEX interfaceIndex = 0; IF_INDEX subInterfaceIndex = 0; if(pInterfaceIndex && pInterfaceIndex->type == FWP_UINT32) interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; if(pSubInterfaceIndex && pSubInterfaceIndex->type == FWP_UINT32) subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; status = FwpsInjectTransportReceiveAsync(pCompletionData->pInjectionData->injectionHandle, pCompletionData->pInjectionData->injectionContext, 0, 0, pCompletionData->pInjectionData->addressFamily, compartmentID, interfaceIndex, subInterfaceIndex, pNetBufferList, CompletePendAuthorization, pCompletionData); } else { UINT64 endpointHandle = 0; FWP_VALUE* pAddressValue = 0; if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) endpointHandle = pMetadata->transportEndpointHandle; pAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, &FWPM_CONDITION_IP_REMOTE_ADDRESS); if(pAddressValue) { if(pCompletionData->pInjectionData->addressFamily == AF_INET) { UINT32 tempAddress = htonl(pAddressValue->uint32); #pragma warning(push) #pragma warning(disable: 6014) /// pRemoteAddress will be freed in completionFn using PendAuthorizationCompletionDataDestroy HLPR_NEW_ARRAY(pRemoteAddress, BYTE, IPV4_ADDRESS_SIZE, WFPSAMPLER_CALLOUT_DRIVER_TAG); HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, status); #pragma warning(pop) RtlCopyMemory(pRemoteAddress, &tempAddress, IPV4_ADDRESS_SIZE); } else { #pragma warning(push) #pragma warning(disable: 6014) /// pRemoteAddress will be freed in completionFn using PendAuthorizationCompletionDataDestroy HLPR_NEW_ARRAY(pRemoteAddress, BYTE, IPV6_ADDRESS_SIZE, WFPSAMPLER_CALLOUT_DRIVER_TAG); HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, status); #pragma warning(pop) RtlCopyMemory(pRemoteAddress, pAddressValue->byteArray16->byteArray16, IPV6_ADDRESS_SIZE); if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, FWPS_METADATA_FIELD_REMOTE_SCOPE_ID)) pCompletionData->pSendParams->remoteScopeId = pMetadata->remoteScopeId; } pCompletionData->pSendParams->remoteAddress = pRemoteAddress; } pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); status = FwpsInjectTransportSendAsync(pCompletionData->pInjectionData->injectionHandle, pCompletionData->pInjectionData->injectionContext, endpointHandle, 0, pCompletionData->pSendParams, pCompletionData->pInjectionData->addressFamily, compartmentID, pNetBufferList, CompletePendAuthorization, pCompletionData); } HLPR_BAIL_LABEL: if(status != STATUS_SUCCESS) { if(pNetBufferList) { FwpsFreeCloneNetBufferList(pNetBufferList, 0); pNetBufferList = 0; } if(pCompletionData) PendAuthorizationCompletionDataDestroy(&pCompletionData, TRUE); } #if DBG DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_INFO_LEVEL, " <--- PrvCloneAuthorizedNBLAndInject() [status: %#x]\n", status); #endif /// DBG return status; }
/* * -------------------------------------------------------------------------- * OvsTcpSegmentyNBL -- * * Segment TCP payload, and prepend each segment with ether/IP/TCP header. * Leave headRoom for additional encap. * * Please note, * NBL should have OVS_BUFFER_CONTEXT setup before calling * this function. * The NBL should already have ref to itself so that during copy * it will not be freed. * Currently this API assert there is only one NB in an NBL, it needs * to be fixed if we receive multiple NBs in an NBL. * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsTcpSegmentNBL(PVOID ovsContext, PNET_BUFFER_LIST nbl, POVS_PACKET_HDR_INFO hdrInfo, UINT32 mss, UINT32 headRoom) { POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; #ifdef DBG POVS_NBL_POOL ovsPool = &context->ovsPool; #endif POVS_BUFFER_CONTEXT dstCtx, srcCtx; UINT32 size, hdrSize, seqNumber; PNET_BUFFER_LIST newNbl; PNET_BUFFER nb, newNb; NDIS_STATUS status; UINT16 segmentSize; ULONG copiedSize; 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); ASSERT(NET_BUFFER_NEXT_NB(nb) == NULL); /* Figure out the segment header size */ status = GetSegmentHeaderInfo(nbl, hdrInfo, &hdrSize, &seqNumber); if (status != NDIS_STATUS_SUCCESS) { OVS_LOG_INFO("Cannot parse NBL header"); return NULL; } size = NET_BUFFER_DATA_LENGTH(nb) - hdrSize; /* XXX add to ovsPool counters? */ newNbl = NdisAllocateFragmentNetBufferList(nbl, NULL, NULL, hdrSize, mss, hdrSize + headRoom , 0, 0); if (newNbl == NULL) { return NULL; } /* Now deal with TCP payload */ for (newNb = NET_BUFFER_LIST_FIRST_NB(newNbl); newNb != NULL; newNb = NET_BUFFER_NEXT_NB(newNb)) { segmentSize = (size > mss ? mss : size) & 0xffff; if (headRoom) { NdisAdvanceNetBufferDataStart(newNb, headRoom, FALSE, NULL); } /* Now copy the eth/IP/TCP header and fix up */ status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, hdrSize, nb, 0, &copiedSize); if (status != NDIS_STATUS_SUCCESS || hdrSize != copiedSize) { goto nblcopy_error; } status = FixSegmentHeader(newNb, segmentSize, seqNumber); if (status != NDIS_STATUS_SUCCESS) { goto nblcopy_error; } /* Move on to the next segment */ size -= segmentSize; seqNumber += segmentSize; } status = OvsAllocateNBLContext(context, newNbl); if (status != NDIS_STATUS_SUCCESS) { goto nblcopy_error; } status = OvsCopyNBLInfo(nbl, newNbl, srcCtx, hdrSize + headRoom, FALSE); if (status != NDIS_STATUS_SUCCESS) { goto nbl_context_error; } newNbl->ParentNetBufferList = nbl; /* Remember it's a fragment NBL so we can free it properly */ dstCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl); ASSERT(dstCtx != NULL); dstCtx->flags = OVS_BUFFER_FRAGMENT | OVS_BUFFER_PRIVATE_CONTEXT | OVS_BUFFER_PRIVATE_FORWARD_CONTEXT | OVS_BUFFER_SEND_BUFFER; dstCtx->refCount = 1; dstCtx->magic = OVS_CTX_MAGIC; dstCtx->dataOffsetDelta = hdrSize + headRoom; InterlockedIncrement((LONG volatile *)&srcCtx->refCount); #ifdef DBG InterlockedIncrement((LONG volatile *)&ovsPool->fragNBLCount); OvsDumpNetBufferList(nbl); OvsDumpForwardingDetails(nbl); OvsDumpNetBufferList(newNbl); OvsDumpForwardingDetails(newNbl); #endif OVS_LOG_TRACE("Segment nbl %p to newNbl: %p", nbl, newNbl); return newNbl; nbl_context_error: OvsFreeNBLContext(context, newNbl); nblcopy_error: #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->fragNBLCount); #endif NdisFreeFragmentNetBufferList(newNbl, hdrSize + headRoom, 0); return NULL; }
/* *---------------------------------------------------------------------------- * 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; }
// Write procedure of the device NTSTATUS SlDeviceWriteProc(DEVICE_OBJECT *device_object, IRP *irp) { SL_DEVICE *dev = *((SL_DEVICE **)device_object->DeviceExtension); NTSTATUS ret = STATUS_UNSUCCESSFUL; IO_STACK_LOCATION *irp_stack = IoGetCurrentIrpStackLocation(irp); UINT ret_size = 0; if (dev->IsBasicDevice == false) { // Adapter device SL_FILE *f = irp_stack->FileObject->FsContext; if (irp_stack->Parameters.Write.Length == SL_EXCHANGE_BUFFER_SIZE) { UCHAR *buf = irp->UserBuffer; if (dev->Halting || dev->Adapter->Halt || buf == NULL) { // Halting } else { // Write the packet MDL *mdl; UINT num = SL_NUM_PACKET(buf); mdl = IoAllocateMdl(buf, SL_EXCHANGE_BUFFER_SIZE, false, false, NULL); if (mdl != NULL) { MmProbeAndLockPages(mdl, KernelMode, IoReadAccess); } ret = true; ret_size = SL_EXCHANGE_BUFFER_SIZE; if (num >= 1 && num <= SL_MAX_PACKET_EXCHANGE) { UINT i, j; NET_BUFFER_LIST *nbl_head = NULL; NET_BUFFER_LIST *nbl_tail = NULL; UINT num_packets = 0; NDIS_HANDLE adapter_handle = NULL; SlLock(f->Adapter->Lock); if (f->Adapter->NumPendingSendPackets <= SL_MAX_PACKET_QUEUED) { // Admit to send only if the number of packets being transmitted does not exceed the specified limit adapter_handle = f->Adapter->AdapterHandle; } if (adapter_handle != NULL) { // Lock the file list which opens the same adapter SlLockList(dev->FileList); for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Lock the receive queue of other file lists SlLock(other->RecvLock); other->SetEventFlag = false; } } for (i = 0;i < num;i++) { UINT packet_size = SL_SIZE_OF_PACKET(buf, i); UCHAR *packet_buf; NET_BUFFER_LIST *nbl = NULL; bool ok = false; if (packet_size > SL_MAX_PACKET_SIZE) { packet_size = SL_MAX_PACKET_SIZE; } else if (packet_size < SL_PACKET_HEADER_SIZE) { packet_size = SL_PACKET_HEADER_SIZE; } packet_buf = (UCHAR *)SL_ADDR_OF_PACKET(buf, i); for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Insert into the receive queue of the other file lists if (other->NumRecvPackets < SL_MAX_PACKET_QUEUED) { SL_PACKET *q = SlMalloc(sizeof(SL_PACKET)); SlCopy(q->Data, packet_buf, packet_size); q->Size = packet_size; q->Next = NULL; if (other->RecvPacketHead == NULL) { other->RecvPacketHead = q; } else { other->RecvPacketTail->Next = q; } other->RecvPacketTail = q; other->NumRecvPackets++; other->SetEventFlag = true; } } } // Allocate a new NET_BUFFER_LIST if (f->NetBufferListPool != NULL) { nbl = NdisAllocateNetBufferList(f->NetBufferListPool, 16, 0); if (nbl != NULL) { nbl->SourceHandle = adapter_handle; } } if (nbl != NULL) { // Get the NET_BUFFER from the NET_BUFFER_LIST NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; if (nb != NULL && OK(NdisRetreatNetBufferDataStart(nb, packet_size, 0, NULL))) { // Buffer copy UCHAR *dst = NdisGetDataBuffer(nb, packet_size, NULL, 1, 0); if (dst != NULL) { SlCopy(dst, packet_buf, packet_size); ok = true; } else { NdisAdvanceNetBufferDataStart(nb, packet_size, false, NULL); } } } if (ok == false) { if (nbl != NULL) { NdisFreeNetBufferList(nbl); } } else { if (nbl_head == NULL) { nbl_head = nbl; } if (nbl_tail != NULL) { NET_BUFFER_LIST_NEXT_NBL(nbl_tail) = nbl; } nbl_tail = nbl; *((void **)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl)) = f; num_packets++; } } for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Release the receive queue of other file lists SlUnlock(other->RecvLock); // Set an event if (other->SetEventFlag) { SlSet(other->Event); } } } SlUnlockList(dev->FileList); if (nbl_head != NULL) { InterlockedExchangeAdd(&f->NumSendingPacketets, num_packets); InterlockedExchangeAdd(&f->Adapter->NumPendingSendPackets, num_packets); SlUnlock(f->Adapter->Lock); NdisSendNetBufferLists(adapter_handle, nbl_head, 0, 0); } else { SlUnlock(f->Adapter->Lock); } } else { SlUnlock(f->Adapter->Lock); } } if (mdl != NULL) { MmUnlockPages(mdl); IoFreeMdl(mdl); } } } } irp->IoStatus.Information = ret_size; irp->IoStatus.Status = ret; IoCompleteRequest(irp, IO_NO_INCREMENT); return ret; }