/*++ 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" 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 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].pDataPacket); if (pNak->pPendingData[i].PacketIndex < pReceive->FECGroupSize) { j++; } else { k++; } PgmFreeMem (pNak->pPendingData[i].pDataPacket); } 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 UnUsed ) { LIST_ENTRY NaksList, DataList; tNAK_FORWARD_DATA *pNak; LIST_ENTRY *pEntry; ULONG NumBufferedData = 0; ULONG NumNaks = 0; PGMLockHandle OldIrq; ASSERT (pReceive->pReceiver); PgmLock (pReceive, OldIrq); 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); while (!IsListEmpty (&pReceive->pReceiver->PendingNaksList)) { pEntry = RemoveHeadList (&pReceive->pReceiver->PendingNaksList); InitializeListHead (pEntry); } 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++; } PgmLog (PGM_LOG_INFORM_STATUS, DBG_SEND, "CleanupPendingNaks", "pReceive=<%p>, NumBufferedData=<%d=%d>, TotalDataPackets=<%d>, NumNaks=<%d * %d>\n", pReceive, (ULONG) pReceive->pReceiver->NumPacketGroupsPendingClient, NumBufferedData, (ULONG) pReceive->pReceiver->TotalDataPacketsBuffered, NumNaks, (ULONG) pReceive->FECGroupSize); // ASSERT (NumBufferedData == pReceive->pReceiver->NumPacketGroupsPendingClient); pReceive->pReceiver->NumPacketGroupsPendingClient = 0; 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; // // Don't abort if we are currently indicating, or if we have // already aborted! // if (pReceive->SessionFlags & (PGM_SESSION_FLAG_IN_INDICATE | PGM_SESSION_DISCONNECT_INDICATED)) { return (TRUE); } if ((pReceive->SessionFlags & PGM_SESSION_TERMINATED_ABORT) || ((pReceive->SessionFlags & PGM_SESSION_TERMINATED_GRACEFULLY) && (IsListEmpty (&pReceive->pReceiver->BufferedDataList)) && SEQ_GEQ (pReceive->pReceiver->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_DISCONNECT_INDICATED; 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); } PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "CheckIndicateDisconnect", "Disconnecting pReceive=<%p:%p>, with %s\n", pReceive, pReceive->ClientSessionContext, (DisconnectFlag == TDI_DISCONNECT_RELEASE ? "TDI_DISCONNECT_RELEASE":"TDI_DISCONNECT_ABORT")); 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, NULL, FALSE)) { CleanupPendingNaks (pReceive, (PVOID) TRUE, NULL); } 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! // PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "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; do { if (pOptionHeader->OptionLength > BytesLeft) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "Incorrectly formatted Options: OptionLength=<%d> > BytesLeft=<%d>, NumProcessed=<%d>\n", pOptionHeader->OptionLength, BytesLeft, NumOptionsProcessed); status = STATUS_UNSUCCESSFUL; break; } status = STATUS_SUCCESS; // By default 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))) { PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions", "NAK_LIST: Num Naks=<%d>\n", (pOptionHeader->OptionLength-4)/4); if (pNaksList) { ProcessNakOption (pOptionHeader, pNaksList); } else { ASSERT (0); } OptionsFlags |= PGM_OPTION_FLAG_NAK_LIST; } else { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "NAK_LIST: PacketType=<%x>, Length=<0x%x>, pPacketOptions=<%x>\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): { status = STATUS_UNSUCCESSFUL; 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; status = STATUS_SUCCESS; OptionsFlags |= PGM_OPTION_FLAG_FRAGMENT; } else { MessageFirstSequence = ntohl (pOptionsData[0]); MessageOffset = ntohl (pOptionsData[1]); MessageLength = ntohl (pOptionsData[2]); if ((MessageLength) && (MessageOffset <= MessageLength)) { PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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; } status = STATUS_SUCCESS; OptionsFlags |= PGM_OPTION_FLAG_FRAGMENT; } else { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "FRAGMENT: MsgOffset/Length=<%d/%d>\n", MessageOffset, MessageLength); } } } else { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "FRAGMENT: OptionLength=<%d> != PGM_PACKET_OPT_FRAGMENT_LENGTH=<%d>\n", pOptionHeader->OptionLength, PGM_PACKET_OPT_FRAGMENT_LENGTH); } break; } case (PACKET_OPTION_JOIN): { if (pOptionHeader->OptionLength == PGM_PACKET_OPT_JOIN_LENGTH) { PgmCopyMemory (pOptionsData, (pOptionHeader + 1), sizeof(ULONG)); PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions", "JOIN: LateJoinerSeq=<%d>\n", ntohl (pOptionsData[0])); if (pPacketOptions) { pPacketOptions->LateJoinerSequence = ntohl (pOptionsData[0]); } OptionsFlags |= PGM_OPTION_FLAG_JOIN; } else { status = STATUS_UNSUCCESSFUL; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "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) { PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions", "SYN\n"); OptionsFlags |= PGM_OPTION_FLAG_SYN; } else { status = STATUS_UNSUCCESSFUL; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "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) { PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions", "FIN\n"); OptionsFlags |= PGM_OPTION_FLAG_FIN; } else { status = STATUS_UNSUCCESSFUL; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "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) { PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions", "RST\n"); OptionsFlags |= PGM_OPTION_FLAG_RST; } else { status = STATUS_UNSUCCESSFUL; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "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)); PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "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)); PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "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)); PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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 { status = STATUS_UNSUCCESSFUL; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "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): { PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "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: { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "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); status = STATUS_UNSUCCESSFUL; // Init for next option! } 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 { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions", "BytesLeft=<%d> + TotalOptionsLength=<%d> != BytesAvailable=<%d>\n", BytesLeft, TotalOptionsLength, BytesAvailable); status = STATUS_INVALID_BUFFER_SIZE; } } PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "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; // // 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) { PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "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); PgmFreeMem (pNak->pPendingData[i].pDataPacket); 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 ) { SEQ_TYPE SequencesInWindow = 1 + LeadingEdgeSeqNumber - TrailingEdgeSeqNumber; if (SEQ_GT (SequencesInWindow, pReceive->pReceiver->MaxSequencesInWindow)) { pReceive->pReceiver->MaxSequencesInWindow = SequencesInWindow; } if (TrailingEdgeSeqNumber) { if ((!pReceive->pReceiver->MinSequencesInWindow) || SEQ_LT (SequencesInWindow, pReceive->pReceiver->MinSequencesInWindow)) { pReceive->pReceiver->MinSequencesInWindow = SequencesInWindow; } pReceive->pReceiver->StatSumOfWindowSeqs += SequencesInWindow; pReceive->pReceiver->NumWindowSamples++; } } VOID UpdateSampleTimeWindowInformation( IN tRECEIVE_SESSION *pReceive ) { ULONGLONG NcfRDataTimeout; // // No need to update if there is no data // if (!pReceive->RateKBitsPerSecLast || !pReceive->pReceiver->NumWindowSamples || // Avoid divide by 0 error !pReceive->TotalPacketsInLastInterval) // Avoid divide by 0 error { return; } ASSERT (INITIAL_NAK_OUTSTANDING_TIMEOUT_MSECS >= BASIC_TIMER_GRANULARITY_IN_MSECS); // // Now, update the window information // if (pReceive->pReceiver->StatSumOfWindowSeqs) { pReceive->pReceiver->AverageSequencesInWindow = pReceive->pReceiver->StatSumOfWindowSeqs / pReceive->pReceiver->NumWindowSamples; } if (pReceive->pReceiver->AverageSequencesInWindow) { pReceive->pReceiver->WindowSizeLastInMSecs = ((pReceive->pReceiver->AverageSequencesInWindow * pReceive->TotalBytes) << LOG2_BITS_PER_BYTE) / (pReceive->TotalPacketsInLastInterval * pReceive->RateKBitsPerSecLast); } else { pReceive->pReceiver->WindowSizeLastInMSecs = ((pReceive->pReceiver->MaxSequencesInWindow * pReceive->TotalBytes) << LOG2_BITS_PER_BYTE) / (pReceive->TotalPacketsInLastInterval * pReceive->RateKBitsPerSecLast); } pReceive->pReceiver->MaxRDataResponseTCFromWindow = pReceive->pReceiver->WindowSizeLastInMSecs / (NCF_WAITING_RDATA_MAX_RETRIES * BASIC_TIMER_GRANULARITY_IN_MSECS); // // Now, update the NcfRData timeout information // if (pReceive->pReceiver->StatSumOfNcfRDataTicks && pReceive->pReceiver->NumNcfRDataTicksSamples) { pReceive->pReceiver->AverageNcfRDataResponseTC = pReceive->pReceiver->StatSumOfNcfRDataTicks / pReceive->pReceiver->NumNcfRDataTicksSamples; } if (pReceive->pReceiver->AverageNcfRDataResponseTC) { NcfRDataTimeout = (pReceive->pReceiver->AverageNcfRDataResponseTC + pReceive->pReceiver->MaxOutstandingNakTimeout) >> 1; if (NcfRDataTimeout > (pReceive->pReceiver->AverageNcfRDataResponseTC << 1)) { NcfRDataTimeout = pReceive->pReceiver->AverageNcfRDataResponseTC << 1; } if (NcfRDataTimeout > INITIAL_NAK_OUTSTANDING_TIMEOUT_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS) { pReceive->pReceiver->OutstandingNakTimeout = NcfRDataTimeout; } else { pReceive->pReceiver->OutstandingNakTimeout = INITIAL_NAK_OUTSTANDING_TIMEOUT_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS; } } } //---------------------------------------------------------------------------- VOID RemoveRedundantNaks( 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; } PgmFreeMem (pNak->pPendingData[i].pDataPacket); 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; } PgmFreeMem (pNak->pPendingData[i].pDataPacket); 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 (((pNak->NumDataPackets + pNak->NumParityPackets) >= pNak->PacketsInGroup) || ((pNak->NextIndexToIndicate + pNak->NumDataPackets) >= pNak->PacketsInGroup)) { ASSERT ((!fEliminateExtraParityPackets) || (!IsListEmpty (&pNak->PendingLinkage))); RemoveEntryList (&pNak->PendingLinkage); InitializeListHead (&pNak->PendingLinkage); } } //---------------------------------------------------------------------------- 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 // PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_SEND, "PgmSendNakCompletion", "SUCCEEDED\n"); } else { PgmLog (PGM_LOG_ERROR, DBG_SEND, "PgmSendNakCompletion", "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'))))) { PgmLog (PGM_LOG_ERROR, DBG_SEND, "PgmSendNak", "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->TSIPort); 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->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); PgmLog (PGM_LOG_INFORM_PATH, DBG_SEND, "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++) { PgmLog (PGM_LOG_INFORM_PATH, DBG_SEND, "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); 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); ASSERT (NT_SUCCESS (status)); PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "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 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; SEQ_TYPE LastSequenceNumber; PGMLockHandle OldIrq, OldIrq1; ULONG NumMissingPackets, TotalSeqsNacked = 0; BOOLEAN fSendSelectiveNak, fSendParityNak; UCHAR i, j; ULONG NumPendingNaks = 0; ULONG NumOutstandingNaks = 0; if ((!pReceive->pReceiver->LastSpmSource) || ((pReceive->pReceiver->DataPacketsPendingNaks <= OUT_OF_ORDER_PACKETS_BEFORE_NAK) && ((pReceive->pReceiver->LastNakSendTime + (NAK_MAX_WAIT_TIMEOUT_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS)) > PgmDynamicConfig.ReceiversTimerTickCount))) { PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "CheckSendPendingNaks", "No Naks to send for pReceive=<%p>, LastSpmSource=<%x>, NumDataPackets=<%d>, LastSendTime=<%d:%d>, Current=<%d:%d>\n", pReceive, pReceive->pReceiver->LastSpmSource, pReceive->pReceiver->DataPacketsPendingNaks, pReceive->pReceiver->LastNakSendTime+(NAK_MAX_WAIT_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')))) { PgmLog (PGM_LOG_ERROR, DBG_SEND, "CheckSendPendingNaks", "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); fSendSelectiveNak = fSendParityNak = FALSE; pEntry = &pReceive->pReceiver->PendingNaksList; while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->PendingNaksList) { pNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, PendingLinkage); NumMissingPackets = pNak->PacketsInGroup - (pNak->NumDataPackets + pNak->NumParityPackets); ASSERT (NumMissingPackets); // // if this Nak is outside the trailing window, then we are hosed! // if (SEQ_GT (pReceive->pReceiver->LastTrailingEdgeSeqNum, pNak->SequenceNumber)) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CheckSendPendingNaks", "Sequence # [%d] out of trailing edge <%d>, NumNcfs received=<%d>\n", (ULONG) pNak->SequenceNumber, (ULONG) pReceive->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) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CheckSendPendingNaks", "Pending Nak for Sequence # [%d] Timed out! Num Ncfs received=<%d>, Window=<%d--%d> ( %d seqs)\n", (ULONG) pNak->SequenceNumber, pNak->WaitingNcfRetries, (ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber, (ULONG) (1+pReceive->pReceiver->FurthestKnownGroupSequenceNumber- pReceive->pReceiver->LastTrailingEdgeSeqNum)); pReceive->SessionFlags |= PGM_SESSION_FLAG_NAK_TIMED_OUT; break; } if ((pNak->PacketsInGroup > 1) && (pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT)) { 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 + ((NAK_REPEAT_INTERVAL_MSECS + (NAK_RANDOM_BACKOFF_MSECS/NumMissingPackets)) / BASIC_TIMER_GRANULARITY_IN_MSECS); TotalSeqsNacked += NumMissingPackets; NumMissingPackets = 0; } else { for (i=pNak->NextIndexToIndicate; iPacketsInGroup; i++) { if ((pNak->pPendingData[i].ActualIndexOfDataPacket >= pNak->OriginalGroupSize) && (!pNak->pPendingData[i].NcfsReceivedForActualIndex)) { pSelectiveNaks->Sequences[pSelectiveNaks->NumSequences++] = pNak->SequenceNumber+i; TotalSeqsNacked++; if ((!--NumMissingPackets) || (pSelectiveNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1))) { LastSequenceNumber = pNak->SequenceNumber+i; break; } } } if (!NumMissingPackets) { pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NAK_REPEAT_INTERVAL_MSECS + NAK_RANDOM_BACKOFF_MSECS) / BASIC_TIMER_GRANULARITY_IN_MSECS); } 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) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CheckSendPendingNaks", "Outstanding Nak for Sequence # [%d] Timed out!, Window=<%d--%d> ( %d seqs), Ncfs=<%d>, FirstNak=<%d>\n", (ULONG) pNak->SequenceNumber, (ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber, (ULONG) (1+pReceive->pReceiver->FurthestKnownGroupSequenceNumber-pReceive->pReceiver->LastTrailingEdgeSeqNum), pNak->WaitingRDataRetries, (ULONG) pReceive->pReceiver->FirstNakSequenceNumber); pReceive->SessionFlags |= PGM_SESSION_FLAG_NAK_TIMED_OUT; break; } pNak->WaitingNcfRetries = 0; pNak->OutstandingNakTimeout = 0; pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NAK_RANDOM_BACKOFF_MSECS/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')))) { PgmLog (PGM_LOG_ERROR, DBG_SEND, "CheckSendPendingNaks", "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')))) { PgmLog (PGM_LOG_ERROR, DBG_SEND, "CheckSendPendingNaks", "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 we had some packets left to be sent from the // last Nak, include those sequences now // if (NumMissingPackets) { for (i=(UCHAR) (1+LastSequenceNumber-pNak->SequenceNumber); iPacketsInGroup; i++) { if (pNak->pPendingData[i].ActualIndexOfDataPacket >= pNak->OriginalGroupSize) { pSelectiveNaks->Sequences[pSelectiveNaks->NumSequences++] = pNak->SequenceNumber+i; TotalSeqsNacked++; if ((!--NumMissingPackets) || (pSelectiveNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1))) { LastSequenceNumber = pNak->SequenceNumber+i; break; } } } // // We could encounter a situation where we could have received // a packet while sending the Nak, so we should reset our MissingPacket // count accordingly // if (i >= pNak->PacketsInGroup) { NumMissingPackets = 0; } if (!NumMissingPackets) { pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NAK_REPEAT_INTERVAL_MSECS + NAK_RANDOM_BACKOFF_MSECS) / BASIC_TIMER_GRANULARITY_IN_MSECS); } if (pSelectiveNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1)) { fSendSelectiveNak = TRUE; } } } if (pReceive->SessionFlags & PGM_SESSION_TERMINATED_ABORT) { break; } } pReceive->pReceiver->NumPendingNaks = NumPendingNaks; pReceive->pReceiver->NumOutstandingNaks = NumOutstandingNaks; if (!IsListEmpty (&NaksList)) { pReceive->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))) { PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "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 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, Frequency; LARGE_INTEGER DeltaTime, GranularTimeElapsed; ULONG NumTimeouts; ULONG LastSessionInterval; PgmLock (&PgmDynamicConfig, OldIrq); if (IsListEmpty (&PgmDynamicConfig.CurrentReceivers)) { // // Stop the timer if we don't have any receivers currently // PgmDynamicConfig.GlobalFlags &= ~PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING; PgmUnlock (&PgmDynamicConfig, OldIrq); PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ReceiveTimerTimeout", "Not restarting Timer since no Receivers currently active!\n"); return; } Now = KeQueryPerformanceCounter (&Frequency); DeltaTime.QuadPart = Now.QuadPart - PgmDynamicConfig.LastReceiverTimeout.QuadPart; 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); LastSessionInterval = (ULONG) (PgmDynamicConfig.ReceiversTimerTickCount - pReceiver->LastSessionTickCount); if ((LastSessionInterval > MAX_SPM_INTERVAL_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS) && (LastSessionInterval > (pReceiver->MaxSpmInterval << 5))) // (32 * MaxSpmInterval) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ReceiveTimerTimeout", "Disconnecting session because no SPMs received for <%x:%x> Msecs\n", LastSessionInterval); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; } 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 { pReceive->RateKBitsPerSecOverall = (pReceive->TotalBytes << LOG2_BITS_PER_BYTE) / ((PgmDynamicConfig.ReceiversTimerTickCount-pReceiver->StartTickCount) * BASIC_TIMER_GRANULARITY_IN_MSECS); pReceive->RateKBitsPerSecLast = (pReceive->TotalBytes - pReceive->TotalBytesAtLastInterval) >> (LOG2_INTERNAL_RATE_CALCULATION_FREQUENCY-LOG2_BITS_PER_BYTE); // // 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); } pReceive->pReceiver->StatSumOfWindowSeqs = pReceive->pReceiver->NumWindowSamples = 0; // pReceive->pReceiver->StatSumOfNcfRDataTicks = pReceive->pReceiver->NumNcfRDataTicksSamples = 0; } PgmUnlock (pReceive, OldIrq1); PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "ReceiveTimerTimeout", "Checking for pending Naks for pReceive=<%p>, Addr=<%x>\n", pReceive, pReceiver->ListenMCastIpAddress); CheckSendPendingNaks (pReceiver->pAddress, pReceive, &OldIrq); } else { pEntry = pEntry->Blink; RemoveEntryList (&pReceiver->Linkage); PgmUnlock (&PgmDynamicConfig, OldIrq1); CheckIndicateDisconnect (pReceiver->pAddress, pReceive, NULL, &OldIrq1, FALSE); PgmUnlock (pReceive, OldIrq); PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ReceiveTimerTimeout", "PGM_SESSION_ON_TIMER flag cleared for pReceive=<%p>, Addr=<%x>\n", pReceive, pReceiver->ListenMCastIpAddress); PGM_DEREFERENCE_ADDRESS (pReceiver->pAddress, REF_ADDRESS_RECEIVE_ACTIVE); PGM_DEREFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_TIMER_RUNNING); PgmLock (&PgmDynamicConfig, OldIrq); } } 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, 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; SEQ_TYPE LastSequenceNumber; 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)) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ExtractNakNcfSequences", "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) // LastSequenceNumber = pNakNcfList->pNakSequences[0] - FECGroupSize; for (i=0; i < pNakNcfList->NumSequences; i++) { PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ExtractNakNcfSequences", "[%d] Sequence# = <%d>\n", i, (ULONG) pNakNcfList->pNakSequences[i]); // // If this is a parity Nak, then we need to separate the TG_SQN from the PKT_SQN // if (pNakNcfList->NakType == NAK_TYPE_PARITY) { pNakNcfList->NumNaks[i] = (USHORT) (pNakNcfList->pNakSequences[i] & FECSequenceMask) + 1; ASSERT (pNakNcfList->NumNaks[i] <= FECGroupSize); pNakNcfList->pNakSequences[i] &= FECGroupMask; } else { pNakNcfList->NumNaks[i] = 1; } if (SEQ_LEQ (pNakNcfList->pNakSequences[i], LastSequenceNumber)) { // // This list is not ordered, so just bail! // PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ExtractNakNcfSequences", "[%d] Unordered list! Sequence#<%d> before <%d>\n", i, (ULONG) LastSequenceNumber, (ULONG) pNakNcfList->pNakSequences[i]); return (STATUS_DATA_NOT_ACCEPTED); } LastSequenceNumber = pNakNcfList->pNakSequences[i]; } return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS CheckAndAddNakRequests( IN tRECEIVE_SESSION *pReceive, IN SEQ_TYPE *pLatestSequenceNumber, OUT tNAK_FORWARD_DATA **ppThisNak, IN enum eNAK_TIMEOUT NakTimeoutType ) { 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; // // Verify that the FurthestKnownGroupSequenceNumber is on a Group boundary // ASSERT (!(FurthestGroupSequenceNumber & FECGroupMask)); if (SEQ_LT (ThisSequenceNumber, pReceive->pReceiver->FirstNakSequenceNumber)) { if (ppThisNak) { ASSERT (0); *ppThisNak = NULL; } return (STATUS_SUCCESS); } if (SEQ_GT (ThisGroupSequenceNumber, (FurthestGroupSequenceNumber + 1000)) && !(pReceive->SessionFlags & PGM_SESSION_FLAG_FIRST_PACKET)) { PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "CheckAndAddNakRequests", "WARNING!!! Too many successive packets lost =<%d>!!! Expecting Next=<%d>, FurthestKnown=<%d>, This=<%d>\n", (ULONG) (ThisGroupSequenceNumber - FurthestGroupSequenceNumber), (ULONG) pReceive->pReceiver->FirstNakSequenceNumber, (ULONG) FurthestGroupSequenceNumber, (ULONG) ThisGroupSequenceNumber); } // // 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 (&pReceive->pReceiver->ParityContextLookaside); } else { pLastNak = ExAllocateFromNPagedLookasideList (&pReceive->pReceiver->NonParityContextLookaside); } if (!pLastNak) { pReceive->pReceiver->FurthestKnownGroupSequenceNumber = FurthestGroupSequenceNumber; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CheckAndAddNakRequests", "STATUS_INSUFFICIENT_RESOURCES allocating tNAK_FORWARD_DATA, Size=<%d>, Seq=<%d>\n", NakRequestSize, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber); return (STATUS_INSUFFICIENT_RESOURCES); } PgmZeroMemory (pLastNak, NakRequestSize); if (pReceive->FECOptions) { pLastNak->OriginalGroupSize = pLastNak->PacketsInGroup = pReceive->FECGroupSize; } else { pLastNak->OriginalGroupSize = pLastNak->PacketsInGroup = 1; } for (i=0; iOriginalGroupSize; i++) { pLastNak->pPendingData[i].ActualIndexOfDataPacket = pLastNak->OriginalGroupSize; } FurthestGroupSequenceNumber += pReceive->FECGroupSize; pLastNak->SequenceNumber = FurthestGroupSequenceNumber; pLastNak->MinPacketLength = pReceive->MaxFECPacketLength; if (NakTimeoutType == NAK_OUTSTANDING) { pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + pReceive->pReceiver->OutstandingNakTimeout; pLastNak->PendingNakTimeout = 0; pLastNak->WaitingNcfRetries = 0; } else { switch (NakTimeoutType) { case (NAK_PENDING_0): { pLastNak->PendingNakTimeout = Pending0NakTimeout; pLastNak->OutstandingNakTimeout = 0; break; } case (NAK_PENDING_RB): { pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NAK_RANDOM_BACKOFF_MSECS/pReceive->FECGroupSize) / BASIC_TIMER_GRANULARITY_IN_MSECS); pLastNak->OutstandingNakTimeout = 0; break; } case (NAK_PENDING_RPT_RB): { pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NAK_REPEAT_INTERVAL_MSECS +(NAK_RANDOM_BACKOFF_MSECS/pReceive->FECGroupSize))/ BASIC_TIMER_GRANULARITY_IN_MSECS); pLastNak->OutstandingNakTimeout = 0; break; } default: { ASSERT (0); } } } InsertTailList (&pReceive->pReceiver->NaksForwardDataList, &pLastNak->Linkage); InsertTailList (&pReceive->pReceiver->PendingNaksList, &pLastNak->PendingLinkage); PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "CheckAndAddNakRequests", "ADDing NAK request for SeqNum=<%d>, Furthest=<%d>\n", (ULONG) pLastNak->SequenceNumber, (ULONG) FurthestGroupSequenceNumber); } pReceive->pReceiver->FurthestKnownGroupSequenceNumber = FurthestGroupSequenceNumber; if (pLastNak) { pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + NAK_REPEAT_INTERVAL_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS; } else if ((ppThisNak) && (!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList))) { // // We need to extract the Nak entry for this packet // If this sequence is nearer to the tail end, we will search // from the tail end, otherwise we will search from the head end // MidSequenceNumber = pReceive->pReceiver->FirstNakSequenceNumber + ((pReceive->pReceiver->FurthestKnownGroupSequenceNumber - pReceive->pReceiver->FirstNakSequenceNumber) >> 1); if (SEQ_GT (ThisSequenceNumber, MidSequenceNumber)) { // // Search backwards starting from the tail end // pEntry = &pReceive->pReceiver->PendingNaksList; while ((pEntry = pEntry->Blink) != &pReceive->pReceiver->PendingNaksList) { pLastNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, PendingLinkage); if (SEQ_LEQ (pLastNak->SequenceNumber, ThisGroupSequenceNumber)) { break; } } } else { // // Search from the head // pEntry = &pReceive->pReceiver->PendingNaksList; while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->PendingNaksList) { pLastNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, PendingLinkage); if (SEQ_GEQ (pLastNak->SequenceNumber, ThisGroupSequenceNumber)) { break; } } } ASSERT (pLastNak); if (pLastNak->SequenceNumber != ThisGroupSequenceNumber) { pLastNak = NULL; } } 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 NumMissingPackets; BOOLEAN fFECWithNoParityNak = FALSE; if (PacketLength < sizeof(tBASIC_NAK_NCF_PACKET_HEADER)) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ReceiverProcessNakNcfPacket", "PacketLength=<%d>, Min=<%d>, ...\n", PacketLength, sizeof(tBASIC_NAK_NCF_PACKET_HEADER)); return (STATUS_DATA_NOT_ACCEPTED); } ASSERT (!pNakNcfPacket->CommonHeader.TSDULength); PgmZeroMemory (&NakNcfList, sizeof (tNAKS_LIST)); PgmLock (pReceive, OldIrq); status = ExtractNakNcfSequences (pNakNcfPacket, (PacketLength - sizeof(tBASIC_NAK_NCF_PACKET_HEADER)), &NakNcfList, pReceive->FECGroupSize); if (!NT_SUCCESS (status)) { PgmUnlock (pReceive, OldIrq); PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ReceiverProcessNakNcfPacket", "ExtractNakNcfSequences returned <%x>\n", status); return (status); } PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "ReceiverProcessNakNcfPacket", "NumSequences=[%d] Range=<%d--%d>, Furthest=<%d>\n", NakNcfList.NumSequences, (ULONG) NakNcfList.pNakSequences[0], (ULONG) NakNcfList.pNakSequences[NakNcfList.NumSequences-1], (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber); // // Compares apples to apples and oranges to oranges // i.e. Process parity Naks only if we are parity-aware, and vice-versa // if (pReceive->pReceiver->SessionNakType != NakNcfList.NakType) { PgmUnlock (pReceive, OldIrq); PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ReceiverProcessNakNcfPacket", "Received a %s Nak! Not processing ... \n", ((pReceive->FECGroupSize > 1) ? "Non-parity" : "Parity")); return (STATUS_SUCCESS); } // // Special case: If we have FEC enabled, but not with OnDemand parity, // then we will process Ncf requests only // pEntry = &pReceive->pReceiver->PendingNaksList; fFECWithNoParityNak = pReceive->FECOptions && !(pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT); if (fFECWithNoParityNak && (PacketType == PACKET_TYPE_NAK)) { pEntry = pEntry->Blink; } i = 0; FECGroupMask = pReceive->FECGroupSize - 1; while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->PendingNaksList) { pLastNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, PendingLinkage); while (SEQ_LT (NakNcfList.pNakSequences[i], pLastNak->SequenceNumber)) { if (++i == NakNcfList.NumSequences) { PgmUnlock (pReceive, OldIrq); PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ReceiverProcessNakNcfPacket", "Received Ncf for <%d> Sequences -- none in our range\n", i); return (STATUS_SUCCESS); } } LastSequenceNumber = NakNcfList.pNakSequences[i] & ~FECGroupMask; if (SEQ_GT (LastSequenceNumber, pLastNak->SequenceNumber)) { continue; } NumMissingPackets = pLastNak->PacketsInGroup - (pLastNak->NumDataPackets + pLastNak->NumParityPackets); ASSERT (pLastNak->SequenceNumber == LastSequenceNumber); ASSERT (NumMissingPackets); PacketIndex = (ULONG) (NakNcfList.pNakSequences[i] & FECGroupMask); 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 + ((NAK_REPEAT_INTERVAL_MSECS + (NAK_RANDOM_BACKOFF_MSECS/NumMissingPackets))/ BASIC_TIMER_GRANULARITY_IN_MSECS); } else { ASSERT (pLastNak->OutstandingNakTimeout); pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + (pReceive->pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries); if ((pLastNak->WaitingRDataRetries >= (NCF_WAITING_RDATA_MAX_RETRIES >> 1)) && ((pReceive->pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries) < pReceive->pReceiver->MaxRDataResponseTCFromWindow)) { pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + (pReceive->pReceiver->MaxRDataResponseTCFromWindow<<1); } } } // NCF case -- check if we have this data packet! else if ((fFECWithNoParityNak && (pLastNak->pPendingData[PacketIndex].ActualIndexOfDataPacket >= pLastNak->OriginalGroupSize)) || (!fFECWithNoParityNak && (NakNcfList.NumNaks[i] >= NumMissingPackets))) { if (!pLastNak->FirstNcfTickCount) { pLastNak->FirstNcfTickCount = PgmDynamicConfig.ReceiversTimerTickCount; } if (fFECWithNoParityNak) { pLastNak->pPendingData[PacketIndex].NcfsReceivedForActualIndex++; for (j=0; jPacketsInGroup; j++) { if ((pLastNak->pPendingData[j].ActualIndexOfDataPacket >= pLastNak->OriginalGroupSize) && (!pLastNak->pPendingData[j].NcfsReceivedForActualIndex)) { break; } } } if (!fFECWithNoParityNak || (j >= pLastNak->PacketsInGroup)) { pLastNak->PendingNakTimeout = 0; pLastNak->WaitingNcfRetries = 0; pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + (pReceive->pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries); if ((pLastNak->WaitingRDataRetries >= (NCF_WAITING_RDATA_MAX_RETRIES >> 1)) && ((pReceive->pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries) < pReceive->pReceiver->MaxRDataResponseTCFromWindow)) { pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + (pReceive->pReceiver->MaxRDataResponseTCFromWindow<<1); } } } if (fFECWithNoParityNak) { pEntry = pEntry->Blink; // There may be more Ncfs for the same group! } if (++i == NakNcfList.NumSequences) { PgmUnlock (pReceive, OldIrq); PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ReceiverProcessNakNcfPacket", "Received Ncf for <%d> Sequences, some in our list\n", i); return (STATUS_SUCCESS); } } // // 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 // if (PacketType == PACKET_TYPE_NAK) { status = CheckAndAddNakRequests (pReceive,&NakNcfList.pNakSequences[NakNcfList.NumSequences-1], NULL, NAK_PENDING_RPT_RB); } else // PacketType == PACKET_TYPE_NCF { status = CheckAndAddNakRequests (pReceive, &NakNcfList.pNakSequences[NakNcfList.NumSequences-1], NULL, NAK_OUTSTANDING); } 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 GroupMask = GroupSize - 1; ULONG NakRequestSize = sizeof(tNAK_FORWARD_DATA) + ((GroupSize-1) * sizeof(tPENDING_DATA)); USHORT MinPacketLength; UCHAR i; NTSTATUS status = STATUS_SUCCESS; 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; // // 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); if (!NT_SUCCESS (status)) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CoalesceSelectiveNaksIntoGroups", "CheckAndAddNakRequests returned <%x>\n", status); return (status); } ASSERT (LastSequenceNumber == pReceive->pReceiver->FurthestKnownGroupSequenceNumber); ExInitializeNPagedLookasideList (&pReceive->pReceiver->ParityContextLookaside, NULL, NULL, 0, NakRequestSize, PGM_TAG('2'), PARITY_CONTEXT_LOOKASIDE_DEPTH); if (SEQ_GT (pReceive->pReceiver->FirstNakSequenceNumber, LastSequenceNumber)) { pReceive->pReceiver->FurthestKnownGroupSequenceNumber = LastGroupSequenceNumber; ASSERT (IsListEmpty (&pReceive->pReceiver->NaksForwardDataList)); PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "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); } // // 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))) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CoalesceSelectiveNaksIntoGroups", "STATUS_INSUFFICIENT_RESOURCES allocating tNAK_FORWARD_DATA\n"); status = STATUS_INSUFFICIENT_RESOURCES; break; } PgmZeroMemory (pNewNak, NakRequestSize); InitializeListHead (&pNewNak->PendingLinkage); 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) { ASSERT (!IsListEmpty (&pOldNak->PendingLinkage)); RemoveEntryList (&pOldNak->PendingLinkage); InitializeListHead (&pOldNak->PendingLinkage); } else { ASSERT (pOldNak->NumDataPackets == 1); ASSERT (IsListEmpty (&pOldNak->PendingLinkage)); } 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->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 (pNewNak, FALSE); ASSERT (!pNewNak->NumParityPackets); if (pNewNak->NumDataPackets < pNewNak->PacketsInGroup) // No parity packets yet! { pNewNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + ((NAK_RANDOM_BACKOFF_MSECS/(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->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 (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 + ((NAK_RANDOM_BACKOFF_MSECS/(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 // 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)) { InsertTailList (&pReceive->pReceiver->PendingNaksList, &pNewNak->PendingLinkage); } } AdjustReceiveBufferLists (pReceive); // // Now, set the FirstKnownGroupSequenceNumber // 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 + ((NAK_REPEAT_INTERVAL_MSECS + (NAK_RANDOM_BACKOFF_MSECS / (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); } if (pNewNak) { pReceive->pReceiver->FurthestKnownGroupSequenceNumber = pNewNak->SequenceNumber; } else { pReceive->pReceiver->FurthestKnownGroupSequenceNumber &= ~GroupMask; } PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "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_SUCCESS); } //---------------------------------------------------------------------------- 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 --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG ReceiveFlags; ULONG BytesLeftInMessage, ClientBytesTaken; PIO_STACK_LOCATION pIrpSp; PTDI_REQUEST_KERNEL_RECEIVE pClientParams; PTDI_IND_RECEIVE evReceive = NULL; PVOID RcvEvContext = NULL; CONNECTION_CONTEXT ClientSessionContext; PIRP pIrpReceive; ULONG BytesAvailableToIndicate = BytesAvailable; ULONG BytesToCopy; ASSERT ((!pReceive->pReceiver->CurrentMessageLength) || (pReceive->pReceiver->CurrentMessageLength == MessageLength)); ASSERT (pReceive->pReceiver->CurrentMessageProcessed == MessageOffset); pReceive->pReceiver->CurrentMessageLength = MessageLength; pReceive->pReceiver->CurrentMessageProcessed = MessageOffset; BytesLeftInMessage = MessageLength - MessageOffset; PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "PgmIndicateToClient", "MessageLen=<%d/%d>, MessageOff=<%d>, CurrentML=<%d>, CurrentMP=<%d>\n", BytesAvailableToIndicate, MessageLength, MessageOffset, pReceive->pReceiver->CurrentMessageLength, pReceive->pReceiver->CurrentMessageProcessed); // // We may have a receive Irp pending from a previous indication, // so see if need to fill that first! // while ((BytesAvailableToIndicate) && ((pIrpReceive = pReceive->pReceiver->pIrpReceive) || (!IsListEmpty (&pReceive->pReceiver->ReceiveIrpsList)))) { if (!pIrpReceive) { // // The client had posted a receive Irp, so use it now! // pIrpReceive = CONTAINING_RECORD (pReceive->pReceiver->ReceiveIrpsList.Flink, IRP, Tail.Overlay.ListEntry); RemoveEntryList (&pIrpReceive->Tail.Overlay.ListEntry); pIrpSp = IoGetCurrentIrpStackLocation (pIrpReceive); pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE) &pIrpSp->Parameters; pReceive->pReceiver->pIrpReceive = pIrpReceive; pReceive->pReceiver->TotalBytesInMdl = pClientParams->ReceiveLength; pReceive->pReceiver->BytesInMdl = 0; } // // Copy whatever bytes we can into it // if (BytesAvailableToIndicate > (pReceive->pReceiver->TotalBytesInMdl - pReceive->pReceiver->BytesInMdl)) { BytesToCopy = pReceive->pReceiver->TotalBytesInMdl - pReceive->pReceiver->BytesInMdl; } else { BytesToCopy = BytesAvailableToIndicate; } ClientBytesTaken = 0; status = TdiCopyBufferToMdl (pDataBuffer, 0, BytesToCopy, pReceive->pReceiver->pIrpReceive->MdlAddress, pReceive->pReceiver->BytesInMdl, &ClientBytesTaken); pReceive->pReceiver->BytesInMdl += ClientBytesTaken; pReceive->pReceiver->CurrentMessageProcessed += ClientBytesTaken; BytesLeftInMessage -= ClientBytesTaken; BytesAvailableToIndicate -= ClientBytesTaken; pDataBuffer += ClientBytesTaken; if ((!ClientBytesTaken) || (pReceive->pReceiver->BytesInMdl >= pReceive->pReceiver->TotalBytesInMdl) || (!BytesLeftInMessage)) { // // The Irp is full, so complete the Irp! // pIrpReceive = pReceive->pReceiver->pIrpReceive; pIrpReceive->IoStatus.Information = pReceive->pReceiver->BytesInMdl; if (BytesLeftInMessage) { pIrpReceive->IoStatus.Status = STATUS_BUFFER_OVERFLOW; } else { ASSERT (pReceive->pReceiver->CurrentMessageLength == pReceive->pReceiver->CurrentMessageProcessed); pIrpReceive->IoStatus.Status = STATUS_SUCCESS; } // // Before releasing the lock, set the parameters for the next receive // pReceive->pReceiver->pIrpReceive = NULL; pReceive->pReceiver->TotalBytesInMdl = pReceive->pReceiver->BytesInMdl = 0; PgmUnlock (pReceive, *pOldIrqReceive); PgmUnlock (pAddress, *pOldIrqAddress); PgmCancelCancelRoutine (pIrpReceive); PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "PgmIndicateToClient", "Completing prior pIrp=<%p>, Bytes=<%d>, BytesLeft=<%d>\n", pIrpReceive, 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 (pReceive->pReceiver->CurrentMessageLength == pReceive->pReceiver->CurrentMessageProcessed); pReceive->pReceiver->CurrentMessageLength = pReceive->pReceiver->CurrentMessageProcessed = 0; } *pBytesTaken = BytesAvailable - BytesAvailableToIndicate; 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 PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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)))) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmIndicateToClient", "pReceive=<%p>, pIrp=<%p> Cancelled during Receive!\n", pReceive, pIrpReceive); PgmIoComplete (pIrpReceive, STATUS_CANCELLED, 0); PgmLock (pAddress, *pOldIrqAddress); PgmLock (pReceive, *pOldIrqReceive); pReceive->pReceiver->CurrentMessageProcessed += ClientBytesTaken; *pBytesTaken = BytesAvailable - BytesAvailableToIndicate; return (STATUS_UNSUCCESSFUL); } PgmLock (pAddress, *pOldIrqAddress); PgmLock (pReceive, *pOldIrqReceive); pReceive->pReceiver->CurrentMessageProcessed += ClientBytesTaken; if (!pReceive->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); } PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmIndicateToClient", "pReceive=<%p> disassociated during Receive!\n", pReceive); *pBytesTaken = BytesAvailable - BytesAvailableToIndicate; 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, pReceive->pReceiver->BytesInMdl, &ClientBytesTaken); BytesLeftInMessage -= ClientBytesTaken; BytesAvailableToIndicate -= ClientBytesTaken; pDataBuffer = pDataBuffer + ClientBytesTaken; pReceive->pReceiver->CurrentMessageProcessed += ClientBytesTaken; PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "PgmIndicateToClient", "Client's evReceive returned pIrp=<%p>, BytesInIrp=<%d>, Copied <%d> bytes\n", pIrpReceive, pClientParams->ReceiveLength, ClientBytesTaken); if ((!ClientBytesTaken) || (ClientBytesTaken >= pClientParams->ReceiveLength) || (pReceive->pReceiver->CurrentMessageLength == pReceive->pReceiver->CurrentMessageProcessed)) { // // The Irp is full, so complete the Irp! // pIrpReceive->IoStatus.Information = ClientBytesTaken; if (pReceive->pReceiver->CurrentMessageLength == pReceive->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 // pReceive->pReceiver->TotalBytesInMdl = pReceive->pReceiver->BytesInMdl = 0; PgmUnlock (pReceive, *pOldIrqReceive); PgmUnlock (pAddress, *pOldIrqAddress); PgmCancelCancelRoutine (pIrpReceive); IoCompleteRequest (pIrpReceive, IO_NETWORK_INCREMENT); PgmLock (pAddress, *pOldIrqAddress); PgmLock (pReceive, *pOldIrqReceive); } else { pReceive->pReceiver->TotalBytesInMdl = pClientParams->ReceiveLength; pReceive->pReceiver->BytesInMdl = ClientBytesTaken; pReceive->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 ((pReceive->pReceiver->pIrpReceive) || (!IsListEmpty (&pReceive->pReceiver->ReceiveIrpsList))) { status = STATUS_SUCCESS; } else { pReceive->SessionFlags |= PGM_SESSION_WAIT_FOR_RECEIVE_IRP; } } if (pReceive->pReceiver->CurrentMessageLength == pReceive->pReceiver->CurrentMessageProcessed) { pReceive->pReceiver->CurrentMessageLength = pReceive->pReceiver->CurrentMessageProcessed = 0; } if ((NT_SUCCESS (status)) || (status == STATUS_DATA_NOT_ACCEPTED)) { PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "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 { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmIndicateToClient", "Unexpected status=<%x>\n", status); ASSERT (0); } *pBytesTaken = BytesAvailable - BytesAvailableToIndicate; 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) && !(pReceive->SessionFlags & PGM_SESSION_DISCONNECT_INDICATED)) { 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) { PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "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++; 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 // PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmIndicateGroup", "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++; continue; } if (DataBytes > (pNak->pPendingData[i].MessageLength - pNak->pPendingData[i].MessageOffset)) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmIndicateGroup", "[%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); PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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 (!(pNak->pPendingData[i].DecodeBuffer = PgmAllocMem (MinBufferSize, PGM_TAG('3')))) { ASSERT (0); PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "DecodeParityPackets", "STATUS_INSUFFICIENT_RESOURCES[2] ...\n"); return (STATUS_INSUFFICIENT_RESOURCES); } PgmFreeMem (pNak->pPendingData[i].pDataPacket); pNak->pPendingData[i].pDataPacket = pNak->pPendingData[i].DecodeBuffer; } 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); } 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) { 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) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "DecodeParityPackets", "[%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 { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "DecodeParityPackets", "FECDecode returned <%x>\n", status); ASSERT (0); status = STATUS_UNSUCCESSFUL; } 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; // // 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)) || (IsListEmpty (&pReceive->pReceiver->BufferedDataList))) { return (STATUS_SUCCESS); } pReceive->SessionFlags |= PGM_SESSION_FLAG_IN_INDICATE; pNextNak = CONTAINING_RECORD (pReceive->pReceiver->BufferedDataList.Flink, tNAK_FORWARD_DATA, Linkage); ASSERT (pNextNak->SequenceNumber == pReceive->pReceiver->NextODataSequenceNumber); do { // // 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); } else { ASSERT ((pNextNak->NextIndexToIndicate + pNextNak->NumDataPackets) >= pNextNak->PacketsInGroup); // The above assertion can be greater if we have only partially indicated a group status = STATUS_SUCCESS; } if (NT_SUCCESS (status)) { status = PgmIndicateGroup (pAddress, pReceive, pOldIrqAddress, pOldIrqReceive, pNextNak); } else { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CheckIndicatePendedData", "DecodeParityPackets returned <%x>\n", status); } 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; } PacketsIndicated = pNextNak->NumDataPackets + pNextNak->NumParityPackets; pReceive->pReceiver->TotalDataPacketsBuffered -= PacketsIndicated; pReceive->pReceiver->DataPacketsPendingIndicate -= PacketsIndicated; pReceive->pReceiver->NumPacketGroupsPendingClient--; ASSERT (pReceive->pReceiver->TotalDataPacketsBuffered >= pReceive->pReceiver->NumPacketGroupsPendingClient); // // Advance to the next group boundary // pReceive->pReceiver->NextODataSequenceNumber += pNextNak->OriginalGroupSize; RemoveEntryList (&pNextNak->Linkage); FreeNakContext (pReceive, pNextNak); if (IsListEmpty (&pReceive->pReceiver->BufferedDataList)) { break; } ASSERT (pReceive->pReceiver->NumPacketGroupsPendingClient); pNextNak = CONTAINING_RECORD (pReceive->pReceiver->BufferedDataList.Flink, tNAK_FORWARD_DATA, Linkage); ASSERT (pNextNak->SequenceNumber == pReceive->pReceiver->NextODataSequenceNumber); pReceive->pReceiver->NextODataSequenceNumber = pNextNak->SequenceNumber; if (SEQ_LT(pReceive->pReceiver->FirstNakSequenceNumber, pReceive->pReceiver->NextODataSequenceNumber)) { pReceive->pReceiver->FirstNakSequenceNumber = pReceive->pReceiver->NextODataSequenceNumber; } } while (1); PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "CheckIndicatePendedData", "status=<%x>, pReceive=<%p>, SessionFlags=<%x>\n", status, pReceive, pReceive->SessionFlags); pReceive->SessionFlags &= ~PGM_SESSION_FLAG_IN_INDICATE; 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 = NULL; ULONG MessageLength, DataOffset, BytesTaken, DataBytes; ULONGLONG NcfRDataTickCounts; NTSTATUS status; USHORT TSDULength; tPACKET_OPTIONS PacketOptions; UCHAR i, PacketIndex, NakIndex; BOOLEAN fIsParityPacket; PUCHAR pDataBuffer; fIsParityPacket = pOData->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY; ASSERT (PacketLength <= pReceive->MaxMTULength); // // Extract all the information that we need from the packet options right now! // 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)) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmHandleNewData", "ProcessOptions returned <%x>, SeqNum=[%d]: NumOutOfOrder=<%d> ...\n", status, (ULONG) ThisDataSequenceNumber, pReceive->pReceiver->TotalDataPacketsBuffered); ASSERT (0); pReceive->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); pReceive->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); pLastNak = NULL; BytesTaken = 0; // // If we are not parity-enabled, and this is the next expected data packet, // we can try to indicate this data over here only // if ((!pReceive->FECOptions) && ((ULONG) ThisDataSequenceNumber == (ULONG) pReceive->pReceiver->NextODataSequenceNumber) && (IsListEmpty (&pReceive->pReceiver->BufferedDataList)) && (!fIsParityPacket) && !(pReceive->SessionFlags & (PGM_SESSION_FLAG_IN_INDICATE | PGM_SESSION_WAIT_FOR_RECEIVE_IRP | PGM_SESSION_DISCONNECT_INDICATED | PGM_SESSION_TERMINATED_ABORT))) { ASSERT (!pReceive->pReceiver->NumPacketGroupsPendingClient); if (!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList)) { pLastNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Flink, tNAK_FORWARD_DATA, Linkage); ASSERT ((pLastNak->SequenceNumber == ThisDataSequenceNumber) && (!pLastNak->pPendingData[0].pDataPacket)); } 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)) { PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "PgmHandleNewData", "Dropping SeqNum=[%d] since it's a NULL message [%d / %d]!\n", (ULONG) (pReceive->pReceiver->NextODataSequenceNumber), PacketOptions.MessageOffset, PacketOptions.MessageLength); BytesTaken = DataBytes; status = STATUS_SUCCESS; } // // If we are starting receiving in the midst of a message, we should also ignore // else 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)); PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "PgmHandleNewData", "Dropping SeqNum=[%d] since it's a PARTIAL message [%d / %d]!\n", (ULONG) (pReceive->pReceiver->NextODataSequenceNumber), PacketOptions.MessageOffset, PacketOptions.MessageLength); BytesTaken = DataBytes; status = STATUS_SUCCESS; } else if ((pReceive->pReceiver->CurrentMessageProcessed != PacketOptions.MessageOffset) || ((pReceive->pReceiver->CurrentMessageProcessed) && (pReceive->pReceiver->CurrentMessageLength != PacketOptions.MessageLength))) { // // Our state expects us to be in the middle of a message, but // the current packets do not show this // PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmHandleNewData", "SeqNum=[%d] Expecting MsgLen=<%d>, MsgOff=<%d>, have MsgLen=<%d>, MsgOff=<%d>\n", (ULONG) pReceive->pReceiver->NextODataSequenceNumber, pReceive->pReceiver->CurrentMessageLength, pReceive->pReceiver->CurrentMessageProcessed, PacketOptions.MessageLength, PacketOptions.MessageOffset); ASSERT (0); BytesTaken = DataBytes; pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; status = STATUS_UNSUCCESSFUL; } else { 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; PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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 (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_FIN) { pReceive->pReceiver->FinDataSequenceNumber = ThisDataSequenceNumber; pReceive->SessionFlags |= PGM_SESSION_TERMINATED_GRACEFULLY; } if (BytesTaken == DataBytes) { if (pLastNak) { if ((PacketType == PACKET_TYPE_RDATA) && (pLastNak->FirstNcfTickCount)) { AdjustNcfRDataResponseTimes (pReceive, pLastNak); } ASSERT (!IsListEmpty (&pLastNak->PendingLinkage)); RemoveEntryList (&pLastNak->PendingLinkage); InitializeListHead (&pLastNak->PendingLinkage); RemoveEntryList (&pLastNak->Linkage); FreeNakContext (pReceive, pLastNak); } else { pReceive->pReceiver->FurthestKnownGroupSequenceNumber++; } pReceive->pReceiver->NextODataSequenceNumber++; pReceive->pReceiver->FirstNakSequenceNumber = pReceive->pReceiver->NextODataSequenceNumber; if (pLastNak) { // // Now, move any Naks contexts for which the group is complete // to the BufferedDataList // AdjustReceiveBufferLists (pReceive); } return (status); } } // // First, ensure we have a Nak context available for this data // status = CheckAndAddNakRequests (pReceive, &ThisDataSequenceNumber, &pLastNak, NAK_PENDING_RB); if ((!NT_SUCCESS (status)) || (!pLastNak)) { PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "PgmHandleNewData", "CheckAndAddNakRequests for <%d> returned <%x>, pLastNak=<%p>\n", ThisDataSequenceNumber, status, pLastNak); if (NT_SUCCESS (status)) { pReceive->pReceiver->NumDupPacketsBuffered++; } else { pReceive->pReceiver->NumDataPacketsDropped++; } return (status); } // // If this group has a different GroupSize, set that now // if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE) { if (!(PacketOptions.FECContext.NumPacketsInThisGroup) || (pReceive->FECOptions && (PacketOptions.FECContext.NumPacketsInThisGroup >= pReceive->FECGroupSize))) { // // Bad Packet! // ASSERT (0); status = STATUS_DATA_NOT_ACCEPTED; } else 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; } // // 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; // // Get rid of any of the excess (NULL) data packets // RemoveRedundantNaks (pLastNak, TRUE); } else if (pLastNak->PacketsInGroup != PacketOptions.FECContext.NumPacketsInThisGroup) { ASSERT (0); status = STATUS_DATA_NOT_ACCEPTED; } } if (status == STATUS_DATA_NOT_ACCEPTED) { pReceive->pReceiver->NumDataPacketsDropped++; return (status); } // // // See if we even need this packet! // if (fIsParityPacket) { // // Do not handle parity packets if we are not aware of FEC, // or it is a partial group size = 1 packet // if ((pLastNak->PacketsInGroup == 1) || // Do not handle parity packets if we are not aware of FEC ((pLastNak->NumDataPackets+pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) || ((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup)) { pReceive->pReceiver->NumDupPacketsBuffered++; status = STATUS_DATA_NOT_ACCEPTED; } else { // // Determine the ParityPacket Index // PacketIndex = (UCHAR) (ThisDataSequenceNumber & (pReceive->FECGroupSize-1)); 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); } } } else // This is a non-parity packet { PacketIndex = (UCHAR) (ThisDataSequenceNumber & (pReceive->FECGroupSize-1)); if ((PacketIndex >= pLastNak->PacketsInGroup) || (PacketIndex < pLastNak->NextIndexToIndicate)) { // // We don't need this Packet! // pReceive->pReceiver->NumDupPacketsBuffered++; status = STATUS_DATA_NOT_ACCEPTED; } } 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); pReceive->pReceiver->NumDupPacketsBuffered++; status = STATUS_DATA_NOT_ACCEPTED; break; } } } if (status == STATUS_DATA_NOT_ACCEPTED) { AdjustReceiveBufferLists (pReceive); // In case this became a partial group return (status); } #ifdef MAX_BUFF_DBG { if (pReceive->pReceiver->NumPacketGroupsPendingClient > MaxPacketGroupsPendingClient) { MaxPacketGroupsPendingClient = pReceive->pReceiver->NumPacketGroupsPendingClient; } if (pReceive->pReceiver->TotalDataPacketsBuffered >= MaxPacketsBuffered) { MaxPacketsBuffered = pReceive->pReceiver->TotalDataPacketsBuffered; } if (pReceive->pReceiver->DataPacketsPendingIndicate >= MaxPacketsPendingIndicate) { MaxPacketsPendingIndicate = pReceive->pReceiver->DataPacketsPendingIndicate; } if (pReceive->pReceiver->DataPacketsPendingNaks >= MaxPacketsPendingNaks) { MaxPacketsPendingNaks = pReceive->pReceiver->DataPacketsPendingNaks; } ASSERT (pReceive->pReceiver->TotalDataPacketsBuffered == (pReceive->pReceiver->DataPacketsPendingIndicate + pReceive->pReceiver->DataPacketsPendingNaks)); } #endif // MAX_BUFF_DBG if (pReceive->pReceiver->TotalDataPacketsBuffered >= MAX_PACKETS_BUFFERED) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmHandleNewData", "[%d]: Excessive number of packets buffered=<%d> > <%d>, Aborting ...\n", (ULONG) ThisDataSequenceNumber, (ULONG) pReceive->pReceiver->TotalDataPacketsBuffered, (ULONG) MAX_PACKETS_BUFFERED); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; return (STATUS_INSUFFICIENT_RESOURCES); } if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_FIN) { pReceive->pReceiver->FinDataSequenceNumber = pLastNak->SequenceNumber + (pLastNak->NumDataPackets - 1); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_GRACEFULLY; PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "PgmHandleNewData", "SeqNum=[%d]: Got a FIN!!!\n", (ULONG) pReceive->pReceiver->FinDataSequenceNumber); } if ((PacketType == PACKET_TYPE_RDATA) && (pLastNak->FirstNcfTickCount) && (((pLastNak->NumDataPackets + pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) || ((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup))) { AdjustNcfRDataResponseTimes (pReceive, pLastNak); } // // 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) { ASSERT (PacketIndex < pReceive->FECGroupSize); ASSERT (pLastNak->pPendingData[PacketIndex].ActualIndexOfDataPacket == pLastNak->OriginalGroupSize); if ((PacketLength + sizeof (tPOST_PACKET_FEC_CONTEXT)) <= pLastNak->MinPacketLength) { pDataBuffer = PgmAllocMem (pLastNak->MinPacketLength, PGM_TAG('D')); } else { pDataBuffer = PgmAllocMem ((PacketLength+sizeof(tPOST_PACKET_FEC_CONTEXT)), PGM_TAG('D')); } if (!pDataBuffer) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmHandleNewData", "[%d]: STATUS_INSUFFICIENT_RESOURCES <%d> bytes, NumDataPackets=<%d>, Aborting ...\n", (ULONG) ThisDataSequenceNumber, pLastNak->MinPacketLength, (ULONG) pReceive->pReceiver->TotalDataPacketsBuffered); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; return (STATUS_INSUFFICIENT_RESOURCES); } PgmCopyMemory (pDataBuffer, pOData, PacketLength); // // 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) { PgmFreeMem (pLastNak->pPendingData[i].pDataPacket); pLastNak->pPendingData[i].pDataPacket = NULL; pLastNak->pPendingData[i].PacketLength = pLastNak->pPendingData[i].DataOffset = 0; break; } } ASSERT (i < pLastNak->PacketsInGroup); pLastNak->NumParityPackets--; NakIndex = i; } ASSERT (!pLastNak->pPendingData[NakIndex].pDataPacket); pLastNak->pPendingData[NakIndex].pDataPacket = pDataBuffer; 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 = PgmAllocMem ((PacketLength+sizeof(tPOST_PACKET_FEC_CONTEXT)-sizeof(USHORT)), PGM_TAG('P')); if (!pDataBuffer) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmHandleNewData", "[%d -- Parity]: STATUS_INSUFFICIENT_RESOURCES <%d> bytes, NumDataPackets=<%d>, Aborting ...\n", (ULONG) ThisDataSequenceNumber, PacketLength, (ULONG) pReceive->pReceiver->TotalDataPacketsBuffered); pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; return (STATUS_INSUFFICIENT_RESOURCES); } pLastNak->pPendingData[NakIndex].pDataPacket = pDataBuffer; // // This is a new parity packet // PgmCopyMemory (pDataBuffer, pOData, PacketLength); pLastNak->pPendingData[NakIndex].PacketIndex = PacketIndex; pLastNak->pPendingData[NakIndex].PacketLength = PacketLength; pLastNak->pPendingData[NakIndex].DataOffset = (USHORT) DataOffset; 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)); } } pLastNak->AllOptionsFlags |= PacketOptions.OptionsFlags; pReceive->pReceiver->TotalDataPacketsBuffered++; pReceive->pReceiver->DataPacketsPendingNaks++; // // See if this group is complete // if (((pLastNak->NumDataPackets + pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) || ((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup)) { ASSERT (!IsListEmpty (&pLastNak->PendingLinkage)); RemoveEntryList (&pLastNak->PendingLinkage); InitializeListHead (&pLastNak->PendingLinkage); AdjustReceiveBufferLists (pReceive); } PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "PgmHandleNewData", "SeqNum=[%d]: NumOutOfOrder=<%d> ...\n", (ULONG) ThisDataSequenceNumber, pReceive->pReceiver->TotalDataPacketsBuffered); return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- NTSTATUS ProcessDataPacket( IN tADDRESS_CONTEXT *pAddress, IN tRECEIVE_SESSION *pReceive, IN INT SourceAddressLength, IN PVOID pSourceAddress, 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 SourceAddressLength -- Length of source address IN pSourceAddress -- Address of remote host 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)) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessDataPacket", "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); 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) == (ULONG) 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))) { PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ProcessDataPacket", "NETWORK problems -- data loss=<%d> packets > window size!\n\tExpecting=<%d>, FurthestKnown=<%d>, Trail=<%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); PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "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, pNextNak->WaitingRDataRetries, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber, (ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum, (ULONG) ThisTrailingEdge, (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++; PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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); PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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; ASSERT (PacketLength >= sizeof(tBASIC_SPM_PACKET_HEADER)); // // 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)) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket", "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); ASSERT (!TSDULength); ASSERT (PathNLA.IpAddress); 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) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket", "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 // PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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); PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "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))) { PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "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); PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "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)); } } // // 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; PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "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; PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "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; PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ProcessSpmPacket", "Got a FIN! FinSeq=<%d>, NextODataSeq=<%d>, FurthestData=<%d>\n", (ULONG) pReceive->pReceiver->FinDataSequenceNumber, (ULONG) pReceive->pReceiver->NextODataSequenceNumber, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber); } // // See if we need to abort // if (CheckIndicateDisconnect (pAddress, pReceive, &OldIrq, &OldIrq1, TRUE)) { PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); return (STATUS_SUCCESS); } // // If the Leading edge is > our current leading edge, then // we need to send NAKs for the missing data Packets // status = CheckAndAddNakRequests (pReceive, &LeadingEdgeSeqNumber, NULL, NAK_PENDING_RB); if (!NT_SUCCESS (status)) { PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket", "CheckAndAddNakRequests returned <%x>\n", status); return (status); } // // 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; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket", "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; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket", "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; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket", "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)) { // // We will proceed backwards from the end since we have a higher // probability of finding the leading edge group near the end! // pEntry = &pReceive->pReceiver->PendingNaksList; while ((pEntry = pEntry->Blink) != &pReceive->pReceiver->PendingNaksList) { pNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, PendingLinkage); if (SEQ_GT (pNak->SequenceNumber, LeadingEdgeSeqNumber)) { continue; } if ((pNak->SequenceNumber == LeadingEdgeSeqNumber) && (pNak->PacketsInGroup == pReceive->FECGroupSize)) { // // We have already coalesced the list, so the packets should // be ordered into groups! // pNak->PacketsInGroup = PacketOptions.FECContext.NumPacketsInThisGroup; RemoveRedundantNaks (pNak, TRUE); } break; } } else { PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessSpmPacket", "WARNING .. PARITY_CUR_TGSIZE ThisGroupSize=<%x>, FECGroupSize=<%x>\n", PacketOptions.FECContext.NumPacketsInThisGroup, pReceive->FECGroupSize); } } if (fFirstSpm) { status = CheckIndicatePendedData (pAddress, pReceive, &OldIrq, &OldIrq1); } } PgmUnlock (pReceive, OldIrq1); PgmUnlock (pAddress, OldIrq); PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "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 PVOID pSourceAddress, 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 pSourceAddress -- 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 --*/ { NTSTATUS status = STATUS_SUCCESS; tIPADDRESS SrcIpAddress; TA_IP_ADDRESS *pRemoteAddress = (PTA_IP_ADDRESS) pSourceAddress; // // We have an active connection for this TSI, so process the data appropriately // switch (PacketType) { case (PACKET_TYPE_SPM): { if (PGM_VERIFY_HANDLE (pSession, PGM_VERIFY_SESSION_RECEIVE)) { pSession->TotalBytes += PacketLength; pSession->TotalPacketsInLastInterval++; pSession->pReceiver->LastSessionTickCount = PgmDynamicConfig.ReceiversTimerTickCount; status = ProcessSpmPacket (pAddress, pSession, PacketLength, (tBASIC_SPM_PACKET_HEADER UNALIGNED *) pPgmHeader); } else { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmProcessIncomingPacket", "Received SPM packet, not on Receiver session! pSession=<%p>\n", pSession); status = STATUS_DATA_NOT_ACCEPTED; } break; } case (PACKET_TYPE_ODATA): case (PACKET_TYPE_RDATA): { if (PGM_VERIFY_HANDLE (pSession, PGM_VERIFY_SESSION_RECEIVE)) { if (PacketType == PACKET_TYPE_ODATA) { pSession->pReceiver->NumODataPacketsReceived++; pSession->pReceiver->LastSessionTickCount = PgmDynamicConfig.ReceiversTimerTickCount; } else { pSession->pReceiver->NumRDataPacketsReceived++; } pSession->TotalBytes += PacketLength; pSession->TotalPacketsInLastInterval++; status = ProcessDataPacket (pAddress, pSession, SourceAddressLength, pSourceAddress, PacketLength, (tBASIC_DATA_PACKET_HEADER UNALIGNED *) pPgmHeader, PacketType); } else { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmProcessIncomingPacket", "Received Data packet, not on Receiver session! pSession=<%p>\n", pSession); status = STATUS_DATA_NOT_ACCEPTED; } break; } case (PACKET_TYPE_NCF): { if (PGM_VERIFY_HANDLE (pSession, PGM_VERIFY_SESSION_RECEIVE)) { status = ReceiverProcessNakNcfPacket (pAddress, pSession, PacketLength, (tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *) pPgmHeader, PacketType); } else { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmProcessIncomingPacket", "Received Ncf packet, not on Receiver session! pSession=<%p>\n", pSession); status = STATUS_DATA_NOT_ACCEPTED; } break; } case (PACKET_TYPE_NAK): { if (pSession->pSender) { ASSERT (!pSession->pReceiver); status = SenderProcessNakPacket (pAddress, pSession, PacketLength, (tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *) pPgmHeader); } else { ASSERT (pSession->pReceiver); // // If the Nak was sent by us, then we can ignore it! // SrcIpAddress = ntohl (((PTDI_ADDRESS_IP) &pRemoteAddress->Address[0].Address)->in_addr); if (!SrcIsUs (SrcIpAddress)) { status = ReceiverProcessNakNcfPacket (pAddress, pSession, PacketLength, (tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED*)pPgmHeader, PacketType); } ASSERT (NT_SUCCESS (status)); } break; } default: { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmProcessIncomingPacket", "Unknown PacketType=<%x>, PacketLength=<%d>\n", PacketType, PacketLength); ASSERT (0); return (STATUS_DATA_NOT_ACCEPTED); } } PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "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; LARGE_INTEGER Frequency; // // 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)) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmNewInboundConnection", "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); PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmNewInboundConnection", "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)) { PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "PgmNewInboundConnection", "Client REJECTed incoming session: status=<%x>, pAddress=<%p>, evConn=<%p>\n", status, pAddress, pAddress->evConnect); *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)) { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmNewInboundConnection", "Invalid Connection Handle=<%p>\n", pReceive); PgmUnlock (pAddress, OldIrq1); PgmUnlock (&PgmDynamicConfig, OldIrq); *ppReceive = NULL; return (STATUS_INTERNAL_ERROR); } ASSERT (ConnectId == pReceive->ClientSessionContext); PgmLock (pReceive, OldIrq2); 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 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) { if (!(pReceive->pFECBuffer = PgmAllocMem ((pReceive->MaxFECPacketLength * pAddress->FECGroupSize * 2), PGM_TAG('3')))) { PgmUnlock (pReceive, OldIrq2); PgmUnlock (pAddress, OldIrq1); PgmUnlock (&PgmDynamicConfig, OldIrq); *ppReceive = NULL; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmNewInboundConnection", "STATUS_INSUFFICIENT_RESOURCES allocating pFECBuffer, %d bytes\n", (pReceive->MaxFECPacketLength * pAddress->FECGroupSize * 2)); return (STATUS_INSUFFICIENT_RESOURCES); } else if (!NT_SUCCESS (status = CreateFECContext (&pReceive->FECContext, pAddress->FECGroupSize, FEC_MAX_BLOCK_SIZE, TRUE))) { PgmFreeMem (pReceive->pFECBuffer); pReceive->pFECBuffer = NULL; PgmUnlock (pReceive, OldIrq2); PgmUnlock (pAddress, OldIrq1); PgmUnlock (&PgmDynamicConfig, OldIrq); *ppReceive = NULL; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmNewInboundConnection", "CreateFECContext returned <%x>\n", status); return (status); } 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'), PARITY_CONTEXT_LOOKASIDE_DEPTH); } pAddress->LastSpmSource = pAddress->FECOptions = pAddress->FECGroupSize = 0; } // // Initialize our Connect info // Save the SourceId and Src port for this connection // PgmCopyMemory (pReceive->GSI, pPgmHeader->CommonHeader.gSourceId, SOURCE_ID_LENGTH); PgmCopyMemory (&PortNum, &pPgmHeader->CommonHeader.SrcPort, sizeof (USHORT)); pReceive->TSIPort = 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; ExInitializeNPagedLookasideList (&pReceive->pReceiver->NonParityContextLookaside, NULL, NULL, 0, sizeof (tNAK_FORWARD_DATA), PGM_TAG ('2'), NON_PARITY_CONTEXT_LOOKASIDE_DEPTH); // // 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->OutstandingNakTimeout = INITIAL_NAK_OUTSTANDING_TIMEOUT_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS; 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); // // Since this is the first receive for this session, see if we need to // start the receive timer // KeQueryPerformanceCounter (&Frequency); PgmDynamicConfig.TimeoutGranularity.QuadPart = (Frequency.QuadPart * BASIC_TIMER_GRANULARITY_IN_MSECS) / 1000; InsertTailList (&PgmDynamicConfig.CurrentReceivers, &pReceive->pReceiver->Linkage); if (!(PgmDynamicConfig.GlobalFlags & PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING)) { PgmDynamicConfig.GlobalFlags |= PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING; PgmDynamicConfig.LastReceiverTimeout = KeQueryPerformanceCounter (NULL); pReceive->pReceiver->StartTickCount = PgmDynamicConfig.ReceiversTimerTickCount = 1; 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); PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "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)) { PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "ProcessReceiveCompletionRoutine", "pIrp=<%p>, pRcvBuffer=<%p>, Status=<%x> Length=<%d>\n", pIrp, Context, pIrp->IoStatus.Status, 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 { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessReceiveCompletionRoutine", "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, IN TDI_CMSGHDR *pControlData, 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 TSDULength, TSIPort, LocalSessionPort, PacketSessionPort; 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; DropCount = GetRandomInteger (MinDropInterval, MaxDropInterval); /* 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 ASSERT (BytesAvailable < MAX_RECEIVE_SIZE); PgmLock (&PgmDynamicConfig, OldIrq); if (BytesIndicated > PgmDynamicConfig.MaxMTU) { PgmDynamicConfig.MaxMTU = BytesIndicated; } if (!PGM_VERIFY_HANDLE (pAddress, PGM_VERIFY_ADDRESS)) { PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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 // ASSERT (BytesIndicated <= BytesAvailable); if (!(ReceiveDatagramFlags & TDI_RECEIVE_ENTIRE_MESSAGE) && (BytesAvailable != BytesIndicated)) { // // // 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; PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "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; PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "TdiRcvDatagramHandler", "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*4 + sizeof(tCOMMON_HEADER))) || (pIpAddress->TAAddressCount != 1) || (pIpAddress->Address[0].AddressLength != TDI_ADDRESS_LENGTH_IP) || (pIpAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_IP)) { // // Need to get at least our header from transport! // PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "TdiRcvDatagramHandler", "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)); PgmCopyMemory (&TSDULength, &pPgmHeader->TSDULength, sizeof (USHORT)); TSDULength = ntohs (TSDULength); BytesIndicated -= (pIp->HeaderLength * 4); BytesAvailable -= (pIp->HeaderLength * 4); ASSERT (BytesIndicated == BytesAvailable); // // Now, Verify Checksum // if ((XSum = tcpxsum (0, (CHAR *) pPgmHeader, BytesIndicated)) != 0xffff) { // // Need to get at least our header from transport! // PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "TdiRcvDatagramHandler", "Bad Checksum on Pgm Packet (type=<%x>)! XSum=<%x> -- Rejecting packet\n", pPgmHeader->Type, XSum); // ASSERT (0); 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 (&TSIPort, &pPgmHeader->DestPort, sizeof (USHORT)); PgmCopyMemory (&PacketSessionPort, &pPgmHeader->SrcPort, sizeof (USHORT)); } else { PgmCopyMemory (&TSIPort, &pPgmHeader->SrcPort, sizeof (USHORT)); PgmCopyMemory (&PacketSessionPort, &pPgmHeader->DestPort, sizeof (USHORT)); } TSIPort = ntohs (TSIPort); PacketSessionPort = ntohs (PacketSessionPort); // // If this packet is for a different session port, drop it // if (pAddress->ReceiverMCastAddr) { LocalSessionPort = pAddress->ReceiverMCastPort; } else { LocalSessionPort = pAddress->SenderMCastPort; } if (LocalSessionPort != PacketSessionPort) { PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "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)) && (0 == strncmp (pSession->GSI, pPgmHeader->gSourceId, SOURCE_ID_LENGTH)) && (TSIPort == pSession->TSIPort) && !(pSession->SessionFlags & (PGM_SESSION_DISCONNECT_INDICATED | PGM_SESSION_TERMINATED_ABORT))) { if (pSession->pSender) { PGM_REFERENCE_SESSION_SEND (pSession, REF_SESSION_TDI_RCV_HANDLER, TRUE); PgmUnlock (pSession, OldIrq1); break; } ASSERT (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 { PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "TdiRcvDatagramHandler", "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; } 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 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, pSourceAddress, ReceiveDatagramFlags, (tBASIC_DATA_PACKET_HEADER UNALIGNED *) pPgmHeader, BytesIndicated, &pSession); if (!NT_SUCCESS (status)) { PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "TdiRcvDatagramHandler", "pAddress=<%p> FAILed to accept new connection, PacketType=<%x>, status=<%x>\n", pAddress, PacketType, status); } } else if (PacketType == PACKET_TYPE_SPM) { 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 ((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); } } // // Now, handle the packet appropriately // status = PgmProcessIncomingPacket (pAddress, pSession, SourceAddressLength, pSourceAddress, BytesIndicated, pPgmHeader, PacketType); PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "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); PgmLog (PGM_LOG_ERROR, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "PgmCancelReceiveIrp", "pIrp=<%p> pReceive=<%p>, pAddress=<%p>\n", pIrp, pReceive, pReceive->pReceiver->pAddress); 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))) { PgmLog (PGM_LOG_ERROR, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "PgmReceive", "Invalid Handles pReceive=<%p>, pAddress=<%p>\n", pReceive, pAddress); status = STATUS_INVALID_HANDLE; } else if (pReceive->SessionFlags & PGM_SESSION_DISCONNECT_INDICATED) { PgmLog (PGM_LOG_INFORM_PATH, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "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); PgmLog (PGM_LOG_ERROR, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "PgmReceive", "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); PgmLog (PGM_LOG_ERROR, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "PgmReceive", "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); PgmLog (PGM_LOG_INFORM_ALL_FUNCS, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "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 // 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); } //----------------------------------------------------------------------------