/*++ Copyright (c) 2000-2000 Microsoft Corporation Module Name: Receive.c Abstract: This module implements Receive handlers and other routines the PGM Transport and other routines that are specific to the NT implementation of a driver. Author: Mohammad Shabbir Alam (MAlam) 3-30-2000 Revision History: --*/ #include "precomp.h" #ifdef FILE_LOGGING #include "receive.tmh" #endif // FILE_LOGGING typedef struct in_pktinfo { tIPADDRESS ipi_addr; // destination IPv4 address UINT ipi_ifindex; // received interface index } IP_PKTINFO; //******************* Pageable Routine Declarations **************** #ifdef ALLOC_PRAGMA #endif //******************* Pageable Routine Declarations **************** VOID FreeDataBuffer( IN tRECEIVE_SESSION *pReceive, IN tPENDING_DATA *pPendingData ) { ASSERT (pPendingData->pDataPacket); if (pPendingData->PendingDataFlags & PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG) { ExFreeToNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside, pPendingData->pDataPacket); if ((0 == --pReceive->pReceiver->NumDataBuffersFromLookaside) && !(pReceive->SessionFlags & PGM_SESSION_DATA_FROM_LOOKASIDE)) { ASSERT (pReceive->pReceiver->MaxBufferLength > pReceive->pReceiver->DataBufferLookasideLength); pReceive->pReceiver->MaxBufferLength += 100; pReceive->pReceiver->DataBufferLookasideLength = pReceive->pReceiver->MaxBufferLength; pReceive->SessionFlags |= PGM_SESSION_DATA_FROM_LOOKASIDE; ExDeleteNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside); ASSERT (pReceive->pReceiver->MaxPacketsBufferedInLookaside); ExInitializeNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside, NULL, NULL, 0, pReceive->pReceiver->DataBufferLookasideLength, PGM_TAG ('D'), pReceive->pReceiver->MaxPacketsBufferedInLookaside); } pPendingData->PendingDataFlags &= ~PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG; } else { PgmFreeMem (pPendingData->pDataPacket); } pPendingData->pDataPacket = NULL; pPendingData->PacketLength = pPendingData->DataOffset = 0; } PVOID AllocateDataBuffer( IN tRECEIVE_SESSION *pReceive, IN tPENDING_DATA *pPendingData, IN ULONG BufferSize ) { ASSERT (!pPendingData->pDataPacket); ASSERT (!(pPendingData->PendingDataFlags & PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG)); if ((pReceive->SessionFlags & PGM_SESSION_DATA_FROM_LOOKASIDE) && (BufferSize <= pReceive->pReceiver->DataBufferLookasideLength)) { if (pPendingData->pDataPacket = ExAllocateFromNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside)) { pReceive->pReceiver->NumDataBuffersFromLookaside++; pPendingData->PendingDataFlags |= PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG; } } else { pReceive->SessionFlags &= ~PGM_SESSION_DATA_FROM_LOOKASIDE; // Ensure no more lookaside! pPendingData->pDataPacket = PgmAllocMem (BufferSize, PGM_TAG('D')); if (BufferSize > pReceive->pReceiver->MaxBufferLength) { pReceive->pReceiver->MaxBufferLength = BufferSize; } } return (pPendingData->pDataPacket); } PVOID ReAllocateDataBuffer( IN tRECEIVE_SESSION *pReceive, IN tPENDING_DATA *pPendingData, IN ULONG BufferSize ) { ULONG SavedFlags1, SavedFlags2; PUCHAR pSavedPacket1, pSavedPacket2; // // First, save the context for the current buffer // SavedFlags1 = pPendingData->PendingDataFlags; pSavedPacket1 = pPendingData->pDataPacket; pPendingData->PendingDataFlags = 0; pPendingData->pDataPacket = NULL; if (AllocateDataBuffer (pReceive, pPendingData, BufferSize)) { ASSERT (pPendingData->pDataPacket); // // Now, save the context for the new buffer // SavedFlags2 = pPendingData->PendingDataFlags; pSavedPacket2 = pPendingData->pDataPacket; // // Free the original buffer // pPendingData->PendingDataFlags = SavedFlags1; pPendingData->pDataPacket = pSavedPacket1; FreeDataBuffer (pReceive, pPendingData); // // Reset the information for the new buffer! // if (SavedFlags2 & PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG) { pPendingData->PendingDataFlags = SavedFlags1 | PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG; } else { pPendingData->PendingDataFlags = SavedFlags1 & ~PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG; } pPendingData->pDataPacket = pSavedPacket2; return (pPendingData->pDataPacket); } // // Failure case! // pPendingData->pDataPacket = pSavedPacket1; pPendingData->PendingDataFlags = SavedFlags1; return (NULL); } //---------------------------------------------------------------------------- VOID RemovePendingIrps( IN tRECEIVE_SESSION *pReceive, IN LIST_ENTRY *pIrpsList ) { PIRP pIrp; if (pIrp = pReceive->pReceiver->pIrpReceive) { pReceive->pReceiver->pIrpReceive = NULL; pIrp->IoStatus.Information = pReceive->pReceiver->BytesInMdl; InsertTailList (pIrpsList, &pIrp->Tail.Overlay.ListEntry); } while (!IsListEmpty (&pReceive->pReceiver->ReceiveIrpsList)) { pIrp = CONTAINING_RECORD (pReceive->pReceiver->ReceiveIrpsList.Flink, IRP, Tail.Overlay.ListEntry); RemoveEntryList (&pIrp->Tail.Overlay.ListEntry); InsertTailList (pIrpsList, &pIrp->Tail.Overlay.ListEntry); pIrp->IoStatus.Information = 0; } } //---------------------------------------------------------------------------- VOID FreeNakContext( IN tRECEIVE_SESSION *pReceive, IN tNAK_FORWARD_DATA *pNak ) /*++ Routine Description: This routine free's the context used for tracking missing sequences Arguments: IN pReceive -- Receive context IN pNak -- Nak Context to be free'ed Return Value: NONE --*/ { UCHAR i, j, k, NumPackets; // // Free any memory for non-parity data // j = k = 0; NumPackets = pNak->NumDataPackets + pNak->NumParityPackets; for (i=0; ipPendingData[i].PacketIndex < pReceive->FECGroupSize) { j++; } else { k++; } FreeDataBuffer (pReceive, &pNak->pPendingData[i]); } ASSERT (j == pNak->NumDataPackets); ASSERT (k == pNak->NumParityPackets); // // Return the pNak memory based on whether it was allocated // from the parity or non-parity lookaside list // if (pNak->OriginalGroupSize > 1) { ExFreeToNPagedLookasideList (&pReceive->pReceiver->ParityContextLookaside, pNak); } else { ExFreeToNPagedLookasideList (&pReceive->pReceiver->NonParityContextLookaside, pNak); } } //---------------------------------------------------------------------------- VOID CleanupPendingNaks( IN tRECEIVE_SESSION *pReceive, IN PVOID fDerefReceive, IN PVOID fReceiveLockHeld ) { LIST_ENTRY NaksList, DataList; tNAK_FORWARD_DATA *pNak; LIST_ENTRY *pEntry; ULONG NumBufferedData = 0; ULONG NumNaks = 0; PGMLockHandle OldIrq; ASSERT (pReceive->pReceiver); if (!fReceiveLockHeld) { PgmLock (pReceive, OldIrq); } else { ASSERT (!fDerefReceive); } DataList.Flink = pReceive->pReceiver->BufferedDataList.Flink; DataList.Blink = pReceive->pReceiver->BufferedDataList.Blink; pReceive->pReceiver->BufferedDataList.Flink->Blink = &DataList; pReceive->pReceiver->BufferedDataList.Blink->Flink = &DataList; InitializeListHead (&pReceive->pReceiver->BufferedDataList); NaksList.Flink = pReceive->pReceiver->NaksForwardDataList.Flink; NaksList.Blink = pReceive->pReceiver->NaksForwardDataList.Blink; pReceive->pReceiver->NaksForwardDataList.Flink->Blink = &NaksList; pReceive->pReceiver->NaksForwardDataList.Blink->Flink = &NaksList; InitializeListHead (&pReceive->pReceiver->NaksForwardDataList); RemoveAllPendingReceiverEntries (pReceive->pReceiver); pReceive->pReceiver->NumDataBuffersFromLookaside++; // So that we don't assert if (!fReceiveLockHeld) { PgmUnlock (pReceive, OldIrq); } // // Cleanup any pending Nak entries // while (!IsListEmpty (&DataList)) { pEntry = RemoveHeadList (&DataList); pNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage); FreeNakContext (pReceive, pNak); NumBufferedData++; } while (!IsListEmpty (&NaksList)) { pEntry = RemoveHeadList (&NaksList); pNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage); FreeNakContext (pReceive, pNak); NumNaks++; } PgmTrace (LogStatus, ("CleanupPendingNaks: " \ "pReceive=<%p>, FirstNak=<%d>, NumBufferedData=<%d=%d>, TotalDataPackets=<%d>, NumNaks=<%d * %d>\n", pReceive, (ULONG) pReceive->pReceiver->FirstNakSequenceNumber, (ULONG) pReceive->pReceiver->NumPacketGroupsPendingClient, NumBufferedData, (ULONG) pReceive->pReceiver->TotalDataPacketsBuffered, NumNaks, (ULONG) pReceive->FECGroupSize)); // ASSERT (NumBufferedData == pReceive->pReceiver->NumPacketGroupsPendingClient); pReceive->pReceiver->NumPacketGroupsPendingClient = 0; if (!fReceiveLockHeld) { PgmLock (pReceive, OldIrq); } pReceive->pReceiver->NumDataBuffersFromLookaside--; // Undoing what we did earlier! ASSERT (!pReceive->pReceiver->NumDataBuffersFromLookaside); if (pReceive->SessionFlags & PGM_SESSION_DATA_FROM_LOOKASIDE) { pReceive->pReceiver->MaxBufferLength = 0; pReceive->SessionFlags &= ~PGM_SESSION_DATA_FROM_LOOKASIDE; // Ensure no more lookaside! ExDeleteNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside); } if (pReceive->pReceiver->pReceiveData) { PgmFreeMem (pReceive->pReceiver->pReceiveData); pReceive->pReceiver->pReceiveData = NULL; } if (!fReceiveLockHeld) { PgmUnlock (pReceive, OldIrq); if (fDerefReceive) { PGM_DEREFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_CLEANUP_NAKS); } } } //---------------------------------------------------------------------------- BOOLEAN CheckIndicateDisconnect( IN tADDRESS_CONTEXT *pAddress, IN tRECEIVE_SESSION *pReceive, IN PGMLockHandle *pOldIrqAddress, IN PGMLockHandle *pOldIrqReceive, IN BOOLEAN fAddressLockHeld ) { ULONG DisconnectFlag; NTSTATUS status; BOOLEAN fDisconnectIndicated = FALSE; LIST_ENTRY PendingIrpsList; PIRP pIrp; tNAK_FORWARD_DATA *pNak; SEQ_TYPE FirstNakSequenceNumber; // // Don't abort if we are currently indicating, or if we have // already aborted! // fDisconnectIndicated = (pReceive->SessionFlags & PGM_SESSION_CLIENT_DISCONNECTED ? TRUE : FALSE); if (pReceive->SessionFlags & (PGM_SESSION_FLAG_IN_INDICATE | PGM_SESSION_CLIENT_DISCONNECTED)) { return (fDisconnectIndicated); } if (IsListEmpty (&pReceive->pReceiver->NaksForwardDataList)) { FirstNakSequenceNumber = pReceive->pReceiver->FirstNakSequenceNumber; } else { pNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Flink, tNAK_FORWARD_DATA, Linkage); FirstNakSequenceNumber = pNak->SequenceNumber + pNak->NextIndexToIndicate; } if ((pReceive->SessionFlags & PGM_SESSION_TERMINATED_ABORT) || ((pReceive->SessionFlags & PGM_SESSION_TERMINATED_GRACEFULLY) && (IsListEmpty (&pReceive->pReceiver->BufferedDataList)) && SEQ_GEQ (FirstNakSequenceNumber, (pReceive->pReceiver->FinDataSequenceNumber+1)))) { // // The session has terminated, so let the client know // if (pReceive->SessionFlags & PGM_SESSION_TERMINATED_ABORT) { DisconnectFlag = TDI_DISCONNECT_ABORT; } else { DisconnectFlag = TDI_DISCONNECT_RELEASE; } pReceive->SessionFlags |= (PGM_SESSION_FLAG_IN_INDICATE | PGM_SESSION_CLIENT_DISCONNECTED); InitializeListHead (&PendingIrpsList); RemovePendingIrps (pReceive, &PendingIrpsList); PGM_REFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_CLEANUP_NAKS, TRUE); PgmUnlock (pReceive, *pOldIrqReceive); if (fAddressLockHeld) { PgmUnlock (pAddress, *pOldIrqAddress); } while (!IsListEmpty (&PendingIrpsList)) { pIrp = CONTAINING_RECORD (PendingIrpsList.Flink, IRP, Tail.Overlay.ListEntry); PgmCancelCancelRoutine (pIrp); RemoveEntryList (&pIrp->Tail.Overlay.ListEntry); pIrp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest (pIrp, IO_NETWORK_INCREMENT); } PgmTrace (LogStatus, ("CheckIndicateDisconnect: " \ "Disconnecting pReceive=<%p:%p>, with %s, FirstNak=<%d>, NextOData=<%d>\n", pReceive, pReceive->ClientSessionContext, (DisconnectFlag == TDI_DISCONNECT_RELEASE ? "TDI_DISCONNECT_RELEASE":"TDI_DISCONNECT_ABORT"), FirstNakSequenceNumber, pReceive->pReceiver->NextODataSequenceNumber)); status = (*pAddress->evDisconnect) (pAddress->DiscEvContext, pReceive->ClientSessionContext, 0, NULL, 0, NULL, DisconnectFlag); fDisconnectIndicated = TRUE; // // See if we can Enqueue the Nak cleanup request to a Worker thread // if (STATUS_SUCCESS != PgmQueueForDelayedExecution (CleanupPendingNaks, pReceive, (PVOID) TRUE, (PVOID) FALSE, FALSE)) { CleanupPendingNaks (pReceive, (PVOID) TRUE, (PVOID) FALSE); } if (fAddressLockHeld) { PgmLock (pAddress, *pOldIrqAddress); } PgmLock (pReceive, *pOldIrqReceive); pReceive->SessionFlags &= ~PGM_SESSION_FLAG_IN_INDICATE; // // We may have received a disassociate while disconnecting, so // complete the irp here // if (pIrp = pReceive->pIrpDisassociate) { pReceive->pIrpDisassociate = NULL; PgmUnlock (pReceive, *pOldIrqReceive); if (fAddressLockHeld) { PgmUnlock (pAddress, *pOldIrqAddress); } PgmIoComplete (pIrp, STATUS_SUCCESS, 0); if (fAddressLockHeld) { PgmLock (pAddress, *pOldIrqAddress); } PgmLock (pReceive, *pOldIrqReceive); } } return (fDisconnectIndicated); } //---------------------------------------------------------------------------- VOID ProcessNakOption( IN tPACKET_OPTION_GENERIC UNALIGNED *pOptionHeader, OUT tNAKS_LIST *pNaksList ) /*++ Routine Description: This routine processes the Nak list option in the Pgm packet Arguments: IN pOptionHeader -- The Nak List option ptr OUT pNaksList -- The parameters extracted (i.e. list of Nak sequences) Return Value: NONE --*/ { UCHAR i, NumNaks; ULONG pPacketNaks[MAX_SEQUENCES_PER_NAK_OPTION]; NumNaks = (pOptionHeader->OptionLength - 4) / 4; ASSERT (NumNaks <= MAX_SEQUENCES_PER_NAK_OPTION); PgmCopyMemory (pPacketNaks, (pOptionHeader + 1), (pOptionHeader->OptionLength - 4)); for (i=0; i < NumNaks; i++) { // // Do not fill in the 0th entry, since that is from the packet header itself // pNaksList->pNakSequences[i+1] = (SEQ_TYPE) ntohl (pPacketNaks[i]); } pNaksList->NumSequences = (USHORT) i; } //---------------------------------------------------------------------------- NTSTATUS ProcessOptions( IN tPACKET_OPTION_LENGTH UNALIGNED *pPacketExtension, IN ULONG BytesAvailable, IN ULONG PacketType, OUT tPACKET_OPTIONS *pPacketOptions, OUT tNAKS_LIST *pNaksList ) /*++ Routine Description: This routine processes the options fields on an incoming Pgm packet and returns the options information extracted in the OUT parameters Arguments: IN pPacketExtension -- Options section of the packet IN BytesAvailable -- from the start of the options IN PacketType -- Whether Data or Spm packet, etc OUT pPacketOptions -- Structure containing the parameters from the options Return Value: NTSTATUS - Final status of the operation --*/ { tPACKET_OPTION_GENERIC UNALIGNED *pOptionHeader; ULONG BytesLeft = BytesAvailable; UCHAR i; ULONG MessageFirstSequence, MessageLength, MessageOffset; ULONG pOptionsData[3]; ULONG OptionsFlags = 0; ULONG NumOptionsProcessed = 0; USHORT TotalOptionsLength = 0; NTSTATUS status = STATUS_UNSUCCESSFUL; pPacketOptions->OptionsLength = 0; // Init pPacketOptions->OptionsFlags = 0; // Init if (BytesLeft > sizeof(tPACKET_OPTION_LENGTH)) { PgmCopyMemory (&TotalOptionsLength, &pPacketExtension->TotalOptionsLength, sizeof (USHORT)); TotalOptionsLength = ntohs (TotalOptionsLength); } // // First process the Option extension // if ((BytesLeft < ((sizeof(tPACKET_OPTION_LENGTH) + sizeof(tPACKET_OPTION_GENERIC)))) || // Ext+opt (pPacketExtension->Type != PACKET_OPTION_LENGTH) || (pPacketExtension->Length != 4) || (BytesLeft < TotalOptionsLength)) // Verify length { // // Need to get at least our header from transport! // PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "BytesLeft=<%d> < Min=<%d>, TotalOptionsLength=<%d>, ExtLength=<%d>, ExtType=<%x>\n", BytesLeft, ((sizeof(tPACKET_OPTION_LENGTH) + sizeof(tPACKET_OPTION_GENERIC))), (ULONG) TotalOptionsLength, pPacketExtension->Length, pPacketExtension->Type)); return (status); } // // Now, process each option // pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) (pPacketExtension + 1); BytesLeft -= sizeof(tPACKET_OPTION_LENGTH); NumOptionsProcessed = 0; status = STATUS_SUCCESS; // By default do { if (pOptionHeader->OptionLength > BytesLeft) { PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "Incorrectly formatted Options: OptionLength=<%d> > BytesLeft=<%d>, NumProcessed=<%d>\n", pOptionHeader->OptionLength, BytesLeft, NumOptionsProcessed)); status = STATUS_UNSUCCESSFUL; break; } switch (pOptionHeader->E_OptionType & ~PACKET_OPTION_TYPE_END_BIT) { case (PACKET_OPTION_NAK_LIST): { if (((PacketType == PACKET_TYPE_NAK) || (PacketType == PACKET_TYPE_NCF) || (PacketType == PACKET_TYPE_NNAK)) && ((pOptionHeader->OptionLength >= PGM_PACKET_OPT_MIN_NAK_LIST_LENGTH) && (pOptionHeader->OptionLength <= PGM_PACKET_OPT_MAX_NAK_LIST_LENGTH))) { PgmTrace (LogPath, ("ProcessOptions: " \ "NAK_LIST: Num Naks=<%d>\n", (pOptionHeader->OptionLength-4)/4)); if (!pNaksList) { ASSERT (0); status = STATUS_UNSUCCESSFUL; break; } ProcessNakOption (pOptionHeader, pNaksList); OptionsFlags |= PGM_OPTION_FLAG_NAK_LIST; } else { PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "NAK_LIST: PacketType=<%x>, Length=<0x%x>, pPacketOptions=<%p>\n", PacketType, pOptionHeader->OptionLength, pPacketOptions)); status = STATUS_UNSUCCESSFUL; } break; } /* // Not supported for now! case (PACKET_OPTION_REDIRECT): { ASSERT (pOptionHeader->OptionLength > 4); // 4 + sizeof(NLA) break; } */ case (PACKET_OPTION_FRAGMENT): { if (pOptionHeader->OptionLength == PGM_PACKET_OPT_FRAGMENT_LENGTH) { PgmCopyMemory (pOptionsData, (pOptionHeader + 1), (3 * sizeof(ULONG))); if (pOptionHeader->Reserved_F_Opx & PACKET_OPTION_RES_F_OPX_ENCODED_BIT) { pPacketOptions->MessageFirstSequence = pOptionsData[0]; pPacketOptions->MessageOffset = pOptionsData[1]; pPacketOptions->MessageLength = pOptionsData[2]; pPacketOptions->FECContext.FragmentOptSpecific = pOptionHeader->U_OptSpecific; OptionsFlags |= PGM_OPTION_FLAG_FRAGMENT; } else { MessageFirstSequence = ntohl (pOptionsData[0]); MessageOffset = ntohl (pOptionsData[1]); MessageLength = ntohl (pOptionsData[2]); if ((MessageLength) && (MessageOffset <= MessageLength)) { PgmTrace (LogPath, ("ProcessOptions: " \ "FRAGMENT: MsgOffset/Length=<%d/%d>\n", MessageOffset, MessageLength)); if (pPacketOptions) { pPacketOptions->MessageFirstSequence = MessageFirstSequence; pPacketOptions->MessageOffset = MessageOffset; pPacketOptions->MessageLength = MessageLength; // pPacketOptions->FECContext.FragmentOptSpecific = PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT; } OptionsFlags |= PGM_OPTION_FLAG_FRAGMENT; } else { PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "FRAGMENT: MsgOffset/Length=<%d/%d>\n", MessageOffset, MessageLength)); status = STATUS_UNSUCCESSFUL; } } } else { PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "FRAGMENT: OptionLength=<%d> != PGM_PACKET_OPT_FRAGMENT_LENGTH=<%d>\n", pOptionHeader->OptionLength, PGM_PACKET_OPT_FRAGMENT_LENGTH)); status = STATUS_UNSUCCESSFUL; } break; } case (PACKET_OPTION_JOIN): { if (pOptionHeader->OptionLength == PGM_PACKET_OPT_JOIN_LENGTH) { PgmCopyMemory (pOptionsData, (pOptionHeader + 1), sizeof(ULONG)); PgmTrace (LogPath, ("ProcessOptions: " \ "JOIN: LateJoinerSeq=<%d>\n", ntohl (pOptionsData[0]))); if (pPacketOptions) { pPacketOptions->LateJoinerSequence = ntohl (pOptionsData[0]); } OptionsFlags |= PGM_OPTION_FLAG_JOIN; } else { status = STATUS_UNSUCCESSFUL; PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "JOIN: OptionLength=<%d> != PGM_PACKET_OPT_JOIN_LENGTH=<%d>\n", pOptionHeader->OptionLength, PGM_PACKET_OPT_JOIN_LENGTH)); } break; } case (PACKET_OPTION_SYN): { if (pOptionHeader->OptionLength == PGM_PACKET_OPT_SYN_LENGTH) { PgmTrace (LogPath, ("ProcessOptions: " \ "SYN\n")); OptionsFlags |= PGM_OPTION_FLAG_SYN; } else { status = STATUS_UNSUCCESSFUL; PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "SYN: OptionLength=<%d> != PGM_PACKET_OPT_SYN_LENGTH=<%d>\n", pOptionHeader->OptionLength, PGM_PACKET_OPT_SYN_LENGTH)); } break; } case (PACKET_OPTION_FIN): { if (pOptionHeader->OptionLength == PGM_PACKET_OPT_FIN_LENGTH) { PgmTrace (LogPath, ("ProcessOptions: " \ "FIN\n")); OptionsFlags |= PGM_OPTION_FLAG_FIN; } else { status = STATUS_UNSUCCESSFUL; PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "FIN: OptionLength=<%d> != PGM_PACKET_OPT_FIN_LENGTH=<%d>\n", pOptionHeader->OptionLength, PGM_PACKET_OPT_FIN_LENGTH)); } break; } case (PACKET_OPTION_RST): { if (pOptionHeader->OptionLength == PGM_PACKET_OPT_RST_LENGTH) { PgmTrace (LogPath, ("ProcessOptions: " \ "RST\n")); OptionsFlags |= PGM_OPTION_FLAG_RST; } else { status = STATUS_UNSUCCESSFUL; PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "RST: OptionLength=<%d> != PGM_PACKET_OPT_RST_LENGTH=<%d>\n", pOptionHeader->OptionLength, PGM_PACKET_OPT_RST_LENGTH)); } break; } // // FEC options // case (PACKET_OPTION_PARITY_PRM): { if (pOptionHeader->OptionLength == PGM_PACKET_OPT_PARITY_PRM_LENGTH) { PgmCopyMemory (pOptionsData, (pOptionHeader + 1), sizeof(ULONG)); PgmTrace (LogPath, ("ProcessOptions: " \ "PARITY_PRM: OptionsSpecific=<%x>, FECGroupInfo=<%d>\n", pOptionHeader->U_OptSpecific, ntohl (pOptionsData[0]))); if (pPacketOptions) { pOptionsData[0] = ntohl (pOptionsData[0]); ASSERT (((UCHAR) pOptionsData[0]) == pOptionsData[0]); pPacketOptions->FECContext.ReceiverFECOptions = pOptionHeader->U_OptSpecific; pPacketOptions->FECContext.FECGroupInfo = (UCHAR) pOptionsData[0]; } OptionsFlags |= PGM_OPTION_FLAG_PARITY_PRM; } else { status = STATUS_UNSUCCESSFUL; PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "PARITY_PRM: OptionLength=<%d> != PGM_PACKET_OPT_PARITY_PRM_LENGTH=<%d>\n", pOptionHeader->OptionLength, PGM_PACKET_OPT_PARITY_PRM_LENGTH)); } break; } case (PACKET_OPTION_PARITY_GRP): { if (pOptionHeader->OptionLength == PGM_PACKET_OPT_PARITY_GRP_LENGTH) { PgmCopyMemory (pOptionsData, (pOptionHeader + 1), sizeof(ULONG)); PgmTrace (LogPath, ("ProcessOptions: " \ "PARITY_GRP: FECGroupInfo=<%d>\n", ntohl (pOptionsData[0]))); if (pPacketOptions) { pOptionsData[0] = ntohl (pOptionsData[0]); ASSERT (((UCHAR) pOptionsData[0]) == pOptionsData[0]); pPacketOptions->FECContext.FECGroupInfo = (UCHAR) pOptionsData[0]; } OptionsFlags |= PGM_OPTION_FLAG_PARITY_GRP; } else { status = STATUS_UNSUCCESSFUL; PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "PARITY_GRP: OptionLength=<%d> != PGM_PACKET_OPT_PARITY_GRP_LENGTH=<%d>\n", pOptionHeader->OptionLength, PGM_PACKET_OPT_PARITY_GRP_LENGTH)); } break; } case (PACKET_OPTION_CURR_TGSIZE): { if (pOptionHeader->OptionLength == PGM_PACKET_OPT_PARITY_CUR_TGSIZE_LENGTH) { PgmCopyMemory (pOptionsData, (pOptionHeader + 1), sizeof(ULONG)); if (pOptionsData[0]) { PgmTrace (LogPath, ("ProcessOptions: " \ "CURR_TGSIZE: NumPacketsInThisGroup=<%d>\n", ntohl (pOptionsData[0]))); if (pPacketOptions) { pPacketOptions->FECContext.NumPacketsInThisGroup = (UCHAR) (ntohl (pOptionsData[0])); } OptionsFlags |= PGM_OPTION_FLAG_PARITY_CUR_TGSIZE; } else { PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "CURR_TGSIZE: NumPacketsInThisGroup=<%d>\n", ntohl (pOptionsData[0]))); status = STATUS_UNSUCCESSFUL; } } else { status = STATUS_UNSUCCESSFUL; PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "PARITY_GRP: OptionLength=<%d> != PGM_PACKET_OPT_PARITY_CUR_TGSIZE_LENGTH=<%d>\n", pOptionHeader->OptionLength, PGM_PACKET_OPT_PARITY_CUR_TGSIZE_LENGTH)); } break; } case (PACKET_OPTION_REDIRECT): case (PACKET_OPTION_CR): case (PACKET_OPTION_CRQST): case (PACKET_OPTION_NAK_BO_IVL): case (PACKET_OPTION_NAK_BO_RNG): case (PACKET_OPTION_NBR_UNREACH): case (PACKET_OPTION_PATH_NLA): case (PACKET_OPTION_INVALID): { PgmTrace (LogStatus, ("ProcessOptions: " \ "WARNING: PacketType=<%x>: Unhandled Option=<%x>, OptionLength=<%d>\n", PacketType, (pOptionHeader->E_OptionType & ~PACKET_OPTION_TYPE_END_BIT), pOptionHeader->OptionLength)); OptionsFlags |= PGM_OPTION_FLAG_UNRECOGNIZED; break; } default: { PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "PacketType=<%x>: Unrecognized Option=<%x>, OptionLength=<%d>\n", PacketType, (pOptionHeader->E_OptionType & ~PACKET_OPTION_TYPE_END_BIT), pOptionHeader->OptionLength)); ASSERT (0); // We do not recognize this option, but we will continue anyway! OptionsFlags |= PGM_OPTION_FLAG_UNRECOGNIZED; status = STATUS_UNSUCCESSFUL; break; } } if (!NT_SUCCESS (status)) { break; } NumOptionsProcessed++; BytesLeft -= pOptionHeader->OptionLength; if (pOptionHeader->E_OptionType & PACKET_OPTION_TYPE_END_BIT) { break; } pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) (((UCHAR *) pOptionHeader) + pOptionHeader->OptionLength); } while (BytesLeft >= sizeof(tPACKET_OPTION_GENERIC)); ASSERT (NT_SUCCESS (status)); if (NT_SUCCESS (status)) { if ((BytesLeft + TotalOptionsLength) == BytesAvailable) { pPacketOptions->OptionsLength = TotalOptionsLength; pPacketOptions->OptionsFlags = OptionsFlags; } else { PgmTrace (LogError, ("ProcessOptions: ERROR -- " \ "BytesLeft=<%d> + TotalOptionsLength=<%d> != BytesAvailable=<%d>\n", BytesLeft, TotalOptionsLength, BytesAvailable)); status = STATUS_INVALID_BUFFER_SIZE; } } PgmTrace (LogAllFuncs, ("ProcessOptions: " \ "Processed <%d> options, TotalOptionsLength=<%d>\n", NumOptionsProcessed, TotalOptionsLength)); return (status); } //---------------------------------------------------------------------------- ULONG AdjustReceiveBufferLists( IN tRECEIVE_SESSION *pReceive ) { tNAK_FORWARD_DATA *pNak; UCHAR TotalPackets, i; ULONG NumMoved = 0; ULONG DataPacketsMoved = 0; // // Update the last consumed time if we did not have any data buffered prior // to this // if (IsListEmpty (&pReceive->pReceiver->BufferedDataList)) { pReceive->pReceiver->LastDataConsumedTime = PgmDynamicConfig.ReceiversTimerTickCount; } // // Assume we have no Naks pending // pReceive->pReceiver->FirstNakSequenceNumber = pReceive->pReceiver->FurthestKnownGroupSequenceNumber + pReceive->FECGroupSize; while (!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList)) { // // Move any Naks contexts for which the group is complete // to the BufferedDataList // pNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Flink, tNAK_FORWARD_DATA, Linkage); if (((pNak->NumDataPackets + pNak->NumParityPackets) < pNak->PacketsInGroup) && ((pNak->NextIndexToIndicate + pNak->NumDataPackets) < pNak->PacketsInGroup)) { pReceive->pReceiver->FirstNakSequenceNumber = pNak->SequenceNumber; break; } // // If this is a partial group with extraneous parity packets, // remove the parity packets // if ((pNak->NextIndexToIndicate) && (pNak->NumParityPackets) && ((pNak->NextIndexToIndicate + pNak->NumDataPackets) >= pNak->PacketsInGroup)) { // // Start from the end and go backwards // i = TotalPackets = pNak->NumDataPackets + pNak->NumParityPackets; while (i && pNak->NumParityPackets) { i--; // Convert from packet # to index if (pNak->pPendingData[i].PacketIndex >= pNak->OriginalGroupSize) { PgmTrace (LogAllFuncs, ("AdjustReceiveBufferLists: " \ "Extraneous parity [%d] -- NextIndex=<%d>, Data=<%d>, Parity=<%d>, PktsInGrp=<%d>\n", i, (ULONG) pNak->NextIndexToIndicate, (ULONG) pNak->NumDataPackets, (ULONG) pNak->NumParityPackets, (ULONG) pNak->PacketsInGroup)); FreeDataBuffer (pReceive, &pNak->pPendingData[i]); if (i != (TotalPackets - 1)) { PgmCopyMemory (&pNak->pPendingData[i], &pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA)); } PgmZeroMemory (&pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA)); pNak->NumParityPackets--; TotalPackets--; pReceive->pReceiver->DataPacketsPendingNaks--; pReceive->pReceiver->TotalDataPacketsBuffered--; } } // // Re-Init all the indices // for (i=0; iOriginalGroupSize; i++) { pNak->pPendingData[i].ActualIndexOfDataPacket = pNak->OriginalGroupSize; } // // Set the indices only for the data packets // for (i=0; ipPendingData[i].PacketIndex < pNak->OriginalGroupSize) { pNak->pPendingData[pNak->pPendingData[i].PacketIndex].ActualIndexOfDataPacket = i; } } } RemoveEntryList (&pNak->Linkage); InsertTailList (&pReceive->pReceiver->BufferedDataList, &pNak->Linkage); NumMoved++; DataPacketsMoved += (pNak->NumDataPackets + pNak->NumParityPackets); } pReceive->pReceiver->NumPacketGroupsPendingClient += NumMoved; pReceive->pReceiver->DataPacketsPendingIndicate += DataPacketsMoved; pReceive->pReceiver->DataPacketsPendingNaks -= DataPacketsMoved; ASSERT (pReceive->pReceiver->TotalDataPacketsBuffered == (pReceive->pReceiver->DataPacketsPendingIndicate + pReceive->pReceiver->DataPacketsPendingNaks)); return (NumMoved); } //---------------------------------------------------------------------------- VOID AdjustNcfRDataResponseTimes( IN tRECEIVE_SESSION *pReceive, IN PNAK_FORWARD_DATA pLastNak ) { ULONGLONG NcfRDataTickCounts; NcfRDataTickCounts = PgmDynamicConfig.ReceiversTimerTickCount - pLastNak->FirstNcfTickCount; pReceive->pReceiver->StatSumOfNcfRDataTicks += NcfRDataTickCounts; pReceive->pReceiver->NumNcfRDataTicksSamples++; if (!pReceive->pReceiver->NumNcfRDataTicksSamples) { // // This will be the divisor below, so it has to be non-zero! // ASSERT (0); return; } if ((NcfRDataTickCounts > pReceive->pReceiver->MaxOutstandingNakTimeout) && (pReceive->pReceiver->MaxOutstandingNakTimeout != pReceive->pReceiver->MaxRDataResponseTCFromWindow)) { if (pReceive->pReceiver->MaxRDataResponseTCFromWindow && NcfRDataTickCounts > pReceive->pReceiver->MaxRDataResponseTCFromWindow) { pReceive->pReceiver->MaxOutstandingNakTimeout = pReceive->pReceiver->MaxRDataResponseTCFromWindow; } else { pReceive->pReceiver->MaxOutstandingNakTimeout = NcfRDataTickCounts; } // // Since we just updated the Max value, we should also // recalculate the default timeout // pReceive->pReceiver->AverageNcfRDataResponseTC = pReceive->pReceiver->StatSumOfNcfRDataTicks / pReceive->pReceiver->NumNcfRDataTicksSamples; NcfRDataTickCounts = (pReceive->pReceiver->AverageNcfRDataResponseTC + pReceive->pReceiver->MaxOutstandingNakTimeout) >> 1; if (NcfRDataTickCounts > (pReceive->pReceiver->AverageNcfRDataResponseTC << 1)) { NcfRDataTickCounts = pReceive->pReceiver->AverageNcfRDataResponseTC << 1; } if (NcfRDataTickCounts > pReceive->pReceiver->OutstandingNakTimeout) { pReceive->pReceiver->OutstandingNakTimeout = NcfRDataTickCounts; } } } //---------------------------------------------------------------------------- VOID UpdateSpmIntervalInformation( IN tRECEIVE_SESSION *pReceive ) { ULONG LastIntervalTickCount = (ULONG) (PgmDynamicConfig.ReceiversTimerTickCount - pReceive->pReceiver->LastSpmTickCount); if (!LastIntervalTickCount) { return; } pReceive->pReceiver->LastSpmTickCount = PgmDynamicConfig.ReceiversTimerTickCount; if (LastIntervalTickCount > pReceive->pReceiver->MaxSpmInterval) { pReceive->pReceiver->MaxSpmInterval = LastIntervalTickCount; } /* if (pReceive->pReceiver->NumSpmIntervalSamples) { pReceive->pReceiver->StatSumOfSpmIntervals += pReceive->pReceiver->LastSpmTickCount; pReceive->pReceiver->NumSpmIntervalSamples++; pReceive->pReceiver->AverageSpmInterval = pReceive->pReceiver->StatSumOfSpmIntervals / pReceive->pReceiver->NumSpmIntervalSamples; } */ } //---------------------------------------------------------------------------- VOID UpdateRealTimeWindowInformation( IN tRECEIVE_SESSION *pReceive, IN SEQ_TYPE LeadingEdgeSeqNumber, IN SEQ_TYPE TrailingEdgeSeqNumber ) { tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver; SEQ_TYPE SequencesInWindow = 1 + LeadingEdgeSeqNumber - TrailingEdgeSeqNumber; if (SEQ_GT (SequencesInWindow, pReceiver->MaxSequencesInWindow)) { pReceiver->MaxSequencesInWindow = SequencesInWindow; } if (TrailingEdgeSeqNumber) { if ((!pReceiver->MinSequencesInWindow) || SEQ_LT (SequencesInWindow, pReceiver->MinSequencesInWindow)) { pReceiver->MinSequencesInWindow = SequencesInWindow; } pReceiver->StatSumOfWindowSeqs += SequencesInWindow; pReceiver->NumWindowSamples++; } } VOID UpdateSampleTimeWindowInformation( IN tRECEIVE_SESSION *pReceive ) { ULONGLONG NcfRDataTimeout; tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver; // // No need to update if there is no data // if (!pReceive->MaxRateKBitsPerSec || !pReceive->TotalPacketsReceived) // Avoid divide by 0 error { return; } // // Now, update the window information // if (pReceiver->NumWindowSamples) { pReceiver->AverageSequencesInWindow = pReceiver->StatSumOfWindowSeqs / pReceiver->NumWindowSamples; } if (pReceiver->AverageSequencesInWindow) { pReceiver->WindowSizeLastInMSecs = ((pReceiver->AverageSequencesInWindow * pReceive->TotalBytes) << LOG2_BITS_PER_BYTE) / (pReceive->TotalPacketsReceived * pReceive->MaxRateKBitsPerSec); } else { pReceiver->WindowSizeLastInMSecs = ((pReceiver->MaxSequencesInWindow * pReceive->TotalBytes) << LOG2_BITS_PER_BYTE) / (pReceive->TotalPacketsReceived * pReceive->MaxRateKBitsPerSec); } pReceiver->MaxRDataResponseTCFromWindow = pReceiver->WindowSizeLastInMSecs / (NCF_WAITING_RDATA_MAX_RETRIES * BASIC_TIMER_GRANULARITY_IN_MSECS); PgmTrace (LogPath, ("UpdateSampleTimeWindowInformation: " \ "pReceive=<%p>, MaxRate=<%I64d>, AvgSeqsInWindow=<%I64d>, WinSzinMSecsLast=<%I64d>\n", pReceive, pReceive->MaxRateKBitsPerSec, pReceiver->AverageSequencesInWindow, pReceiver->WindowSizeLastInMSecs)); // // Now, update the NcfRData timeout information // if (pReceiver->StatSumOfNcfRDataTicks && pReceiver->NumNcfRDataTicksSamples) { pReceiver->AverageNcfRDataResponseTC = pReceiver->StatSumOfNcfRDataTicks / pReceiver->NumNcfRDataTicksSamples; } if (pReceiver->AverageNcfRDataResponseTC) { NcfRDataTimeout = (pReceiver->AverageNcfRDataResponseTC + pReceiver->MaxOutstandingNakTimeout) >> 1; if (NcfRDataTimeout > (pReceiver->AverageNcfRDataResponseTC << 1)) { NcfRDataTimeout = pReceiver->AverageNcfRDataResponseTC << 1; } if (NcfRDataTimeout > pReceiver->InitialOutstandingNakTimeout/BASIC_TIMER_GRANULARITY_IN_MSECS) { pReceiver->OutstandingNakTimeout = NcfRDataTimeout; } else { pReceiver->OutstandingNakTimeout = pReceiver->InitialOutstandingNakTimeout / BASIC_TIMER_GRANULARITY_IN_MSECS; } } } //---------------------------------------------------------------------------- VOID RemoveRedundantNaks( IN tRECEIVE_SESSION *pReceive, IN tNAK_FORWARD_DATA *pNak, IN BOOLEAN fEliminateExtraParityPackets ) { UCHAR i, TotalPackets; ASSERT (fEliminateExtraParityPackets || !pNak->NumParityPackets); TotalPackets = pNak->NumDataPackets + pNak->NumParityPackets; // // First, eliminate the NULL Packets // if (pNak->PacketsInGroup < pNak->OriginalGroupSize) { i = 0; while (i < pNak->OriginalGroupSize) { if ((pNak->pPendingData[i].PacketIndex < pNak->PacketsInGroup) || // Non-NULL Data packet (pNak->pPendingData[i].PacketIndex >= pNak->OriginalGroupSize)) // Parity packet { // // Ignore for now! // i++; continue; } FreeDataBuffer (pReceive, &pNak->pPendingData[i]); if (i != (TotalPackets-1)) { PgmCopyMemory (&pNak->pPendingData[i], &pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA)); } PgmZeroMemory (&pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA)); pNak->NumDataPackets--; TotalPackets--; } ASSERT (pNak->NumDataPackets <= TotalPackets); if (fEliminateExtraParityPackets) { // // If we still have extra parity packets, free those also // i = 0; while ((i < TotalPackets) && (TotalPackets > pNak->PacketsInGroup)) { ASSERT (pNak->NumParityPackets); if (pNak->pPendingData[i].PacketIndex < pNak->OriginalGroupSize) { // // Ignore data packets // i++; continue; } FreeDataBuffer (pReceive, &pNak->pPendingData[i]); if (i != (TotalPackets-1)) { PgmCopyMemory (&pNak->pPendingData[i], &pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA)); } PgmZeroMemory (&pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA)); pNak->NumParityPackets--; TotalPackets--; } ASSERT (TotalPackets <= pNak->PacketsInGroup); } } // // Re-Init all the indices // for (i=0; iOriginalGroupSize; i++) { pNak->pPendingData[i].ActualIndexOfDataPacket = pNak->OriginalGroupSize; } // // Set the indices only for the data packets // for (i=0; ipPendingData[i].PacketIndex < pNak->OriginalGroupSize) { pNak->pPendingData[pNak->pPendingData[i].PacketIndex].ActualIndexOfDataPacket = i; } } if ((fEliminateExtraParityPackets) && (((pNak->NumDataPackets + pNak->NumParityPackets) >= pNak->PacketsInGroup) || ((pNak->NextIndexToIndicate + pNak->NumDataPackets) >= pNak->PacketsInGroup))) { RemovePendingReceiverEntry (pNak); } } //---------------------------------------------------------------------------- VOID PgmSendNakCompletion( IN tRECEIVE_SESSION *pReceive, IN tNAK_CONTEXT *pNakContext, IN NTSTATUS status ) /*++ Routine Description: This is the Completion routine called by IP on completing a NakSend Arguments: IN pReceive -- Receive context IN pNakContext -- Nak Context to be free'ed IN status -- status of send from tansport Return Value: NONE --*/ { PGMLockHandle OldIrq; PgmLock (pReceive, OldIrq); if (NT_SUCCESS (status)) { // // Set the Receiver Nak statistics // PgmTrace (LogAllFuncs, ("PgmSendNakCompletion: " \ "SUCCEEDED\n")); } else { PgmTrace (LogError, ("PgmSendNakCompletion: ERROR -- " \ "status=<%x>\n", status)); } if (!(--pNakContext->RefCount)) { PgmUnlock (pReceive, OldIrq); // // Free the Memory and deref the Session context for this Nak // PgmFreeMem (pNakContext); PGM_DEREFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_SEND_NAK); } else { PgmUnlock (pReceive, OldIrq); } } //---------------------------------------------------------------------------- NTSTATUS PgmSendNak( IN tRECEIVE_SESSION *pReceive, IN tNAKS_CONTEXT *pNakSequences ) /*++ Routine Description: This routine sends a Nak packet with the number of sequences specified Arguments: IN pReceive -- Receive context IN pNakSequences -- List of Sequence #s Return Value: NTSTATUS - Final status of the operation --*/ { tBASIC_NAK_NCF_PACKET_HEADER *pNakPacket; tNAK_CONTEXT *pNakContext; tPACKET_OPTION_LENGTH *pPacketExtension; tPACKET_OPTION_GENERIC *pOptionHeader; ULONG i; ULONG XSum; USHORT OptionsLength = 0; NTSTATUS status; if ((!pNakSequences->NumSequences) || (pNakSequences->NumSequences > (MAX_SEQUENCES_PER_NAK_OPTION+1)) || (!(pNakContext = PgmAllocMem ((2*sizeof(ULONG)+PGM_MAX_NAK_NCF_HEADER_LENGTH), PGM_TAG('2'))))) { PgmTrace (LogError, ("PgmSendNak: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES allocating pNakContext\n")); return (STATUS_INSUFFICIENT_RESOURCES); } PgmZeroMemory (pNakContext, (2*sizeof(ULONG)+PGM_MAX_NAK_NCF_HEADER_LENGTH)); pNakContext->RefCount = 2; // 1 for the unicast, and the other for the MCast Nak pNakPacket = &pNakContext->NakPacket; pNakPacket->CommonHeader.SrcPort = htons (pReceive->pReceiver->ListenMCastPort); pNakPacket->CommonHeader.DestPort = htons (pReceive->TSI.hPort); pNakPacket->CommonHeader.Type = PACKET_TYPE_NAK; if (pNakSequences->NakType == NAK_TYPE_PARITY) { pNakPacket->CommonHeader.Options = PACKET_HEADER_OPTIONS_PARITY; pReceive->pReceiver->TotalParityNaksSent += pNakSequences->NumSequences; } else { pNakPacket->CommonHeader.Options = 0; pReceive->pReceiver->TotalSelectiveNaksSent += pNakSequences->NumSequences; } PgmCopyMemory (&pNakPacket->CommonHeader.gSourceId, pReceive->TSI.GSI, SOURCE_ID_LENGTH); pNakPacket->RequestedSequenceNumber = htonl ((ULONG) pNakSequences->Sequences[0]); pNakPacket->SourceNLA.NLA_AFI = htons (IPV4_NLA_AFI); pNakPacket->SourceNLA.IpAddress = htonl (pReceive->pReceiver->SenderIpAddress); pNakPacket->MCastGroupNLA.NLA_AFI = htons (IPV4_NLA_AFI); pNakPacket->MCastGroupNLA.IpAddress = htonl (pReceive->pReceiver->ListenMCastIpAddress); PgmTrace (LogPath, ("PgmSendNak: " \ "Sending Naks for:\n\t[%d]\n", (ULONG) pNakSequences->Sequences[0])); if (pNakSequences->NumSequences > 1) { pPacketExtension = (tPACKET_OPTION_LENGTH *) (pNakPacket + 1); pPacketExtension->Type = PACKET_OPTION_LENGTH; pPacketExtension->Length = PGM_PACKET_EXTENSION_LENGTH; OptionsLength += PGM_PACKET_EXTENSION_LENGTH; pOptionHeader = (tPACKET_OPTION_GENERIC *) (pPacketExtension + 1); pOptionHeader->E_OptionType = PACKET_OPTION_NAK_LIST; pOptionHeader->OptionLength = 4 + (UCHAR) ((pNakSequences->NumSequences-1) * sizeof(ULONG)); for (i=1; iNumSequences; i++) { PgmTrace (LogPath, ("PgmSendNak: " \ "\t[%d]\n", (ULONG) pNakSequences->Sequences[i])); ((PULONG) (pOptionHeader))[i] = htonl ((ULONG) pNakSequences->Sequences[i]); } pOptionHeader->E_OptionType |= PACKET_OPTION_TYPE_END_BIT; // One and only (last) opt pNakPacket->CommonHeader.Options |=(PACKET_HEADER_OPTIONS_PRESENT | PACKET_HEADER_OPTIONS_NETWORK_SIGNIFICANT); OptionsLength = PGM_PACKET_EXTENSION_LENGTH + pOptionHeader->OptionLength; pPacketExtension->TotalOptionsLength = htons (OptionsLength); } OptionsLength += sizeof(tBASIC_NAK_NCF_PACKET_HEADER); // Now is whole pkt pNakPacket->CommonHeader.Checksum = 0; XSum = 0; XSum = tcpxsum (XSum, (CHAR *) pNakPacket, OptionsLength); pNakPacket->CommonHeader.Checksum = (USHORT) (~XSum); PGM_REFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_SEND_NAK, FALSE); // // First multicast the Nak // status = TdiSendDatagram (pReceive->pReceiver->pAddress->pFileObject, pReceive->pReceiver->pAddress->pDeviceObject, pNakPacket, OptionsLength, PgmSendNakCompletion, // Completion pReceive, // Context1 pNakContext, // Context2 pReceive->pReceiver->ListenMCastIpAddress, pReceive->pReceiver->ListenMCastPort, FALSE); ASSERT (NT_SUCCESS (status)); // // Now, Unicast the Nak // status = TdiSendDatagram (pReceive->pReceiver->pAddress->pFileObject, pReceive->pReceiver->pAddress->pDeviceObject, pNakPacket, OptionsLength, PgmSendNakCompletion, // Completion pReceive, // Context1 pNakContext, // Context2 pReceive->pReceiver->LastSpmSource, IPPROTO_RM, FALSE); ASSERT (NT_SUCCESS (status)); PgmTrace (LogAllFuncs, ("PgmSendNak: " \ "Sent %s Nak for <%d> Sequences [%d--%d] to <%x:%d>\n", (pNakSequences->NakType == NAK_TYPE_PARITY ? "PARITY" : "SELECTIVE"), pNakSequences->NumSequences, (ULONG) pNakSequences->Sequences[0], (ULONG) pNakSequences->Sequences[pNakSequences->NumSequences-1], pReceive->pReceiver->SenderIpAddress, IPPROTO_RM)); return (status); } //---------------------------------------------------------------------------- VOID CheckSendPendingNaks( IN tADDRESS_CONTEXT *pAddress, IN tRECEIVE_SESSION *pReceive, IN tRECEIVE_CONTEXT *pReceiver, IN PGMLockHandle *pOldIrq ) /*++ Routine Description: This routine checks if any Naks need to be sent and sends them as required The PgmDynamicConfig lock is held on entry and exit from this routine Arguments: IN pAddress -- Address object context IN pReceive -- Receive context IN pOldIrq -- Irq for PgmDynamicConfig Return Value: NONE --*/ { tNAKS_CONTEXT *pNakContext, *pSelectiveNaks = NULL; tNAKS_CONTEXT *pParityNaks = NULL; LIST_ENTRY NaksList; LIST_ENTRY *pEntry; tNAK_FORWARD_DATA *pNak; PGMLockHandle OldIrq, OldIrq1; ULONG NumMissingPackets, PacketsInGroup, NumNaks, TotalSeqsNacked = 0; BOOLEAN fSendSelectiveNak, fSendParityNak; UCHAR i, j; ULONG NumPendingNaks = 0; ULONG NumOutstandingNaks = 0; USHORT Index; ULONG NakRandomBackoffMSecs, NakRepeatIntervalMSecs; if ((!pReceiver->LastSpmSource) || ((pReceiver->DataPacketsPendingNaks <= OUT_OF_ORDER_PACKETS_BEFORE_NAK) && ((pReceiver->LastNakSendTime + (pReceive->pReceiver->InitialOutstandingNakTimeout>>2)) > PgmDynamicConfig.ReceiversTimerTickCount))) { PgmTrace (LogPath, ("CheckSendPendingNaks: " \ "No Naks to send for pReceive=<%p>, LastSpmSource=<%x>, NumDataPackets=<%d>, LastSendTime=<%I64d>, Current=<%I64d>\n", pReceive, pReceiver->LastSpmSource, pReceiver->DataPacketsPendingNaks, pReceiver->LastNakSendTime+(NAK_MIN_INITIAL_BACKOFF_TIMEOUT_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS), PgmDynamicConfig.ReceiversTimerTickCount)); return; } InitializeListHead (&NaksList); if (!(pSelectiveNaks = PgmAllocMem (sizeof (tNAKS_CONTEXT), PGM_TAG('5'))) || !(pParityNaks = PgmAllocMem (sizeof (tNAKS_CONTEXT), PGM_TAG('6')))) { PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES allocating pNakContext\n")); if (pSelectiveNaks) { PgmFreeMem (pSelectiveNaks); } return; } PgmZeroMemory (pSelectiveNaks, sizeof (tNAKS_CONTEXT)); PgmZeroMemory (pParityNaks, sizeof (tNAKS_CONTEXT)); pParityNaks->NakType = NAK_TYPE_PARITY; pSelectiveNaks->NakType = NAK_TYPE_SELECTIVE; InsertTailList (&NaksList, &pParityNaks->Linkage); InsertTailList (&NaksList, &pSelectiveNaks->Linkage); PgmLock (pAddress, OldIrq); PgmLock (pReceive, OldIrq1); AdjustReceiveBufferLists (pReceive); if (pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED) { NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS_OPT; NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS_OPT; } else { NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS; NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS; } fSendSelectiveNak = fSendParityNak = FALSE; pEntry = &pReceiver->PendingNaksList; while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->PendingNaksList) { pNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, SendNakLinkage); NumMissingPackets = pNak->PacketsInGroup - (pNak->NumDataPackets + pNak->NumParityPackets); ASSERT (NumMissingPackets); // // if this Nak is outside the trailing window, then we are hosed! // if (SEQ_GT (pReceiver->LastTrailingEdgeSeqNum, pNak->SequenceNumber)) { PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \ "Sequence # [%d] out of trailing edge <%d>, NumNcfs received=<%d>\n", (ULONG) pNak->SequenceNumber, (ULONG) pReceiver->LastTrailingEdgeSeqNum, pNak->WaitingRDataRetries)); pReceive->SessionFlags |= PGM_SESSION_FLAG_NAK_TIMED_OUT; break; } // // See if we are currently in NAK pending mode // if (pNak->PendingNakTimeout) { NumPendingNaks += NumMissingPackets; if (PgmDynamicConfig.ReceiversTimerTickCount > pNak->PendingNakTimeout) { // // Time out Naks only after we have received a FIN! // if (pNak->WaitingNcfRetries++ >= NAK_WAITING_NCF_MAX_RETRIES) { PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \ "Pending Nak for Sequence # [%d] Timed out! Num Naks sent=<%d>, Window=<%d--%d> ( %d seqs)\n", (ULONG) pNak->SequenceNumber, pNak->WaitingNcfRetries, (ULONG) pReceiver->LastTrailingEdgeSeqNum, (ULONG) pReceiver->FurthestKnownGroupSequenceNumber, (ULONG) (1+pReceiver->FurthestKnownGroupSequenceNumber- pReceiver->LastTrailingEdgeSeqNum))); pReceive->SessionFlags |= PGM_SESSION_FLAG_NAK_TIMED_OUT; break; } if ((pNak->PacketsInGroup > 1) && (pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT) && ((pNak->SequenceNumber != pReceiver->FurthestKnownGroupSequenceNumber) || (SEQ_GEQ (pReceiver->FurthestKnownSequenceNumber, (pNak->SequenceNumber+pReceive->FECGroupSize-1))))) { ASSERT (NumMissingPackets <= pReceive->FECGroupSize); pParityNaks->Sequences[pParityNaks->NumSequences] = (SEQ_TYPE) (pNak->SequenceNumber + NumMissingPackets - 1); if (++pParityNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1)) { fSendParityNak = TRUE; } pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NakRepeatIntervalMSecs + (NakRandomBackoffMSecs/NumMissingPackets)) / BASIC_TIMER_GRANULARITY_IN_MSECS); TotalSeqsNacked += NumMissingPackets; NumMissingPackets = 0; } else { NumNaks = 0; if (pReceive->FECOptions) { if (pNak->SequenceNumber == pReceiver->FurthestKnownGroupSequenceNumber) { ASSERT (SEQ_GEQ (pReceiver->FurthestKnownSequenceNumber, pReceiver->FurthestKnownGroupSequenceNumber)); PacketsInGroup = pReceiver->FurthestKnownSequenceNumber - pNak->SequenceNumber + 1; ASSERT (PacketsInGroup >= (ULONG) (pNak->NumDataPackets + pNak->NumParityPackets)); } else { PacketsInGroup = pNak->PacketsInGroup; } NumMissingPackets = PacketsInGroup - (pNak->NextIndexToIndicate +pNak->NumDataPackets +pNak->NumParityPackets); ASSERT ((NumMissingPackets) || (pNak->SequenceNumber == pReceiver->FurthestKnownGroupSequenceNumber)); for (i=pNak->NextIndexToIndicate; ipPendingData[i].ActualIndexOfDataPacket >= pNak->OriginalGroupSize) { if (!pNak->pPendingData[i].NcfsReceivedForActualIndex) { pSelectiveNaks->Sequences[pSelectiveNaks->NumSequences++] = pNak->SequenceNumber+i; NumNaks++; } if (pSelectiveNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1)) { if (!(pSelectiveNaks = PgmAllocMem (sizeof (tNAKS_CONTEXT), PGM_TAG('5')))) { PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES allocating pSelectiveNaks\n")); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; break; } PgmZeroMemory (pSelectiveNaks, sizeof (tNAKS_CONTEXT)); pSelectiveNaks->NakType = NAK_TYPE_SELECTIVE; InsertTailList (&NaksList, &pSelectiveNaks->Linkage); } if (!--NumMissingPackets) { break; } } } ASSERT (!NumMissingPackets); if (NumNaks) { TotalSeqsNacked += NumNaks; } else { pNak->WaitingNcfRetries--; } } else { pSelectiveNaks->Sequences[pSelectiveNaks->NumSequences++] = pNak->SequenceNumber; TotalSeqsNacked++; } pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NakRepeatIntervalMSecs + NakRandomBackoffMSecs) / BASIC_TIMER_GRANULARITY_IN_MSECS); if (!pSelectiveNaks) { break; } if (pSelectiveNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1)) { fSendSelectiveNak = TRUE; } } } } else if (pNak->OutstandingNakTimeout) { NumOutstandingNaks += NumMissingPackets; if (PgmDynamicConfig.ReceiversTimerTickCount > pNak->OutstandingNakTimeout) { // // We have timed-out waiting for RData -- Reset the Timeout to send // a Nak after the Random Backoff (if we have not exceeded the Data retries) // if (pNak->WaitingRDataRetries++ == NCF_WAITING_RDATA_MAX_RETRIES) { PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \ "Outstanding Nak for Sequence # [%d] Timed out!, Window=<%d--%d> ( %d seqs), Ncfs=<%d>, FirstNak=<%d>\n", (ULONG) pNak->SequenceNumber, (ULONG) pReceiver->LastTrailingEdgeSeqNum, (ULONG) pReceiver->FurthestKnownGroupSequenceNumber, (ULONG) (1+pReceiver->FurthestKnownGroupSequenceNumber-pReceiver->LastTrailingEdgeSeqNum), pNak->WaitingRDataRetries, (ULONG) pReceiver->FirstNakSequenceNumber)); pReceive->SessionFlags |= PGM_SESSION_FLAG_NAK_TIMED_OUT; break; } pNak->WaitingNcfRetries = 0; pNak->OutstandingNakTimeout = 0; pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NakRandomBackoffMSecs/NumMissingPackets) / BASIC_TIMER_GRANULARITY_IN_MSECS); for (i=0; iPacketsInGroup; i++) { pNak->pPendingData[i].NcfsReceivedForActualIndex = 0; } NumMissingPackets = 0; } } while (fSendSelectiveNak || fSendParityNak) { if (fSendSelectiveNak) { if (!(pSelectiveNaks = PgmAllocMem (sizeof (tNAKS_CONTEXT), PGM_TAG('5')))) { PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES allocating pSelectiveNaks\n")); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; break; } PgmZeroMemory (pSelectiveNaks, sizeof (tNAKS_CONTEXT)); pSelectiveNaks->NakType = NAK_TYPE_SELECTIVE; InsertTailList (&NaksList, &pSelectiveNaks->Linkage); fSendSelectiveNak = FALSE; } if (fSendParityNak) { if (!(pParityNaks = PgmAllocMem (sizeof (tNAKS_CONTEXT), PGM_TAG('6')))) { PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES allocating pParityNaks\n")); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; break; } PgmZeroMemory (pParityNaks, sizeof (tNAKS_CONTEXT)); pParityNaks->NakType = NAK_TYPE_PARITY; InsertTailList (&NaksList, &pParityNaks->Linkage); fSendParityNak = FALSE; } } if (pReceive->SessionFlags & PGM_SESSION_TERMINATED_ABORT) { break; } } pReceiver->NumPendingNaks = NumPendingNaks; pReceiver->NumOutstandingNaks = NumOutstandingNaks; if (!IsListEmpty (&NaksList)) { pReceiver->LastNakSendTime = PgmDynamicConfig.ReceiversTimerTickCount; } PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); PgmUnlock (&PgmDynamicConfig, *pOldIrq); while (!IsListEmpty (&NaksList)) { pNakContext = CONTAINING_RECORD (NaksList.Flink, tNAKS_CONTEXT, Linkage); if (pNakContext->NumSequences && !(pReceive->SessionFlags & (PGM_SESSION_FLAG_NAK_TIMED_OUT | PGM_SESSION_TERMINATED_ABORT))) { PgmTrace (LogAllFuncs, ("CheckSendPendingNaks: " \ "Sending %s Nak for <%d> sequences, [%d -- %d]!\n", (pNakContext->NakType == NAK_TYPE_PARITY ? "Parity" : "Selective"), pNakContext->NumSequences, (ULONG) pNakContext->Sequences[0], (ULONG) pNakContext->Sequences[MAX_SEQUENCES_PER_NAK_OPTION])); PgmSendNak (pReceive, pNakContext); } RemoveEntryList (&pNakContext->Linkage); PgmFreeMem (pNakContext); } PgmLock (&PgmDynamicConfig, *pOldIrq); } //---------------------------------------------------------------------------- VOID CheckForSessionTimeout( IN tRECEIVE_SESSION *pReceive, IN tRECEIVE_CONTEXT *pReceiver ) { ULONG LastInterval; LastInterval = (ULONG) (PgmDynamicConfig.ReceiversTimerTickCount - pReceiver->LastSessionTickCount); if ((LastInterval > MAX_SPM_INTERVAL_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS) && (LastInterval > (pReceiver->MaxSpmInterval << 5))) // (32 * MaxSpmInterval) { PgmTrace (LogError, ("ReceiveTimerTimeout: ERROR -- " \ "Disconnecting session because no SPM or Data packets received for <%d> Msecs\n", (LastInterval * BASIC_TIMER_GRANULARITY_IN_MSECS))); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; return; } LastInterval = (ULONG) (PgmDynamicConfig.ReceiversTimerTickCount - pReceiver->LastDataConsumedTime); if ((!IsListEmpty (&pReceiver->BufferedDataList)) && (LastInterval > MAX_DATA_CONSUMPTION_TIME_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS)) { PgmTrace (LogError, ("ReceiveTimerTimeout: ERROR -- " \ "Disconnecting session because Data has not been consumed for <%I64x> Msecs\n", (LastInterval * BASIC_TIMER_GRANULARITY_IN_MSECS))); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; } } //---------------------------------------------------------------------------- VOID ReceiveTimerTimeout( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArg1, IN PVOID SystemArg2 ) /*++ Routine Description: This timeout routine is called periodically to cycle through the list of active receivers and send any Naks if required Arguments: IN Dpc IN DeferredContext -- Our context for this timer IN SystemArg1 IN SystemArg2 Return Value: NONE --*/ { LIST_ENTRY *pEntry; PGMLockHandle OldIrq, OldIrq1; tRECEIVE_CONTEXT *pReceiver; tRECEIVE_SESSION *pReceive; NTSTATUS status; LARGE_INTEGER Now; LARGE_INTEGER DeltaTime, GranularTimeElapsed; ULONG NumTimeouts; BOOLEAN fReStartTimer = TRUE; ULONGLONG BytesInLastInterval; PgmLock (&PgmDynamicConfig, OldIrq); ASSERT (!IsListEmpty (&PgmDynamicConfig.CurrentReceivers)); Now.QuadPart = KeQueryInterruptTime (); DeltaTime.QuadPart = Now.QuadPart - PgmDynamicConfig.LastReceiverTimeout.QuadPart; // // If more than a certain number of timeouts have elapsed, we should skip the // optimization since it could result in a big loop! // Let's limit the optimization to 256 times the TimeoutGranularity for now. // if (DeltaTime.QuadPart > (PgmDynamicConfig.TimeoutGranularity.QuadPart << 8)) { NumTimeouts = (ULONG) (DeltaTime.QuadPart / PgmDynamicConfig.TimeoutGranularity.QuadPart); GranularTimeElapsed.QuadPart = NumTimeouts * PgmDynamicConfig.TimeoutGranularity.QuadPart; } else { for (GranularTimeElapsed.QuadPart = 0, NumTimeouts = 0; DeltaTime.QuadPart > PgmDynamicConfig.TimeoutGranularity.QuadPart; NumTimeouts++) { GranularTimeElapsed.QuadPart += PgmDynamicConfig.TimeoutGranularity.QuadPart; DeltaTime.QuadPart -= PgmDynamicConfig.TimeoutGranularity.QuadPart; } } if (!NumTimeouts) { PgmInitTimer (&PgmDynamicConfig.SessionTimer); PgmStartTimer (&PgmDynamicConfig.SessionTimer, BASIC_TIMER_GRANULARITY_IN_MSECS, ReceiveTimerTimeout, NULL); PgmUnlock (&PgmDynamicConfig, OldIrq); return; } PgmDynamicConfig.ReceiversTimerTickCount += NumTimeouts; PgmDynamicConfig.LastReceiverTimeout.QuadPart += GranularTimeElapsed.QuadPart; pEntry = &PgmDynamicConfig.CurrentReceivers; while ((pEntry = pEntry->Flink) != &PgmDynamicConfig.CurrentReceivers) { pReceiver = CONTAINING_RECORD (pEntry, tRECEIVE_CONTEXT, Linkage); pReceive = pReceiver->pReceive; PgmLock (pReceive, OldIrq1); CheckForSessionTimeout (pReceive, pReceiver); if (pReceive->SessionFlags & (PGM_SESSION_FLAG_NAK_TIMED_OUT | PGM_SESSION_TERMINATED_ABORT)) { pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; pReceive->SessionFlags &= ~PGM_SESSION_ON_TIMER; } if (pReceive->SessionFlags & PGM_SESSION_ON_TIMER) { pReceive->RateCalcTimeout += NumTimeouts; if ((pReceive->RateCalcTimeout >= (INTERNAL_RATE_CALCULATION_FREQUENCY/BASIC_TIMER_GRANULARITY_IN_MSECS)) && (pReceiver->StartTickCount != PgmDynamicConfig.ReceiversTimerTickCount)) // Avoid Div by 0 { BytesInLastInterval = pReceive->TotalBytes - pReceive->TotalBytesAtLastInterval; pReceive->RateKBitsPerSecOverall = (pReceive->TotalBytes << LOG2_BITS_PER_BYTE) / ((PgmDynamicConfig.ReceiversTimerTickCount-pReceiver->StartTickCount) * BASIC_TIMER_GRANULARITY_IN_MSECS); pReceive->RateKBitsPerSecLast = BytesInLastInterval >> (LOG2_INTERNAL_RATE_CALCULATION_FREQUENCY-LOG2_BITS_PER_BYTE); if (pReceive->RateKBitsPerSecLast > pReceive->MaxRateKBitsPerSec) { pReceive->MaxRateKBitsPerSec = pReceive->RateKBitsPerSecLast; } // // Now, Reset for next calculations // pReceive->DataBytesAtLastInterval = pReceive->DataBytes; pReceive->TotalBytesAtLastInterval = pReceive->TotalBytes; pReceive->RateCalcTimeout = 0; // // Now, update the window information, if applicable // if (pReceive->RateKBitsPerSecLast) { UpdateSampleTimeWindowInformation (pReceive); } pReceiver->StatSumOfWindowSeqs = pReceiver->NumWindowSamples = 0; // pReceiver->StatSumOfNcfRDataTicks = pReceiver->NumNcfRDataTicksSamples = 0; } if (IsListEmpty (&pReceiver->PendingNaksList)) { pReceiver->NumPendingNaks = 0; pReceiver->NumOutstandingNaks = 0; PgmUnlock (pReceive, OldIrq1); PgmTrace (LogAllFuncs, ("ReceiveTimerTimeout: " \ "No pending Naks for pReceive=<%p>, Addr=<%x>\n", pReceive, pReceiver->ListenMCastIpAddress)); } else { PgmUnlock (pReceive, OldIrq1); PgmTrace (LogAllFuncs, ("ReceiveTimerTimeout: " \ "Checking for pending Naks for pReceive=<%p>, Addr=<%x>\n", pReceive, pReceiver->ListenMCastIpAddress)); CheckSendPendingNaks (pReceiver->pAddress, pReceive, pReceiver, &OldIrq); } } else if (!(pReceive->SessionFlags & PGM_SESSION_FLAG_IN_INDICATE)) { PgmTrace (LogStatus, ("ReceiveTimerTimeout: " \ "PGM_SESSION_ON_TIMER flag cleared for pReceive=<%p>, Addr=<%x>\n", pReceive, pReceiver->ListenMCastIpAddress)); pEntry = pEntry->Blink; RemoveEntryList (&pReceiver->Linkage); if (IsListEmpty (&PgmDynamicConfig.CurrentReceivers)) { fReStartTimer = FALSE; PgmDynamicConfig.GlobalFlags &= ~PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING; PgmTrace (LogStatus, ("ReceiveTimerTimeout: " \ "Not restarting Timer since no Receivers currently active!\n")); } PgmUnlock (&PgmDynamicConfig, OldIrq1); CheckIndicateDisconnect (pReceiver->pAddress, pReceive, NULL, &OldIrq1, FALSE); PgmUnlock (pReceive, OldIrq); PGM_DEREFERENCE_ADDRESS (pReceiver->pAddress, REF_ADDRESS_RECEIVE_ACTIVE); PGM_DEREFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_TIMER_RUNNING); if (!fReStartTimer) { return; } PgmLock (&PgmDynamicConfig, OldIrq); } else { PgmUnlock (pReceive, OldIrq1); } } PgmInitTimer (&PgmDynamicConfig.SessionTimer); PgmStartTimer (&PgmDynamicConfig.SessionTimer, BASIC_TIMER_GRANULARITY_IN_MSECS, ReceiveTimerTimeout, NULL); PgmUnlock (&PgmDynamicConfig, OldIrq); } //---------------------------------------------------------------------------- NTSTATUS ExtractNakNcfSequences( IN tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *pNakNcfPacket, IN ULONG BytesAvailable, OUT tNAKS_LIST *pNakNcfList, OUT SEQ_TYPE *pLastSequenceNumber, IN UCHAR FECGroupSize ) /*++ Routine Description: This routine is called to process a Nak/Ncf packet and extract all the Sequences specified therein into a list. It also verifies that the sequences are unique and sorted Arguments: IN pNakNcfPacket -- Nak/Ncf packet IN BytesAvailable -- PacketLength OUT pNakNcfList -- List of sequences returned on success Return Value: NTSTATUS - Final status of the operation --*/ { NTSTATUS status; ULONG i; tPACKET_OPTIONS PacketOptions; USHORT ThisSequenceIndex; SEQ_TYPE LastSequenceNumber, ThisSequenceNumber, ThisSequenceGroup; SEQ_TYPE NextUnsentSequenceNumber, NextUnsentSequenceGroup; SEQ_TYPE FECSequenceMask = FECGroupSize - 1; SEQ_TYPE FECGroupMask = ~FECSequenceMask; // Must be called with the Session lock held! PgmZeroMemory (pNakNcfList, sizeof (tNAKS_LIST)); if (pNakNcfPacket->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY) { pNakNcfList->NakType = NAK_TYPE_PARITY; } else { pNakNcfList->NakType = NAK_TYPE_SELECTIVE; } PgmZeroMemory (&PacketOptions, sizeof (tPACKET_OPTIONS)); if (pNakNcfPacket->CommonHeader.Options & PACKET_HEADER_OPTIONS_PRESENT) { status = ProcessOptions ((tPACKET_OPTION_LENGTH *) (pNakNcfPacket + 1), BytesAvailable, (pNakNcfPacket->CommonHeader.Type & 0x0f), &PacketOptions, pNakNcfList); if (!NT_SUCCESS (status)) { PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \ "ProcessOptions returned <%x>\n", status)); return (STATUS_DATA_NOT_ACCEPTED); } ASSERT (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_NAK_LIST); } pNakNcfList->pNakSequences[0] = (SEQ_TYPE) ntohl (pNakNcfPacket->RequestedSequenceNumber); pNakNcfList->NumSequences += 1; // // Now, adjust the sequences according to our local relative sequence number // (This is to account for wrap-arounds) // if (pLastSequenceNumber) { NextUnsentSequenceNumber = *pLastSequenceNumber; } else { NextUnsentSequenceNumber = FECGroupSize + pNakNcfList->pNakSequences[pNakNcfList->NumSequences-1]; } NextUnsentSequenceGroup = NextUnsentSequenceNumber & FECGroupMask; LastSequenceNumber = pNakNcfList->pNakSequences[0] - FECGroupSize; for (i=0; i < pNakNcfList->NumSequences; i++) { ThisSequenceNumber = pNakNcfList->pNakSequences[i]; // // If this is a parity Nak, then we need to separate the TG_SQN from the PKT_SQN // ThisSequenceGroup = ThisSequenceNumber & FECGroupMask; ThisSequenceIndex = (USHORT) (ThisSequenceNumber & FECSequenceMask); pNakNcfList->pNakSequences[i] = ThisSequenceGroup; pNakNcfList->NakIndex[i] = ThisSequenceIndex; PgmTrace (LogPath, ("ExtractNakNcfSequences: " \ "[%d] Sequence# = <%d> ==> [%d:%d]\n", i, (ULONG) pNakNcfList->pNakSequences[i], ThisSequenceNumber, ThisSequenceIndex)); if (SEQ_LEQ (ThisSequenceNumber, LastSequenceNumber)) { // // This list is not ordered, so just bail! // PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \ "[%d] Unordered list! Sequence#<%d> before <%d>\n", i, (ULONG) LastSequenceNumber, (ULONG) ThisSequenceNumber)); return (STATUS_DATA_NOT_ACCEPTED); } if (pNakNcfList->NakType == NAK_TYPE_SELECTIVE) { if (SEQ_LEQ (ThisSequenceNumber, LastSequenceNumber)) { // // This list is not ordered, so just bail! // PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \ "[%d] Unordered Selective list! Sequence#<%d> before <%d>\n", i, (ULONG) LastSequenceNumber, (ULONG) ThisSequenceNumber)); return (STATUS_DATA_NOT_ACCEPTED); } if (SEQ_GEQ (ThisSequenceNumber, NextUnsentSequenceNumber)) { pNakNcfList->NumSequences = (USHORT) i; // Don't want to include this sequence! PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \ "Invalid Selective Nak = [%d] further than leading edge = [%d]\n", (ULONG) ThisSequenceNumber, (ULONG) NextUnsentSequenceNumber)); break; } LastSequenceNumber = ThisSequenceNumber; } else // pNakNcfList->NakType == NAK_TYPE_PARITY { if (SEQ_LEQ (ThisSequenceGroup, LastSequenceNumber)) { // // This list is not ordered, so just bail! // PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \ "[%d] Unordered Parity list! Sequence#<%d> before <%d>\n", i, (ULONG) LastSequenceNumber, (ULONG) ThisSequenceNumber)); return (STATUS_DATA_NOT_ACCEPTED); } if (SEQ_GEQ (ThisSequenceGroup, NextUnsentSequenceGroup)) { pNakNcfList->NumSequences = (USHORT) i; // Don't want to include this sequence! PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \ "Invalid Parity Nak = [%d] further than leading edge = [%d]\n", (ULONG) ThisSequenceGroup, (ULONG) NextUnsentSequenceGroup)); break; } LastSequenceNumber = ThisSequenceGroup; pNakNcfList->NumParityNaks[i]++; } } if (!pNakNcfList->NumSequences) { PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \ "No Valid %s Naks in List, First Nak=<%d>!\n", (pNakNcfList->NakType == NAK_TYPE_PARITY ? "Parity" : "Selective"), (ULONG) ThisSequenceNumber)); return (STATUS_DATA_NOT_ACCEPTED); } if (pLastSequenceNumber) { *pLastSequenceNumber = LastSequenceNumber; } if (pNakNcfList->NumSequences) { return (STATUS_SUCCESS); } else { return (STATUS_UNSUCCESSFUL); } } //---------------------------------------------------------------------------- NTSTATUS CheckAndAddNakRequests( IN tRECEIVE_SESSION *pReceive, IN SEQ_TYPE *pLatestSequenceNumber, OUT tNAK_FORWARD_DATA **ppThisNak, IN enum eNAK_TIMEOUT NakTimeoutType, IN BOOLEAN fSetFurthestKnown ) { tNAK_FORWARD_DATA *pOldNak; tNAK_FORWARD_DATA *pLastNak; SEQ_TYPE MidSequenceNumber; SEQ_TYPE FECGroupMask = pReceive->FECGroupSize-1; SEQ_TYPE ThisSequenceNumber = *pLatestSequenceNumber; SEQ_TYPE ThisGroupSequenceNumber = ThisSequenceNumber & ~FECGroupMask; SEQ_TYPE FurthestGroupSequenceNumber = pReceive->pReceiver->FurthestKnownGroupSequenceNumber; ULONG NakRequestSize = sizeof(tNAK_FORWARD_DATA) + ((pReceive->FECGroupSize-1) * sizeof(tPENDING_DATA)); ULONGLONG Pending0NakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + 2; LIST_ENTRY *pEntry; UCHAR i; ULONG NakRandomBackoffMSecs, NakRepeatIntervalMSecs; tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver; // // Verify that the FurthestKnownGroupSequenceNumber is on a Group boundary // ASSERT (!(FurthestGroupSequenceNumber & FECGroupMask)); if (SEQ_LT (ThisSequenceNumber, pReceiver->FirstNakSequenceNumber)) { if (ppThisNak) { ASSERT (0); *ppThisNak = NULL; } return (STATUS_SUCCESS); } if (SEQ_GT (ThisGroupSequenceNumber, (pReceiver->NextODataSequenceNumber + MAX_SEQUENCES_IN_RCV_WINDOW))) { PgmTrace (LogError, ("CheckAndAddNakRequests: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES -- Too many packets in Window [%d, %d] = %d Sequences\n", (ULONG) pReceiver->NextODataSequenceNumber, (ULONG) ThisGroupSequenceNumber, (ULONG) (ThisGroupSequenceNumber - pReceiver->NextODataSequenceNumber + 1))); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; return (STATUS_INSUFFICIENT_RESOURCES); } if (SEQ_GT (ThisGroupSequenceNumber, (FurthestGroupSequenceNumber + 1000)) && !(pReceive->SessionFlags & PGM_SESSION_FLAG_FIRST_PACKET)) { PgmTrace (LogStatus, ("CheckAndAddNakRequests: " \ "WARNING!!! Too many successive packets lost =<%d>!!! Expecting Next=<%d>, FurthestKnown=<%d>, This=<%d>\n", (ULONG) (ThisGroupSequenceNumber - FurthestGroupSequenceNumber), (ULONG) pReceiver->FirstNakSequenceNumber, (ULONG) FurthestGroupSequenceNumber, (ULONG) ThisGroupSequenceNumber)); } if (pReceiver->pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED) { NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS_OPT; NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS_OPT; } else { NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS; NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS; } // // Add any Nak requests if necessary! // FurthestGroupSequenceNumber must be a multiple of the FECGroupSize (if applicable) // pLastNak = NULL; while (SEQ_LT (FurthestGroupSequenceNumber, ThisGroupSequenceNumber)) { if (pReceive->FECOptions) { pLastNak = ExAllocateFromNPagedLookasideList (&pReceiver->ParityContextLookaside); } else { pLastNak = ExAllocateFromNPagedLookasideList (&pReceiver->NonParityContextLookaside); } if (!pLastNak) { pReceiver->FurthestKnownGroupSequenceNumber = FurthestGroupSequenceNumber; pReceiver->FurthestKnownSequenceNumber = pReceiver->FurthestKnownSequenceNumber + FECGroupMask; // End of prev group PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES allocating tNAK_FORWARD_DATA, Size=<%d>, Seq=<%d>\n", NakRequestSize, (ULONG) pReceiver->FurthestKnownGroupSequenceNumber)); return (STATUS_INSUFFICIENT_RESOURCES); } PgmZeroMemory (pLastNak, NakRequestSize); if (pReceive->FECOptions) { pLastNak->OriginalGroupSize = pLastNak->PacketsInGroup = pReceive->FECGroupSize; for (i=0; iOriginalGroupSize; i++) { pLastNak->pPendingData[i].ActualIndexOfDataPacket = pLastNak->OriginalGroupSize; } } else { pLastNak->OriginalGroupSize = pLastNak->PacketsInGroup = 1; pLastNak->pPendingData[0].ActualIndexOfDataPacket = 1; } FurthestGroupSequenceNumber += pReceive->FECGroupSize; pLastNak->SequenceNumber = FurthestGroupSequenceNumber; pLastNak->MinPacketLength = pReceive->MaxFECPacketLength; if (NakTimeoutType == NAK_OUTSTANDING) { pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + pReceiver->OutstandingNakTimeout; pLastNak->PendingNakTimeout = 0; pLastNak->WaitingNcfRetries = 0; } else { pLastNak->OutstandingNakTimeout = 0; switch (NakTimeoutType) { case (NAK_PENDING_0): { pLastNak->PendingNakTimeout = Pending0NakTimeout; break; } case (NAK_PENDING_RB): { pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NakRandomBackoffMSecs/pReceive->FECGroupSize) / BASIC_TIMER_GRANULARITY_IN_MSECS); break; } case (NAK_PENDING_RPT_RB): { pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NakRepeatIntervalMSecs +(NakRandomBackoffMSecs/pReceive->FECGroupSize))/ BASIC_TIMER_GRANULARITY_IN_MSECS); break; } default: { ASSERT (0); } } } InsertTailList (&pReceiver->NaksForwardDataList, &pLastNak->Linkage); AppendPendingReceiverEntry (pReceiver, pLastNak); PgmTrace (LogPath, ("CheckAndAddNakRequests: " \ "ADDing NAK request for SeqNum=<%d>, Furthest=<%d>\n", (ULONG) pLastNak->SequenceNumber, (ULONG) FurthestGroupSequenceNumber)); } pReceiver->FurthestKnownGroupSequenceNumber = FurthestGroupSequenceNumber; if (SEQ_GT (ThisSequenceNumber, pReceiver->FurthestKnownSequenceNumber)) { if (fSetFurthestKnown) { pReceiver->FurthestKnownSequenceNumber = ThisSequenceNumber; } else if (SEQ_GT (FurthestGroupSequenceNumber, pReceiver->FurthestKnownSequenceNumber)) { pReceiver->FurthestKnownSequenceNumber = FurthestGroupSequenceNumber + FECGroupMask; } } if (pLastNak) { pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + NakRepeatIntervalMSecs / BASIC_TIMER_GRANULARITY_IN_MSECS; } else if ((ppThisNak) && (!IsListEmpty (&pReceiver->NaksForwardDataList))) { pLastNak = FindReceiverEntry (pReceiver, ThisGroupSequenceNumber); ASSERT (!pLastNak || (pLastNak->SequenceNumber == ThisGroupSequenceNumber)); } if (ppThisNak) { *ppThisNak = pLastNak; } return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS ReceiverProcessNakNcfPacket( IN tADDRESS_CONTEXT *pAddress, IN tRECEIVE_SESSION *pReceive, IN ULONG PacketLength, IN tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *pNakNcfPacket, IN UCHAR PacketType ) /*++ Routine Description: This is the common routine for processing Nak or Ncf packets Arguments: IN pAddress -- Address object context IN pReceive -- Receive context IN PacketLength -- Length of packet received from the wire IN pNakNcfPacket -- Nak/Ncf packet IN PacketType -- whether Nak or Ncf Return Value: NTSTATUS - Final status of the call --*/ { PGMLockHandle OldIrq; ULONG i, j, PacketIndex; tNAKS_LIST NakNcfList; SEQ_TYPE LastSequenceNumber, FECGroupMask; NTSTATUS status; LIST_ENTRY *pEntry; tNAK_FORWARD_DATA *pLastNak; ULONG PacketsInGroup, NumMissingPackets; ULONG NakRandomBackoffMSecs, NakRepeatIntervalMSecs; BOOLEAN fValidNcf, fFECWithSelectiveNaksOnly = FALSE; tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver; ASSERT (!pNakNcfPacket->CommonHeader.TSDULength); PgmZeroMemory (&NakNcfList, sizeof (tNAKS_LIST)); PgmLock (pReceive, OldIrq); status = ExtractNakNcfSequences (pNakNcfPacket, (PacketLength - sizeof(tBASIC_NAK_NCF_PACKET_HEADER)), &NakNcfList, NULL, pReceive->FECGroupSize); if (!NT_SUCCESS (status)) { PgmUnlock (pReceive, OldIrq); PgmTrace (LogError, ("ReceiverProcessNakNcfPacket: ERROR -- " \ "ExtractNakNcfSequences returned <%x>\n", status)); return (status); } PgmTrace (LogAllFuncs, ("ReceiverProcessNakNcfPacket: " \ "NumSequences=[%d] Range=<%d--%d>, Furthest=<%d>\n", NakNcfList.NumSequences, (ULONG) NakNcfList.pNakSequences[0], (ULONG) NakNcfList.pNakSequences[NakNcfList.NumSequences-1], (ULONG) pReceiver->FurthestKnownGroupSequenceNumber)); // // Compares apples to apples and oranges to oranges // i.e. Process parity Naks only if we are parity-aware, and vice-versa // Exception is ofr the case of a selective Ncf received when we have OnDemand parity // if ((pReceiver->SessionNakType == NakNcfList.NakType) || ((PacketType == PACKET_TYPE_NCF) && (NakNcfList.NakType == NAK_TYPE_SELECTIVE))) { if (pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED) { NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS_OPT; NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS_OPT; } else { NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS; NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS; } } else { PgmUnlock (pReceive, OldIrq); PgmTrace (LogPath, ("ReceiverProcessNakNcfPacket: " \ "Received a %s Nak! Not processing ... \n", ((pReceive->FECGroupSize > 1) ? "Non-parity" : "Parity"))); return (STATUS_SUCCESS); } i = 0; FECGroupMask = pReceive->FECGroupSize - 1; // // Special case: If we have FEC enabled, but not with OnDemand parity, // then we will process Ncf requests only // fFECWithSelectiveNaksOnly = pReceive->FECOptions && !(pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT); if ((PacketType == PACKET_TYPE_NAK) && (fFECWithSelectiveNaksOnly)) { i = NakNcfList.NumSequences; } for ( ; i SequenceNumber == LastSequenceNumber); if ((pReceive->FECOptions) && (LastSequenceNumber == pReceiver->FurthestKnownGroupSequenceNumber)) { ASSERT (SEQ_GEQ (pReceiver->FurthestKnownSequenceNumber, pReceiver->FurthestKnownGroupSequenceNumber)); PacketsInGroup = pReceiver->FurthestKnownSequenceNumber - pLastNak->SequenceNumber + 1; ASSERT (PacketsInGroup >= (ULONG) (pLastNak->NumDataPackets + pLastNak->NumParityPackets)); } else { PacketsInGroup = pLastNak->PacketsInGroup; } NumMissingPackets = PacketsInGroup - (pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets + pLastNak->NumParityPackets); } if ((!pLastNak) || (!NumMissingPackets)) { continue; } if (PacketType == PACKET_TYPE_NAK) { // // If we are currently waiting for a Nak or Ncf, we need to // reset the timeout for either of the 2 scenarios // if (pLastNak->PendingNakTimeout) // We are waiting for a Nak { pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NakRepeatIntervalMSecs + (NakRandomBackoffMSecs/NumMissingPackets))/ BASIC_TIMER_GRANULARITY_IN_MSECS); } else { ASSERT (pLastNak->OutstandingNakTimeout); pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + (pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries); if ((pLastNak->WaitingRDataRetries >= (NCF_WAITING_RDATA_MAX_RETRIES >> 1)) && ((pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries) < pReceiver->MaxRDataResponseTCFromWindow)) { pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + (pReceiver->MaxRDataResponseTCFromWindow<<1); } } } else // NCF case { PacketIndex = NakNcfList.NakIndex[i]; fValidNcf = FALSE; // first check for OnDemand case if (pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT) { // We may also need to process selective naks in certain cases if (NakNcfList.NakType == NAK_TYPE_SELECTIVE) { fValidNcf = TRUE; pLastNak->pPendingData[PacketIndex].NcfsReceivedForActualIndex++; for (j=0; jpPendingData[j].ActualIndexOfDataPacket >= pLastNak->OriginalGroupSize) && (!pLastNak->pPendingData[j].NcfsReceivedForActualIndex)) { fValidNcf = FALSE; break; } } if (!pLastNak->FirstNcfTickCount) { pLastNak->FirstNcfTickCount = PgmDynamicConfig.ReceiversTimerTickCount; } } else if (NakNcfList.NumParityNaks[i] >= NumMissingPackets) // Parity Nak { fValidNcf = TRUE; if (!pLastNak->FirstNcfTickCount) { pLastNak->FirstNcfTickCount = PgmDynamicConfig.ReceiversTimerTickCount; } } } // Selective Naks only -- with or without FEC else if (pLastNak->pPendingData[PacketIndex].ActualIndexOfDataPacket >= pLastNak->OriginalGroupSize) { fValidNcf = TRUE; if (fFECWithSelectiveNaksOnly) { pLastNak->pPendingData[PacketIndex].NcfsReceivedForActualIndex++; for (j=0; jPacketsInGroup; j++) { if ((pLastNak->pPendingData[j].ActualIndexOfDataPacket >= pLastNak->OriginalGroupSize) && (!pLastNak->pPendingData[j].NcfsReceivedForActualIndex)) { fValidNcf = FALSE; break; } } } if (!pLastNak->FirstNcfTickCount) { pLastNak->FirstNcfTickCount = PgmDynamicConfig.ReceiversTimerTickCount; } } if (fValidNcf) { pLastNak->PendingNakTimeout = 0; pLastNak->WaitingNcfRetries = 0; pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + (pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries); if ((pLastNak->WaitingRDataRetries >= (NCF_WAITING_RDATA_MAX_RETRIES >> 1)) && ((pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries) < pReceiver->MaxRDataResponseTCFromWindow)) { pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + (pReceiver->MaxRDataResponseTCFromWindow<<1); } } } } // // So, we need to create new Nak contexts for the remaining Sequences // Since the Sequences are ordered, just pick the highest one, and // create Naks for all up to that // LastSequenceNumber = NakNcfList.pNakSequences[NakNcfList.NumSequences-1] + NakNcfList.NakIndex[NakNcfList.NumSequences-1]; if (NakNcfList.NakType == NAK_TYPE_PARITY) { LastSequenceNumber--; } if (PacketType == PACKET_TYPE_NAK) { status = CheckAndAddNakRequests (pReceive, &LastSequenceNumber, NULL, NAK_PENDING_RPT_RB, TRUE); } else // PacketType == PACKET_TYPE_NCF { status = CheckAndAddNakRequests (pReceive, &LastSequenceNumber, NULL, NAK_OUTSTANDING, TRUE); } PgmUnlock (pReceive, OldIrq); return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS CoalesceSelectiveNaksIntoGroups( IN tRECEIVE_SESSION *pReceive, IN UCHAR GroupSize ) { PNAK_FORWARD_DATA pOldNak, pNewNak; LIST_ENTRY NewNaksList; LIST_ENTRY OldNaksList; LIST_ENTRY *pEntry; SEQ_TYPE FirstGroupSequenceNumber, LastGroupSequenceNumber, LastSequenceNumber; SEQ_TYPE FurthestKnownSequenceNumber; SEQ_TYPE GroupMask = GroupSize - 1; ULONG NakRequestSize = sizeof(tNAK_FORWARD_DATA) + ((GroupSize-1) * sizeof(tPENDING_DATA)); USHORT MinPacketLength; UCHAR i; NTSTATUS status = STATUS_SUCCESS; ULONG NakRandomBackoffMSecs, NakRepeatIntervalMSecs; ASSERT (pReceive->FECGroupSize == 1); ASSERT (GroupSize > 1); // // First, call AdjustReceiveBufferLists to ensure that FirstNakSequenceNumber is current // AdjustReceiveBufferLists (pReceive); FirstGroupSequenceNumber = pReceive->pReceiver->FirstNakSequenceNumber & ~GroupMask; LastGroupSequenceNumber = pReceive->pReceiver->FurthestKnownGroupSequenceNumber & ~GroupMask; FurthestKnownSequenceNumber = pReceive->pReceiver->FurthestKnownSequenceNumber; // Save // // If the next packet seq we are expecting is > the furthest known sequence #, // then we don't need to do anything // LastSequenceNumber = LastGroupSequenceNumber + (GroupSize-1); // // First, add Nak requests for the missing packets in furthest group! // status = CheckAndAddNakRequests (pReceive, &LastSequenceNumber, NULL, NAK_PENDING_RB, FALSE); if (!NT_SUCCESS (status)) { PgmTrace (LogError, ("CoalesceSelectiveNaksIntoGroups: ERROR -- " \ "CheckAndAddNakRequests returned <%x>\n", status)); return (status); } pReceive->pReceiver->FurthestKnownSequenceNumber = FurthestKnownSequenceNumber; // Reset ASSERT (LastSequenceNumber == pReceive->pReceiver->FurthestKnownGroupSequenceNumber); ASSERT (pReceive->pReceiver->MaxPacketsBufferedInLookaside); ExInitializeNPagedLookasideList (&pReceive->pReceiver->ParityContextLookaside, NULL, NULL, 0, NakRequestSize, PGM_TAG('2'), (USHORT) (pReceive->pReceiver->MaxPacketsBufferedInLookaside/GroupSize)); if (SEQ_GT (pReceive->pReceiver->FirstNakSequenceNumber, LastSequenceNumber)) { pReceive->pReceiver->FurthestKnownGroupSequenceNumber = LastGroupSequenceNumber; ASSERT (IsListEmpty (&pReceive->pReceiver->NaksForwardDataList)); PgmTrace (LogStatus, ("CoalesceSelectiveNaksIntoGroups: " \ "[1] NextOData=<%d>, FirstNak=<%d>, FirstGroup=<%d>, LastGroup=<%d>, no Naks pending!\n", (ULONG) pReceive->pReceiver->NextODataSequenceNumber, (ULONG) pReceive->pReceiver->FirstNakSequenceNumber, (ULONG) FirstGroupSequenceNumber, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber)); return (STATUS_SUCCESS); } if (pReceive->pReceiver->pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED) { NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS_OPT; NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS_OPT; } else { NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS; NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS; } // // We will start coalescing from the end of the list in case we run // into failures // Also, we will ignore the first Group since it may be a partial group, // or we may have indicated some of the data already, so we may not know // the exact data length // pOldNak = pNewNak = NULL; InitializeListHead (&NewNaksList); InitializeListHead (&OldNaksList); while (SEQ_GEQ (LastGroupSequenceNumber, FirstGroupSequenceNumber)) { if (!(pNewNak = ExAllocateFromNPagedLookasideList (&pReceive->pReceiver->ParityContextLookaside))) { PgmTrace (LogError, ("CoalesceSelectiveNaksIntoGroups: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES allocating tNAK_FORWARD_DATA\n")); status = STATUS_INSUFFICIENT_RESOURCES; break; } PgmZeroMemory (pNewNak, NakRequestSize); InitializeListHead (&pNewNak->SendNakLinkage); InitializeListHead (&pNewNak->LookupLinkage); pNewNak->OriginalGroupSize = pNewNak->PacketsInGroup = GroupSize; pNewNak->SequenceNumber = LastGroupSequenceNumber; MinPacketLength = pReceive->MaxFECPacketLength; for (i=0; iOriginalGroupSize; i++) { pNewNak->pPendingData[i].ActualIndexOfDataPacket = pNewNak->OriginalGroupSize; } i = 0; while (SEQ_GEQ (LastSequenceNumber, LastGroupSequenceNumber) && (!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList))) { pEntry = RemoveTailList (&pReceive->pReceiver->NaksForwardDataList); pOldNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage); if (!pOldNak->NumDataPackets) { RemovePendingReceiverEntry (pOldNak); } else { ASSERT (pOldNak->NumDataPackets == 1); ASSERT (IsListEmpty (&pOldNak->SendNakLinkage)); ASSERT (IsListEmpty (&pOldNak->LookupLinkage)); } ASSERT (pOldNak->SequenceNumber == LastSequenceNumber); ASSERT (pOldNak->OriginalGroupSize == 1); if (pOldNak->pPendingData[0].pDataPacket) { ASSERT (pOldNak->NumDataPackets == 1); pNewNak->NumDataPackets++; PgmCopyMemory (&pNewNak->pPendingData[i], &pOldNak->pPendingData[0], sizeof (tPENDING_DATA)); pNewNak->pPendingData[i].PacketIndex = (UCHAR) (LastSequenceNumber - LastGroupSequenceNumber); pNewNak->pPendingData[LastSequenceNumber-LastGroupSequenceNumber].ActualIndexOfDataPacket = i; i++; pOldNak->pPendingData[0].pDataPacket = NULL; pOldNak->pPendingData[0].PendingDataFlags = 0; pOldNak->NumDataPackets--; if (pOldNak->MinPacketLength < MinPacketLength) { MinPacketLength = pOldNak->MinPacketLength; } if ((pOldNak->ThisGroupSize) && (pOldNak->ThisGroupSize < GroupSize)) { if (pNewNak->PacketsInGroup == GroupSize) { pNewNak->PacketsInGroup = pOldNak->ThisGroupSize; } else { ASSERT (pNewNak->PacketsInGroup == pOldNak->ThisGroupSize); } } } InsertHeadList (&OldNaksList, &pOldNak->Linkage); LastSequenceNumber--; } pNewNak->MinPacketLength = MinPacketLength; // // See if we need to get rid of any excess (NULL) data packets // RemoveRedundantNaks (pReceive, pNewNak, FALSE); ASSERT (!pNewNak->NumParityPackets); if (pNewNak->NumDataPackets < pNewNak->PacketsInGroup) // No parity packets yet! { pNewNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NakRandomBackoffMSecs/(pNewNak->PacketsInGroup-pNewNak->NumDataPackets))/ BASIC_TIMER_GRANULARITY_IN_MSECS); } InsertHeadList (&NewNaksList, &pNewNak->Linkage); LastGroupSequenceNumber -= GroupSize; } // // If we succeeded in allocating all NewNaks above, set the // NextIndexToIndicate for the first group. // We may also need to adjust FirstNakSequenceNumber and NextODataSequenceNumber // if ((pNewNak) && (pNewNak->SequenceNumber == FirstGroupSequenceNumber)) { if (SEQ_GT (pReceive->pReceiver->FirstNakSequenceNumber, pNewNak->SequenceNumber)) { pNewNak->NextIndexToIndicate = (UCHAR) (pReceive->pReceiver->FirstNakSequenceNumber - pNewNak->SequenceNumber); pReceive->pReceiver->FirstNakSequenceNumber = pNewNak->SequenceNumber; ASSERT (pNewNak->NextIndexToIndicate < GroupSize); ASSERT ((pNewNak->NextIndexToIndicate + pNewNak->NumDataPackets) <= pNewNak->PacketsInGroup); } ASSERT (pReceive->pReceiver->FirstNakSequenceNumber == pNewNak->SequenceNumber); // // We may have data available for this group already in the buffered // list (if it has not been indicated already) -- we should move it here // while ((pNewNak->NextIndexToIndicate) && (!IsListEmpty (&pReceive->pReceiver->BufferedDataList))) { ASSERT (pNewNak->NumDataPackets < pNewNak->OriginalGroupSize); pEntry = RemoveTailList (&pReceive->pReceiver->BufferedDataList); pOldNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage); pReceive->pReceiver->NumPacketGroupsPendingClient--; pReceive->pReceiver->DataPacketsPendingIndicate--; pReceive->pReceiver->DataPacketsPendingNaks++; pNewNak->NextIndexToIndicate--; ASSERT (pOldNak->pPendingData[0].pDataPacket); ASSERT ((pOldNak->NumDataPackets == 1) && (pOldNak->OriginalGroupSize == 1)); ASSERT (pOldNak->SequenceNumber == (pNewNak->SequenceNumber + pNewNak->NextIndexToIndicate)); PgmCopyMemory (&pNewNak->pPendingData[pNewNak->NumDataPackets], &pOldNak->pPendingData[0], sizeof (tPENDING_DATA)); pNewNak->pPendingData[pNewNak->NumDataPackets].PacketIndex = pNewNak->NextIndexToIndicate; pNewNak->pPendingData[pNewNak->NextIndexToIndicate].ActualIndexOfDataPacket = pNewNak->NumDataPackets; pNewNak->NumDataPackets++; if (pOldNak->MinPacketLength < pNewNak->MinPacketLength) { pNewNak->MinPacketLength = pOldNak->MinPacketLength; } if ((pOldNak->ThisGroupSize) && (pOldNak->ThisGroupSize < GroupSize)) { if (pNewNak->PacketsInGroup == GroupSize) { pNewNak->PacketsInGroup = pOldNak->ThisGroupSize; } else { ASSERT (pNewNak->PacketsInGroup == pOldNak->ThisGroupSize); } } pOldNak->pPendingData[0].pDataPacket = NULL; pOldNak->pPendingData[0].PendingDataFlags = 0; pOldNak->NumDataPackets--; InsertHeadList (&OldNaksList, &pOldNak->Linkage); } if (SEQ_GEQ (pReceive->pReceiver->NextODataSequenceNumber, pNewNak->SequenceNumber)) { ASSERT (pReceive->pReceiver->NextODataSequenceNumber == (pReceive->pReceiver->FirstNakSequenceNumber + pNewNak->NextIndexToIndicate)); ASSERT (IsListEmpty (&pReceive->pReceiver->BufferedDataList)); pReceive->pReceiver->NextODataSequenceNumber = pNewNak->SequenceNumber; } else { ASSERT ((0 == pNewNak->NextIndexToIndicate) && !(IsListEmpty (&pReceive->pReceiver->BufferedDataList))); } if (SEQ_GT (pReceive->pReceiver->LastTrailingEdgeSeqNum, pReceive->pReceiver->FirstNakSequenceNumber)) { pReceive->pReceiver->LastTrailingEdgeSeqNum = pReceive->pReceiver->FirstNakSequenceNumber; } RemoveRedundantNaks (pReceive, pNewNak, FALSE); if ((pNewNak->NextIndexToIndicate + pNewNak->NumDataPackets) >= pNewNak->PacketsInGroup) { // This entry will be moved automatically to the buffered data list // when we call AdjustReceiveBufferLists below pNewNak->PendingNakTimeout = 0; } else { pNewNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NakRandomBackoffMSecs/(pNewNak->PacketsInGroup-(pNewNak->NextIndexToIndicate+pNewNak->NumDataPackets)))/ BASIC_TIMER_GRANULARITY_IN_MSECS); } } ASSERT (IsListEmpty (&pReceive->pReceiver->NaksForwardDataList)); ASSERT (IsListEmpty (&pReceive->pReceiver->PendingNaksList)); if (!IsListEmpty (&NewNaksList)) { // // Now, move the new list to the end of the current list // NewNaksList.Flink->Blink = pReceive->pReceiver->NaksForwardDataList.Blink; NewNaksList.Blink->Flink = &pReceive->pReceiver->NaksForwardDataList; pReceive->pReceiver->NaksForwardDataList.Blink->Flink = NewNaksList.Flink; pReceive->pReceiver->NaksForwardDataList.Blink = NewNaksList.Blink; } while (!IsListEmpty (&OldNaksList)) { pEntry = RemoveHeadList (&OldNaksList); pOldNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage); FreeNakContext (pReceive, pOldNak); } // // Put the pending Naks in the PendingNaks list // pReceive->pReceiver->ReceiveDataIndexShift = gFECLog2[GroupSize]; pEntry = &pReceive->pReceiver->NaksForwardDataList; while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->NaksForwardDataList) { pNewNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage); if (((pNewNak->NumDataPackets + pNewNak->NumParityPackets) < pNewNak->PacketsInGroup) && ((pNewNak->NextIndexToIndicate + pNewNak->NumDataPackets) < pNewNak->PacketsInGroup)) { AppendPendingReceiverEntry (pReceive->pReceiver, pNewNak); } } AdjustReceiveBufferLists (pReceive); pNewNak = NULL; if (!(IsListEmpty (&pReceive->pReceiver->NaksForwardDataList))) { // // For the last context, set the Nak timeout appropriately // pNewNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Blink, tNAK_FORWARD_DATA, Linkage); if (pNewNak->NumDataPackets < pNewNak->PacketsInGroup) { pNewNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NakRepeatIntervalMSecs + (NakRandomBackoffMSecs / (pNewNak->PacketsInGroup-pNewNak->NumDataPackets))) / BASIC_TIMER_GRANULARITY_IN_MSECS); } } else if (!(IsListEmpty (&pReceive->pReceiver->BufferedDataList))) { pNewNak = CONTAINING_RECORD (pReceive->pReceiver->BufferedDataList.Blink, tNAK_FORWARD_DATA, Linkage); } // // Now, set the FirstKnownGroupSequenceNumber // if (pNewNak) { pReceive->pReceiver->FurthestKnownGroupSequenceNumber = pNewNak->SequenceNumber; } else { pReceive->pReceiver->FurthestKnownGroupSequenceNumber &= ~GroupMask; } PgmTrace (LogStatus, ("CoalesceSelectiveNaksIntoGroups: " \ "[2] NextOData=<%d>, FirstNak=<%d->%d>, FirstGroup=<%d>, LastGroup=<%d>\n", (ULONG) pReceive->pReceiver->NextODataSequenceNumber, (ULONG) pReceive->pReceiver->FirstNakSequenceNumber, (pNewNak ? (ULONG) pNewNak->NextIndexToIndicate : (ULONG) 0), (ULONG) FirstGroupSequenceNumber, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber)); return (status); // If we had failed earlier, we should still fail! } //---------------------------------------------------------------------------- NTSTATUS PgmIndicateToClient( IN tADDRESS_CONTEXT *pAddress, IN tRECEIVE_SESSION *pReceive, IN ULONG BytesAvailable, IN PUCHAR pDataBuffer, IN ULONG MessageOffset, IN ULONG MessageLength, OUT ULONG *pBytesTaken, IN PGMLockHandle *pOldIrqAddress, IN PGMLockHandle *pOldIrqReceive ) /*++ Routine Description: This routine tries to indicate the Data packet provided to the client It is called with the pAddress and pReceive locks held Arguments: IN pAddress -- Address object context IN pReceive -- Receive context IN BytesAvailableToIndicate -- Length of packet received from the wire IN pPgmDataHeader -- Data packet IN pOldIrqAddress -- OldIrq for the Address lock IN pOldIrqReceive -- OldIrq for the Receive lock Return Value: NTSTATUS - Final status of the call --*/ { PIO_STACK_LOCATION pIrpSp; PTDI_REQUEST_KERNEL_RECEIVE pClientParams; CONNECTION_CONTEXT ClientSessionContext; PIRP pIrpReceive; ULONG ReceiveFlags, BytesTaken, BytesToCopy, BytesLeftInMessage, ClientBytesTaken; tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver; NTSTATUS status = STATUS_SUCCESS; PTDI_IND_RECEIVE evReceive = NULL; PVOID RcvEvContext = NULL; ULONG BytesAvailableToIndicate = BytesAvailable; if (pReceive->SessionFlags & (PGM_SESSION_CLIENT_DISCONNECTED | PGM_SESSION_TERMINATED_ABORT)) { PgmTrace (LogError, ("PgmIndicateToClient: ERROR -- " \ "pReceive=<%p> disassociated during Receive!\n", pReceive)); *pBytesTaken = 0; return (STATUS_DATA_NOT_ACCEPTED); } ASSERT ((!pReceiver->CurrentMessageLength) || (pReceiver->CurrentMessageLength == MessageLength)); ASSERT (pReceiver->CurrentMessageProcessed == MessageOffset); pReceiver->CurrentMessageLength = MessageLength; pReceiver->CurrentMessageProcessed = MessageOffset; BytesLeftInMessage = MessageLength - MessageOffset; PgmTrace (LogAllFuncs, ("PgmIndicateToClient: " \ "MessageLen=<%d/%d>, MessageOff=<%d>, CurrentML=<%d>, CurrentMP=<%d>\n", BytesAvailableToIndicate, MessageLength, MessageOffset, pReceiver->CurrentMessageLength, pReceiver->CurrentMessageProcessed)); // // We may have a receive Irp pending from a previous indication, // so see if need to fill that first! // while ((BytesAvailableToIndicate) && ((pIrpReceive = pReceiver->pIrpReceive) || (!IsListEmpty (&pReceiver->ReceiveIrpsList)))) { if (!pIrpReceive) { // // The client had posted a receive Irp, so use it now! // pIrpReceive = CONTAINING_RECORD (pReceiver->ReceiveIrpsList.Flink, IRP, Tail.Overlay.ListEntry); RemoveEntryList (&pIrpReceive->Tail.Overlay.ListEntry); pIrpSp = IoGetCurrentIrpStackLocation (pIrpReceive); pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE) &pIrpSp->Parameters; pReceiver->pIrpReceive = pIrpReceive; pReceiver->TotalBytesInMdl = pClientParams->ReceiveLength; pReceiver->BytesInMdl = 0; } // // Copy whatever bytes we can into it // if (BytesAvailableToIndicate > (pReceiver->TotalBytesInMdl - pReceiver->BytesInMdl)) { BytesToCopy = pReceiver->TotalBytesInMdl - pReceiver->BytesInMdl; } else { BytesToCopy = BytesAvailableToIndicate; } ClientBytesTaken = 0; status = TdiCopyBufferToMdl (pDataBuffer, 0, BytesToCopy, pReceiver->pIrpReceive->MdlAddress, pReceiver->BytesInMdl, &ClientBytesTaken); pReceiver->BytesInMdl += ClientBytesTaken; pReceiver->CurrentMessageProcessed += ClientBytesTaken; BytesLeftInMessage -= ClientBytesTaken; BytesAvailableToIndicate -= ClientBytesTaken; pDataBuffer += ClientBytesTaken; if ((!ClientBytesTaken) || (pReceiver->BytesInMdl >= pReceiver->TotalBytesInMdl) || (!BytesLeftInMessage)) { // // The Irp is full, so complete the Irp! // pIrpReceive = pReceiver->pIrpReceive; pIrpReceive->IoStatus.Information = pReceiver->BytesInMdl; if (BytesLeftInMessage) { pIrpReceive->IoStatus.Status = STATUS_BUFFER_OVERFLOW; } else { ASSERT (pReceiver->CurrentMessageLength == pReceiver->CurrentMessageProcessed); pIrpReceive->IoStatus.Status = STATUS_SUCCESS; } // // Before releasing the lock, set the parameters for the next receive // pReceiver->pIrpReceive = NULL; pReceiver->TotalBytesInMdl = pReceiver->BytesInMdl = 0; PgmUnlock (pReceive, *pOldIrqReceive); PgmUnlock (pAddress, *pOldIrqAddress); PgmCancelCancelRoutine (pIrpReceive); PgmTrace (LogPath, ("PgmIndicateToClient: " \ "Completing prior pIrp=<%p>, Bytes=<%d>, BytesLeft=<%d>\n", pIrpReceive, (ULONG) pIrpReceive->IoStatus.Information, BytesAvailableToIndicate)); IoCompleteRequest (pIrpReceive, IO_NETWORK_INCREMENT); PgmLock (pAddress, *pOldIrqAddress); PgmLock (pReceive, *pOldIrqReceive); } } // // If there are no more bytes left to indicate, return // if (BytesAvailableToIndicate == 0) { if (!BytesLeftInMessage) { ASSERT (pReceiver->CurrentMessageLength == pReceiver->CurrentMessageProcessed); pReceiver->CurrentMessageLength = pReceiver->CurrentMessageProcessed = 0; } if (BytesTaken = (BytesAvailable - BytesAvailableToIndicate)) { *pBytesTaken = BytesTaken; pReceiver->LastDataConsumedTime = PgmDynamicConfig.ReceiversTimerTickCount; } return (STATUS_SUCCESS); } // call the Client Event Handler pIrpReceive = NULL; ClientBytesTaken = 0; evReceive = pAddress->evReceive; ClientSessionContext = pReceive->ClientSessionContext; RcvEvContext = pAddress->RcvEvContext; ASSERT (RcvEvContext); PgmUnlock (pReceive, *pOldIrqReceive); PgmUnlock (pAddress, *pOldIrqAddress); ReceiveFlags = TDI_RECEIVE_NORMAL; if (PgmGetCurrentIrql()) { ReceiveFlags |= TDI_RECEIVE_AT_DISPATCH_LEVEL; } #if 0 if (BytesLeftInMessage == BytesAvailableToIndicate) { ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; } status = (*evReceive) (RcvEvContext, ClientSessionContext, ReceiveFlags, BytesAvailableToIndicate, BytesAvailableToIndicate, &ClientBytesTaken, pDataBuffer, &pIrpReceive); #else ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; status = (*evReceive) (RcvEvContext, ClientSessionContext, ReceiveFlags, BytesAvailableToIndicate, BytesLeftInMessage, &ClientBytesTaken, pDataBuffer, &pIrpReceive); #endif // 0 PgmTrace (LogPath, ("PgmIndicateToClient: " \ "Client's evReceive returned status=<%x>, ReceiveFlags=<%x>, Client took <%d/%d|%d>, pIrp=<%p>\n", status, ReceiveFlags, ClientBytesTaken, BytesAvailableToIndicate, BytesLeftInMessage, pIrpReceive)); if (ClientBytesTaken > BytesAvailableToIndicate) { ClientBytesTaken = BytesAvailableToIndicate; } ASSERT (ClientBytesTaken <= BytesAvailableToIndicate); BytesAvailableToIndicate -= ClientBytesTaken; BytesLeftInMessage -= ClientBytesTaken; pDataBuffer = pDataBuffer + ClientBytesTaken; if ((status == STATUS_MORE_PROCESSING_REQUIRED) && (pIrpReceive) && (!NT_SUCCESS (PgmCheckSetCancelRoutine (pIrpReceive, PgmCancelReceiveIrp, FALSE)))) { PgmTrace (LogError, ("PgmIndicateToClient: ERROR -- " \ "pReceive=<%p>, pIrp=<%p> Cancelled during Receive!\n", pReceive, pIrpReceive)); PgmIoComplete (pIrpReceive, STATUS_CANCELLED, 0); PgmLock (pAddress, *pOldIrqAddress); PgmLock (pReceive, *pOldIrqReceive); pReceiver->CurrentMessageProcessed += ClientBytesTaken; if (BytesTaken = (BytesAvailable - BytesAvailableToIndicate)) { *pBytesTaken = BytesTaken; pReceiver->LastDataConsumedTime = PgmDynamicConfig.ReceiversTimerTickCount; } return (STATUS_UNSUCCESSFUL); } PgmLock (pAddress, *pOldIrqAddress); PgmLock (pReceive, *pOldIrqReceive); pReceiver->CurrentMessageProcessed += ClientBytesTaken; if (!pReceiver->pAddress) { // the connection was disassociated in the interim so do nothing. if (status == STATUS_MORE_PROCESSING_REQUIRED) { PgmUnlock (pReceive, *pOldIrqReceive); PgmUnlock (pAddress, *pOldIrqAddress); PgmIoComplete (pIrpReceive, STATUS_CANCELLED, 0); PgmLock (pAddress, *pOldIrqAddress); PgmLock (pReceive, *pOldIrqReceive); } PgmTrace (LogError, ("PgmIndicateToClient: ERROR -- " \ "pReceive=<%p> disassociated during Receive!\n", pReceive)); if (BytesTaken = (BytesAvailable - BytesAvailableToIndicate)) { *pBytesTaken = BytesTaken; pReceiver->LastDataConsumedTime = PgmDynamicConfig.ReceiversTimerTickCount; } return (STATUS_UNSUCCESSFUL); } if (status == STATUS_MORE_PROCESSING_REQUIRED) { ASSERT (pIrpReceive); ASSERT (pIrpReceive->MdlAddress); pIrpSp = IoGetCurrentIrpStackLocation (pIrpReceive); pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE) &pIrpSp->Parameters; ASSERT (pClientParams->ReceiveLength); ClientBytesTaken = 0; if (pClientParams->ReceiveLength < BytesAvailableToIndicate) { BytesToCopy = pClientParams->ReceiveLength; } else { BytesToCopy = BytesAvailableToIndicate; } status = TdiCopyBufferToMdl (pDataBuffer, 0, BytesToCopy, pIrpReceive->MdlAddress, pReceiver->BytesInMdl, &ClientBytesTaken); BytesLeftInMessage -= ClientBytesTaken; BytesAvailableToIndicate -= ClientBytesTaken; pDataBuffer = pDataBuffer + ClientBytesTaken; pReceiver->CurrentMessageProcessed += ClientBytesTaken; PgmTrace (LogPath, ("PgmIndicateToClient: " \ "Client's evReceive returned pIrp=<%p>, BytesInIrp=<%d>, Copied <%d> bytes\n", pIrpReceive, pClientParams->ReceiveLength, ClientBytesTaken)); if ((!ClientBytesTaken) || (ClientBytesTaken >= pClientParams->ReceiveLength) || (pReceiver->CurrentMessageLength == pReceiver->CurrentMessageProcessed)) { // // The Irp is full, so complete the Irp! // pIrpReceive->IoStatus.Information = ClientBytesTaken; if (pReceiver->CurrentMessageLength == pReceiver->CurrentMessageProcessed) { pIrpReceive->IoStatus.Status = STATUS_SUCCESS; } else { pIrpReceive->IoStatus.Status = STATUS_BUFFER_OVERFLOW; } // // Before releasing the lock, set the parameters for the next receive // pReceiver->TotalBytesInMdl = pReceiver->BytesInMdl = 0; PgmUnlock (pReceive, *pOldIrqReceive); PgmUnlock (pAddress, *pOldIrqAddress); PgmCancelCancelRoutine (pIrpReceive); IoCompleteRequest (pIrpReceive, IO_NETWORK_INCREMENT); PgmLock (pAddress, *pOldIrqAddress); PgmLock (pReceive, *pOldIrqReceive); } else { pReceiver->TotalBytesInMdl = pClientParams->ReceiveLength; pReceiver->BytesInMdl = ClientBytesTaken; pReceiver->pIrpReceive = pIrpReceive; } status = STATUS_SUCCESS; } else if (status == STATUS_DATA_NOT_ACCEPTED) { // // An Irp could have been posted in the interval // between the indicate and acquiring the SpinLocks, // so check for that here // if ((pReceiver->pIrpReceive) || (!IsListEmpty (&pReceiver->ReceiveIrpsList))) { status = STATUS_SUCCESS; } else { pReceive->SessionFlags |= PGM_SESSION_WAIT_FOR_RECEIVE_IRP; } } if (pReceiver->CurrentMessageLength == pReceiver->CurrentMessageProcessed) { pReceiver->CurrentMessageLength = pReceiver->CurrentMessageProcessed = 0; } if ((NT_SUCCESS (status)) || (status == STATUS_DATA_NOT_ACCEPTED)) { PgmTrace (LogAllFuncs, ("PgmIndicateToClient: " \ "status=<%x>, pReceive=<%p>, Taken=<%d>, Available=<%d>\n", status, pReceive, ClientBytesTaken, BytesLeftInMessage)); // // since some bytes were taken (i.e. the session hdr) so // return status success. (otherwise the status is // statusNotAccpeted). // } else { PgmTrace (LogError, ("PgmIndicateToClient: ERROR -- " \ "Unexpected status=<%x>\n", status)); ASSERT (0); } if (BytesTaken = (BytesAvailable - BytesAvailableToIndicate)) { *pBytesTaken = BytesTaken; pReceiver->LastDataConsumedTime = PgmDynamicConfig.ReceiversTimerTickCount; } return (status); } //---------------------------------------------------------------------------- NTSTATUS PgmIndicateGroup( IN tADDRESS_CONTEXT *pAddress, IN tRECEIVE_SESSION *pReceive, IN PGMLockHandle *pOldIrqAddress, IN PGMLockHandle *pOldIrqReceive, IN tNAK_FORWARD_DATA *pNak ) { UCHAR i, j; NTSTATUS status = STATUS_SUCCESS; ULONG BytesTaken, DataBytes, MessageLength; ASSERT (pNak->SequenceNumber == pReceive->pReceiver->NextODataSequenceNumber); j = pNak->NextIndexToIndicate; while (j < pNak->PacketsInGroup) { if (pReceive->SessionFlags & PGM_SESSION_CLIENT_DISCONNECTED) { status = STATUS_DATA_NOT_ACCEPTED; break; } i = pNak->pPendingData[j].ActualIndexOfDataPacket; ASSERT (i < pNak->OriginalGroupSize); if (pReceive->SessionFlags & PGM_SESSION_FLAG_FIRST_PACKET) { // // pReceive->pReceiver->CurrentMessageProcessed would have been set // if we were receiving a fragmented message // or if we had only accounted for a partial message earlier // ASSERT (!(pReceive->pReceiver->CurrentMessageProcessed) && !(pReceive->pReceiver->CurrentMessageLength)); if (pNak->pPendingData[i].MessageOffset) { PgmTrace (LogPath, ("PgmIndicateGroup: " \ "Dropping SeqNum=[%d] since it's a PARTIAL message [%d / %d]!\n", (ULONG) (pReceive->pReceiver->NextODataSequenceNumber + j), pNak->pPendingData[i].MessageOffset, pNak->pPendingData[i].MessageLength)); j++; pNak->NextIndexToIndicate++; status = STATUS_SUCCESS; continue; } pReceive->SessionFlags &= ~PGM_SESSION_FLAG_FIRST_PACKET; } else if ((pReceive->pReceiver->CurrentMessageProcessed != pNak->pPendingData[i].MessageOffset) || // Check Offsets ((pReceive->pReceiver->CurrentMessageProcessed) && // in the midst of a Message, and (pReceive->pReceiver->CurrentMessageLength != pNak->pPendingData[i].MessageLength))) // Check MessageLength { // // Our state expects us to be in the middle of a message, but // the current packets do not show this // PgmTrace (LogError, ("PgmIndicateGroup: ERROR -- " \ "SeqNum=[%d] Expecting MsgLen=<%d>, MsgOff=<%d>, have MsgLen=<%d>, MsgOff=<%d>\n", (ULONG) (pReceive->pReceiver->NextODataSequenceNumber + j), pReceive->pReceiver->CurrentMessageLength, pReceive->pReceiver->CurrentMessageProcessed, pNak->pPendingData[i].MessageLength, pNak->pPendingData[i].MessageOffset)); // ASSERT (0); return (STATUS_UNSUCCESSFUL); } DataBytes = pNak->pPendingData[i].PacketLength - pNak->pPendingData[i].DataOffset; if (!DataBytes) { // // No need to process empty data packets (can happen if the client // picks up partial FEC group) // j++; pNak->NextIndexToIndicate++; status = STATUS_SUCCESS; continue; } if (DataBytes > (pNak->pPendingData[i].MessageLength - pNak->pPendingData[i].MessageOffset)) { PgmTrace (LogError, ("PgmIndicateGroup: ERROR -- " \ "[%d] DataBytes=<%d> > MsgLen=<%d> - MsgOff=<%d> = <%d>\n", (ULONG) (pReceive->pReceiver->NextODataSequenceNumber + j), DataBytes, pNak->pPendingData[i].MessageLength, pNak->pPendingData[i].MessageOffset, (pNak->pPendingData[i].MessageLength - pNak->pPendingData[i].MessageOffset))); ASSERT (0); return (STATUS_UNSUCCESSFUL); } BytesTaken = 0; status = PgmIndicateToClient (pAddress, pReceive, DataBytes, (pNak->pPendingData[i].pDataPacket + pNak->pPendingData[i].DataOffset), pNak->pPendingData[i].MessageOffset, pNak->pPendingData[i].MessageLength, &BytesTaken, pOldIrqAddress, pOldIrqReceive); PgmTrace (LogPath, ("PgmIndicateGroup: " \ "SeqNum=[%d]: PgmIndicate returned<%x>\n", (ULONG) pNak->SequenceNumber, status)); ASSERT (BytesTaken <= DataBytes); pNak->pPendingData[i].MessageOffset += BytesTaken; pNak->pPendingData[i].DataOffset += (USHORT) BytesTaken; if (BytesTaken == DataBytes) { // // Go to the next packet // j++; pNak->NextIndexToIndicate++; pReceive->pReceiver->DataPacketsIndicated++; status = STATUS_SUCCESS; } else if (!NT_SUCCESS (status)) { // // We failed, and if the status was STATUS_DATA_NOT_ACCEPTED, // we also don't have any ReceiveIrps pending either // break; } // // else retry indicating this data until we get an error // } // // If the status is anything other than STATUS_DATA_NOT_ACCEPTED (whether // success or failure), then it means we are done with this data! // return (status); } //---------------------------------------------------------------------------- NTSTATUS DecodeParityPackets( IN tRECEIVE_SESSION *pReceive, IN tNAK_FORWARD_DATA *pNak ) { NTSTATUS status; USHORT MinBufferSize; USHORT DataBytes, FprOffset; UCHAR i; PUCHAR pDataBuffer; tPOST_PACKET_FEC_CONTEXT FECContext; PgmZeroMemory (&FECContext, sizeof (tPOST_PACKET_FEC_CONTEXT)); // // Verify that the our buffer is large enough to hold the data // ASSERT (pReceive->MaxMTULength > pNak->ParityDataSize); MinBufferSize = pNak->ParityDataSize + sizeof(tPOST_PACKET_FEC_CONTEXT) - sizeof(USHORT); ASSERT (pNak->PacketsInGroup == pNak->NumDataPackets + pNak->NumParityPackets); // // Now, copy the data into the DecodeBuffers // FprOffset = pNak->ParityDataSize - sizeof(USHORT) + FIELD_OFFSET (tPOST_PACKET_FEC_CONTEXT, FragmentOptSpecific); pDataBuffer = pReceive->pFECBuffer; for (i=0; iFECGroupSize; i++) { // // See if this is a NULL buffer (for partial groups!) // if (i >= pNak->PacketsInGroup) { ASSERT (!pNak->pPendingData[i].PacketIndex); ASSERT (!pNak->pPendingData[i].pDataPacket); DataBytes = pNak->ParityDataSize - sizeof(USHORT) + sizeof (tPOST_PACKET_FEC_CONTEXT); pNak->pPendingData[i].PacketIndex = i; pNak->pPendingData[i].PacketLength = DataBytes; pNak->pPendingData[i].DataOffset = 0; PgmZeroMemory (pDataBuffer, DataBytes); pDataBuffer [FprOffset] = PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT; pNak->pPendingData[i].DecodeBuffer = pDataBuffer; pDataBuffer += DataBytes; PgmZeroMemory (pDataBuffer, DataBytes); pNak->pPendingData[i].pDataPacket = pDataBuffer; pDataBuffer += DataBytes; continue; } // // See if this is a parity packet! // if (pNak->pPendingData[i].PacketIndex >= pReceive->FECGroupSize) { DataBytes = pNak->pPendingData[i].PacketLength - pNak->pPendingData[i].DataOffset; ASSERT (DataBytes == pNak->ParityDataSize); PgmCopyMemory (pDataBuffer, pNak->pPendingData[i].pDataPacket + pNak->pPendingData[i].DataOffset, DataBytes); pNak->pPendingData[i].DecodeBuffer = pDataBuffer; pDataBuffer += (pNak->ParityDataSize - sizeof(USHORT)); PgmCopyMemory (&FECContext.EncodedTSDULength, pDataBuffer, sizeof (USHORT)); FECContext.FragmentOptSpecific = pNak->pPendingData[i].FragmentOptSpecific; FECContext.EncodedFragmentOptions.MessageFirstSequence = pNak->pPendingData[i].MessageFirstSequence; FECContext.EncodedFragmentOptions.MessageOffset = pNak->pPendingData[i].MessageOffset; FECContext.EncodedFragmentOptions.MessageLength = pNak->pPendingData[i].MessageLength; PgmCopyMemory (pDataBuffer, &FECContext, sizeof (tPOST_PACKET_FEC_CONTEXT)); pDataBuffer += sizeof (tPOST_PACKET_FEC_CONTEXT); continue; } // // This is a Data packet // ASSERT (pNak->pPendingData[i].PacketIndex < pNak->PacketsInGroup); DataBytes = pNak->pPendingData[i].PacketLength - pNak->pPendingData[i].DataOffset; ASSERT ((DataBytes+sizeof(USHORT)) <= pNak->ParityDataSize); // Copy the data PgmCopyMemory (pDataBuffer, pNak->pPendingData[i].pDataPacket + pNak->pPendingData[i].DataOffset, DataBytes); // // Verify that the Data Buffer length is sufficient for the output data // if ((pNak->MinPacketLength < MinBufferSize) && (pNak->pPendingData[i].PacketLength < pNak->ParityDataSize)) { if (!ReAllocateDataBuffer (pReceive, &pNak->pPendingData[i], MinBufferSize)) { ASSERT (0); PgmTrace (LogError, ("DecodeParityPackets: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES[2] ...\n")); return (STATUS_INSUFFICIENT_RESOURCES); } } pNak->pPendingData[i].DecodeBuffer = pDataBuffer; // // Zero the remaining buffer // PgmZeroMemory ((pDataBuffer + DataBytes), (pNak->ParityDataSize - DataBytes)); pDataBuffer += (pNak->ParityDataSize - sizeof(USHORT)); FECContext.EncodedTSDULength = htons (DataBytes); FECContext.FragmentOptSpecific = pNak->pPendingData[i].FragmentOptSpecific; if (FECContext.FragmentOptSpecific & PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT) { // // This bit is set if the option did not exist in the original packet // FECContext.EncodedFragmentOptions.MessageFirstSequence = 0; FECContext.EncodedFragmentOptions.MessageOffset = 0; FECContext.EncodedFragmentOptions.MessageLength = 0; } else { FECContext.EncodedFragmentOptions.MessageFirstSequence = htonl (pNak->pPendingData[i].MessageFirstSequence); FECContext.EncodedFragmentOptions.MessageOffset = htonl (pNak->pPendingData[i].MessageOffset); FECContext.EncodedFragmentOptions.MessageLength = htonl (pNak->pPendingData[i].MessageLength); } PgmCopyMemory (pDataBuffer, &FECContext, sizeof (tPOST_PACKET_FEC_CONTEXT)); pDataBuffer += sizeof (tPOST_PACKET_FEC_CONTEXT); } #ifdef FEC_DBG { UCHAR i; tPOST_PACKET_FEC_CONTEXT UNALIGNED *pFECC; tPOST_PACKET_FEC_CONTEXT FECC; for (i=0; iFECGroupSize; i++) { pFECC = (tPOST_PACKET_FEC_CONTEXT UNALIGNED *) &pNak->pPendingData[i].DecodeBuffer[pNak->ParityDataSize-sizeof(USHORT)]; PgmCopyMemory (&FECC, pFECC, sizeof (tPOST_PACKET_FEC_CONTEXT)); PgmTrace (LogFec, ("\t-- [%d:%d:%d] EncTSDULen=<%x>, Fpr=<%x>, [%x -- %x -- %x]\n", (ULONG) pNak->SequenceNumber, (ULONG) pNak->pPendingData[i].PacketIndex, (ULONG) pNak->pPendingData[i].ActualIndexOfDataPacket, FECC.EncodedTSDULength, FECC.FragmentOptSpecific, FECC.EncodedFragmentOptions.MessageFirstSequence, FECC.EncodedFragmentOptions.MessageOffset, FECC.EncodedFragmentOptions.MessageLength))); } } #endif // FEC_DBG DataBytes = pNak->ParityDataSize - sizeof(USHORT) + sizeof (tPOST_PACKET_FEC_CONTEXT); status = FECDecode (&pReceive->FECContext, &(pNak->pPendingData[0]), DataBytes, pNak->PacketsInGroup); // // Before we do anything else, we should NULL out the dummy DataBuffer // ptrs so that they don't get Free'ed accidentally! // for (i=0; iFECGroupSize; i++) { pNak->pPendingData[i].DecodeBuffer = NULL; if (i >= pNak->PacketsInGroup) { ASSERT (!pNak->pPendingData[i].PendingDataFlags); pNak->pPendingData[i].pDataPacket = NULL; } pNak->pPendingData[i].ActualIndexOfDataPacket = i; } if (NT_SUCCESS (status)) { pNak->NumDataPackets = pNak->PacketsInGroup; pNak->NumParityPackets = 0; DataBytes -= sizeof (tPOST_PACKET_FEC_CONTEXT); for (i=0; iPacketsInGroup; i++) { PgmCopyMemory (&FECContext, &(pNak->pPendingData[i].pDataPacket) [DataBytes], sizeof (tPOST_PACKET_FEC_CONTEXT)); pNak->pPendingData[i].PacketLength = ntohs (FECContext.EncodedTSDULength); if (pNak->pPendingData[i].PacketLength > DataBytes) { PgmTrace (LogError, ("DecodeParityPackets: ERROR -- " \ "[%d] PacketLength=<%d> > MaxDataBytes=<%d>\n", (ULONG) i, (ULONG) pNak->pPendingData[i].PacketLength, (ULONG) DataBytes)); ASSERT (0); return (STATUS_UNSUCCESSFUL); } pNak->pPendingData[i].DataOffset = 0; pNak->pPendingData[i].PacketIndex = i; ASSERT ((pNak->AllOptionsFlags & PGM_OPTION_FLAG_FRAGMENT) || (!FECContext.EncodedFragmentOptions.MessageLength)); if (!(pNak->AllOptionsFlags & PGM_OPTION_FLAG_FRAGMENT) || (FECContext.FragmentOptSpecific & PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT)) { // // This is not a packet fragment // pNak->pPendingData[i].MessageFirstSequence = (ULONG) (SEQ_TYPE) (pNak->SequenceNumber + i); pNak->pPendingData[i].MessageOffset = 0; pNak->pPendingData[i].MessageLength = pNak->pPendingData[i].PacketLength; } else { pNak->pPendingData[i].MessageFirstSequence = ntohl (FECContext.EncodedFragmentOptions.MessageFirstSequence); pNak->pPendingData[i].MessageOffset = ntohl (FECContext.EncodedFragmentOptions.MessageOffset); pNak->pPendingData[i].MessageLength = ntohl (FECContext.EncodedFragmentOptions.MessageLength); } } } else { PgmTrace (LogError, ("DecodeParityPackets: ERROR -- " \ "FECDecode returned <%x>\n", status)); ASSERT (0); status = STATUS_UNSUCCESSFUL; } #ifdef FEC_DBG if (NT_SUCCESS (status)) { UCHAR i; tPOST_PACKET_FEC_CONTEXT UNALIGNED *pFECC; tPOST_PACKET_FEC_CONTEXT FECC; DataBytes = pNak->ParityDataSize - sizeof(USHORT); for (i=0; iPacketsInGroup; i++) { pFECC = (tPOST_PACKET_FEC_CONTEXT UNALIGNED *) &pNak->pPendingData[i].pDataPacket[DataBytes]; PgmCopyMemory (&FECC, pFECC, sizeof (tPOST_PACKET_FEC_CONTEXT)); PgmTrace (LogFec, ("\t++ [%d] EncTSDULen=<%x>, Fpr=<%x>, [%x -- %x -- %x], ==> [%x -- %x -- %x]\n", (ULONG) (pNak->SequenceNumber+i), FECC.EncodedTSDULength, FECC.FragmentOptSpecific, FECC.EncodedFragmentOptions.MessageFirstSequence, FECC.EncodedFragmentOptions.MessageOffset, FECC.EncodedFragmentOptions.MessageLength, pNak->pPendingData[i].MessageFirstSequence, pNak->pPendingData[i].MessageOffset, pNak->pPendingData[i].MessageLength)); } PgmTrace (LogFec, ("\n")); } #endif // FEC_DBG return (status); } //---------------------------------------------------------------------------- NTSTATUS CheckIndicatePendedData( IN tADDRESS_CONTEXT *pAddress, IN tRECEIVE_SESSION *pReceive, IN PGMLockHandle *pOldIrqAddress, IN PGMLockHandle *pOldIrqReceive ) /*++ Routine Description: This routine is typically called if the client signalled an inability to handle indicated data -- it will reattempt to indicate the data to the client It is called with the pAddress and pReceive locks held Arguments: IN pAddress -- Address object context IN pReceive -- Receive context IN pOldIrqAddress -- OldIrq for the Address lock IN pOldIrqReceive -- OldIrq for the Receive lock Return Value: NTSTATUS - Final status of the call --*/ { tNAK_FORWARD_DATA *pNextNak; tPACKET_OPTIONS PacketOptions; ULONG PacketsIndicated; tBASIC_DATA_PACKET_HEADER UNALIGNED *pPgmDataHeader; NTSTATUS status = STATUS_SUCCESS; tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver; // // If we are already indicating data on another thread, or // waiting for the client to post a receive irp, just return // if (pReceive->SessionFlags & (PGM_SESSION_FLAG_IN_INDICATE | PGM_SESSION_WAIT_FOR_RECEIVE_IRP)) { return (STATUS_SUCCESS); } ASSERT (!(pReceive->SessionFlags & PGM_SESSION_CLIENT_DISCONNECTED)); pReceive->SessionFlags |= PGM_SESSION_FLAG_IN_INDICATE; while (!IsListEmpty (&pReceiver->BufferedDataList)) { pNextNak = CONTAINING_RECORD (pReceiver->BufferedDataList.Flink, tNAK_FORWARD_DATA, Linkage); ASSERT ((pReceiver->NumPacketGroupsPendingClient) && (pNextNak->SequenceNumber == pReceiver->NextODataSequenceNumber) && (SEQ_GT(pReceiver->FirstNakSequenceNumber, pReceiver->NextODataSequenceNumber))); // // If we do not have all the data packets, we will need to decode them now // if (pNextNak->NumParityPackets) { ASSERT ((pNextNak->NumParityPackets + pNextNak->NumDataPackets) == pNextNak->PacketsInGroup); status = DecodeParityPackets (pReceive, pNextNak); if (!NT_SUCCESS (status)) { PgmTrace (LogError, ("CheckIndicatePendedData: ERROR -- " \ "DecodeParityPackets returned <%x>\n", status)); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; break; } } else { // The below assertion can be greater if we have only partially indicated a group ASSERT ((pNextNak->NextIndexToIndicate + pNextNak->NumDataPackets) >= pNextNak->PacketsInGroup); } status = PgmIndicateGroup (pAddress, pReceive, pOldIrqAddress, pOldIrqReceive, pNextNak); if (!NT_SUCCESS (status)) { // // If the client cannot accept any more data at this time, so // we will try again later, otherwise terminate this session! // if (status != STATUS_DATA_NOT_ACCEPTED) { ASSERT (0); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; } break; } // // Advance to the next group boundary // pReceiver->NextODataSequenceNumber += pNextNak->OriginalGroupSize; PacketsIndicated = pNextNak->NumDataPackets + pNextNak->NumParityPackets; pReceiver->TotalDataPacketsBuffered -= PacketsIndicated; pReceiver->DataPacketsPendingIndicate -= PacketsIndicated; pReceiver->NumPacketGroupsPendingClient--; ASSERT (pReceiver->TotalDataPacketsBuffered >= pReceiver->NumPacketGroupsPendingClient); RemoveEntryList (&pNextNak->Linkage); FreeNakContext (pReceive, pNextNak); } pReceive->SessionFlags &= ~PGM_SESSION_FLAG_IN_INDICATE; PgmTrace (LogAllFuncs, ("CheckIndicatePendedData: " \ "status=<%x>, pReceive=<%p>, SessionFlags=<%x>\n", status, pReceive, pReceive->SessionFlags)); CheckIndicateDisconnect (pAddress, pReceive, pOldIrqAddress, pOldIrqReceive, TRUE); return (STATUS_SUCCESS); } #ifdef MAX_BUFF_DBG ULONG MaxPacketGroupsPendingClient = 0; ULONG MaxPacketsBuffered = 0; ULONG MaxPacketsPendingIndicate = 0; ULONG MaxPacketsPendingNaks = 0; #endif // MAX_BUFF_DBG //---------------------------------------------------------------------------- NTSTATUS PgmHandleNewData( IN SEQ_TYPE *pThisDataSequenceNumber, IN tADDRESS_CONTEXT *pAddress, IN tRECEIVE_SESSION *pReceive, IN USHORT PacketLength, IN tBASIC_DATA_PACKET_HEADER UNALIGNED *pOData, IN UCHAR PacketType, IN PGMLockHandle *pOldIrqAddress, IN PGMLockHandle *pOldIrqReceive ) /*++ Routine Description: This routine buffers data packets received out-of-order Arguments: IN pThisDataSequenceNumber -- Sequence # of unordered data packet IN pAddress -- Address object context IN pReceive -- Receive context IN PacketLength -- Length of packet received from the wire IN pODataBuffer -- Data packet IN PacketType -- Type of Pgm packet Return Value: NTSTATUS - Final status of the call --*/ { SEQ_TYPE ThisDataSequenceNumber = *pThisDataSequenceNumber; LIST_ENTRY *pEntry; PNAK_FORWARD_DATA pOldNak, pLastNak; ULONG MessageLength, DataOffset, BytesTaken, DataBytes, BufferLength; ULONGLONG NcfRDataTickCounts; NTSTATUS status; USHORT TSDULength; tPACKET_OPTIONS PacketOptions; UCHAR i, PacketIndex, NakIndex; BOOLEAN fIsParityPacket, fPartiallyIndicated; PUCHAR pDataBuffer; tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver; ASSERT (PacketLength <= pReceive->MaxMTULength); fIsParityPacket = pOData->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY; // // First, ensure we have a Nak context available for this data // pLastNak = NULL; status = CheckAndAddNakRequests (pReceive, &ThisDataSequenceNumber, &pLastNak, NAK_PENDING_RB, (BOOLEAN) !fIsParityPacket); if ((!NT_SUCCESS (status)) || (!pLastNak)) { PgmTrace (LogAllFuncs, ("PgmHandleNewData: " \ "CheckAndAddNakRequests for <%d> returned <%x>, pLastNak=<%p>\n", ThisDataSequenceNumber, status, pLastNak)); if (NT_SUCCESS (status)) { pReceiver->NumDupPacketsBuffered++; } else { pReceiver->NumDataPacketsDropped++; } return (status); } // // Now, extract all the information that we need from the packet options // PgmZeroMemory (&PacketOptions, sizeof (tPACKET_OPTIONS)); if (pOData->CommonHeader.Options & PACKET_HEADER_OPTIONS_PRESENT) { status = ProcessOptions ((tPACKET_OPTION_LENGTH *) (pOData + 1), (PacketLength - sizeof(tBASIC_DATA_PACKET_HEADER)), (pOData->CommonHeader.Type & 0x0f), &PacketOptions, NULL); if (!NT_SUCCESS (status)) { PgmTrace (LogError, ("PgmHandleNewData: ERROR -- " \ "ProcessOptions returned <%x>, SeqNum=[%d]: NumOutOfOrder=<%d> ...\n", status, (ULONG) ThisDataSequenceNumber, pReceiver->TotalDataPacketsBuffered)); ASSERT (0); pReceiver->NumDataPacketsDropped++; return (status); } } PgmCopyMemory (&TSDULength, &pOData->CommonHeader.TSDULength, sizeof (USHORT)); TSDULength = ntohs (TSDULength); if (PacketLength != (sizeof(tBASIC_DATA_PACKET_HEADER) + PacketOptions.OptionsLength + TSDULength)) { ASSERT (0); pReceiver->NumDataPacketsDropped++; return (STATUS_DATA_NOT_ACCEPTED); } DataOffset = sizeof (tBASIC_DATA_PACKET_HEADER) + PacketOptions.OptionsLength; DataBytes = TSDULength; ASSERT ((PacketOptions.OptionsFlags & ~PGM_VALID_DATA_OPTION_FLAGS) == 0); BytesTaken = 0; // // If this group has a different GroupSize, set that now // if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE) { if (pLastNak->OriginalGroupSize == 1) { // // This path will be used if we have not yet received // an SPM (so don't know group size, etc), but have a // data packet from a partial group // pLastNak->ThisGroupSize = PacketOptions.FECContext.NumPacketsInThisGroup; } else if (PacketOptions.FECContext.NumPacketsInThisGroup >= pReceive->FECGroupSize) { // // Bad Packet! // ASSERT (0); pReceiver->NumDataPacketsDropped++; return (STATUS_DATA_NOT_ACCEPTED); } // // If we have already received all the data packets, don't do anything here // else if (pLastNak->PacketsInGroup == pReceive->FECGroupSize) { pLastNak->PacketsInGroup = PacketOptions.FECContext.NumPacketsInThisGroup; if (pLastNak->SequenceNumber == pReceiver->FurthestKnownGroupSequenceNumber) { pReceiver->FurthestKnownSequenceNumber = pLastNak->SequenceNumber + pLastNak->PacketsInGroup - 1; } // // Get rid of any of the excess (NULL) data packets // RemoveRedundantNaks (pReceive, pLastNak, TRUE); } else if (pLastNak->PacketsInGroup != PacketOptions.FECContext.NumPacketsInThisGroup) { ASSERT (0); pReceiver->NumDataPacketsDropped++; return (STATUS_DATA_NOT_ACCEPTED); } } if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_FIN) { if (fIsParityPacket) { pReceiver->FinDataSequenceNumber = pLastNak->SequenceNumber + (pLastNak->PacketsInGroup - 1); } else { pReceiver->FinDataSequenceNumber = ThisDataSequenceNumber; } pReceive->SessionFlags |= PGM_SESSION_TERMINATED_GRACEFULLY; PgmTrace (LogStatus, ("PgmHandleNewData: " \ "SeqNum=[%d]: Got a FIN!!!\n", (ULONG) pReceiver->FinDataSequenceNumber)); if (pLastNak) { ASSERT (pLastNak->SequenceNumber == (pReceiver->FinDataSequenceNumber & ~(pReceive->FECGroupSize-1))); pLastNak->PacketsInGroup = (UCHAR) (pReceiver->FinDataSequenceNumber + 1 - pLastNak->SequenceNumber); ASSERT (pLastNak->PacketsInGroup <= pReceive->FECGroupSize); RemoveRedundantNaks (pReceive, pLastNak, TRUE); AdjustReceiveBufferLists (pReceive); } } // // Determine the Packet Index // fPartiallyIndicated = FALSE; if (pReceive->FECOptions) { PacketIndex = (UCHAR) (ThisDataSequenceNumber & (pReceive->FECGroupSize-1)); // // See if we even need this packet! // if (!fIsParityPacket) { // // This is a data packet! // if ((PacketIndex >= pLastNak->PacketsInGroup) || (PacketIndex < pLastNak->NextIndexToIndicate)) { // // We don't need this Packet! // status = STATUS_DATA_NOT_ACCEPTED; } } // // Parity packet // else if (((pLastNak->NumDataPackets+pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) || ((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup)) { status = STATUS_DATA_NOT_ACCEPTED; } else { if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_GRP) { ASSERT (((pOData->CommonHeader.Type & 0x0f) == PACKET_TYPE_RDATA) || ((pOData->CommonHeader.Type & 0x0f) == PACKET_TYPE_ODATA)); ASSERT (PacketOptions.FECContext.FECGroupInfo); PacketIndex += ((USHORT) PacketOptions.FECContext.FECGroupInfo * pReceive->FECGroupSize); } } if (status != STATUS_DATA_NOT_ACCEPTED) { // // Verify that this is not a duplicate of a packet we // may have already received // for (i=0; i < (pLastNak->NumDataPackets+pLastNak->NumParityPackets); i++) { if (pLastNak->pPendingData[i].PacketIndex == PacketIndex) { ASSERT (!fIsParityPacket); status = STATUS_DATA_NOT_ACCEPTED; break; } } } else { AdjustReceiveBufferLists (pReceive); // In case this became a partial group } if (status == STATUS_DATA_NOT_ACCEPTED) { pReceiver->NumDupPacketsBuffered++; return (status); } } else // We are not aware of FEC { // // If we are not aware of options, drop this packet if it is a parity packet! // if (fIsParityPacket) { pReceiver->NumDataPacketsDropped++; return (STATUS_DATA_NOT_ACCEPTED); } PacketIndex = 0; ASSERT (!pLastNak->pPendingData[0].pDataPacket); // // If this is the next expected data packet, let's see if we can try // to indicate this data over here only (avoid a packet copy) // Note: This should be the regular indicate path in the case of non-FEC, // low-loss and low-CPU sessions // if ((ThisDataSequenceNumber == pReceiver->NextODataSequenceNumber) && !(pReceive->SessionFlags & (PGM_SESSION_FLAG_IN_INDICATE | PGM_SESSION_WAIT_FOR_RECEIVE_IRP)) && (IsListEmpty (&pReceiver->BufferedDataList))) { ASSERT (!pReceiver->NumPacketGroupsPendingClient); // // If we are starting receiving in the midst of a message, we should ignore them // if ((pReceive->SessionFlags & PGM_SESSION_FLAG_FIRST_PACKET) && (PacketOptions.MessageOffset)) { // // pReceive->pReceiver->CurrentMessageProcessed would have been set // if we were receiving a fragmented message // or if we had only accounted for a partial message earlier // ASSERT (!(pReceive->pReceiver->CurrentMessageProcessed) && !(pReceive->pReceiver->CurrentMessageLength)); PgmTrace (LogPath, ("PgmHandleNewData: " \ "Dropping SeqNum=[%d] since it's a PARTIAL message [%d / %d]!\n", (ULONG) (pReceive->pReceiver->NextODataSequenceNumber), PacketOptions.MessageOffset, PacketOptions.MessageLength)); DataBytes = 0; status = STATUS_SUCCESS; } else if ((pReceiver->CurrentMessageProcessed != PacketOptions.MessageOffset) || ((pReceiver->CurrentMessageProcessed) && (pReceiver->CurrentMessageLength != PacketOptions.MessageLength))) { // // Our state expects us to be in the middle of a message, but // the current packets do not show this // PgmTrace (LogError, ("PgmHandleNewData: ERROR -- " \ "SeqNum=[%d] Expecting MsgLen=<%d>, MsgOff=<%d>, have MsgLen=<%d>, MsgOff=<%d>\n", (ULONG) pReceiver->NextODataSequenceNumber, pReceiver->CurrentMessageLength, pReceiver->CurrentMessageProcessed, PacketOptions.MessageLength, PacketOptions.MessageOffset)); ASSERT (0); BytesTaken = DataBytes; pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; return (STATUS_UNSUCCESSFUL); } RemoveEntryList (&pLastNak->Linkage); RemovePendingReceiverEntry (pLastNak); pReceiver->NextODataSequenceNumber++; pReceiver->FirstNakSequenceNumber = pReceiver->NextODataSequenceNumber; if (PacketOptions.MessageLength) { MessageLength = PacketOptions.MessageLength; ASSERT (DataBytes <= MessageLength - PacketOptions.MessageOffset); } else { MessageLength = DataBytes; ASSERT (!PacketOptions.MessageOffset); } // // If we have a NULL packet, then skip it // if ((!DataBytes) || (PacketOptions.MessageOffset == MessageLength)) { PgmTrace (LogPath, ("PgmHandleNewData: " \ "Dropping SeqNum=[%d] since it's a NULL message [%d / %d]!\n", (ULONG) (pReceiver->NextODataSequenceNumber), PacketOptions.MessageOffset, PacketOptions.MessageLength)); BytesTaken = DataBytes; status = STATUS_SUCCESS; } else { ASSERT (!(pReceive->SessionFlags & PGM_SESSION_CLIENT_DISCONNECTED)); pReceive->SessionFlags |= PGM_SESSION_FLAG_IN_INDICATE; status = PgmIndicateToClient (pAddress, pReceive, DataBytes, (((PUCHAR) pOData) + DataOffset), PacketOptions.MessageOffset, MessageLength, &BytesTaken, pOldIrqAddress, pOldIrqReceive); pReceive->SessionFlags &= ~(PGM_SESSION_FLAG_IN_INDICATE | PGM_SESSION_FLAG_FIRST_PACKET); pReceive->DataBytes += BytesTaken; PgmTrace (LogPath, ("PgmHandleNewData: " \ "SeqNum=[%d]: PgmIndicate returned<%x>\n", (ULONG) ThisDataSequenceNumber, status)); ASSERT (BytesTaken <= DataBytes); if (!NT_SUCCESS (status)) { // // If the client cannot accept any more data at this time, so // we will try again later, otherwise terminate this session! // if (status != STATUS_DATA_NOT_ACCEPTED) { ASSERT (0); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; BytesTaken = DataBytes; } } } if (BytesTaken == DataBytes) { if ((PacketType == PACKET_TYPE_RDATA) && (pLastNak->FirstNcfTickCount)) { AdjustNcfRDataResponseTimes (pReceive, pLastNak); } FreeNakContext (pReceive, pLastNak); AdjustReceiveBufferLists (pReceive); // move any additional complete groups to the BufferedDataList return (status); } fPartiallyIndicated = TRUE; InsertHeadList (&pReceiver->BufferedDataList, &pLastNak->Linkage); } } #ifdef MAX_BUFF_DBG { if (pReceiver->NumPacketGroupsPendingClient > MaxPacketGroupsPendingClient) { MaxPacketGroupsPendingClient = pReceiver->NumPacketGroupsPendingClient; } if (pReceiver->TotalDataPacketsBuffered >= MaxPacketsBuffered) { MaxPacketsBuffered = pReceiver->TotalDataPacketsBuffered; } if (pReceiver->DataPacketsPendingIndicate >= MaxPacketsPendingIndicate) { MaxPacketsPendingIndicate = pReceiver->DataPacketsPendingIndicate; } if (pReceiver->DataPacketsPendingNaks >= MaxPacketsPendingNaks) { MaxPacketsPendingNaks = pReceiver->DataPacketsPendingNaks; } ASSERT (pReceiver->TotalDataPacketsBuffered == (pReceiver->DataPacketsPendingIndicate + pReceiver->DataPacketsPendingNaks)); } #endif // MAX_BUFF_DBG if (pReceiver->TotalDataPacketsBuffered >= pReceiver->MaxPacketsBufferedInLookaside) { PgmTrace (LogError, ("PgmHandleNewData: ERROR -- " \ "[%d -- %d]: Excessive number of packets buffered=<%d> > <%d>, Aborting ...\n", (ULONG) pReceiver->FirstNakSequenceNumber, (ULONG) ThisDataSequenceNumber, (ULONG) pReceiver->TotalDataPacketsBuffered, (ULONG) pReceiver->MaxPacketsBufferedInLookaside)); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; return (STATUS_INSUFFICIENT_RESOURCES); } // // First, check if we are a data packet // (save unique data packets even if we have extra parity packets) // This can help save CPU! // pDataBuffer = NULL; NakIndex = pLastNak->NumDataPackets + pLastNak->NumParityPackets; if (fIsParityPacket) { BufferLength = PacketLength + sizeof(tPOST_PACKET_FEC_CONTEXT) - sizeof(USHORT); } else if ((PacketLength + sizeof (tPOST_PACKET_FEC_CONTEXT)) <= pLastNak->MinPacketLength) { BufferLength = pLastNak->MinPacketLength; } else { BufferLength = PacketLength + sizeof(tPOST_PACKET_FEC_CONTEXT); } pDataBuffer = NULL; if (!fIsParityPacket) { ASSERT (PacketIndex < pReceive->FECGroupSize); ASSERT (pLastNak->pPendingData[PacketIndex].ActualIndexOfDataPacket == pLastNak->OriginalGroupSize); // // If we have some un-needed parity packets, we // can free that memory now // if (NakIndex >= pLastNak->PacketsInGroup) { ASSERT (pLastNak->NumParityPackets); for (i=0; iPacketsInGroup; i++) { if (pLastNak->pPendingData[i].PacketIndex >= pLastNak->OriginalGroupSize) { pDataBuffer = ReAllocateDataBuffer (pReceive, &pLastNak->pPendingData[i], BufferLength); BufferLength = 0; break; } } ASSERT (i < pLastNak->PacketsInGroup); pLastNak->NumParityPackets--; NakIndex = i; } else { ASSERT (!pLastNak->pPendingData[NakIndex].pDataPacket); } if (BufferLength) { pDataBuffer = AllocateDataBuffer (pReceive, &pLastNak->pPendingData[NakIndex], BufferLength); } if (!pDataBuffer) { PgmTrace (LogError, ("PgmHandleNewData: ERROR -- " \ "[%d]: STATUS_INSUFFICIENT_RESOURCES <%d> bytes, NumDataPackets=<%d>, Aborting ...\n", (ULONG) ThisDataSequenceNumber, pLastNak->MinPacketLength, (ULONG) pReceiver->TotalDataPacketsBuffered)); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; return (STATUS_INSUFFICIENT_RESOURCES); } ASSERT (pLastNak->pPendingData[NakIndex].pDataPacket == pDataBuffer); PgmCopyMemory (pDataBuffer, pOData, PacketLength); pLastNak->pPendingData[NakIndex].PacketLength = PacketLength; pLastNak->pPendingData[NakIndex].DataOffset = (USHORT) (DataOffset + BytesTaken); pLastNak->pPendingData[NakIndex].PacketIndex = PacketIndex; pLastNak->pPendingData[PacketIndex].ActualIndexOfDataPacket = NakIndex; pLastNak->NumDataPackets++; pReceive->DataBytes += PacketLength - (DataOffset + BytesTaken); ASSERT (!(PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_GRP)); // // Save some options for future reference // if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_FRAGMENT) { pLastNak->pPendingData[NakIndex].FragmentOptSpecific = 0; pLastNak->pPendingData[NakIndex].MessageFirstSequence = PacketOptions.MessageFirstSequence; pLastNak->pPendingData[NakIndex].MessageLength = PacketOptions.MessageLength; pLastNak->pPendingData[NakIndex].MessageOffset = PacketOptions.MessageOffset + BytesTaken; } else { // // This is not a fragment // pLastNak->pPendingData[NakIndex].FragmentOptSpecific = PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT; pLastNak->pPendingData[NakIndex].MessageFirstSequence = (ULONG) (SEQ_TYPE) (pLastNak->SequenceNumber + PacketIndex); pLastNak->pPendingData[NakIndex].MessageOffset = BytesTaken; pLastNak->pPendingData[NakIndex].MessageLength = PacketLength - DataOffset; } } else { ASSERT (PacketIndex >= pLastNak->OriginalGroupSize); ASSERT (NakIndex < pLastNak->PacketsInGroup); ASSERT (!pLastNak->pPendingData[NakIndex].pDataPacket); pDataBuffer = AllocateDataBuffer (pReceive, &pLastNak->pPendingData[NakIndex], BufferLength); if (!pDataBuffer) { PgmTrace (LogError, ("PgmHandleNewData: ERROR -- " \ "[%d -- Parity]: STATUS_INSUFFICIENT_RESOURCES <%d> bytes, NumDataPackets=<%d>, Aborting ...\n", (ULONG) ThisDataSequenceNumber, PacketLength, (ULONG) pReceiver->TotalDataPacketsBuffered)); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; return (STATUS_INSUFFICIENT_RESOURCES); } // // This is a new parity packet // ASSERT (pLastNak->pPendingData[NakIndex].pDataPacket == pDataBuffer); PgmCopyMemory (pDataBuffer, pOData, PacketLength); pLastNak->pPendingData[NakIndex].PacketLength = PacketLength; pLastNak->pPendingData[NakIndex].DataOffset = (USHORT) DataOffset; pLastNak->pPendingData[NakIndex].PacketIndex = PacketIndex; pLastNak->pPendingData[NakIndex].FragmentOptSpecific = PacketOptions.FECContext.FragmentOptSpecific; pLastNak->pPendingData[NakIndex].MessageFirstSequence = PacketOptions.MessageFirstSequence; pLastNak->pPendingData[NakIndex].MessageLength = PacketOptions.MessageLength; pLastNak->pPendingData[NakIndex].MessageOffset = PacketOptions.MessageOffset + BytesTaken; pLastNak->NumParityPackets++; pReceive->DataBytes += PacketLength - DataOffset; if (!pLastNak->ParityDataSize) { pLastNak->ParityDataSize = (USHORT) (PacketLength - DataOffset); } else { ASSERT (pLastNak->ParityDataSize == (USHORT) (PacketLength - DataOffset)); } } if ((PacketType == PACKET_TYPE_RDATA) && (pLastNak->FirstNcfTickCount) && (((pLastNak->NumDataPackets + pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) || ((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup))) { AdjustNcfRDataResponseTimes (pReceive, pLastNak); } pLastNak->AllOptionsFlags |= PacketOptions.OptionsFlags; pReceiver->TotalDataPacketsBuffered++; if (fPartiallyIndicated) { pReceiver->NumPacketGroupsPendingClient++; pReceiver->DataPacketsPendingIndicate++; pReceiver->NextODataSequenceNumber = ThisDataSequenceNumber; } else { pReceiver->DataPacketsPendingNaks++; // // See if this group is complete // if (((pLastNak->NumDataPackets + pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) || ((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup)) { RemovePendingReceiverEntry (pLastNak); AdjustReceiveBufferLists (pReceive); } } PgmTrace (LogAllFuncs, ("PgmHandleNewData: " \ "SeqNum=[%d]: NumOutOfOrder=<%d> ...\n", (ULONG) ThisDataSequenceNumber, pReceiver->TotalDataPacketsBuffered)); return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS ProcessDataPacket( IN tADDRESS_CONTEXT *pAddress, IN tRECEIVE_SESSION *pReceive, IN ULONG PacketLength, IN tBASIC_DATA_PACKET_HEADER UNALIGNED *pODataBuffer, IN UCHAR PacketType ) /*++ Routine Description: This routine looks at the data packet received from the wire and handles it appropriately depending on whether it is in order or not Arguments: IN pAddress -- Address object context IN pReceive -- Receive context IN PacketLength -- Length of packet received from the wire IN pODataBuffer -- Data packet IN PacketType -- Type of Pgm packet Return Value: NTSTATUS - Final status of the call --*/ { NTSTATUS status; SEQ_TYPE ThisPacketSequenceNumber; SEQ_TYPE ThisTrailingEdge; tNAK_FORWARD_DATA *pNextNak; ULONG DisconnectFlag; PGMLockHandle OldIrq, OldIrq1; ULONG ulData; if (PacketLength < sizeof(tBASIC_DATA_PACKET_HEADER)) { PgmTrace (LogError, ("ProcessDataPacket: ERROR -- " \ "PacketLength=<%d> < tBASIC_DATA_PACKET_HEADER=<%d>\n", PacketLength, sizeof(tBASIC_DATA_PACKET_HEADER))); return (STATUS_DATA_NOT_ACCEPTED); } PgmLock (pAddress, OldIrq); PgmLock (pReceive, OldIrq1); if (pReceive->SessionFlags & (PGM_SESSION_CLIENT_DISCONNECTED | PGM_SESSION_TERMINATED_ABORT)) { PgmTrace (LogPath, ("ProcessDataPacket: " \ "Dropping packet because session is terminating!\n")); pReceive->pReceiver->NumDataPacketsDropped++; PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); return (STATUS_DATA_NOT_ACCEPTED); } PgmCopyMemory (&ulData, &pODataBuffer->DataSequenceNumber, sizeof(ULONG)); ThisPacketSequenceNumber = (SEQ_TYPE) ntohl (ulData); PgmCopyMemory (&ulData, &pODataBuffer->TrailingEdgeSequenceNumber, sizeof(ULONG)); ThisTrailingEdge = (SEQ_TYPE) ntohl (ulData); ASSERT (ntohl (ulData) == (LONG) ThisTrailingEdge); // // Update our Window information (use offset from Leading edge to account for wrap-around) // if (SEQ_GT (ThisTrailingEdge, pReceive->pReceiver->LastTrailingEdgeSeqNum)) { pReceive->pReceiver->LastTrailingEdgeSeqNum = ThisTrailingEdge; } // // If the next packet we are expecting is out-of-range, then we // should terminate the session // if (SEQ_LT (pReceive->pReceiver->FirstNakSequenceNumber, pReceive->pReceiver->LastTrailingEdgeSeqNum)) { pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; if (SEQ_GT (pReceive->pReceiver->LastTrailingEdgeSeqNum, (1 + pReceive->pReceiver->FurthestKnownGroupSequenceNumber))) { PgmTrace (LogStatus, ("ProcessDataPacket: " \ "NETWORK problems -- data loss=<%d> packets > window size!\n\tExpecting=<%d>, FurthestKnown=<%d>, Window=[%d--%d]=<%d> seqs\n", (ULONG) (1 + ThisPacketSequenceNumber - pReceive->pReceiver->FurthestKnownGroupSequenceNumber), (ULONG) pReceive->pReceiver->FirstNakSequenceNumber, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber, (ULONG) ThisTrailingEdge, (ULONG) ThisPacketSequenceNumber, (ULONG) (1+ThisPacketSequenceNumber-ThisTrailingEdge))); } else { ASSERT (!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList)); pNextNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Flink, tNAK_FORWARD_DATA, Linkage); PgmTrace (LogStatus, ("ProcessDataPacket: " \ "Session window has past TrailingEdge -- Expecting=<%d==%d>, NumNcfs=<%d>, FurthestKnown=<%d>, Window=[%d--%d] = < %d > seqs\n", (ULONG) pReceive->pReceiver->FirstNakSequenceNumber, (ULONG) pNextNak->SequenceNumber, (ULONG) pNextNak->WaitingRDataRetries, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber, (ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum, (ULONG) ThisPacketSequenceNumber, (ULONG) (1+ThisPacketSequenceNumber-ThisTrailingEdge))); } } else if (SEQ_GT (pReceive->pReceiver->FirstNakSequenceNumber, ThisPacketSequenceNumber)) { // // Drop this packet since it is earlier than our window // pReceive->pReceiver->NumDupPacketsOlderThanWindow++; PgmTrace (LogPath, ("ProcessDataPacket: " \ "Dropping this packet, SeqNum=[%d] < NextOData=[%d]\n", (ULONG) ThisPacketSequenceNumber, (ULONG) pReceive->pReceiver->FirstNakSequenceNumber)); } else { if (PacketType == PACKET_TYPE_ODATA) { UpdateRealTimeWindowInformation (pReceive, ThisPacketSequenceNumber, ThisTrailingEdge); } status = PgmHandleNewData (&ThisPacketSequenceNumber, pAddress, pReceive, (USHORT) PacketLength, pODataBuffer, PacketType, &OldIrq, &OldIrq1); PgmTrace (LogPath, ("ProcessDataPacket: " \ "PgmHandleNewData returned <%x>, SeqNum=[%d] < NextOData=[%d]\n", status, (ULONG) ThisPacketSequenceNumber, (ULONG) pReceive->pReceiver->NextODataSequenceNumber)); // // Now, try to indicate any data which may still be pending // status = CheckIndicatePendedData (pAddress, pReceive, &OldIrq, &OldIrq1); } CheckIndicateDisconnect (pAddress, pReceive, &OldIrq, &OldIrq1, TRUE); PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS ProcessSpmPacket( IN tADDRESS_CONTEXT *pAddress, IN tRECEIVE_SESSION *pReceive, IN ULONG PacketLength, IN tBASIC_SPM_PACKET_HEADER UNALIGNED *pSpmPacket ) /*++ Routine Description: This routine processes Spm packets Arguments: IN pAddress -- Address object context IN pReceive -- Receive context IN PacketLength -- Length of packet received from the wire IN pSpmPacket -- Spm packet Return Value: NTSTATUS - Final status of the call --*/ { SEQ_TYPE SpmSequenceNumber, LeadingEdgeSeqNumber, TrailingEdgeSeqNumber; LIST_ENTRY *pEntry; ULONG DisconnectFlag; NTSTATUS status; PGMLockHandle OldIrq, OldIrq1; tPACKET_OPTIONS PacketOptions; PNAK_FORWARD_DATA pNak; USHORT TSDULength; tNLA PathNLA; BOOLEAN fFirstSpm; ULONG ulData; // // First process the options // PgmZeroMemory (&PacketOptions, sizeof (tPACKET_OPTIONS)); if (pSpmPacket->CommonHeader.Options & PACKET_HEADER_OPTIONS_PRESENT) { status = ProcessOptions ((tPACKET_OPTION_LENGTH *) (pSpmPacket + 1), (PacketLength - sizeof(tBASIC_SPM_PACKET_HEADER)), (pSpmPacket->CommonHeader.Type & 0x0f), &PacketOptions, NULL); if (!NT_SUCCESS (status)) { PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \ "ProcessOptions returned <%x>\n", status)); return (STATUS_DATA_NOT_ACCEPTED); } } ASSERT ((PacketOptions.OptionsFlags & ~PGM_VALID_SPM_OPTION_FLAGS) == 0); PgmCopyMemory (&PathNLA, &pSpmPacket->PathNLA, sizeof (tNLA)); PgmCopyMemory (&TSDULength, &pSpmPacket->CommonHeader.TSDULength, sizeof (USHORT)); TSDULength = ntohs (TSDULength); PathNLA.NLA_AFI = ntohs (PathNLA.NLA_AFI); if ((TSDULength) || (IPV4_NLA_AFI != PathNLA.NLA_AFI) || (!PathNLA.IpAddress)) { PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \ "TSDULength=<%d>, PathNLA.IpAddress=<%x>\n", (ULONG) TSDULength, PathNLA.IpAddress)); return (STATUS_DATA_NOT_ACCEPTED); } PgmCopyMemory (&ulData, &pSpmPacket->SpmSequenceNumber, sizeof (ULONG)); SpmSequenceNumber = (SEQ_TYPE) ntohl (ulData); PgmCopyMemory (&ulData, &pSpmPacket->LeadingEdgeSeqNumber, sizeof (ULONG)); LeadingEdgeSeqNumber = (SEQ_TYPE) ntohl (ulData); PgmCopyMemory (&ulData, &pSpmPacket->TrailingEdgeSeqNumber, sizeof (ULONG)); TrailingEdgeSeqNumber = (SEQ_TYPE) ntohl (ulData); // // Verify Packet length // if ((sizeof(tBASIC_SPM_PACKET_HEADER) + PacketOptions.OptionsLength) != PacketLength) { PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \ "Bad PacketLength=<%d>, OptionsLength=<%d>, TSDULength=<%d>\n", PacketLength, PacketOptions.OptionsLength, (ULONG) TSDULength)); return (STATUS_DATA_NOT_ACCEPTED); } PgmLock (pAddress, OldIrq); if (!pReceive) { // // Since we do not have a live connection yet, we will // have to store some state in the Address context // PgmTrace (LogPath, ("ProcessSpmPacket: " \ "[%d] Received SPM before OData for session, LastSpmSource=<%x>, FEC %sabled, Window=[%d - %d]\n", SpmSequenceNumber, PathNLA.IpAddress, (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_PRM ? "EN" : "DIS"), (ULONG) TrailingEdgeSeqNumber, (ULONG) LeadingEdgeSeqNumber)); if ((ntohs (PathNLA.NLA_AFI) == IPV4_NLA_AFI) && (PathNLA.IpAddress)) { pAddress->LastSpmSource = ntohl (PathNLA.IpAddress); } // // Check if the sender is FEC-enabled // if ((PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_PRM) && (PacketOptions.FECContext.ReceiverFECOptions) && (PacketOptions.FECContext.FECGroupInfo > 1)) { pAddress->FECOptions = PacketOptions.FECContext.ReceiverFECOptions; pAddress->FECGroupSize = (UCHAR) PacketOptions.FECContext.FECGroupInfo; ASSERT (PacketOptions.FECContext.FECGroupInfo == pAddress->FECGroupSize); } PgmUnlock (pAddress, OldIrq); return (STATUS_SUCCESS); } PgmLock (pReceive, OldIrq1); UpdateSpmIntervalInformation (pReceive); // // If this is not the first SPM packet (LastSpmSource is not NULL), see if it is out-of-sequence, // otherwise take this as the first packet // if ((pReceive->pReceiver->LastSpmSource) && (SEQ_LEQ (SpmSequenceNumber, pReceive->pReceiver->LastSpmSequenceNumber))) { PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); PgmTrace (LogAllFuncs, ("ProcessSpmPacket: " \ "Out-of-sequence SPM Packet received!\n")); return (STATUS_DATA_NOT_ACCEPTED); } pReceive->pReceiver->LastSpmSequenceNumber = SpmSequenceNumber; // // Save the last Sender NLA // if ((ntohs(PathNLA.NLA_AFI) == IPV4_NLA_AFI) && (PathNLA.IpAddress)) { pReceive->pReceiver->LastSpmSource = ntohl (PathNLA.IpAddress); } else { pReceive->pReceiver->LastSpmSource = pReceive->pReceiver->SenderIpAddress; } UpdateRealTimeWindowInformation (pReceive, LeadingEdgeSeqNumber, TrailingEdgeSeqNumber); // // Update the trailing edge if this is more ahead // if (SEQ_GT (TrailingEdgeSeqNumber, pReceive->pReceiver->LastTrailingEdgeSeqNum)) { pReceive->pReceiver->LastTrailingEdgeSeqNum = TrailingEdgeSeqNumber; } if (SEQ_GT (pReceive->pReceiver->LastTrailingEdgeSeqNum, pReceive->pReceiver->FirstNakSequenceNumber)) { pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; if (SEQ_GT (pReceive->pReceiver->LastTrailingEdgeSeqNum, (1 + pReceive->pReceiver->FurthestKnownGroupSequenceNumber))) { PgmTrace (LogStatus, ("ProcessSpmPacket: " \ "NETWORK problems -- data loss=<%d> packets > window size!\n\tExpecting=<%d>, FurthestKnown=<%d>, Window=[%d--%d] = < %d > seqs\n", (ULONG) (1 + LeadingEdgeSeqNumber - pReceive->pReceiver->FurthestKnownGroupSequenceNumber), (ULONG) pReceive->pReceiver->FirstNakSequenceNumber, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber, (ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum, LeadingEdgeSeqNumber, (ULONG) (1+LeadingEdgeSeqNumber-pReceive->pReceiver->LastTrailingEdgeSeqNum))); } else { ASSERT (!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList)); pNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Flink, tNAK_FORWARD_DATA, Linkage); PgmTrace (LogStatus, ("ProcessSpmPacket: " \ "Session window has past TrailingEdge -- Expecting <%d==%d>, NumNcfs=<%d>, FurthestKnown=<%d>, Window=[%d--%d] = < %d > seqs\n", (ULONG) pReceive->pReceiver->FirstNakSequenceNumber, (ULONG) pNak->SequenceNumber, pNak->WaitingRDataRetries, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber, (ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum, LeadingEdgeSeqNumber, (ULONG) (1+LeadingEdgeSeqNumber-pReceive->pReceiver->LastTrailingEdgeSeqNum))); } } // // If the Leading edge is > our current leading edge, then // we need to send NAKs for the missing data Packets // pNak = NULL; if (SEQ_GEQ (LeadingEdgeSeqNumber, pReceive->pReceiver->FirstNakSequenceNumber)) { status = CheckAndAddNakRequests (pReceive, &LeadingEdgeSeqNumber, &pNak, NAK_PENDING_RB, TRUE); if (!NT_SUCCESS (status)) { PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \ "CheckAndAddNakRequests returned <%x>\n", status)); return (status); } } // // Now, process all the options // if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_RST_N) { pReceive->pReceiver->FinDataSequenceNumber = pReceive->pReceiver->FurthestKnownGroupSequenceNumber; pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; PgmTrace (LogStatus, ("ProcessSpmPacket: " \ "Got an RST_N! FinSeq=<%d>, NextODataSeq=<%d>, FurthestData=<%d>\n", (ULONG) pReceive->pReceiver->FinDataSequenceNumber, (ULONG) pReceive->pReceiver->NextODataSequenceNumber, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber)); } else if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_RST) { pReceive->pReceiver->FinDataSequenceNumber = LeadingEdgeSeqNumber; pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; PgmTrace (LogStatus, ("ProcessSpmPacket: " \ "Got an RST! FinSeq=<%d>, NextODataSeq=<%d>, FurthestData=<%d>\n", (ULONG) pReceive->pReceiver->FinDataSequenceNumber, (ULONG) pReceive->pReceiver->NextODataSequenceNumber, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber)); } else if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_FIN) { pReceive->pReceiver->FinDataSequenceNumber = LeadingEdgeSeqNumber; pReceive->SessionFlags |= PGM_SESSION_TERMINATED_GRACEFULLY; PgmTrace (LogStatus, ("ProcessSpmPacket: " \ "Got a FIN! FinSeq=<%d>, NextODataSeq=<%d>, FirstNak=<%d>, FurthestKnown=<%d>, FurthestGroup=<%d>\n", (ULONG) pReceive->pReceiver->FinDataSequenceNumber, (ULONG) pReceive->pReceiver->NextODataSequenceNumber, (ULONG) pReceive->pReceiver->FirstNakSequenceNumber, (ULONG) pReceive->pReceiver->FurthestKnownSequenceNumber, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber)); // // See if we need to set the Fin Sequence # // if (pNak) { ASSERT (pNak->SequenceNumber == (LeadingEdgeSeqNumber & ~(pReceive->FECGroupSize-1))); pNak->PacketsInGroup = (UCHAR) (LeadingEdgeSeqNumber + 1 - pNak->SequenceNumber); ASSERT (pNak->PacketsInGroup <= pReceive->FECGroupSize); RemoveRedundantNaks (pReceive, pNak, TRUE); AdjustReceiveBufferLists (pReceive); } } // // See if we need to abort // if (CheckIndicateDisconnect (pAddress, pReceive, &OldIrq, &OldIrq1, TRUE)) { PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); return (STATUS_SUCCESS); } // // Check if the sender is FEC-enabled // if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_PRM) { if ((pReceive->FECGroupSize == 1) && (PacketOptions.FECContext.ReceiverFECOptions) && (PacketOptions.FECContext.FECGroupInfo > 1)) { ASSERT (!pReceive->pFECBuffer); if (!(pReceive->pFECBuffer = PgmAllocMem ((pReceive->MaxFECPacketLength * PacketOptions.FECContext.FECGroupInfo*2), PGM_TAG('3')))) { status = STATUS_INSUFFICIENT_RESOURCES; PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES -- MaxFECPacketLength = <%d>, GroupSize=<%d>\n", pReceive->MaxFECPacketLength, PacketOptions.FECContext.FECGroupInfo)); } else if (!NT_SUCCESS (status = CreateFECContext (&pReceive->FECContext, PacketOptions.FECContext.FECGroupInfo, FEC_MAX_BLOCK_SIZE, TRUE))) { PgmFreeMem (pReceive->pFECBuffer); pReceive->pFECBuffer = NULL; PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \ "CreateFECContext returned <%x>\n", status)); } else if (!NT_SUCCESS (status = CoalesceSelectiveNaksIntoGroups (pReceive, (UCHAR) PacketOptions.FECContext.FECGroupInfo))) { DestroyFECContext (&pReceive->FECContext); PgmFreeMem (pReceive->pFECBuffer); pReceive->pFECBuffer = NULL; PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \ "CoalesceSelectiveNaksIntoGroups returned <%x>\n", status)); } else { pReceive->FECOptions = PacketOptions.FECContext.ReceiverFECOptions; pReceive->FECGroupSize = (UCHAR) PacketOptions.FECContext.FECGroupInfo; if (pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT) { pReceive->pReceiver->SessionNakType = NAK_TYPE_PARITY; } ASSERT (PacketOptions.FECContext.FECGroupInfo == pReceive->FECGroupSize); } if (!NT_SUCCESS (status)) { PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); return (STATUS_DATA_NOT_ACCEPTED); } fFirstSpm = TRUE; } else { fFirstSpm = FALSE; } if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE) { // // The Leading edge Packet belongs to a Variable sized group // so set that information appropriately // Determine the group to which this leading edge belongs to // LeadingEdgeSeqNumber &= ~((SEQ_TYPE) (pReceive->FECGroupSize-1)); if ((PacketOptions.FECContext.NumPacketsInThisGroup) && (PacketOptions.FECContext.NumPacketsInThisGroup < pReceive->FECGroupSize) && SEQ_GEQ (LeadingEdgeSeqNumber, pReceive->pReceiver->FirstNakSequenceNumber) && (pNak = FindReceiverEntry (pReceive->pReceiver, LeadingEdgeSeqNumber)) && (pNak->PacketsInGroup == pReceive->FECGroupSize)) { // // We have already coalesced the list, so the packets should // be ordered into groups! // pNak->PacketsInGroup = PacketOptions.FECContext.NumPacketsInThisGroup; if (pNak->SequenceNumber == pReceive->pReceiver->FurthestKnownGroupSequenceNumber) { pReceive->pReceiver->FurthestKnownSequenceNumber = pNak->SequenceNumber + pNak->PacketsInGroup - 1; } RemoveRedundantNaks (pReceive, pNak, TRUE); } else { PgmTrace (LogPath, ("ProcessSpmPacket: " \ "WARNING .. PARITY_CUR_TGSIZE ThisGroupSize=<%x>, FECGroupSize=<%x>\n", PacketOptions.FECContext.NumPacketsInThisGroup, pReceive->FECGroupSize)); } } } status = CheckIndicatePendedData (pAddress, pReceive, &OldIrq, &OldIrq1); PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); PgmTrace (LogAllFuncs, ("ProcessSpmPacket: " \ "NextOData=<%d>, FinDataSeq=<%d> \n", (ULONG) pReceive->pReceiver->NextODataSequenceNumber, (ULONG) pReceive->pReceiver->FinDataSequenceNumber)); return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS PgmProcessIncomingPacket( IN tADDRESS_CONTEXT *pAddress, IN tCOMMON_SESSION_CONTEXT *pSession, IN INT SourceAddressLength, IN PTA_IP_ADDRESS pRemoteAddress, IN ULONG PacketLength, IN tCOMMON_HEADER UNALIGNED *pPgmHeader, IN UCHAR PacketType ) /*++ Routine Description: This routine process an incoming packet and calls the appropriate handler depending on whether is a data packet packet, etc. Arguments: IN pAddress -- Address object context IN pReceive -- Receive context IN SourceAddressLength -- Length of source address IN pRemoteAddress -- Address of remote host IN PacketLength -- Length of packet received from the wire IN pPgmHeader -- Pgm packet IN PacketType -- Type of Pgm packet Return Value: NTSTATUS - Final status of the call --*/ { tIPADDRESS SrcIpAddress; tNLA SourceNLA, MCastGroupNLA; tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *pNakNcfPacket; NTSTATUS status = STATUS_SUCCESS; // // We have an active connection for this TSI, so process the data appropriately // // // First check for SPM packets // if (PACKET_TYPE_SPM == PacketType) { if (PacketLength < sizeof(tBASIC_SPM_PACKET_HEADER)) { PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \ "Bad SPM Packet length: PacketLength=<%d> < sizeof(tBASIC_SPM_PACKET_HEADER)=<%d>\n", PacketLength, sizeof(tBASIC_SPM_PACKET_HEADER))); return (STATUS_DATA_NOT_ACCEPTED); } if (PGM_VERIFY_HANDLE (pSession, PGM_VERIFY_SESSION_RECEIVE)) { pSession->TotalBytes += PacketLength; pSession->TotalPacketsReceived++; pSession->pReceiver->LastSessionTickCount = PgmDynamicConfig.ReceiversTimerTickCount; status = ProcessSpmPacket (pAddress, pSession, PacketLength, (tBASIC_SPM_PACKET_HEADER UNALIGNED *) pPgmHeader); PgmTrace (LogAllFuncs, ("PgmProcessIncomingPacket: " \ "SPM PacketType=<%x> for pSession=<%p> PacketLength=<%d>, status=<%x>\n", PacketType, pSession, PacketLength, status)); } else { PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \ "Received SPM packet, not on Receiver session! pSession=<%p>\n", pSession)); status = STATUS_DATA_NOT_ACCEPTED; } return (status); } // // The only other packets we process are Nak and Ncf packets, so ignore the rest! // if ((PACKET_TYPE_NCF != PacketType) && (PACKET_TYPE_NAK != PacketType)) { PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \ "Unknown PacketType=<%x>, PacketLength=<%d>\n", PacketType, PacketLength)); return (STATUS_DATA_NOT_ACCEPTED); } // // Now, verify packet info for Nak and Ncf packets // if (PacketLength < sizeof(tBASIC_NAK_NCF_PACKET_HEADER)) { PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \ "NakNcf packet! PacketLength=<%d>, Min=<%d>, ...\n", PacketLength, sizeof(tBASIC_NAK_NCF_PACKET_HEADER))); return (STATUS_DATA_NOT_ACCEPTED); } pNakNcfPacket = (tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *) pPgmHeader; PgmCopyMemory (&SourceNLA, &pNakNcfPacket->SourceNLA, sizeof (tNLA)); PgmCopyMemory (&MCastGroupNLA, &pNakNcfPacket->MCastGroupNLA, sizeof (tNLA)); if (((htons(IPV4_NLA_AFI) != SourceNLA.NLA_AFI) || (!SourceNLA.IpAddress)) || ((htons(IPV4_NLA_AFI) != MCastGroupNLA.NLA_AFI) || (!MCastGroupNLA.IpAddress))) { PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \ "NakNcf packet! PacketLength=<%d>, Min=<%d>, ...\n", PacketLength, sizeof(tBASIC_NAK_NCF_PACKET_HEADER))); return (STATUS_DATA_NOT_ACCEPTED); } if (PACKET_TYPE_NCF == PacketType) { if (PGM_VERIFY_HANDLE (pSession, PGM_VERIFY_SESSION_RECEIVE)) { status = ReceiverProcessNakNcfPacket (pAddress, pSession, PacketLength, pNakNcfPacket, PacketType); } else { PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \ "Received Ncf packet, not on Receiver session! pSession=<%p>\n", pSession)); status = STATUS_DATA_NOT_ACCEPTED; } } // Now process NAK packet else if (pSession->pSender) { ASSERT (!pSession->pReceiver); status = SenderProcessNakPacket (pAddress, pSession, PacketLength, pNakNcfPacket); } else { ASSERT (pSession->pReceiver); // // Check for Remote guy's address // If the Nak was sent by us, then we can ignore it! // if ((pRemoteAddress->TAAddressCount == 1) && (pRemoteAddress->Address[0].AddressLength == TDI_ADDRESS_LENGTH_IP) && (pRemoteAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IP) && (SrcIpAddress = ntohl(((PTDI_ADDRESS_IP)&pRemoteAddress->Address[0].Address)->in_addr)) && (!SrcIsUs (SrcIpAddress)) && (SrcIsOnLocalSubnet (SrcIpAddress))) { status = ReceiverProcessNakNcfPacket (pAddress, pSession, PacketLength, pNakNcfPacket, PacketType); } ASSERT (NT_SUCCESS (status)); } PgmTrace (LogAllFuncs, ("PgmProcessIncomingPacket: " \ "PacketType=<%x> for pSession=<%p> PacketLength=<%d>, status=<%x>\n", PacketType, pSession, PacketLength, status)); return (status); } //---------------------------------------------------------------------------- NTSTATUS PgmNewInboundConnection( IN tADDRESS_CONTEXT *pAddress, IN INT SourceAddressLength, IN PVOID pSourceAddress, IN ULONG ReceiveDatagramFlags, IN tBASIC_DATA_PACKET_HEADER UNALIGNED *pPgmHeader, IN ULONG PacketLength, OUT tRECEIVE_SESSION **ppReceive ) /*++ Routine Description: This routine processes a new incoming connection Arguments: IN pAddress -- Address object context IN SourceAddressLength -- Length of source address IN pSourceAddress -- Address of remote host IN ReceiveDatagramFlags-- Flags set by the transport for this packet IN pPgmHeader -- Pgm packet IN PacketLength -- Length of packet received from the wire OUT ppReceive -- pReceive context for this session returned by the client (if successful) Return Value: NTSTATUS - Final status of the call --*/ { NTSTATUS status; tRECEIVE_SESSION *pReceive; CONNECTION_CONTEXT ConnectId; PIO_STACK_LOCATION pIrpSp; TA_IP_ADDRESS RemoteAddress; INT RemoteAddressSize; PTDI_IND_CONNECT evConnect = NULL; PVOID ConEvContext = NULL; PGMLockHandle OldIrq, OldIrq1, OldIrq2; PIRP pIrp = NULL; ULONG ulData; USHORT PortNum; SEQ_TYPE FirstODataSequenceNumber; tPACKET_OPTIONS PacketOptions; // // We need to set the Next expected sequence number, so first see if // there is a late joiner option // PgmZeroMemory (&PacketOptions, sizeof (tPACKET_OPTIONS)); if (pPgmHeader->CommonHeader.Options & PACKET_HEADER_OPTIONS_PRESENT) { status = ProcessOptions ((tPACKET_OPTION_LENGTH *) (pPgmHeader + 1), (PacketLength - sizeof(tBASIC_DATA_PACKET_HEADER)), (pPgmHeader->CommonHeader.Type & 0x0f), &PacketOptions, NULL); if (!NT_SUCCESS (status)) { PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \ "ProcessOptions returned <%x>\n", status)); return (STATUS_DATA_NOT_ACCEPTED); } ASSERT ((PacketOptions.OptionsFlags & ~PGM_VALID_DATA_OPTION_FLAGS) == 0); } PgmCopyMemory (&ulData, &pPgmHeader->DataSequenceNumber, sizeof (ULONG)); FirstODataSequenceNumber = (SEQ_TYPE) ntohl (ulData); PgmLock (pAddress, OldIrq1); // // The Address is already referenced in the calling routine, // so we don not need to reference it here again! // #if 0 if (!IsListEmpty(&pAddress->ListenHead)) { // // Ignore this for now since we have not encountered posted listens! (Is this an ISSUE ?) } #endif // 0 if (!(ConEvContext = pAddress->ConEvContext)) { // // Client has not yet posted a Listen! // take all of the data so that a disconnect will not be held up // by data still in the transport. // PgmUnlock (pAddress, OldIrq1); PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \ "No Connect handler, pAddress=<%p>\n", pAddress)); return (STATUS_DATA_NOT_ACCEPTED); } RemoteAddressSize = offsetof (TA_IP_ADDRESS, Address[0].Address) + sizeof(TDI_ADDRESS_IP); ASSERT (SourceAddressLength <= RemoteAddressSize); PgmCopyMemory (&RemoteAddress, pSourceAddress, RemoteAddressSize); PgmCopyMemory (&((PTDI_ADDRESS_IP) &RemoteAddress.Address[0].Address)->sin_port, &pPgmHeader->CommonHeader.SrcPort, sizeof (USHORT)); RemoteAddress.TAAddressCount = 1; evConnect = pAddress->evConnect; PgmUnlock (pAddress, OldIrq1); status = (*evConnect) (ConEvContext, RemoteAddressSize, &RemoteAddress, 0, NULL, 0, // options length NULL, // Options &ConnectId, &pIrp); if ((status != STATUS_MORE_PROCESSING_REQUIRED) || (pIrp == NULL)) { PgmTrace (LogPath, ("PgmNewInboundConnection: " \ "Client REJECTed incoming session: status=<%x>, pAddress=<%p>, evConn=<%p>\n", status, pAddress, pAddress->evConnect)); if (pIrp) { PgmIoComplete (pIrp, STATUS_INTERNAL_ERROR, 0); } *ppReceive = NULL; return (STATUS_DATA_NOT_ACCEPTED); } PgmLock (&PgmDynamicConfig, OldIrq); PgmLock (pAddress, OldIrq1); // // the pReceive ptr was stored in the FsContext value when the connection // was initially created. // pIrpSp = IoGetCurrentIrpStackLocation (pIrp); pReceive = (tRECEIVE_SESSION *) pIrpSp->FileObject->FsContext; if ((!PGM_VERIFY_HANDLE (pReceive, PGM_VERIFY_SESSION_RECEIVE)) || (pReceive->pAssociatedAddress != pAddress)) { PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \ "pReceive=<%p>, pAddress=<%p : %p>\n", pReceive, (pReceive ? pReceive->pAssociatedAddress : NULL), pAddress)); PgmUnlock (pAddress, OldIrq1); PgmUnlock (&PgmDynamicConfig, OldIrq); PgmIoComplete (pIrp, STATUS_INTERNAL_ERROR, 0); *ppReceive = NULL; return (STATUS_INTERNAL_ERROR); } ASSERT (ConnectId == pReceive->ClientSessionContext); PgmLock (pReceive, OldIrq2); if (!(pReceive->pReceiver->pReceiveData = InitReceiverData (pReceive))) { pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; PgmUnlock (pAddress, OldIrq2); PgmUnlock (&PgmDynamicConfig, OldIrq1); PgmUnlock (pReceive, OldIrq); PgmIoComplete (pIrp, STATUS_INSUFFICIENT_RESOURCES, 0); *ppReceive = NULL; PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES allocating pReceiveData -- pReceive=<%p>, pAddress=<%p>\n", pReceive, pAddress)); return (STATUS_INSUFFICIENT_RESOURCES); } pReceive->pReceiver->SenderIpAddress = ntohl (((PTDI_ADDRESS_IP)&RemoteAddress.Address[0].Address)->in_addr); pReceive->MaxMTULength = (USHORT) PgmDynamicConfig.MaxMTU; pReceive->MaxFECPacketLength = pReceive->MaxMTULength + sizeof (tPOST_PACKET_FEC_CONTEXT) - sizeof (USHORT); ASSERT (!pReceive->pFECBuffer); if (pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED) { pReceive->pReceiver->MaxPacketsBufferedInLookaside = MAX_PACKETS_BUFFERED * 3; // 9000, about 15 Megs! pReceive->pReceiver->InitialOutstandingNakTimeout = INITIAL_NAK_OUTSTANDING_TIMEOUT_MS_OPT / BASIC_TIMER_GRANULARITY_IN_MSECS; } else { pReceive->pReceiver->MaxPacketsBufferedInLookaside = MAX_PACKETS_BUFFERED; pReceive->pReceiver->InitialOutstandingNakTimeout = INITIAL_NAK_OUTSTANDING_TIMEOUT_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS; } ASSERT (pReceive->pReceiver->MaxPacketsBufferedInLookaside); ASSERT (pReceive->pReceiver->InitialOutstandingNakTimeout); pReceive->pReceiver->OutstandingNakTimeout = pReceive->pReceiver->InitialOutstandingNakTimeout; // // If we had received an Spm earlier, then we may need to set // some of the Spm-specific options // pReceive->FECGroupSize = 1; // Default to non-parity mode pReceive->pReceiver->SessionNakType = NAK_TYPE_SELECTIVE; if ((pAddress->LastSpmSource) || (pAddress->FECOptions)) { if (pAddress->LastSpmSource) { pReceive->pReceiver->LastSpmSource = pAddress->LastSpmSource; } else { pReceive->pReceiver->LastSpmSource = pReceive->pReceiver->SenderIpAddress; } if (pAddress->FECOptions) { status = STATUS_SUCCESS; if (!(pReceive->pFECBuffer = PgmAllocMem ((pReceive->MaxFECPacketLength * pAddress->FECGroupSize * 2), PGM_TAG('3')))) { PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES allocating pFECBuffer, %d bytes\n", (pReceive->MaxFECPacketLength * pAddress->FECGroupSize * 2))); status = STATUS_INSUFFICIENT_RESOURCES; } else if (!NT_SUCCESS (status = CreateFECContext (&pReceive->FECContext, pAddress->FECGroupSize, FEC_MAX_BLOCK_SIZE, TRUE))) { PgmFreeMem (pReceive->pFECBuffer); pReceive->pFECBuffer = NULL; PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \ "CreateFECContext returned <%x>\n", status)); } if (!NT_SUCCESS (status)) { if (pReceive->pReceiver->pReceiveData) { PgmFreeMem (pReceive->pReceiver->pReceiveData); pReceive->pReceiver->pReceiveData = NULL; } pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; PgmUnlock (pAddress, OldIrq2); PgmUnlock (&PgmDynamicConfig, OldIrq1); PgmUnlock (pReceive, OldIrq); PgmIoComplete (pIrp, status, 0); *ppReceive = NULL; return (STATUS_DATA_NOT_ACCEPTED); } ASSERT (pAddress->FECGroupSize > 1); pReceive->FECGroupSize = pAddress->FECGroupSize; pReceive->FECOptions = pAddress->FECOptions; if (pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT) { pReceive->pReceiver->SessionNakType = NAK_TYPE_PARITY; } ExInitializeNPagedLookasideList (&pReceive->pReceiver->ParityContextLookaside, NULL, NULL, 0, (sizeof(tNAK_FORWARD_DATA) + ((pReceive->FECGroupSize-1) * sizeof(tPENDING_DATA))), PGM_TAG('2'), (USHORT) (pReceive->pReceiver->MaxPacketsBufferedInLookaside/pReceive->FECGroupSize)); } pAddress->LastSpmSource = pAddress->FECOptions = pAddress->FECGroupSize = 0; } // // Initialize our Connect info // Save the SourceId and Src port for this connection // PgmCopyMemory (&PortNum, &pPgmHeader->CommonHeader.SrcPort, sizeof (USHORT)); PgmCopyMemory (pReceive->TSI.GSI, pPgmHeader->CommonHeader.gSourceId, SOURCE_ID_LENGTH); pReceive->TSI.hPort = ntohs (PortNum); PGM_REFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_TDI_RCV_HANDLER, TRUE); PGM_REFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_TIMER_RUNNING, TRUE); PGM_REFERENCE_ADDRESS (pAddress, REF_ADDRESS_RECEIVE_ACTIVE, TRUE); pReceive->SessionFlags |= (PGM_SESSION_ON_TIMER | PGM_SESSION_FLAG_FIRST_PACKET); pReceive->pReceiver->pAddress = pAddress; pReceive->pReceiver->MaxBufferLength = pReceive->MaxMTULength + sizeof(tPOST_PACKET_FEC_CONTEXT); pReceive->pReceiver->DataBufferLookasideLength = pReceive->pReceiver->MaxBufferLength; pReceive->SessionFlags |= PGM_SESSION_DATA_FROM_LOOKASIDE; ExInitializeNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside, NULL, NULL, 0, pReceive->pReceiver->DataBufferLookasideLength, PGM_TAG ('D'), pReceive->pReceiver->MaxPacketsBufferedInLookaside); ExInitializeNPagedLookasideList (&pReceive->pReceiver->NonParityContextLookaside, NULL, NULL, 0, sizeof (tNAK_FORWARD_DATA), PGM_TAG ('2'), pReceive->pReceiver->MaxPacketsBufferedInLookaside); // // Set the NextODataSequenceNumber and FurthestKnownGroupSequenceNumber based // on this packet's Sequence # and the lateJoin option (if present) // Make sure all of the Sequence numbers are on group boundaries (if not, // set them at the start of the next group) // FirstODataSequenceNumber &= ~((SEQ_TYPE) pReceive->FECGroupSize - 1); if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_JOIN) { PacketOptions.LateJoinerSequence += (pReceive->FECGroupSize - 1); PacketOptions.LateJoinerSequence &= ~((SEQ_TYPE) pReceive->FECGroupSize - 1); pReceive->pReceiver->NextODataSequenceNumber = (SEQ_TYPE) PacketOptions.LateJoinerSequence; } else { // // There is no late joiner option // pReceive->pReceiver->NextODataSequenceNumber = FirstODataSequenceNumber; } pReceive->pReceiver->LastTrailingEdgeSeqNum = pReceive->pReceiver->FirstNakSequenceNumber = pReceive->pReceiver->NextODataSequenceNumber; pReceive->pReceiver->MaxOutstandingNakTimeout = pReceive->pReceiver->OutstandingNakTimeout; // // Set the FurthestKnown Sequence # and Allocate Nak contexts // pReceive->pReceiver->FurthestKnownGroupSequenceNumber = (pReceive->pReceiver->NextODataSequenceNumber- pReceive->FECGroupSize) & ~((SEQ_TYPE) pReceive->FECGroupSize - 1); pReceive->pReceiver->FurthestKnownSequenceNumber = pReceive->pReceiver->NextODataSequenceNumber-1; // // Since this is the first receive for this session, see if we need to // start the receive timer // InsertTailList (&PgmDynamicConfig.CurrentReceivers, &pReceive->pReceiver->Linkage); if (!(PgmDynamicConfig.GlobalFlags & PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING)) { pReceive->pReceiver->StartTickCount = PgmDynamicConfig.ReceiversTimerTickCount = 1; PgmDynamicConfig.LastReceiverTimeout.QuadPart = KeQueryInterruptTime (); PgmDynamicConfig.TimeoutGranularity.QuadPart = BASIC_TIMER_GRANULARITY_IN_MSECS * 10000; // 100ns if (!PgmDynamicConfig.TimeoutGranularity.QuadPart) { ASSERT (0); PgmDynamicConfig.TimeoutGranularity.QuadPart = 1; } PgmDynamicConfig.GlobalFlags |= PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING; PgmInitTimer (&PgmDynamicConfig.SessionTimer); PgmStartTimer (&PgmDynamicConfig.SessionTimer, BASIC_TIMER_GRANULARITY_IN_MSECS, ReceiveTimerTimeout, NULL); } else { pReceive->pReceiver->StartTickCount = PgmDynamicConfig.ReceiversTimerTickCount; } CheckAndAddNakRequests (pReceive, &FirstODataSequenceNumber, NULL, NAK_PENDING_0, TRUE); PgmTrace (LogStatus, ("PgmNewInboundConnection: " \ "New incoming connection, pAddress=<%p>, pReceive=<%p>, ThisSeq=<%d==>%d> (%sparity), StartSeq=<%d>\n", pAddress, pReceive, ntohl(ulData), (ULONG) FirstODataSequenceNumber, (pPgmHeader->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY ? "" : "non-"), (ULONG) pReceive->pReceiver->NextODataSequenceNumber)); PgmUnlock (pReceive, OldIrq2); PgmUnlock (pAddress, OldIrq1); PgmUnlock (&PgmDynamicConfig, OldIrq); // // We are ready to proceed! So, complete the client's Accept Irp // PgmIoComplete (pIrp, STATUS_SUCCESS, 0); // // If we had failed, we would already have returned before now! // *ppReceive = pReceive; return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS ProcessReceiveCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp, IN PVOID Context ) /*++ Routine Description: This routine handles the case when a datagram is too short and and Irp has to be passed back to the transport to get the rest of the datagram. The irp completes through here when full. Arguments: IN DeviceObject - unused. IN Irp - Supplies Irp that the transport has finished processing. IN Context - Supplies the pReceive - the connection data structure Return Value: The final status from the operation (success or an exception). --*/ { NTSTATUS status; PIRP pIoRequestPacket; ULONG BytesTaken; tRCV_COMPLETE_CONTEXT *pRcvContext = (tRCV_COMPLETE_CONTEXT *) Context; ULONG Offset = pRcvContext->BytesAvailable; PVOID pBuffer; ULONG SrcAddressLength; PVOID pSrcAddress; if (pBuffer = MmGetSystemAddressForMdlSafe (pIrp->MdlAddress, HighPagePriority)) { PgmTrace (LogAllFuncs, ("ProcessReceiveCompletionRoutine: " \ "pIrp=<%p>, pRcvBuffer=<%p>, Status=<%x> Length=<%d>\n", pIrp, Context, pIrp->IoStatus.Status, (ULONG) pIrp->IoStatus.Information)); SrcAddressLength = pRcvContext->SrcAddressLength; pSrcAddress = pRcvContext->pSrcAddress; // // just call the regular indication routine as if UDP had done it. // TdiRcvDatagramHandler (pRcvContext->pAddress, SrcAddressLength, pSrcAddress, 0, NULL, TDI_RECEIVE_NORMAL, (ULONG) pIrp->IoStatus.Information, (ULONG) pIrp->IoStatus.Information, &BytesTaken, pBuffer, &pIoRequestPacket); } else { PgmTrace (LogError, ("ProcessReceiveCompletionRoutine: ERROR -- " \ "MmGetSystemA... FAILed, pIrp=<%p>, pLocalBuffer=<%p>\n", pIrp, pRcvContext)); } // // Free the Irp and Mdl and Buffer // IoFreeMdl (pIrp->MdlAddress); pIrp->MdlAddress = NULL; IoFreeIrp (pIrp); PgmFreeMem (pRcvContext); return (STATUS_MORE_PROCESSING_REQUIRED); } #ifdef DROP_DBG ULONG MinDropInterval = 10; ULONG MaxDropInterval = 10; // ULONG DropCount = 10; ULONG DropCount = -1; #endif // DROP_DBG //---------------------------------------------------------------------------- NTSTATUS TdiRcvDatagramHandler( IN PVOID pDgramEventContext, IN INT SourceAddressLength, IN PVOID pSourceAddress, IN INT OptionsLength, #if(WINVER > 0x0500) IN TDI_CMSGHDR *pControlData, #else IN PVOID *pControlData, #endif // WINVER IN ULONG ReceiveDatagramFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *pBytesTaken, IN PVOID pTsdu, OUT PIRP *ppIrp ) /*++ Routine Description: This routine is the handler for receiving all Pgm packets from the transport (protocol == IPPROTO_RM) Arguments: IN pDgramEventContext -- Our context (pAddress) IN SourceAddressLength -- Length of source address IN pSourceAddress -- Address of remote host IN OptionsLength IN pControlData -- ControlData from transport IN ReceiveDatagramFlags -- Flags set by the transport for this packet IN BytesIndicated -- Bytes in this indicate IN BytesAvailable -- total bytes available with the transport OUT pBytesTaken -- bytes taken by us IN pTsdu -- data packet ptr OUT ppIrp -- pIrp if more processing required Return Value: NTSTATUS - Final status of the call --*/ { NTSTATUS status; tCOMMON_HEADER UNALIGNED *pPgmHeader; tBASIC_SPM_PACKET_HEADER UNALIGNED *pSpmPacket; tCOMMON_SESSION_CONTEXT *pSession; PLIST_ENTRY pEntry; PGMLockHandle OldIrq, OldIrq1; USHORT LocalSessionPort, PacketSessionPort; tTSI TSI; PVOID pFECBuffer; UCHAR PacketType; IP_PKTINFO *pPktInfo; PIRP pLocalIrp = NULL; PMDL pLocalMdl = NULL; tRCV_COMPLETE_CONTEXT *pRcvBuffer = NULL; ULONG XSum, BufferLength = 0; IPV4Header *pIp = (IPV4Header *) pTsdu; PTA_IP_ADDRESS pIpAddress = (PTA_IP_ADDRESS) pSourceAddress; tADDRESS_CONTEXT *pAddress = (tADDRESS_CONTEXT *) pDgramEventContext; *pBytesTaken = 0; // Initialize the Bytes Taken! *ppIrp = NULL; #ifdef DROP_DBG // // Drop OData packets only for now! // pPgmHeader = (tCOMMON_HEADER UNALIGNED *) (((PUCHAR)pIp) + (pIp->HeaderLength * 4)); PacketType = pPgmHeader->Type & 0x0f; if ((PacketType == PACKET_TYPE_ODATA) && !(((tBASIC_DATA_PACKET_HEADER *) pPgmHeader)->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY) && !(--DropCount)) { ULONG SequenceNumber; LARGE_INTEGER TimeValue; KeQuerySystemTime (&TimeValue); DropCount = MinDropInterval + ((TimeValue.LowTime >> 8) % (MaxDropInterval - MinDropInterval + 1)); /* PgmCopyMemory (&SequenceNumber, &((tBASIC_DATA_PACKET_HEADER *) pPgmHeader)->DataSequenceNumber, sizeof (ULONG)); DbgPrint("TdiRcvDatagramHandler: Dropping packet, %s SeqNum = %d!\n", (((tBASIC_DATA_PACKET_HEADER *) pPgmHeader)->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY ? "PARITY" : "DATA"), ntohl (SequenceNumber)); */ return (STATUS_DATA_NOT_ACCEPTED); } #endif // DROP_DBG PgmLock (&PgmDynamicConfig, OldIrq); if (BytesIndicated > PgmDynamicConfig.MaxMTU) { PgmDynamicConfig.MaxMTU = BytesIndicated; } if (!PGM_VERIFY_HANDLE (pAddress, PGM_VERIFY_ADDRESS)) { PgmTrace (LogPath, ("TdiRcvDatagramHandler: " \ "Invalid Address handle=<%p>\n", pAddress)); PgmUnlock (&PgmDynamicConfig, OldIrq); return (STATUS_DATA_NOT_ACCEPTED); } // // Now, Reference the Address so that it cannot go away // while we are processing it! // PGM_REFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER, FALSE); PgmUnlock (&PgmDynamicConfig, OldIrq); // // If we do not have the complete datagram, then pass an Irp down to retrieve it // if ((BytesAvailable != BytesIndicated) && !(ReceiveDatagramFlags & TDI_RECEIVE_ENTIRE_MESSAGE)) { ASSERT (BytesIndicated <= BytesAvailable); // // Build an irp to do the receive with and attach a buffer to it. // BufferLength = sizeof (tRCV_COMPLETE_CONTEXT) + BytesAvailable + SourceAddressLength; BufferLength = ((BufferLength + 3)/sizeof(ULONG)) * sizeof(ULONG); if ((pLocalIrp = IoAllocateIrp (pgPgmDevice->pPgmDeviceObject->StackSize, FALSE)) && (pRcvBuffer = PgmAllocMem (BufferLength, PGM_TAG('3'))) && (pLocalMdl = IoAllocateMdl (&pRcvBuffer->BufferData, BytesAvailable, FALSE, FALSE, NULL))) { pLocalIrp->MdlAddress = pLocalMdl; MmBuildMdlForNonPagedPool (pLocalMdl); // Map the pages in memory... TdiBuildReceiveDatagram (pLocalIrp, pAddress->pDeviceObject, pAddress->pFileObject, ProcessReceiveCompletionRoutine, pRcvBuffer, pLocalMdl, BytesAvailable, NULL, NULL, 0); // (ULONG) TDI_RECEIVE_NORMAL) ? // make the next stack location the current one. Normally IoCallDriver // would do this but we are not going through IoCallDriver here, since the // Irp is just passed back with RcvIndication. // ASSERT (pLocalIrp->CurrentLocation > 1); IoSetNextIrpStackLocation (pLocalIrp); // // save the source address and length in the buffer for later // indication back to this routine. // pRcvBuffer->pAddress = pAddress; pRcvBuffer->SrcAddressLength = SourceAddressLength; pRcvBuffer->pSrcAddress = (PVOID) ((PUCHAR)&pRcvBuffer->BufferData + BytesAvailable); PgmCopyMemory (pRcvBuffer->pSrcAddress, pSourceAddress, SourceAddressLength); *pBytesTaken = 0; *ppIrp = pLocalIrp; status = STATUS_MORE_PROCESSING_REQUIRED; PgmTrace (LogAllFuncs, ("TdiRcvDatagramHandler: " \ "BytesI=<%d>, BytesA=<%d>, Flags=<%x>, pIrp=<%p>\n", BytesIndicated, BytesAvailable, ReceiveDatagramFlags, pLocalIrp)); } else { // Cleanup on failure: if (pLocalIrp) { IoFreeIrp (pLocalIrp); } if (pRcvBuffer) { PgmFreeMem (pRcvBuffer); } status = STATUS_DATA_NOT_ACCEPTED; PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \ "INSUFFICIENT_RESOURCES, BuffLen=<%d>, pIrp=<%p>, pBuff=<%p>\n", BufferLength, pLocalIrp, pRcvBuffer)); } PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER); return (status); } // // Now that we have the complete datagram, verify that it is valid // First line of defense against bad packets. // if ((BytesIndicated < (sizeof(IPV4Header) + sizeof(tCOMMON_HEADER))) || (pIp->Version != 4) || (BytesIndicated < ((pIp->HeaderLength<<2) + sizeof(tCOMMON_HEADER)))) { // // Need to get at least our header from transport! // PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \ "IPver=<%d>, BytesI=<%d>, Min=<%d>, AddrType=<%d>\n", pIp->Version, BytesIndicated, (sizeof(IPV4Header) + sizeof(tBASIC_DATA_PACKET_HEADER)), pIpAddress->Address[0].AddressType)); ASSERT (0); PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER); return (STATUS_DATA_NOT_ACCEPTED); } pPgmHeader = (tCOMMON_HEADER UNALIGNED *) (((PUCHAR)pIp) + (pIp->HeaderLength * 4)); BytesIndicated -= (pIp->HeaderLength * 4); BytesAvailable -= (pIp->HeaderLength * 4); // // Now, Verify Checksum // if ((XSum = tcpxsum (0, (CHAR *) pPgmHeader, BytesIndicated)) != 0xffff) { // // Need to get at least our header from transport! // PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \ "Bad Checksum on Pgm Packet (type=<%x>)! XSum=<%x> -- Rejecting packet\n", pPgmHeader->Type, XSum)); PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER); return (STATUS_DATA_NOT_ACCEPTED); } // // Now, determine the TSI, i.e. GSI (from packet) + TSIPort (below) // PacketType = pPgmHeader->Type & 0x0f; if ((PacketType == PACKET_TYPE_NAK) || (PacketType == PACKET_TYPE_NNAK) || (PacketType == PACKET_TYPE_SPMR) || (PacketType == PACKET_TYPE_POLR)) { PgmCopyMemory (&PacketSessionPort, &pPgmHeader->SrcPort, sizeof (USHORT)); PgmCopyMemory (&TSI.hPort, &pPgmHeader->DestPort, sizeof (USHORT)); } else { PgmCopyMemory (&PacketSessionPort, &pPgmHeader->DestPort, sizeof (USHORT)); PgmCopyMemory (&TSI.hPort, &pPgmHeader->SrcPort, sizeof (USHORT)); } PacketSessionPort = ntohs (PacketSessionPort); TSI.hPort = ntohs (TSI.hPort); PgmCopyMemory (&TSI.GSI, &pPgmHeader->gSourceId, SOURCE_ID_LENGTH); // // If this packet is for a different session port, drop it // if (pAddress->ReceiverMCastAddr) { LocalSessionPort = pAddress->ReceiverMCastPort; } else { LocalSessionPort = pAddress->SenderMCastPort; } if (LocalSessionPort != PacketSessionPort) { PgmTrace (LogPath, ("TdiRcvDatagramHandler: " \ "Dropping packet for different Session port, <%x>!=<%x>!\n", LocalSessionPort, PacketSessionPort)); PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER); return (STATUS_DATA_NOT_ACCEPTED); } // // Now check if this receive is for an active connection // pSession = NULL; PgmLock (pAddress, OldIrq); // So that the list cannot change! pEntry = &pAddress->AssociatedConnections; while ((pEntry = pEntry->Flink) != &pAddress->AssociatedConnections) { pSession = CONTAINING_RECORD (pEntry, tCOMMON_SESSION_CONTEXT, Linkage); PgmLock (pSession, OldIrq1); if ((PGM_VERIFY_HANDLE2 (pSession, PGM_VERIFY_SESSION_RECEIVE, PGM_VERIFY_SESSION_SEND)) && (pSession->TSI.ULLTSI == TSI.ULLTSI) && !(pSession->SessionFlags & (PGM_SESSION_CLIENT_DISCONNECTED | PGM_SESSION_TERMINATED_ABORT))) { if (pSession->pReceiver) { PGM_REFERENCE_SESSION_RECEIVE (pSession, REF_SESSION_TDI_RCV_HANDLER, TRUE); if ((pSession->FECOptions) && (BytesIndicated > pSession->MaxMTULength)) { if (pFECBuffer = PgmAllocMem (((BytesIndicated+sizeof(tPOST_PACKET_FEC_CONTEXT)-sizeof(USHORT)) *pSession->FECGroupSize*2), PGM_TAG('3'))) { ASSERT (pSession->pFECBuffer); PgmFreeMem (pSession->pFECBuffer); pSession->pFECBuffer = pFECBuffer; pSession->MaxMTULength = (USHORT) BytesIndicated; pSession->MaxFECPacketLength = pSession->MaxMTULength + sizeof (tPOST_PACKET_FEC_CONTEXT) - sizeof (USHORT); } else { PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \ "STATUS_INSUFFICIENT_RESOURCES -- pFECBuffer=<%d> bytes\n", (BytesIndicated+sizeof(tPOST_PACKET_FEC_CONTEXT)-sizeof(USHORT)))); pSession = NULL; pSession->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; } } PgmUnlock (pSession, OldIrq1); break; } ASSERT (pSession->pSender); PGM_REFERENCE_SESSION_SEND (pSession, REF_SESSION_TDI_RCV_HANDLER, TRUE); PgmUnlock (pSession, OldIrq1); break; } PgmUnlock (pSession, OldIrq1); pSession = NULL; } PgmUnlock (pAddress, OldIrq); if (!pSession) { // We should drop this packet because we received this either because // we may have a loopback session, or we have a listen but this // is not an OData packet if ((pIpAddress->TAAddressCount != 1) || (pIpAddress->Address[0].AddressLength != TDI_ADDRESS_LENGTH_IP) || (pIpAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_IP)) { PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \ "[1] Bad AddrType=<%d>\n", pIpAddress->Address[0].AddressType)); PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER); return (STATUS_DATA_NOT_ACCEPTED); } status = STATUS_DATA_NOT_ACCEPTED; // // New sessions will be accepted only if we are a receiver // Also, new sessions will always be initiated only with an OData packet // Also, verify that the client has posted a connect handler! // if ((pAddress->ReceiverMCastAddr) && (pAddress->ConEvContext)) { if ((PacketType == PACKET_TYPE_ODATA) && (!(pPgmHeader->Options & PACKET_HEADER_OPTIONS_PARITY))) { // // This is a new incoming connection, so see if the // client accepts it. // status = PgmNewInboundConnection (pAddress, SourceAddressLength, pIpAddress, ReceiveDatagramFlags, (tBASIC_DATA_PACKET_HEADER UNALIGNED *) pPgmHeader, BytesIndicated, &pSession); if (!NT_SUCCESS (status)) { PgmTrace (LogAllFuncs, ("TdiRcvDatagramHandler: " \ "pAddress=<%p> FAILed to accept new connection, PacketType=<%x>, status=<%x>\n", pAddress, PacketType, status)); } } else if ((PacketType == PACKET_TYPE_SPM) && (BytesIndicated >= sizeof(tBASIC_SPM_PACKET_HEADER))) { ProcessSpmPacket (pAddress, NULL, // This will signify that we do not have a connection yet BytesIndicated, (tBASIC_SPM_PACKET_HEADER UNALIGNED *) pPgmHeader); } } if (!NT_SUCCESS (status)) { PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER); return (STATUS_DATA_NOT_ACCEPTED); } } #if(WINVER > 0x0500) if ((pAddress->Flags & PGM_ADDRESS_WAITING_FOR_NEW_INTERFACE) && (pAddress->Flags & PGM_ADDRESS_LISTEN_ON_ALL_INTERFACES) && (ReceiveDatagramFlags & TDI_RECEIVE_CONTROL_INFO)) { // // See if we can Enqueue the stop listening request // PgmLock (&PgmDynamicConfig, OldIrq); PgmLock (pAddress, OldIrq1); pPktInfo = (IP_PKTINFO*) TDI_CMSG_DATA (pControlData); PGM_REFERENCE_ADDRESS (pAddress, REF_ADDRESS_STOP_LISTENING, TRUE); if (STATUS_SUCCESS == PgmQueueForDelayedExecution (StopListeningOnAllInterfacesExcept, pAddress, ULongToPtr (pPktInfo->ipi_ifindex), NULL, TRUE)) { pAddress->Flags &= ~PGM_ADDRESS_WAITING_FOR_NEW_INTERFACE; PgmUnlock (pAddress, OldIrq1); PgmUnlock (&PgmDynamicConfig, OldIrq); } else { PgmUnlock (pAddress, OldIrq1); PgmUnlock (&PgmDynamicConfig, OldIrq); PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_STOP_LISTENING); } } #endif // WINVER // // Now, handle the packet appropriately // // Use fast Path for Data packets! // if ((PacketType == PACKET_TYPE_ODATA) || (PacketType == PACKET_TYPE_RDATA)) { if (pAddress->ReceiverMCastAddr) { if (PacketType == PACKET_TYPE_ODATA) { pSession->pReceiver->NumODataPacketsReceived++; } else { pSession->pReceiver->NumRDataPacketsReceived++; } pSession->pReceiver->LastSessionTickCount = PgmDynamicConfig.ReceiversTimerTickCount; pSession->TotalBytes += BytesIndicated; pSession->TotalPacketsReceived++; status = ProcessDataPacket (pAddress, pSession, BytesIndicated, (tBASIC_DATA_PACKET_HEADER UNALIGNED *) pPgmHeader, PacketType); } else { PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \ "Received Data packet, not on Receiver session! pSession=<%p>\n", pSession)); status = STATUS_DATA_NOT_ACCEPTED; } } else { status = PgmProcessIncomingPacket (pAddress, pSession, SourceAddressLength, pIpAddress, BytesIndicated, pPgmHeader, PacketType); } PgmTrace (LogAllFuncs, ("TdiRcvDatagramHandler: " \ "PacketType=<%x> for pSession=<%p> BytesI=<%d>, BytesA=<%d>, status=<%x>\n", PacketType, pSession, BytesIndicated, BytesAvailable, status)); if (pSession->pSender) { PGM_DEREFERENCE_SESSION_SEND (pSession, REF_SESSION_TDI_RCV_HANDLER); } else { ASSERT (pSession->pReceiver); PGM_DEREFERENCE_SESSION_RECEIVE (pSession, REF_SESSION_TDI_RCV_HANDLER); } PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER); // // Only acceptable return codes are STATUS_SUCCESS and STATUS_DATA_NOT_ACCPETED // (STATUS_MORE_PROCESSING_REQUIRED is not valid here because we have no Irp). // if (STATUS_SUCCESS != status) { status = STATUS_DATA_NOT_ACCEPTED; } return (status); } //---------------------------------------------------------------------------- VOID PgmCancelReceiveIrp( IN PDEVICE_OBJECT DeviceContext, IN PIRP pIrp ) /*++ Routine Description: This routine handles the cancelling of a Receive Irp. It must release the cancel spin lock before returning re: IoCancelIrp(). Arguments: Return Value: None --*/ { PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation (pIrp); tRECEIVE_SESSION *pReceive = (tRECEIVE_SESSION *) pIrpSp->FileObject->FsContext; PGMLockHandle OldIrq; PLIST_ENTRY pEntry; if (!PGM_VERIFY_HANDLE (pReceive, PGM_VERIFY_SESSION_RECEIVE)) { IoReleaseCancelSpinLock (pIrp->CancelIrql); PgmTrace (LogError, ("PgmCancelReceiveIrp: ERROR -- " \ "pIrp=<%p> pReceive=<%p>, pAddress=<%p>\n", pIrp, pReceive, (pReceive ? pReceive->pReceiver->pAddress : NULL))); return; } PgmLock (pReceive, OldIrq); // // See if we are actively receiving // if (pIrp == pReceive->pReceiver->pIrpReceive) { pIrp->IoStatus.Information = pReceive->pReceiver->BytesInMdl; pIrp->IoStatus.Status = STATUS_CANCELLED; pReceive->pReceiver->BytesInMdl = pReceive->pReceiver->TotalBytesInMdl = 0; pReceive->pReceiver->pIrpReceive = NULL; PgmUnlock (pReceive, OldIrq); IoReleaseCancelSpinLock (pIrp->CancelIrql); IoCompleteRequest (pIrp,IO_NETWORK_INCREMENT); return; } // // We are not actively receiving, so see if this Irp is // in our Irps list // pEntry = &pReceive->pReceiver->ReceiveIrpsList; while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->ReceiveIrpsList) { if (pEntry == &pIrp->Tail.Overlay.ListEntry) { RemoveEntryList (pEntry); pIrp->IoStatus.Status = STATUS_CANCELLED; pIrp->IoStatus.Information = 0; PgmUnlock (pReceive, OldIrq); IoReleaseCancelSpinLock (pIrp->CancelIrql); IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT); return; } } // // If we have reached here, then the Irp must already // be in the process of being completed! // PgmUnlock (pReceive, OldIrq); IoReleaseCancelSpinLock (pIrp->CancelIrql); } //---------------------------------------------------------------------------- NTSTATUS PgmReceive( IN tPGM_DEVICE *pPgmDevice, IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) /*++ Routine Description: This routine is called via dispatch by the client to post a Receive pIrp Arguments: IN pPgmDevice -- Pgm's Device object context IN pIrp -- Client's request Irp IN pIrpSp -- current request's stack pointer Return Value: NTSTATUS - Final status of the request --*/ { NTSTATUS status; PGMLockHandle OldIrq, OldIrq1, OldIrq2, OldIrq3; tADDRESS_CONTEXT *pAddress = NULL; tRECEIVE_SESSION *pReceive = (tRECEIVE_SESSION *) pIrpSp->FileObject->FsContext; PTDI_REQUEST_KERNEL_RECEIVE pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE) &pIrpSp->Parameters; PgmLock (&PgmDynamicConfig, OldIrq); IoAcquireCancelSpinLock (&OldIrq1); // // Verify that the connection is valid and is associated with an address // if ((!PGM_VERIFY_HANDLE (pReceive, PGM_VERIFY_SESSION_RECEIVE)) || (!(pAddress = pReceive->pAssociatedAddress)) || (!PGM_VERIFY_HANDLE (pAddress, PGM_VERIFY_ADDRESS))) { PgmTrace (LogError, ("PgmReceive: ERROR -- " \ "Invalid Handles pReceive=<%p>, pAddress=<%p>\n", pReceive, pAddress)); status = STATUS_INVALID_HANDLE; } else if (pReceive->SessionFlags & PGM_SESSION_CLIENT_DISCONNECTED) { PgmTrace (LogPath, ("PgmReceive: " \ "Receive Irp=<%p> was posted after session has been Disconnected, pReceive=<%p>, pAddress=<%p>\n", pIrp, pReceive, pAddress)); status = STATUS_CANCELLED; } else if (!pClientParams->ReceiveLength) { ASSERT (0); PgmTrace (LogError, ("PgmReceive: ERROR -- " \ "Invalid Handles pReceive=<%p>, pAddress=<%p>\n", pReceive, pAddress)); status = STATUS_UNSUCCESSFUL; } else { status = STATUS_SUCCESS; } if (!NT_SUCCESS (status)) { IoReleaseCancelSpinLock (OldIrq1); PgmUnlock (&PgmDynamicConfig, OldIrq); pIrp->IoStatus.Information = 0; return (status); } PgmLock (pAddress, OldIrq2); PgmLock (pReceive, OldIrq3); if (!NT_SUCCESS (PgmCheckSetCancelRoutine (pIrp, PgmCancelReceiveIrp, TRUE))) { PgmUnlock (pReceive, OldIrq3); PgmUnlock (pAddress, OldIrq2); IoReleaseCancelSpinLock (OldIrq1); PgmUnlock (&PgmDynamicConfig, OldIrq); PgmTrace (LogError, ("PgmReceive: ERROR -- " \ "Could not set Cancel routine on receive Irp=<%p>, pReceive=<%p>, pAddress=<%p>\n", pIrp, pReceive, pAddress)); return (STATUS_CANCELLED); } IoReleaseCancelSpinLock (OldIrq3); PGM_REFERENCE_ADDRESS (pAddress, REF_ADDRESS_CLIENT_RECEIVE, TRUE); PGM_REFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_CLIENT_RECEIVE, TRUE); PgmUnlock (&PgmDynamicConfig, OldIrq2); PgmTrace (LogAllFuncs, ("PgmReceive: " \ "Client posted ReceiveIrp = <%p> for pReceive=<%p>\n", pIrp, pReceive)); InsertTailList (&pReceive->pReceiver->ReceiveIrpsList, &pIrp->Tail.Overlay.ListEntry); pReceive->SessionFlags &= ~PGM_SESSION_WAIT_FOR_RECEIVE_IRP; // // Now, try to indicate any data which may still be pending // // if (!(pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED)) { status = CheckIndicatePendedData (pAddress, pReceive, &OldIrq, &OldIrq1); } PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); PGM_DEREFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_CLIENT_RECEIVE); PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_CLIENT_RECEIVE); return (STATUS_PENDING); } //----------------------------------------------------------------------------