|
|
/*++
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; i<NumPackets; i++) { ASSERT (pNak->pPendingData[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; i<pNak->OriginalGroupSize; i++) { pNak->pPendingData[i].ActualIndexOfDataPacket = pNak->OriginalGroupSize; }
//
// Set the indices only for the data packets
//
for (i=0; i<TotalPackets; i++) { if (pNak->pPendingData[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; i<pNak->OriginalGroupSize; i++) { pNak->pPendingData[i].ActualIndexOfDataPacket = pNak->OriginalGroupSize; }
//
// Set the indices only for the data packets
//
for (i=0; i<TotalPackets; i++) { if (pNak->pPendingData[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; i<pNakSequences->NumSequences; 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; i<pNak->PacketsInGroup; 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; i<pNak->PacketsInGroup; 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); i<pNak->PacketsInGroup; 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; i<pLastNak->OriginalGroupSize; 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; j<pLastNak->PacketsInGroup; 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; i<pNewNak->OriginalGroupSize; 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; i<pReceive->FECGroupSize; 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; i<pReceive->FECGroupSize; 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; i<pNak->PacketsInGroup; 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; i<pLastNak->PacketsInGroup; 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); }
//----------------------------------------------------------------------------
|