windows-nt/Source/XPSP1/NT/net/mcast/pgm/sys/receive.c
2020-09-26 16:20:57 +08:00

6171 lines
229 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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