Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6864 lines
259 KiB

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