CNBL::CNBL(PNET_BUFFER_LIST NBL, PPARANDIS_ADAPTER Context, CParaNdisTX &ParentTXPath) : m_NBL(NBL) , m_Context(Context) , m_ParentTXPath(&ParentTXPath) { m_NBL->Scratch = this; m_LsoInfo.Value = NET_BUFFER_LIST_INFO(m_NBL, TcpLargeSendNetBufferListInfo); m_CsoInfo.Value = NET_BUFFER_LIST_INFO(m_NBL, TcpIpChecksumNetBufferListInfo); }
bool CNBL::ParsePriority() { NDIS_NET_BUFFER_LIST_8021Q_INFO priorityInfo; priorityInfo.Value = m_Context->ulPriorityVlanSetting ? NET_BUFFER_LIST_INFO(m_NBL, Ieee8021QNetBufferListInfo) : nullptr; if (!priorityInfo.TagHeader.VlanId) { priorityInfo.TagHeader.VlanId = m_Context->VlanId; } if (priorityInfo.TagHeader.CanonicalFormatId || !IsValidVlanId(m_Context, priorityInfo.TagHeader.VlanId)) { DPrintf(0, ("[%s] Discarded invalid priority tag %p\n", __FUNCTION__, priorityInfo.Value)); return false; } else if (priorityInfo.Value) { // ignore priority, if configured if (!IsPrioritySupported(m_Context)) priorityInfo.TagHeader.UserPriority = 0; // ignore VlanId, if specified if (!IsVlanSupported(m_Context)) priorityInfo.TagHeader.VlanId = 0; if (priorityInfo.Value) { m_TCI = static_cast<UINT16>(priorityInfo.TagHeader.UserPriority << 13 | priorityInfo.TagHeader.VlanId); DPrintf(1, ("[%s] Populated priority tag %p\n", __FUNCTION__, priorityInfo.Value)); } } return true; }
/* *---------------------------------------------------------------------------- * OvsCalculateUDPChecksum * Calculate UDP checksum *---------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsCalculateUDPChecksum(PNET_BUFFER_LIST curNbl, PNET_BUFFER curNb, IPHdr *ipHdr, UDPHdr *udpHdr, UINT32 packetLength) { NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; UINT16 checkSum; csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo); /* Next check if UDP checksum has been calculated. */ if (!csumInfo.Receive.UdpChecksumSucceeded) { UINT32 l4Payload; checkSum = udpHdr->check; l4Payload = packetLength - sizeof(EthHdr) - ipHdr->ihl * 4; udpHdr->check = 0; udpHdr->check = IPPseudoChecksum((UINT32 *)&ipHdr->saddr, (UINT32 *)&ipHdr->daddr, IPPROTO_UDP, (UINT16)l4Payload); udpHdr->check = CalculateChecksumNB(curNb, (UINT16)l4Payload, sizeof(EthHdr) + ipHdr->ihl * 4); if (checkSum != udpHdr->check) { OVS_LOG_TRACE("UDP checksum incorrect."); return NDIS_STATUS_INVALID_PACKET; } } csumInfo.Receive.UdpChecksumSucceeded = 1; NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = csumInfo.Value; return NDIS_STATUS_SUCCESS; }
PNET_BUFFER_LIST CNBL::DetachInternalObject() { // do it for both LsoV1 and LsoV2 if (IsLSO()) { m_LsoInfo.LsoV1TransmitComplete.TcpPayload = m_TransferSize; } //Flush changes made in LSO structures NET_BUFFER_LIST_INFO(m_NBL, TcpLargeSendNetBufferListInfo) = m_LsoInfo.Value; auto Res = m_NBL; m_NBL = nullptr; return Res; }
static __inline VOID NBLSet8021QInfo(PPARANDIS_ADAPTER pContext, PNET_BUFFER_LIST pNBL, PNET_PACKET_INFO pPacketInfo) { NDIS_NET_BUFFER_LIST_8021Q_INFO qInfo; qInfo.Value = NULL; if (IsPrioritySupported(pContext)) qInfo.TagHeader.UserPriority = pPacketInfo->Vlan.UserPriority; if (IsVlanSupported(pContext)) qInfo.TagHeader.VlanId = pPacketInfo->Vlan.VlanId; if(qInfo.Value != NULL) pContext->extraStatistics.framesRxPriority++; NET_BUFFER_LIST_INFO(pNBL, Ieee8021QNetBufferListInfo) = qInfo.Value; }
static __inline VOID NBLSetRSCInfo(PPARANDIS_ADAPTER pContext, PNET_BUFFER_LIST pNBL, PNET_PACKET_INFO PacketInfo, UINT nCoalescedSegments) { NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO qCSInfo; qCSInfo.Value = NULL; qCSInfo.Receive.IpChecksumSucceeded = TRUE; qCSInfo.Receive.IpChecksumValueInvalid = TRUE; qCSInfo.Receive.TcpChecksumSucceeded = TRUE; qCSInfo.Receive.TcpChecksumValueInvalid = TRUE; NET_BUFFER_LIST_INFO(pNBL, TcpIpChecksumNetBufferListInfo) = qCSInfo.Value; NET_BUFFER_LIST_COALESCED_SEG_COUNT(pNBL) = (USHORT) nCoalescedSegments; NET_BUFFER_LIST_DUP_ACK_COUNT(pNBL) = 0; NdisInterlockedAddLargeStatistic(&pContext->RSC.Statistics.CoalescedOctets, PacketInfo->L2PayloadLen); NdisInterlockedAddLargeStatistic(&pContext->RSC.Statistics.CoalesceEvents, 1); NdisInterlockedAddLargeStatistic(&pContext->RSC.Statistics.CoalescedPkts, nCoalescedSegments); }
PNET_BUFFER_LIST CNBL::DetachInternalObject() { m_MappedBuffers.ForEach([this](CNB *NB) { NB->ReleaseResources(); }); // do it for both LsoV1 and LsoV2 if (IsLSO()) { m_LsoInfo.LsoV1TransmitComplete.TcpPayload = m_TransferSize; } //Flush changes made in LSO structures NET_BUFFER_LIST_INFO(m_NBL, TcpLargeSendNetBufferListInfo) = m_LsoInfo.Value; auto Res = m_NBL; m_NBL = nullptr; return Res; }
/********************************************************** NDIS6 implementation of packet indication Parameters: context PVOID pBuffersDescriptor - VirtIO buffer descriptor of data buffer BOOLEAN bPrepareOnly - only return NBL for further indication in batch Return value: TRUE is packet indicated FALSE if not (in this case, the descriptor should be freed now) If priority header is in the packet. it will be removed and *pLength decreased ***********************************************************/ tPacketIndicationType ParaNdis_PrepareReceivedPacket( PARANDIS_ADAPTER *pContext, pRxNetDescriptor pBuffersDesc, PUINT pnCoalescedSegmentsCount) { PMDL pMDL = pBuffersDesc->Holder; PNET_BUFFER_LIST pNBL = NULL; *pnCoalescedSegmentsCount = 1; if (pMDL) { ULONG nBytesStripped = 0; PNET_PACKET_INFO pPacketInfo = &pBuffersDesc->PacketInfo; if (pContext->ulPriorityVlanSetting && pPacketInfo->hasVlanHeader) { nBytesStripped = ParaNdis_StripVlanHeaderMoveHead(pPacketInfo); } ParaNdis_PadPacketToMinimalLength(pPacketInfo); ParaNdis_AdjustRxBufferHolderLength(pBuffersDesc, nBytesStripped); pNBL = NdisAllocateNetBufferAndNetBufferList(pContext->BufferListsPool, 0, 0, pMDL, nBytesStripped, pPacketInfo->dataLength); if (pNBL) { virtio_net_hdr *pHeader = (virtio_net_hdr *) pBuffersDesc->PhysicalPages[0].Virtual; tChecksumCheckResult csRes; pNBL->SourceHandle = pContext->MiniportHandle; NBLSetRSSInfo(pContext, pNBL, pPacketInfo); NBLSet8021QInfo(pContext, pNBL, pPacketInfo); pNBL->MiniportReserved[0] = pBuffersDesc; #if PARANDIS_SUPPORT_RSC if(pHeader->gso_type != VIRTIO_NET_HDR_GSO_NONE) { *pnCoalescedSegmentsCount = PktGetTCPCoalescedSegmentsCount(pPacketInfo, pContext->MaxPacketSize.nMaxDataSize); NBLSetRSCInfo(pContext, pNBL, pPacketInfo, *pnCoalescedSegmentsCount); } else #endif { csRes = ParaNdis_CheckRxChecksum( pContext, pHeader->flags, &pBuffersDesc->PhysicalPages[PARANDIS_FIRST_RX_DATA_PAGE], pPacketInfo, nBytesStripped, TRUE); if (csRes.value) { NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO qCSInfo; qCSInfo.Value = NULL; qCSInfo.Receive.IpChecksumFailed = csRes.flags.IpFailed; qCSInfo.Receive.IpChecksumSucceeded = csRes.flags.IpOK; qCSInfo.Receive.TcpChecksumFailed = csRes.flags.TcpFailed; qCSInfo.Receive.TcpChecksumSucceeded = csRes.flags.TcpOK; qCSInfo.Receive.UdpChecksumFailed = csRes.flags.UdpFailed; qCSInfo.Receive.UdpChecksumSucceeded = csRes.flags.UdpOK; NET_BUFFER_LIST_INFO(pNBL, TcpIpChecksumNetBufferListInfo) = qCSInfo.Value; DPrintf(1, ("Reporting CS %X->%X\n", csRes.value, (ULONG)(ULONG_PTR)qCSInfo.Value)); } } pNBL->Status = NDIS_STATUS_SUCCESS; #if defined(ENABLE_HISTORY_LOG) { tTcpIpPacketParsingResult packetReview = ParaNdis_CheckSumVerify( RtlOffsetToPointer(pPacketInfo->headersBuffer, ETH_HEADER_SIZE), pPacketInfo->dataLength, pcrIpChecksum | pcrTcpChecksum | pcrUdpChecksum, __FUNCTION__ ); ParaNdis_DebugHistory(pContext, hopPacketReceived, pNBL, pPacketInfo->dataLength, (ULONG)(ULONG_PTR)qInfo.Value, packetReview.value); } #endif } } return pNBL; }
/* *---------------------------------------------------------------------------- * 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; }
// NDIS packet reception notification procedure void SlNdisReceiveNetBufferListsProc(NDIS_HANDLE protocol_binding_context, NET_BUFFER_LIST *net_buffer_lists, NDIS_PORT_NUMBER port_number, ULONG NumberOfNetBufferLists, ULONG receive_flags) { SL_ADAPTER *a = (SL_ADAPTER *)protocol_binding_context; UINT i; UINT return_flags = 0; NET_BUFFER_LIST *nbl; UCHAR *tmp_buffer; UINT tmp_size; if (net_buffer_lists == NULL || NumberOfNetBufferLists == 0) { return; } if (a->AdapterHandle2 == NULL) { a->AdapterHandle2 = a->AdapterHandle; } if (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(receive_flags)) { NDIS_SET_RETURN_FLAG(return_flags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL); } if (a->Halt || a->Device == NULL || a->Device->Halting || a->Ready == false || a->AdapterHandle == NULL) { goto LABEL_CLEANUP; } tmp_buffer = a->TmpBuffer; tmp_size = sizeof(a->TmpBuffer); nbl = net_buffer_lists; SlLockList(a->Device->FileList); { if (a->Halt == false) { for (i = 0;i < SL_LIST_NUM(a->Device->FileList);i++) { // Lock the receive queue SL_FILE *f = SL_LIST_DATA(a->Device->FileList, i); SlLock(f->RecvLock); } while (nbl != NULL) { NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); bool is_vlan = false; UCHAR vlan_tag[2]; if (NET_BUFFER_LIST_INFO(nbl, Ieee8021QNetBufferListInfo) != 0) { NDIS_NET_BUFFER_LIST_8021Q_INFO qinfo; qinfo.Value = NET_BUFFER_LIST_INFO(nbl, Ieee8021QNetBufferListInfo); if (qinfo.TagHeader.VlanId != 0) { USHORT tag_us; is_vlan = true; tag_us = (qinfo.TagHeader.UserPriority & 0x07 << 13) | (qinfo.TagHeader.CanonicalFormatId & 0x01 << 12) | (qinfo.TagHeader.VlanId & 0x0FFF); vlan_tag[0] = ((UCHAR *)(&tag_us))[1]; vlan_tag[1] = ((UCHAR *)(&tag_us))[0]; } } while (nb != NULL) { UINT size = NET_BUFFER_DATA_LENGTH(nb); if (size >= 14 && size <= tmp_size && size <= (UINT)((is_vlan == false) ? SL_MAX_PACKET_SIZE : (SL_MAX_PACKET_SIZE - 4))) { UCHAR *ptr = NdisGetDataBuffer(nb, size, tmp_buffer, 1, 0); if (ptr != NULL) { // Insert the queue to all waiting files for (i = 0;i < SL_LIST_NUM(a->Device->FileList);i++) { SL_FILE *f = SL_LIST_DATA(a->Device->FileList, i); if (f->NumRecvPackets < SL_MAX_PACKET_QUEUED) { SL_PACKET *q = SlMalloc(sizeof(SL_PACKET)); if (is_vlan == false) { // Normal packet SlCopy(q->Data, ptr, size); q->Size = size; } else { // Insert a tag in the case of IEEE802.1Q packet SlCopy(q->Data, ptr, 12); q->Data[12] = 0x81; q->Data[13] = 0x00; SlCopy(&q->Data[14], vlan_tag, 2); SlCopy(&q->Data[16], &ptr[12], size - 12); q->Size = size + 4; } q->Next = NULL; if (f->RecvPacketHead == NULL) { f->RecvPacketHead = q; } else { f->RecvPacketTail->Next = q; } f->RecvPacketTail = q; f->NumRecvPackets++; } } } } nb = NET_BUFFER_NEXT_NB(nb); } nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); } // Hit the event for (i = 0;i < SL_LIST_NUM(a->Device->FileList);i++) { SL_FILE *f = SL_LIST_DATA(a->Device->FileList, i); // Unlock the receive queue SlUnlock(f->RecvLock); SlSet(f->Event); } } } SlUnlockList(a->Device->FileList); LABEL_CLEANUP: if (NDIS_TEST_RECEIVE_CAN_PEND(receive_flags)) { NdisReturnNetBufferLists(a->AdapterHandle2, net_buffer_lists, return_flags); } }