windows-nt/Source/XPSP1/NT/net/sfm/atalk/sys/araputil.c
2020-09-26 16:20:57 +08:00

3190 lines
84 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
araputil.c
Abstract:
This module implements utility routines needed for the ARAP functionality
Author:
Shirish Koti
Revision History:
15 Nov 1996 Initial Version
--*/
#include <atalk.h>
#pragma hdrstop
// File module number for errorlogging
#define FILENUM ARAPUTIL
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE_ARAP, DerefMnpSendBuf)
#pragma alloc_text(PAGE_ARAP, DerefArapConn)
#pragma alloc_text(PAGE_ARAP, ArapReleaseAddr)
#pragma alloc_text(PAGE_ARAP, ArapCleanup)
#pragma alloc_text(PAGE_ARAP, PrepareConnectionResponse)
#pragma alloc_text(PAGE_ARAP, ArapExtractAtalkSRP)
#pragma alloc_text(PAGE_ARAP, ArapQueueSendBytes)
#pragma alloc_text(PAGE_ARAP, ArapGetSendBuf)
#pragma alloc_text(PAGE_ARAP, ArapRefillSendQ)
#endif
//***
//
// Function: AllocArapConn
// Allocate a connection element and initialize fields
//
// Parameters: none
//
// Return: pointer to a newly allocated connection element
//
//***$
PARAPCONN
AllocArapConn(
IN ULONG LinkSpeed
)
{
PARAPCONN pArapConn;
v42bis_connection_t *pV42bis;
PUCHAR pBuf;
LONG RetryTime;
if ( (pArapConn = AtalkAllocZeroedMemory(sizeof(ARAPCONN))) == NULL)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("AllocArapConn: alloc failed\n"));
return(NULL);
}
//
// allocate v42bis buffers (it v42bis is enabled that is)
//
if (ArapGlobs.V42bisEnabled)
{
pV42bis = AtalkAllocZeroedMemory(sizeof(v42bis_connection_t));
if (pV42bis == NULL)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("AllocArapConn: alloc for v42 failed\n"));
AtalkFreeMemory(pArapConn);
return(NULL);
}
//
// allocate overflow buffer for the decode side
//
if ((pBuf = AtalkAllocZeroedMemory(MAX_P2)) == NULL)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("AllocArapConn: alloc for v42-2 failed\n"));
AtalkFreeMemory(pArapConn);
AtalkFreeMemory(pV42bis);
return(NULL);
}
pV42bis->decode.OverFlowBuf = pBuf;
pV42bis->decode.OverFlowBytes = 0;
pV42bis->encode.OverFlowBuf = pBuf;
pV42bis->encode.OverFlowBytes = 0;
pArapConn->pV42bis = pV42bis;
}
//
// if v42bis is not enabled, don't need no buffers!
//
else
{
pArapConn->pV42bis = NULL;
}
#if DBG
pArapConn->Signature = ARAPCONN_SIGNATURE;
//
// for debug builds, we can set registry parm to keep a trace of events
// If that setting is enabled, alloc a buffer to store the trace
//
if (ArapGlobs.SniffMode)
{
pBuf = AtalkAllocZeroedMemory(ARAP_SNIFF_BUFF_SIZE);
if (pBuf == NULL)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("AllocArapConn: alloc for trace buffer failed\n"));
// don't fail the call if this alloc fails
}
pArapConn->pDbgTraceBuffer = pArapConn->pDbgCurrPtr = pBuf;
// put in a guard signature, to catch overrun
if (pArapConn->pDbgTraceBuffer)
{
*((DWORD *)&(pArapConn->pDbgTraceBuffer[ARAP_SNIFF_BUFF_SIZE-4])) = 0xcafebeef;
}
}
#endif
pArapConn->State = MNP_IDLE;
// Creation refcount and a line-up refcount
pArapConn->RefCount = 2;
pArapConn->MnpState.WindowSize = ArapGlobs.MaxLTFrames;
pArapConn->MnpState.RecvCredit = pArapConn->MnpState.WindowSize;
pArapConn->LinkSpeed = LinkSpeed;
//
// T401 timer value in tick counts (1 tick = 100ms).
// we'll keep this at 1 second (i.e. 10 ticks) for a 33.6 (and faster) modem.
// If we are on a slower modem, increase it proportionately. So, a 28.8 modem
// would get RetryTime=2.0sec, 14.4 would get 3.7sec, 9600 baud would get 4.2sec etc.
//
RetryTime = 15;
if (LinkSpeed < 336)
{
RetryTime += (((336 - LinkSpeed) + 5)/10);
}
// make sure our calculation didn't go haywire...
if (RetryTime < ARAP_MIN_RETRY_INTERVAL)
{
RetryTime = ARAP_MIN_RETRY_INTERVAL;
}
else if (RetryTime > ARAP_MAX_RETRY_INTERVAL)
{
RetryTime = ARAP_MAX_RETRY_INTERVAL;
}
pArapConn->SendRetryTime = RetryTime;
pArapConn->SendRetryBaseTime = RetryTime;
// T402 is 0.5 times T401 value
pArapConn->T402Duration = (pArapConn->SendRetryTime/2);
//
// T403 should be at least 59 seconds. We don't really kill after this
// period of inactivity. We simply tell the dll and it does whatever is the
// policy: so just use whatever time period the dll tells us
//
pArapConn->T403Duration = ArapGlobs.MnpInactiveTime;
//
// T404: spec says 3 sec for 2400 baud and faster, 7 sec for anything slower
// Let's use 4 seconds here
//
pArapConn->T404Duration = 30;
// initialize all the timer values
pArapConn->LATimer = 0;
pArapConn->InactivityTimer = pArapConn->T403Duration + AtalkGetCurrentTick();
// set this to a high value for now: we'll set it when the conn is up
pArapConn->FlowControlTimer = AtalkGetCurrentTick() + 36000;
InitializeListHead(&pArapConn->MiscPktsQ);
InitializeListHead(&pArapConn->ReceiveQ);
InitializeListHead(&pArapConn->ArapDataQ);
InitializeListHead(&pArapConn->RetransmitQ);
InitializeListHead(&pArapConn->HighPriSendQ);
InitializeListHead(&pArapConn->MedPriSendQ);
InitializeListHead(&pArapConn->LowPriSendQ);
InitializeListHead(&pArapConn->SendAckedQ);
INITIALIZE_SPIN_LOCK(&pArapConn->SpinLock);
// start the retransmit timer for this connection
AtalkTimerInitialize( &pArapConn->RetryTimer,
(TIMER_ROUTINE)ArapRetryTimer,
ARAP_TIMER_INTERVAL) ;
AtalkTimerScheduleEvent(&pArapConn->RetryTimer);
pArapConn->Flags |= RETRANSMIT_TIMER_ON;
// put a timer refcount
pArapConn->RefCount++;
return( pArapConn );
}
//***
//
// Function: ArapAcceptIrp
// Determine if the irp submitted by the dll is acceptable now
//
// Parameters: pIrp - the incoming irp
// IoControlCode - the control code (what's the irp for?)
// pfDerefDefPort - was default adapter referenced?
//
// Return: TRUE if the irp is acceptable, FALSE otherwise
//
//***$
BOOLEAN
ArapAcceptIrp(
IN PIRP pIrp,
IN ULONG IoControlCode,
IN BOOLEAN *pfDerefDefPort
)
{
KIRQL OldIrql;
PARAP_SEND_RECV_INFO pSndRcvInfo=NULL;
PARAPCONN pArapConn;
BOOLEAN fAccept=FALSE;
BOOLEAN fUnblockPnP=FALSE;
*pfDerefDefPort = FALSE;
//
// we allow these ioctls to come in any time
//
if ((IoControlCode == IOCTL_ARAP_SELECT) ||
(IoControlCode == IOCTL_ARAP_DISCONNECT) ||
(IoControlCode == IOCTL_ARAP_CONTINUE_SHUTDOWN) )
{
return(TRUE);
}
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pArapConn = pSndRcvInfo->AtalkContext;
//
// put a IrpProcess refcount on the default port so it won't go away via pnp etc.
//
if (!AtalkReferenceRasDefPort())
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapProcessIoctl: Default port gone, or going %lx not accepted (%lx)\n",
pIrp,IoControlCode));
pSndRcvInfo->StatusCode = ARAPERR_STACK_IS_NOT_ACTIVE;
return(FALSE);
}
// note the fact that we have referenced the default adapter
*pfDerefDefPort = TRUE;
//
// now it's simple to decide whether we want to accept this irp or not
//
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql);
fAccept = (ArapStackState == ARAP_STATE_ACTIVE) ? TRUE : FALSE;
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
return(fAccept);
}
//***
//
// Function: ArapCancelIrp
// Cancel the irp. Currently, only select irps are cancellable
//
// Parameters: pIrp - the incoming irp
//
// Return: none
//
//***$
VOID
ArapCancelIrp(
IN PIRP pIrp
)
{
KIRQL OldIrql;
PLIST_ENTRY pList;
PARAPCONN pArapConn;
NTSTATUS ReturnStatus=STATUS_SUCCESS;
//
// kill all connections and don't accept any more irps:
// cancelling a select irp is a blasphemy!
//
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql);
if (ArapSelectIrp == NULL)
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,("ArapCancelIrp: weird race condition!\n"));
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
return;
}
ASSERT (pIrp == ArapSelectIrp);
ArapSelectIrp = NULL;
if (ArapStackState == ARAP_STATE_ACTIVE)
{
ArapStackState = ARAP_STATE_ACTIVE_WAITING;
}
else if (ArapStackState == ARAP_STATE_INACTIVE)
{
ArapStackState = ARAP_STATE_INACTIVE_WAITING;
}
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
if (RasPortDesc == NULL)
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,("ArapCancelIrp: RasPortDesc is null!\n"));
ARAP_COMPLETE_IRP(pIrp, 0, STATUS_CANCELLED, &ReturnStatus);
return;
}
//
// now, go kill all the arap connections
//
ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
pList = RasPortDesc->pd_ArapConnHead.Flink;
while (pList != &RasPortDesc->pd_ArapConnHead)
{
pArapConn = CONTAINING_RECORD(pList, ARAPCONN, Linkage);
ASSERT(pArapConn->Signature == ARAPCONN_SIGNATURE);
// if this connection is already disconnected, skip it
ACQUIRE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
if (pArapConn->State == MNP_DISCONNECTED)
{
pList = pArapConn->Linkage.Flink;
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
continue;
}
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("ArapCancelIrp: killing ARAP connection %lx\n",pArapConn));
ArapCleanup(pArapConn);
ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
pList = RasPortDesc->pd_ArapConnHead.Flink;
}
//
// walk through the list to see if any connection(s) disconnected but is
// waiting for a select irp to come down. We know the select irp is never
// going to come down, so pretend that dll has been told and deref the puppy
// for telling the dll (which will probably free the connection)
//
pList = RasPortDesc->pd_ArapConnHead.Flink;
while (pList != &RasPortDesc->pd_ArapConnHead)
{
pArapConn = CONTAINING_RECORD(pList, ARAPCONN, Linkage);
ASSERT(pArapConn->Signature == ARAPCONN_SIGNATURE);
ACQUIRE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
if (pArapConn->Flags & DISCONNECT_NO_IRP)
{
pArapConn->Flags &= ~DISCONNECT_NO_IRP;
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("ArapCancelIrp: faking dll-completion for dead connection %lx\n",pArapConn));
DerefArapConn(pArapConn);
ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
pList = RasPortDesc->pd_ArapConnHead.Flink;
}
else
{
pList = pArapConn->Linkage.Flink;
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
}
}
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
// finally, complete that cancelled irp!
ARAP_COMPLETE_IRP(pIrp, 0, STATUS_CANCELLED, &ReturnStatus);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapCancelIrp: select irp cancelled and completed (%lx)\n",pIrp));
}
//***
//
// Function: ArapGetSelectIrp
// Get the select irp, after some checks
//
// Parameters: ppIrp - pointer to irp pointer, contains select irp on return
//
// Return: none
//
//***$
VOID
ArapGetSelectIrp(
IN PIRP *ppIrp
)
{
KIRQL OldIrql;
KIRQL OldIrql2;
*(ppIrp) = NULL;
IoAcquireCancelSpinLock(&OldIrql);
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql2);
if (ArapSelectIrp && (!ArapSelectIrp->Cancel))
{
ArapSelectIrp->CancelRoutine = NULL;
*(ppIrp) = ArapSelectIrp;
ArapSelectIrp = NULL;
}
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql2);
IoReleaseCancelSpinLock(OldIrql);
}
//***
//
// Function: FindArapConnByContx
// Finds the corresponding connection element, given the dll's
// context
//
// Parameters: pDllContext - the dll context for the connection
//
// Return: pointer to the corresponding connection element, if found
//
//***$
PARAPCONN
FindArapConnByContx(
IN PVOID pDllContext
)
{
PARAPCONN pArapConn=NULL;
PARAPCONN pWalker;
PLIST_ENTRY pList;
KIRQL OldIrql;
// ARAP not configured?
if (!RasPortDesc)
{
return(NULL);
}
ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
if (!(RasPortDesc->pd_Flags & PD_ACTIVE))
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("FindArapConnByContx: ArapPort not active, ignoring\n"));
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
return(NULL);
}
pList = RasPortDesc->pd_ArapConnHead.Flink;
//
// walk through all the Arap clients to see if we find ours
//
while (pList != &RasPortDesc->pd_ArapConnHead)
{
pWalker = CONTAINING_RECORD(pList, ARAPCONN, Linkage);
pList = pWalker->Linkage.Flink;
if (pWalker->pDllContext == pDllContext)
{
pArapConn = pWalker;
break;
}
}
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
return( pArapConn );
}
//***
//
// Function: FindAndRefArapConnByAddr
// Finds the corresponding connection element, given the network
// address (of the remote client)
//
// Parameters: destNode - network addr of the destination (remote client)
// pdwFlags - pointer to a dword to return Flags field
//
// Return: pointer to the corresponding connection element, if found
//
//***$
PARAPCONN
FindAndRefArapConnByAddr(
IN ATALK_NODEADDR destNode,
OUT DWORD *pdwFlags
)
{
PARAPCONN pArapConn=NULL;
PARAPCONN pWalker;
PLIST_ENTRY pList;
KIRQL OldIrql;
// ARAP not configured?
if (!RasPortDesc)
{
return(NULL);
}
ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
if (!(RasPortDesc->pd_Flags & PD_ACTIVE))
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("FindAndRefArapConnByAddr: ArapPort not active, ignoring\n"));
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
return(NULL);
}
pList = RasPortDesc->pd_ArapConnHead.Flink;
//
// walk through all the Arap clients to see if we find ours
//
while (pList != &RasPortDesc->pd_ArapConnHead)
{
pWalker = CONTAINING_RECORD(pList, ARAPCONN, Linkage);
pList = pWalker->Linkage.Flink;
if (ATALK_NODES_EQUAL(&pWalker->NetAddr, &destNode))
{
ACQUIRE_SPIN_LOCK_DPC(&pWalker->SpinLock);
//
// we return the pArapConn only if the MNP connection is up and
// and the ARAP connection is also up (or, if ARAP connection isn't
// up yet then only if we are searching for a node)
//
//
if ((pWalker->State == MNP_UP) &&
((pWalker->Flags & ARAP_CONNECTION_UP) ||
(pWalker->Flags & ARAP_FINDING_NODE)) )
{
pArapConn = pWalker;
pArapConn->RefCount++;
*pdwFlags = pWalker->Flags;
}
else if (pWalker->Flags & ARAP_CONNECTION_UP)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("FindAndRefArapConnByAddr: found pArapConn (%lx), but state=%ld,Flags=%lx\n",
pWalker,pWalker->State,pWalker->Flags));
}
RELEASE_SPIN_LOCK_DPC(&pWalker->SpinLock);
break;
}
}
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
return(pArapConn);
}
//***
//
// Function: FindAndRefRasConnByAddr
// Finds the corresponding connection element, given the network
// address (of the remote client)
//
// Parameters: destNode - network addr of the destination (remote client)
// pdwFlags - pointer to a dword to return Flags field
// pfThisIsPPP - pointer to a bool: is this PPP or ARAP connection?
//
// Return: pointer to the corresponding connection element, if found
//
//***$
PVOID
FindAndRefRasConnByAddr(
IN ATALK_NODEADDR destNode,
OUT DWORD *pdwFlags,
OUT BOOLEAN *pfThisIsPPP
)
{
PVOID pRasConn;
// RAS not configured?
if (!RasPortDesc)
{
return(NULL);
}
pRasConn = (PVOID)FindAndRefPPPConnByAddr(destNode,pdwFlags);
if (pRasConn)
{
*pfThisIsPPP = TRUE;
return(pRasConn);
}
pRasConn = FindAndRefArapConnByAddr(destNode, pdwFlags);
*pfThisIsPPP = FALSE;
return(pRasConn);
}
//***
//
// Function: ArapConnIsValid
// Make sure that what we think is a connection is indeed a
// connection (i.e., it's there in our list of connections)
//
// Parameters: pArapConn - the connection in question
//
// Return: TRUE if the connection is valid, FALSE otherwise
//
//***$
BOOLEAN
ArapConnIsValid(
IN PARAPCONN pArapConn
)
{
PARAPCONN pWalker;
PLIST_ENTRY pList;
KIRQL OldIrql;
BOOLEAN fIsValid=FALSE;
// ARAP not configured?
if (!RasPortDesc)
{
return(FALSE);
}
ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
pList = RasPortDesc->pd_ArapConnHead.Flink;
//
// walk through all the Arap conns and see if we find the given conn
//
while (pList != &RasPortDesc->pd_ArapConnHead)
{
pWalker = CONTAINING_RECORD(pList, ARAPCONN, Linkage);
pList = pWalker->Linkage.Flink;
if (pWalker == pArapConn)
{
ACQUIRE_SPIN_LOCK_DPC(&pWalker->SpinLock);
ASSERT(pWalker->Signature == ARAPCONN_SIGNATURE);
if (!(pWalker->Flags & ARAP_GOING_AWAY))
{
// put a validation refcount
pWalker->RefCount++;
fIsValid = TRUE;
}
RELEASE_SPIN_LOCK_DPC(&pWalker->SpinLock);
break;
}
}
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
return( fIsValid );
}
//***
//
// Function: DerefMnpSendBuf
// This routine dereferences the MNP send. When the refcount
// goes to zero, we free it
//
// Parameters: pMnpSendBuf - the MNP send to be dereferenced
//
// Return: Nothing
//
//***$
VOID
DerefMnpSendBuf(
IN PMNPSENDBUF pMnpSendBuf,
IN BOOLEAN fNdisSendComplete
)
{
KIRQL OldIrql;
PARAPCONN pArapConn;
BOOLEAN fFreeIt=FALSE;
DBG_ARAP_CHECK_PAGED_CODE();
pArapConn = pMnpSendBuf->pArapConn;
ARAPTRACE(("Entered DerefMnpSendBuf (%lx %lx refcount=%d ComplFn=%lx)\n",
pArapConn,pMnpSendBuf,pMnpSendBuf->RefCount,pMnpSendBuf->ComplRoutine));
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
//
// catch weird things like freeing twice
// (we subtract 0x100 in completion routine, to mark that it has run)
//
ASSERT((pMnpSendBuf->Signature == MNPSMSENDBUF_SIGNATURE) ||
(pMnpSendBuf->Signature == MNPSMSENDBUF_SIGNATURE-0x100) ||
(pMnpSendBuf->Signature == MNPLGSENDBUF_SIGNATURE) ||
(pMnpSendBuf->Signature == MNPLGSENDBUF_SIGNATURE-0x100));
ASSERT(pMnpSendBuf->RefCount > 0);
// Mark that Ndis completed our send
if (fNdisSendComplete)
{
pMnpSendBuf->Flags = 0;
}
pMnpSendBuf->RefCount--;
if (pMnpSendBuf->RefCount == 0)
{
fFreeIt = TRUE;
}
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
if (!fFreeIt)
{
return;
}
// make sure it's not still sitting on retransmit queue
ASSERT(IsListEmpty(&pMnpSendBuf->Linkage));
#if DBG
pMnpSendBuf->Signature--;
#endif
ArapNdisFreeBuf(pMnpSendBuf);
// remove that MNPSend refcount
DerefArapConn(pArapConn);
}
//***
//
// Function: DerefArapConn
// Decrements the refcount of the connection element by 1. If the
// refcount goes to 0, releases network addr and frees it
//
// Parameters: pArapConn - connection element in question
//
// Return: none
//
//***$
VOID
DerefArapConn(
IN PARAPCONN pArapConn
)
{
KIRQL OldIrql;
BOOLEAN fKill = FALSE;
DBG_ARAP_CHECK_PAGED_CODE();
ARAPTRACE(("Entered DerefArapConn (%lx refcount=%d)\n",pArapConn,pArapConn->RefCount));
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
ASSERT(pArapConn->Signature == ARAPCONN_SIGNATURE);
ASSERT(pArapConn->RefCount > 0);
pArapConn->RefCount--;
if (pArapConn->RefCount == 0)
{
fKill = TRUE;
pArapConn->Flags |= ARAP_GOING_AWAY;
}
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
if (!fKill)
{
return;
}
ASSERT(pArapConn->pRecvIoctlIrp == NULL);
ASSERT(pArapConn->pIoctlIrp == NULL);
ASSERT(IsListEmpty(&pArapConn->HighPriSendQ));
ASSERT(IsListEmpty(&pArapConn->MedPriSendQ));
ASSERT(IsListEmpty(&pArapConn->LowPriSendQ));
ASSERT(IsListEmpty(&pArapConn->SendAckedQ));
ASSERT(IsListEmpty(&pArapConn->ReceiveQ));
ASSERT(IsListEmpty(&pArapConn->ArapDataQ));
ASSERT(IsListEmpty(&pArapConn->RetransmitQ));
/* ASSERT(pArapConn->SendsPending == 0); */
ASSERT(!(pArapConn->Flags & RETRANSMIT_TIMER_ON));
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("DerefArapConn: refcount 0, freeing %lx\n", pArapConn));
ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
RemoveEntryList(&pArapConn->Linkage);
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
#if ARAP_STATIC_MODE
// "release" the network addr used by this client
ArapReleaseAddr(pArapConn);
#endif //ARAP_STATIC_MODE
// free that v42bis buffer
if (pArapConn->pV42bis)
{
// free the decode-side overflow buffer
AtalkFreeMemory(pArapConn->pV42bis->decode.OverFlowBuf);
AtalkFreeMemory(pArapConn->pV42bis);
}
#if DBG
ArapDbgDumpMnpHist(pArapConn);
// if we had allocated a sniff buffer, free it
if (pArapConn->pDbgTraceBuffer)
{
AtalkFreeMemory(pArapConn->pDbgTraceBuffer);
}
//
// let's catch if someone tries to access this after a free
//
RtlFillMemory(pArapConn,sizeof(ARAPCONN),0x7);
pArapConn->Signature = 0xDEADBEEF;
#endif
// and finally, we say good bye
AtalkFreeMemory(pArapConn);
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql);
ArapConnections--;
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
#if ARAP_STATIC_MODE
// This will delete a route if this is the last connection that vanished
ArapDeleteArapRoute();
#endif //ARAP_STATIC_MODE
// connection is completely gone: see if arap pages can be unlocked
AtalkUnlockArapIfNecessary();
}
//***
//
// Function: ArapCleanup
// Once the client goes into the disconnecting state (as a result
// of either local side or remote side initiating the disconnect)
// this routine gets called. This does all the cleanup, such as
// completing all unacked sends, uncompleted receives, stopping
// retransmit timers, completing any irp in progress.
//
// Parameters: pArapConn - connection element being cleaned up
//
// Return: none
//
//***$
VOID
ArapCleanup(
IN PARAPCONN pArapConn
)
{
KIRQL OldIrql;
PIRP pIrp;
PIRP pRcvIrp;
PIRP pDiscIrp;
PARAP_SEND_RECV_INFO pSndRcvInfo;
PARAP_SEND_RECV_INFO pDiscSndRcvInfo;
PLIST_ENTRY pSendHighList;
PLIST_ENTRY pSendMedList;
PLIST_ENTRY pSendLowList;
PLIST_ENTRY pSendAckList;
PLIST_ENTRY pRcvList;
PLIST_ENTRY pArapList;
PLIST_ENTRY pReXmitList;
PLIST_ENTRY pMiscPktList;
PMNPSENDBUF pMnpSendBuf;
PARAPBUF pArapBuf;
DWORD StatusCode;
BOOLEAN fStopTimer=FALSE;
BOOLEAN fArapConnUp=FALSE;
DWORD dwBytesToDll;
NTSTATUS ReturnStatus=STATUS_SUCCESS;
DBG_ARAP_CHECK_PAGED_CODE();
// first thing, see if we should send out a disconnect message
// we make this check without holding the lock because ArapSendLDPacket does
// the right thing. If, due to an extremely tiny window, we don't call
// ArapSendLDPacket, no big deal!
if (pArapConn->State < MNP_LDISCONNECTING)
{
ArapSendLDPacket(pArapConn, 0xFF);
}
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
ASSERT(pArapConn->Signature == ARAPCONN_SIGNATURE);
// if we have already run the cleanup, nothing to do!
if (pArapConn->State == MNP_DISCONNECTED)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapCleanup: cleanup already done once on (%lx)\n",pArapConn));
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
return;
}
pSendHighList = pArapConn->HighPriSendQ.Flink;
InitializeListHead(&pArapConn->HighPriSendQ);
pSendMedList = pArapConn->MedPriSendQ.Flink;
InitializeListHead(&pArapConn->MedPriSendQ);
pSendLowList = pArapConn->LowPriSendQ.Flink;
InitializeListHead(&pArapConn->LowPriSendQ);
pSendAckList = pArapConn->SendAckedQ.Flink;
InitializeListHead(&pArapConn->SendAckedQ);
pRcvList = pArapConn->ReceiveQ.Flink;
InitializeListHead(&pArapConn->ReceiveQ);
pArapList = pArapConn->ArapDataQ.Flink;
InitializeListHead(&pArapConn->ArapDataQ);
pReXmitList = pArapConn->RetransmitQ.Flink;
InitializeListHead(&pArapConn->RetransmitQ);
pMiscPktList = pArapConn->MiscPktsQ.Flink;
InitializeListHead(&pArapConn->MiscPktsQ);
pIrp = pArapConn->pIoctlIrp;
pArapConn->pIoctlIrp = NULL;
pRcvIrp = pArapConn->pRecvIoctlIrp;
pArapConn->pRecvIoctlIrp = NULL;
if (pArapConn->State == MNP_RDISCONNECTING)
{
pArapConn->Flags |= ARAP_REMOTE_DISCONN;
StatusCode = ARAPERR_RDISCONNECT_COMPLETE;
}
else
{
StatusCode = ARAPERR_LDISCONNECT_COMPLETE;
}
pArapConn->State = MNP_DISCONNECTED;
pArapConn->Flags &= ~ARAP_DATA_WAITING;
// if the timer is running, stop it
if (pArapConn->Flags & RETRANSMIT_TIMER_ON)
{
fStopTimer = TRUE;
pArapConn->Flags &= ~RETRANSMIT_TIMER_ON;
// creation refcount and timer refcount better be on here
ASSERT(pArapConn->RefCount >= 2);
}
fArapConnUp = FALSE;
// was ARAP connection up?
if (pArapConn->Flags & ARAP_CONNECTION_UP)
{
fArapConnUp = TRUE;
}
#if DBG
//
// if we are sniffing, mark the sniff to indicate the end (so dll knows it
// got all the sniff info)
//
if (pArapConn->pDbgCurrPtr)
{
PSNIFF_INFO pSniff;
pSniff = (PSNIFF_INFO)(pArapConn->pDbgCurrPtr);
pSniff->Signature = ARAP_SNIFF_SIGNATURE;
pSniff->TimeStamp = (DWORD)AtalkGetCurrentTick();
pSniff->Location = 0xfedc;
pSniff->FrameLen = 0;
pArapConn->SniffedBytes += sizeof(SNIFF_INFO);
pArapConn->pDbgCurrPtr += sizeof(SNIFF_INFO);
}
#endif
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
if (fStopTimer)
{
if (AtalkTimerCancelEvent(&pArapConn->RetryTimer, NULL))
{
// remove the timer refcount
DerefArapConn(pArapConn);
}
}
//
// call completion routine for all the sends that have been acked
//
while (pSendAckList != &pArapConn->SendAckedQ)
{
pMnpSendBuf = CONTAINING_RECORD(pSendAckList,MNPSENDBUF,Linkage);
pSendAckList = pSendAckList->Flink;
#if DBG
InitializeListHead(&pMnpSendBuf->Linkage);
#endif
// call the completion routine which will do cleanup for this buffer
(pMnpSendBuf->ComplRoutine)(pMnpSendBuf,ARAPERR_NO_ERROR);
}
// whatever is on the ReceiveQ, try and send it off to the destination
// since this data came before the disconnect came in
while (1)
{
if ((pArapBuf = ArapExtractAtalkSRP(pArapConn)) == NULL)
{
// no more data left (or no complete SRP): done here
break;
}
// was ARAP connection up? route only if it's up, otherwise drop it!
if (fArapConnUp)
{
ArapRoutePacketFromWan( pArapConn, pArapBuf );
}
// we received AppleTalk data but connection wasn't up! Drop pkt
else
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapCleanup: (%lx) AT data, but conn not up\n",pArapConn));
}
#if DBG
//
// yeah, there is no spinlock: given our state, no one else will be
// touching this field. Besides, this is debug-only!
//
pArapConn->RecvsPending -= pArapBuf->DataSize;
#endif
// done with this buffer
ARAP_FREE_RCVBUF(pArapBuf);
}
//
// if there are any buffers left on the ReceiveQ, they are basically
// incomplete SRP's: throw them away
//
while (pRcvList != &pArapConn->ReceiveQ)
{
pArapBuf = CONTAINING_RECORD(pRcvList, ARAPBUF, Linkage);
pRcvList = pRcvList->Flink;
#if DBG
// same deal again with spinlock again...
pArapConn->RecvsPending -= pArapBuf->DataSize;
#endif
ARAP_FREE_RCVBUF(pArapBuf);
}
//
// free up all the packets on ArapDataQ: these are not going to be of
// any use, whether or not the connection was up, so best just to throw
// them away (why complicate life?)
//
while (pArapList != &pArapConn->ArapDataQ)
{
pArapBuf = CONTAINING_RECORD(pArapList, ARAPBUF, Linkage);
pArapList = pArapList->Flink;
#if DBG
// same deal again with spinlock again...
pArapConn->RecvsPending -= pArapBuf->DataSize;
#endif
ARAP_FREE_RCVBUF(pArapBuf);
}
//
// free up all the packets on the MiscPktsQ
//
while (pMiscPktList != &pArapConn->MiscPktsQ)
{
pArapBuf = CONTAINING_RECORD(pMiscPktList, ARAPBUF, Linkage);
pMiscPktList = pMiscPktList->Flink;
ARAP_FREE_RCVBUF(pArapBuf);
}
//
// call completion routine for all the sends that were waiting to be sent
// on the high priority Q
//
while (pSendHighList != &pArapConn->HighPriSendQ)
{
pMnpSendBuf = CONTAINING_RECORD(pSendHighList,MNPSENDBUF,Linkage);
pSendHighList = pSendHighList->Flink;
#if DBG
InitializeListHead(&pMnpSendBuf->Linkage);
#endif
// call the completion routine which will do cleanup for this buffer
(pMnpSendBuf->ComplRoutine)(pMnpSendBuf,ARAPERR_DISCONNECT_IN_PROGRESS);
}
//
// call completion routine for all the sends that were waiting to be sent
// on the Med priority Q
//
while (pSendMedList != &pArapConn->MedPriSendQ)
{
pMnpSendBuf = CONTAINING_RECORD(pSendMedList,MNPSENDBUF,Linkage);
pSendMedList = pSendMedList->Flink;
#if DBG
InitializeListHead(&pMnpSendBuf->Linkage);
#endif
// call the completion routine which will do cleanup for this buffer
(pMnpSendBuf->ComplRoutine)(pMnpSendBuf,ARAPERR_DISCONNECT_IN_PROGRESS);
}
//
// call completion routine for all the sends that were waiting to be sent
// on the Low priority Q
//
while (pSendLowList != &pArapConn->LowPriSendQ)
{
pMnpSendBuf = CONTAINING_RECORD(pSendLowList,MNPSENDBUF,Linkage);
pSendLowList = pSendLowList->Flink;
#if DBG
InitializeListHead(&pMnpSendBuf->Linkage);
#endif
// call the completion routine which will do cleanup for this buffer
(pMnpSendBuf->ComplRoutine)(pMnpSendBuf,ARAPERR_DISCONNECT_IN_PROGRESS);
}
//
// free up all the packets on RetransmitQ that were waiting to be acked
//
while (pReXmitList != &pArapConn->RetransmitQ)
{
pMnpSendBuf = CONTAINING_RECORD(pReXmitList, MNPSENDBUF, Linkage);
pReXmitList = pReXmitList->Flink;
#if DBG
InitializeListHead(&pMnpSendBuf->Linkage);
#endif
// call the completion routine which will do cleanup for this buffer
(pMnpSendBuf->ComplRoutine)(pMnpSendBuf,ARAPERR_DISCONNECT_IN_PROGRESS);
}
// if there was an irp in progress, complete it first!
if (pIrp)
{
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pSndRcvInfo->StatusCode = ARAPERR_DISCONNECT_IN_PROGRESS;
ARAP_COMPLETE_IRP(pIrp, sizeof(ARAP_SEND_RECV_INFO), STATUS_SUCCESS,
&ReturnStatus);
}
// if there was an irp in progress, complete it first!
if (pRcvIrp)
{
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pRcvIrp->AssociatedIrp.SystemBuffer;
pSndRcvInfo->StatusCode = ARAPERR_DISCONNECT_IN_PROGRESS;
ARAP_COMPLETE_IRP(pRcvIrp, sizeof(ARAP_SEND_RECV_INFO), STATUS_SUCCESS,
&ReturnStatus);
}
// if we have any sniff info, give it to dll.
ARAP_DUMP_DBG_TRACE(pArapConn);
//
// now, try telling the dll that the connection went away. (We can do this
// only using a "select" irp)
//
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
ArapGetSelectIrp(&pDiscIrp);
// no select irp? just mark that fact, so on next select we'll tell dll
if (!pDiscIrp)
{
pArapConn->Flags |= DISCONNECT_NO_IRP;
}
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
//
// if we did find select irp, go ahead and tell the dll
//
if (pDiscIrp)
{
dwBytesToDll = 0;
#if DBG
//
// if we have some sniff info that we couldn't deliver earlier through
// the sniff irp, then give them through this irp: it's going back
// "empty" anyway!
//
if (pArapConn->pDbgTraceBuffer && pArapConn->SniffedBytes > 0)
{
dwBytesToDll = ArapFillIrpWithSniffInfo(pArapConn,pDiscIrp);
}
#endif
pDiscSndRcvInfo = (PARAP_SEND_RECV_INFO)pDiscIrp->AssociatedIrp.SystemBuffer;
pDiscSndRcvInfo->pDllContext = pArapConn->pDllContext;
pDiscSndRcvInfo->AtalkContext = ARAP_INVALID_CONTEXT;
pDiscSndRcvInfo->DataLen = dwBytesToDll;
pDiscSndRcvInfo->StatusCode = StatusCode;
dwBytesToDll += sizeof(ARAP_SEND_RECV_INFO);
ARAP_COMPLETE_IRP(pDiscIrp, dwBytesToDll, STATUS_SUCCESS,
&ReturnStatus);
// we told dll: remove this link
pArapConn->pDllContext = NULL;
// we told the dll, remove the connect refcount
DerefArapConn(pArapConn);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapCleanup: told dll (%lx refcount=%d)\n",pArapConn,pArapConn->RefCount));
}
else
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapCleanup: no select irp, dll doesn't yet know (%lx) died\n", pArapConn));
}
// remove the creation refcount
DerefArapConn(pArapConn);
}
//***
//
// Function: PrepareConnectionResponse
// This routine parses the connect request that is first sent by
// the remote client and forms a connection response (LR frame)
// based on the options negotiated.
// This routine basically does the MNP negotiation.
//
// Parameters: pArapConn - connection element being cleaned up
// pReq - buffer containting client's original connect request
// pFrame - buffer in which we put the response
// BytesWritten - how big is the connection response
//
// Return: result of the operation (ARAPERR_....)
//
//***$
DWORD
PrepareConnectionResponse(
IN PARAPCONN pArapConn,
IN PBYTE pReq, // buffer with client's request
IN DWORD ReqLen, // how big is the request
OUT PBYTE pFrame, // buffer with our response to the client
OUT USHORT * pMnpLen
)
{
PBYTE pReqEnd;
PBYTE pFrameStart;
BYTE VarLen;
KIRQL OldIrql;
BYTE NumLTFrames=0;
USHORT MaxInfoLen=0;
USHORT FrameLen;
BYTE Mandatory[5];
BOOLEAN fOptimized=FALSE;
BOOLEAN fMaxLen256=FALSE;
BOOLEAN fV42Bis=FALSE;
BOOLEAN fArapV20=TRUE;
DWORD dwReqToSkip;
DWORD dwFrameToSkip;
BYTE JunkBuffer[MNP_MINPKT_SIZE+1];
DWORD i;
BOOLEAN fNonStd=FALSE;
DBG_ARAP_CHECK_PAGED_CODE();
#if DBG
//
// validate our assumption that all the clients always send out the same
// LR request packet
//
for (i=0; i<sizeof(ArapDbgLRPacket); i++ )
{
if (pReq[3+i] != ArapDbgLRPacket[i])
{
fNonStd = TRUE;
}
}
if (fNonStd)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("Arap Prepare..Response: non-standard LR-request packet\n"));
DbgPrint(" Std : ");
for (i=0; i<sizeof(ArapDbgLRPacket); i++)
{
DbgPrint("%2X ",ArapDbgLRPacket[i]);
}
DbgPrint("\n");
DbgPrint(" This: ");
for (i=0; i<sizeof(ArapDbgLRPacket); i++)
{
DbgPrint("%2X ",pReq[3+i]);
}
DbgPrint("\n");
}
#endif
for (i=0; i<5; i++)
{
Mandatory[i] = 0;
}
//
// in case of callback, we get an LR response from the dial-in client. When
// that happens, we still want to run through this routine to make sure all
// parameters are legal, etc., and also to "configure" the connection based
// on the parms negotiated. In that case, however, there is no output frame
// needed. So, we just write the output frame to junkbuffer
// which is obviously never used.
//
if (pFrame == NULL)
{
pFrame = &JunkBuffer[0];
}
pFrameStart = pFrame;
//
// see if this is ARAP v2.0 or v1.0 connection: we know the frame is good,
// so just look at the first byte
//
if (*pReq == MNP_SYN)
{
fArapV20 = FALSE;
}
// now copy those three start flag bytes
*pFrame++ = *pReq++;
*pFrame++ = *pReq++;
*pFrame++ = *pReq++;
// first byte (after the start flag, that is) is the length byte
FrameLen = *pReq;
if ((FrameLen > ReqLen) || (FrameLen > MNP_MINPKT_SIZE))
{
ASSERT(0);
return(ARAPERR_BAD_FORMAT);
}
pReqEnd = pReq + FrameLen;
pReq += 2; // skip over length ind. and type ind.
// Constant parameter 1 must have a value of 2
if ((*pReq++) != MNP_LR_CONST1 )
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("Error: MNP_LR_CONST1 missing in conn req %lx:\n",pArapConn));
return(ARAPERR_BAD_FORMAT);
}
// we build the return frame as we parse the incoming frame: we are supposed
// to return only those options that we both receive and understand.
//
pFrame++; // we'll put the length byte at the end
*pFrame++ = MNP_LR;
*pFrame++ = MNP_LR_CONST1;
// parse all the "variable" parms until we reach end of the frame
//
while (pReq < pReqEnd)
{
switch (*pReq++)
{
//
// nothing to do here, other than verify it's valid
//
case MNP_LR_CONST2:
VarLen = *pReq++;
if ( (VarLen == 6) &&
(*(pReq ) == 1) && (*(pReq+1) == 0) &&
(*(pReq+2) == 0) && (*(pReq+3) == 0) &&
(*(pReq+4) == 0) && (*(pReq+5) == 255) )
{
*pFrame++ = MNP_LR_CONST2;
*pFrame++ = VarLen;
RtlCopyMemory(pFrame, pReq, VarLen);
pFrame += VarLen;
pReq += VarLen;
}
else
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("Error: bad MNP_LR_CONST2 in conn req %lx:\n",pArapConn));
return(ARAPERR_BAD_FORMAT);
}
Mandatory[0] = 1;
break;
//
// octet-oriented or bit-oriented framing
//
case MNP_LR_FRAMING:
pReq++; // skip over length byte
//
// we only support octet-oriented framing mode
//
if (*pReq++ < MNP_FRMMODE_OCTET)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("Error: (%lx) unsupported framing mode %d requested:\n",
pArapConn,*(pReq-1)));
ASSERT(0);
return(ARAPERR_BAD_FORMAT);
}
*pFrame++ = MNP_LR_FRAMING;
*pFrame++ = 1;
*pFrame++ = MNP_FRMMODE_OCTET;
Mandatory[1] = 1;
break;
//
// Max. number of outstanding LT frames, k
//
case MNP_LR_NUMLTFRMS:
pReq++; // skip over length byte
NumLTFrames = *pReq++;
if (NumLTFrames > ArapGlobs.MaxLTFrames)
{
NumLTFrames = ArapGlobs.MaxLTFrames;
}
*pFrame++ = MNP_LR_NUMLTFRMS;
*pFrame++ = 1;
*pFrame++ = NumLTFrames;
Mandatory[2] = 1;
break;
//
// Max. information field length, N401
//
case MNP_LR_INFOLEN:
pReq++; // skip over length byte
MaxInfoLen = *((USHORT *)pReq);
ASSERT(MaxInfoLen <= 256);
*pFrame++ = MNP_LR_INFOLEN;
*pFrame++ = 2;
// we are just copying whatever client gave
*pFrame++ = *pReq++;
*pFrame++ = *pReq++;
Mandatory[3] = 1;
break;
//
// data optimization info
//
case MNP_LR_DATAOPT:
pReq++; // skip over length byte
if ((*pReq) & 0x1)
{
fMaxLen256 = TRUE;
}
if ((*pReq) & 0x2)
{
fOptimized = TRUE;
}
*pFrame++ = MNP_LR_DATAOPT;
*pFrame++ = 1;
*pFrame++ = *pReq++;
Mandatory[4] = 1;
break;
//
// v42 parameter negotiation
//
case MNP_LR_V42BIS:
fV42Bis = v42bisInit( pArapConn,
pReq,
&dwReqToSkip,
pFrame,
&dwFrameToSkip );
pReq += dwReqToSkip;
pFrame += dwFrameToSkip;
break;
//
// what the heck is this option? just skip over it!
//
default:
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("Prepare..Response (%lx): unknown option %lx len=%ld type=%ld\n",
pArapConn, *(pReq-1), *pReq, *(pReq+1)));
VarLen = *pReq++;
pReq += VarLen;
break;
}
}
//
// make sure we got all the mandatory parameters
//
for (i=0; i<5; i++)
{
if (Mandatory[i] == 0)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("PrepareConnectionResponse: parm %d missing (%lx):\n",i,pArapConn));
return(ARAPERR_BAD_FORMAT);
}
}
// copy the stop flag
*pFrame++ = (fArapV20)? MNP_ESC : MNP_DLE;
*pFrame++ = MNP_ETX;
// store all the negotiated info
pArapConn->BlockId = (fMaxLen256)? BLKID_MNP_LGSENDBUF : BLKID_MNP_SMSENDBUF;
if (fOptimized)
{
pArapConn->Flags |= MNP_OPTIMIZED_DATA;
}
if (fV42Bis)
{
pArapConn->Flags |= MNP_V42BIS_NEGOTIATED;
}
else
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("Prepare..Response: WARNING!! v42bis NOT negotiated on (%lx):\n",pArapConn));
}
if (fArapV20)
{
pArapConn->Flags |= ARAP_V20_CONNECTION;
// save the Syn,Dle,Stx,Etx bytes depeding on what connection this is
pArapConn->MnpState.SynByte = MNP_SOH;
pArapConn->MnpState.DleByte = MNP_ESC;
pArapConn->MnpState.StxByte = MNP_STX;
pArapConn->MnpState.EtxByte = MNP_ETX;
}
else
{
pArapConn->MnpState.SynByte = MNP_SYN;
pArapConn->MnpState.DleByte = MNP_DLE;
pArapConn->MnpState.StxByte = MNP_STX;
pArapConn->MnpState.EtxByte = MNP_ETX;
}
//
// if we are doing callback, we should act like the client
//
if ((pArapConn->Flags & ARAP_CALLBACK_MODE) && fArapV20)
{
pArapConn->MnpState.LTByte = MNP_LT_V20CLIENT;
}
else
{
pArapConn->MnpState.LTByte = MNP_LT;
}
pArapConn->MnpState.WindowSize = NumLTFrames;
pArapConn->MnpState.UnAckedLimit = (NumLTFrames/2);
if (pArapConn->MnpState.UnAckedLimit == 0)
{
pArapConn->MnpState.UnAckedLimit = 1;
}
pArapConn->MnpState.MaxPktSize = MaxInfoLen;
pArapConn->MnpState.SendCredit = NumLTFrames;
// how big is the mnp frame
if (pMnpLen)
{
*pMnpLen = (USHORT)(pFrame - pFrameStart);
// write the length byte
// (length byte is after the 3 start flag bytes: that's why (pFrameStart+3))
// (and exclude (3 start + 2 stop + 1 len bytes):that's why (*pMnpLen) - 6)
*(pFrameStart+3) = (*pMnpLen) - 6;
}
return( ARAPERR_NO_ERROR );
}
//***
//
// Function: ArapExtractAtalkSRP
// This routine extracts one complete SRP out of the receive
// buffer queue. One SRP could be split up into multiple receive
// buffers, or one receive buffer could contain several SRPs: it
// depends on how the client sent the data.
//
// Parameters: pArapConn - connection element in question
//
// Return: Pointer to a buffer containing one complete SRP
// NULL if there is no data, or if a complete SRP hasn't yet arrived
//
//***$
PARAPBUF
ArapExtractAtalkSRP(
IN PARAPCONN pArapConn
)
{
KIRQL OldIrql;
USHORT BytesInThisBuffer;
USHORT SrpLen;
USHORT SrpModLen;
PARAPBUF pArapBuf=NULL;
PARAPBUF pRecvBufNew=NULL;
PARAPBUF pReturnBuf=NULL;
PARAPBUF pNextRecvBuf=NULL;
PLIST_ENTRY pRcvList;
DWORD BytesOnQ;
USHORT BytesRemaining;
USHORT BytesToCopy;
BYTE DGroupByte;
BYTE TmpArray[4];
USHORT i;
DBG_ARAP_CHECK_PAGED_CODE();
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
ARAP_CHECK_RCVQ_INTEGRITY(pArapConn);
ArapSRP_TryNext:
// list is empty?
if ((pRcvList = pArapConn->ReceiveQ.Flink) == &pArapConn->ReceiveQ)
{
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
return(NULL);
}
pArapBuf = CONTAINING_RECORD(pRcvList, ARAPBUF, Linkage);
// Debug only: make sure first few bytes are right...
ARAP_CHECK_RCVQ_INTEGRITY(pArapConn);
BytesInThisBuffer = pArapBuf->DataSize;
//
// if datasize goes to 0, we free the buffer right away. Also, at indicate time
// if datasize is 0, we never insert a buffer: so this had better be non-zero!
//
ASSERT(BytesInThisBuffer > 0);
//
// most common case: we at least have the 2 length bytes in the first buffer
//
if (BytesInThisBuffer >= sizeof(USHORT))
{
// get the SRP length from the length field (network to host order)
GETSHORT2SHORT(&SrpLen, pArapBuf->CurrentBuffer);
}
//
// alright, last byte of the first buffer is the 1st length byte.
// pick up the 2nd length byte from the next buffer
//
else
{
ARAP_BYTES_ON_RECVQ(pArapConn, &BytesOnQ);
if (BytesOnQ < sizeof(USHORT))
{
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
return(NULL);
}
pRcvList = pArapBuf->Linkage.Flink;
ASSERT(pRcvList != &pArapConn->ReceiveQ);
pNextRecvBuf = CONTAINING_RECORD(pRcvList, ARAPBUF, Linkage);
TmpArray[0] = pArapBuf->CurrentBuffer[0];
TmpArray[1] = pNextRecvBuf->CurrentBuffer[0];
GETSHORT2SHORT(&SrpLen, &TmpArray[0]);
}
if (SrpLen > ARAP_MAXPKT_SIZE_INCOMING)
{
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapExtractSRP: (%lx) too big a packet (%ld)\n",pArapConn,SrpLen));
// can't recover! kill connection here
ArapCleanup(pArapConn);
return(NULL);
}
// add the 2 len bytes. We will always deal with an SRP along
// with these 2 len bytes
SrpModLen = SrpLen + sizeof(USHORT);
//
// let's deal with the simplest case first
// (the whole pkt is just one complete SRP):
//
if (SrpModLen == BytesInThisBuffer)
{
RemoveEntryList(&pArapBuf->Linkage);
// length of the SRP pkt, plus the 2 length bytes
pArapBuf->DataSize = SrpModLen;
pReturnBuf = pArapBuf;
}
//
// The packet contains more than one SRP
// allocate a new buffer and copy the SRP, leaving remaining bytes behind
//
else if (SrpModLen < BytesInThisBuffer)
{
ARAP_GET_RIGHTSIZE_RCVBUF(SrpModLen, &pRecvBufNew);
if (pRecvBufNew == NULL)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapExtractSRP: (%lx) mem alloc failed\n",pArapConn));
return(NULL);
}
RtlCopyMemory( &pRecvBufNew->Buffer[0],
pArapBuf->CurrentBuffer,
SrpModLen);
pRecvBufNew->DataSize = SrpModLen;
// changes to reflect the bytes we 'removed' in the original buffer
pArapBuf->DataSize -= SrpModLen;
pArapBuf->CurrentBuffer = pArapBuf->CurrentBuffer + SrpModLen;
pReturnBuf = pRecvBufNew;
// Debug only: make sure what we're leaving behind is good...
ARAP_CHECK_RCVQ_INTEGRITY(pArapConn);
}
//
// packet contains a partial SRP (this is a rare case, but possible)
// we must traverse the queue until we "collect" the whole SRP. If we still
// can't get one complete SRP, it's not arrived yet!
//
else // if (SrpModLen > BytesInThisBuffer)
{
ARAP_BYTES_ON_RECVQ(pArapConn, &BytesOnQ);
//
// if we have a full srp (split up across multiple buffers on the Q)
//
if (BytesOnQ >= SrpModLen)
{
//
// allocate a new buffer to hold this fragmented SRP
//
ARAP_GET_RIGHTSIZE_RCVBUF(SrpModLen, &pRecvBufNew);
if (pRecvBufNew == NULL)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapExtractSRP: (%lx) mem alloc failed at II\n",pArapConn));
return(NULL);
}
pRecvBufNew->DataSize = SrpModLen;
pNextRecvBuf = pArapBuf;
BytesRemaining = SrpModLen;
while (BytesRemaining)
{
BytesToCopy = (BytesRemaining > pNextRecvBuf->DataSize) ?
pNextRecvBuf->DataSize : BytesRemaining;
RtlCopyMemory( pRecvBufNew->CurrentBuffer,
pNextRecvBuf->CurrentBuffer,
BytesToCopy );
pRecvBufNew->CurrentBuffer += BytesToCopy;
pNextRecvBuf->CurrentBuffer += BytesToCopy;
pNextRecvBuf->DataSize -= BytesToCopy;
BytesRemaining -= BytesToCopy;
pRcvList = pNextRecvBuf->Linkage.Flink;
// are we done with this buffer? if so, unlink it and free it
if (pNextRecvBuf->DataSize == 0)
{
RemoveEntryList(&pNextRecvBuf->Linkage);
ARAP_FREE_RCVBUF(pNextRecvBuf);
}
else
{
// didn't free up the buffer? we had better be done!
ASSERT(BytesRemaining == 0);
// Debug only: make sure what we're leaving behind is good...
ARAP_CHECK_RCVQ_INTEGRITY(pArapConn);
}
// there should be more data on the queue or we should be done
ASSERT(pRcvList != &pArapConn->ReceiveQ || BytesRemaining == 0);
pNextRecvBuf = CONTAINING_RECORD(pRcvList, ARAPBUF, Linkage);
}
pRecvBufNew->CurrentBuffer = &pRecvBufNew->Buffer[0];
pReturnBuf = pRecvBufNew;
}
else
{
pReturnBuf = NULL;
}
}
if (pReturnBuf)
{
DGroupByte = pReturnBuf->CurrentBuffer[ARAP_DGROUP_OFFSET];
#if DBG
ARAP_DBG_TRACE(pArapConn,21105,pReturnBuf,0,0,0);
GETSHORT2SHORT(&SrpLen, pReturnBuf->CurrentBuffer);
ASSERT(pReturnBuf->DataSize == SrpLen+2);
ASSERT(SrpLen <= ARAP_MAXPKT_SIZE_INCOMING);
if (DGroupByte != 0x10 && DGroupByte != 0x50 &&
(pArapConn->Flags & ARAP_CONNECTION_UP))
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapExtract: DGrpByte %x\n",DGroupByte));
ASSERT(0);
}
//
// see if we have an arap packet embedded inside another arap packet
// This is an expensive way, but it's debug only: who cares!
//
for (i=6; i<(pReturnBuf->DataSize-6); i++)
{
if ((pReturnBuf->CurrentBuffer[i] == 0x10) ||
(pReturnBuf->CurrentBuffer[i] == 0x50))
{
if (pReturnBuf->CurrentBuffer[i+1] == 0)
{
if (pReturnBuf->CurrentBuffer[i+2] == 0)
{
if (pReturnBuf->CurrentBuffer[i+3] == 0x2)
{
if (pReturnBuf->CurrentBuffer[i+4] == 0)
{
if (pReturnBuf->CurrentBuffer[i+5] == 0)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapExtract: ERROR?: embedded arap packet at %lx? (%lx)\n",
&pReturnBuf->CurrentBuffer[i],pReturnBuf));
}
}
}
}
}
}
}
#endif
//
// is it out-of-band ARAP data? If so, put it on its own queue, and
// try to extract another SRP
//
if (!(DGroupByte & ARAP_SFLAG_PKT_DATA))
{
InsertTailList(&pArapConn->ArapDataQ, &pReturnBuf->Linkage);
goto ArapSRP_TryNext;
}
}
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
return(pReturnBuf);
}
//***
//
// Function: ArapQueueSendBytes
// This routine takes compressed data and puts it into MNP packets
// ready to be sent out. If the last unsent buffer on the queue
// has some room left, it first stuffs as many bytes as possible
// in that buffer. After that, if necessary, it allocates a new
// buffer and puts the remaining bytes in that buffer.
// The max data in any buffer can only be the negotiated maximum
// MNP data length (64 or 256 bytes)
//
// Parameters: pArapConn - connection element in question
// pCompressedDataBuffer - pointer to the data to be sent out
// CompressedDataLen - size of the outgoing data
// Priority - priority of the send
//
// Return: Error code
//
//
// NOTES: IMPORTANT: spinlock must be held before calling this routine
//
//***$
DWORD
ArapQueueSendBytes(
IN PARAPCONN pArapConn,
IN PBYTE pCompressedDataBuffer,
IN DWORD CompressedDataLen,
IN DWORD Priority
)
{
DWORD StatusCode;
PLIST_ENTRY pSendQHead;
PMNPSENDBUF pMnpSendBuf=NULL;
PBYTE pFrame, pFrameStart;
DWORD dwRemainingBytes;
PMNPSENDBUF pTailMnpSendBuf=NULL;
PMNPSENDBUF pFirstMnpSendBuf=NULL;
USHORT DataLenInThisPkt;
PLIST_ENTRY pList;
USHORT DataSizeOfOrgTailSend;
PBYTE FreeBufferOfOrgTailSend;
BYTE NumSendsOfOrgTailSend;
BOOLEAN fNeedNewBuffer;
PLIST_ENTRY pSendList;
USHORT BytesFree;
PBYTE pCompressedData;
BYTE DbgStartSeq;
DBG_ARAP_CHECK_PAGED_CODE();
if (Priority == ARAP_SEND_PRIORITY_HIGH)
{
pSendQHead = &pArapConn->HighPriSendQ;
}
else if (Priority == ARAP_SEND_PRIORITY_MED)
{
pSendQHead = &pArapConn->MedPriSendQ;
}
else
{
pSendQHead = &pArapConn->LowPriSendQ;
}
#if DBG
DbgStartSeq = pArapConn->MnpState.NextToSend;
#endif
//
// first, find the last send-buffer on the queue and see if its buffer has
// any bytes free that we can use
//
fNeedNewBuffer = TRUE;
if (!IsListEmpty(pSendQHead))
{
pList = pSendQHead->Blink;
pTailMnpSendBuf = CONTAINING_RECORD(pList, MNPSENDBUF, Linkage);
BytesFree = pTailMnpSendBuf->BytesFree;
//
// if there are more than 3 bytes free, we will use the part of this
// buffer that's free: otherwise, let's go for a new one
//
if (BytesFree > ARAP_HDRSIZE)
{
pMnpSendBuf = pTailMnpSendBuf;
pFrame = pTailMnpSendBuf->FreeBuffer;
pFrameStart = pFrame;
fNeedNewBuffer = FALSE;
// save these, in case we have to bail out
DataSizeOfOrgTailSend = pTailMnpSendBuf->DataSize;
FreeBufferOfOrgTailSend = pTailMnpSendBuf->FreeBuffer;
NumSendsOfOrgTailSend = pMnpSendBuf->NumSends;
// mark that we are stuffing one more send into this buffer
pMnpSendBuf->NumSends++;
}
else
{
pTailMnpSendBuf = NULL;
}
}
dwRemainingBytes = CompressedDataLen;
pCompressedData = pCompressedDataBuffer;
// we are adding so many more bytes to our send queue
pArapConn->SendsPending += CompressedDataLen;
while (dwRemainingBytes)
{
if (fNeedNewBuffer)
{
pMnpSendBuf = ArapGetSendBuf(pArapConn, Priority);
if (pMnpSendBuf == NULL)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapQueueSendBytes: ArapGetSendBuf failed (%lx)\n", pArapConn));
StatusCode = ARAPERR_OUT_OF_RESOURCES;
goto ArapQueueSendBytes_ErrExit;
}
// put MNPSend refcount
pArapConn->RefCount++;
BytesFree = pMnpSendBuf->BytesFree;
pFrameStart = pFrame = pMnpSendBuf->FreeBuffer;
//
// remeber the first buffer, in case we have to bail out!
//
if (!pFirstMnpSendBuf)
{
pFirstMnpSendBuf = pMnpSendBuf;
}
// put this send on the appropriate send queue
InsertTailList(pSendQHead, &pMnpSendBuf->Linkage);
pMnpSendBuf->NumSends = 1;
}
if (dwRemainingBytes > BytesFree)
{
DataLenInThisPkt = BytesFree;
}
else
{
DataLenInThisPkt = (USHORT)dwRemainingBytes;
}
ASSERT(DataLenInThisPkt <= MNP_MAXPKT_SIZE);
ASSERT(DataLenInThisPkt <= pMnpSendBuf->BytesFree);
RtlCopyMemory(pFrame, pCompressedData, DataLenInThisPkt);
dwRemainingBytes -= DataLenInThisPkt;
pCompressedData += DataLenInThisPkt;
ASSERT(pCompressedData <= pCompressedDataBuffer + CompressedDataLen);
pMnpSendBuf->BytesFree -= DataLenInThisPkt;
pMnpSendBuf->DataSize += DataLenInThisPkt;
ASSERT(pMnpSendBuf->DataSize <= MNP_MAXPKT_SIZE);
pFrame += DataLenInThisPkt;
// buffer from this point on is free: we could (in a subsequent call)
// stuff more bytes, starting from this point
pMnpSendBuf->FreeBuffer = pFrame;
//
// we are either done with copying the entire send, or done with this
// buffer: in either case, put those stop flag bytes
//
*pFrame++ = pArapConn->MnpState.DleByte;
*pFrame++ = pArapConn->MnpState.EtxByte;
ASSERT(pMnpSendBuf->FreeBuffer <=
(&pMnpSendBuf->Buffer[0] + 20 + MNP_MAXPKT_SIZE));
AtalkSetSizeOfBuffDescData(&pMnpSendBuf->sb_BuffDesc,
(pMnpSendBuf->DataSize + MNP_OVERHD(pArapConn)));
fNeedNewBuffer = TRUE;
}
ARAP_DBG_TRACE(pArapConn,21205,pCompressedDataBuffer,CompressedDataLen,
Priority,DbgStartSeq);
return( ARAPERR_NO_ERROR );
ArapQueueSendBytes_ErrExit:
//
// we failed somewhere. Undo whatever we did, to restore the original
// state and free up whatever resources were allocated.
//
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapQueueSendBytes_ErrExit (%lx): taking _ErrExit! %ld\n",
pArapConn,StatusCode));
pArapConn->SendsPending -= CompressedDataLen;
// did we fill any bytes in the older buf before allocating new one?
if (pTailMnpSendBuf)
{
pTailMnpSendBuf->DataSize = DataSizeOfOrgTailSend;
pTailMnpSendBuf->FreeBuffer = pFrame = FreeBufferOfOrgTailSend;
pTailMnpSendBuf->NumSends = NumSendsOfOrgTailSend;
// and don't forget those stop flag bytes we overwrote
*pFrame++ = pArapConn->MnpState.DleByte;
*pFrame++ = pArapConn->MnpState.EtxByte;
}
// did we allocate any new buffers? if so, remove and free them
if (pFirstMnpSendBuf)
{
// restore the next-to-send seq num
pArapConn->MnpState.NextToSend = pFirstMnpSendBuf->SeqNum;
while (1)
{
// get the next guy first
pSendList = pFirstMnpSendBuf->Linkage.Flink;
// remove this one
RemoveEntryList(&pFirstMnpSendBuf->Linkage);
ArapNdisFreeBuf(pFirstMnpSendBuf);
// the guy we thought was next might be the head of list: is he?
if (pSendList == pSendQHead)
{
break;
}
pFirstMnpSendBuf = CONTAINING_RECORD(pSendList, MNPSENDBUF, Linkage);
}
}
return(StatusCode);
}
//***
//
// Function: ArapGetSendBuf
// This routine allocates a buffer for MNP send and sets it up
// sending.
// The max data in any buffer can only be the negotiated maximum
// MNP data length (64 or 256 bytes)
//
// Parameters: pArapConn - connection element in question
// Priority - priority of the send
//
// Return: Pointer to the newly allocated send buffer
//
//
// NOTES: IMPORTANT: spinlock must be held before calling this routine
//
//***$
PMNPSENDBUF
ArapGetSendBuf(
IN PARAPCONN pArapConn,
IN DWORD Priority
)
{
PBYTE pFrame;
PBYTE pFrameStart;
BYTE SeqNum;
PMNPSENDBUF pMnpSendBuf;
DBG_ARAP_CHECK_PAGED_CODE();
// allocate an arap send buffer
pMnpSendBuf = AtalkBPAllocBlock(pArapConn->BlockId);
if (pMnpSendBuf == NULL)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapGetSendBuf: alloc failed (%lx)\n", pArapConn));
ASSERT(0);
return( NULL );
}
#if DBG
pMnpSendBuf->Signature = (pArapConn->BlockId == BLKID_MNP_LGSENDBUF)?
MNPLGSENDBUF_SIGNATURE : MNPSMSENDBUF_SIGNATURE;
InitializeListHead(&pMnpSendBuf->Linkage);
#endif
pFrameStart = pFrame = &pMnpSendBuf->Buffer[0];
AtalkNdisBuildARAPHdr(pFrame, pArapConn);
pFrame += WAN_LINKHDR_LEN;
// put the start flag bytes
*pFrame++ = pArapConn->MnpState.SynByte;
*pFrame++ = pArapConn->MnpState.DleByte;
*pFrame++ = pArapConn->MnpState.StxByte;
//
// find out what's going to be the seq num for this send if this is a high
// priority send.
//
//
if (Priority == ARAP_SEND_PRIORITY_HIGH)
{
SeqNum = pArapConn->MnpState.NextToSend;
ADD_ONE(pArapConn->MnpState.NextToSend);
}
//
// For the medium and low priority send, we'll put the seq number when we
// move it to the high-priority queue
//
else
{
SeqNum = 0;
}
// Optimized? put the header-length, type indication and the seq num
if (pArapConn->Flags & MNP_OPTIMIZED_DATA)
{
*pFrame++ = 2;
*pFrame++ = pArapConn->MnpState.LTByte;
*pFrame++ = SeqNum;
}
// ok, non-optimized. put the header-length, type indication, type+len for
// the variable parm and then the seq num
else
{
*pFrame++ = 4;
*pFrame++ = pArapConn->MnpState.LTByte;
*pFrame++ = 1;
*pFrame++ = 1;
*pFrame++ = SeqNum;
}
pMnpSendBuf->BytesFree = (pArapConn->BlockId == BLKID_MNP_SMSENDBUF) ?
MNP_MINPKT_SIZE : MNP_MAXPKT_SIZE;
pMnpSendBuf->DataSize = 0;
pMnpSendBuf->FreeBuffer = pFrame;
// store info that's used in retransmission and send completion
pMnpSendBuf->SeqNum = SeqNum;
pMnpSendBuf->RetryCount = 0;
pMnpSendBuf->pArapConn = pArapConn;
pMnpSendBuf->ComplRoutine = ArapMnpSendComplete;
pMnpSendBuf->TimeAlloced = AtalkGetCurrentTick();
pMnpSendBuf->Flags = 0;
// MNP refcount: removed when this MNP pkt is acked
pMnpSendBuf->RefCount = 1;
((PBUFFER_HDR)pMnpSendBuf)->bh_NdisPkt = NULL;
pArapConn->StatInfo.BytesSent += LT_OVERHEAD(pArapConn);
return(pMnpSendBuf);
}
//***
//
// Function: ArapRefillSendQ
// This routine removes bytes accumulated in the medium and the
// low-priority send queues, and puts them on to the high priority
// queue from where bytes are actually sent out. If sufficient
// bytes haven't accumulated as yet on either of the queus and we
// haven't waited long enough as yet, then we skip that queue (to
// allow more bytes to accumulate)
// The idea behind this is: there are just too many NBP packets
// - directed as well as broadcast - going toward the remote client.
// If we send such a packet as soon as it arrives, we end up sending
// numerous small sized (like 6 or 8 byte) sends to the client and
// that really hurts throughput as typically the max size is 256!
//
// Parameters: pArapConn - connection element in question
//
// Return: TRUE if we moved any data to the higher priority queue
//
//***$
BOOLEAN
ArapRefillSendQ(
IN PARAPCONN pArapConn
)
{
KIRQL OldIrql;
PMNPSENDBUF pMnpSendBuf=NULL;
PLIST_ENTRY pSendHead;
PLIST_ENTRY pSendList;
LONG TimeWaited;
BYTE SeqNum;
BOOLEAN fMovedSomething=FALSE;
BOOLEAN fWaitLonger=FALSE;
DWORD SeqOffset;
DBG_ARAP_CHECK_PAGED_CODE();
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
pSendHead = &pArapConn->MedPriSendQ;
while (1)
{
pSendList = pSendHead->Flink;
//
// if the list is not empty, take a look at the first send. If we have
// accumulated enough bytes, or if we have waited long enough, then it's
// time has come to be moved to the HighPriSendQ. Otherwise, we let it
// stay on the queue for some more coalescing
//
if (pSendList != pSendHead)
{
pMnpSendBuf = CONTAINING_RECORD(pSendList,MNPSENDBUF,Linkage);
TimeWaited = AtalkGetCurrentTick() - pMnpSendBuf->TimeAlloced;
fWaitLonger =
((pMnpSendBuf->DataSize < ARAP_SEND_COALESCE_SIZE_LIMIT) &&
(pMnpSendBuf->NumSends < ARAP_SEND_COALESCE_SRP_LIMIT) &&
(TimeWaited < ARAP_SEND_COALESCE_TIME_LIMIT) );
}
//
// if this list is empty or if this send must wait for some more time
// then we must move to the LOW pri queue. If that's also done, quit
//
if ((pSendList == pSendHead) || fWaitLonger )
{
// if we were on MedPriSendQ, go to the LowPriSendQ
if (pSendHead == &pArapConn->MedPriSendQ)
{
pSendHead = &pArapConn->LowPriSendQ;
continue;
}
else
{
break;
}
}
ASSERT(!fWaitLonger);
//
// time to move this send over to the high pri queue:
// put that seq number we had postponed putting earlier
//
SeqNum = pArapConn->MnpState.NextToSend;
ADD_ONE(pArapConn->MnpState.NextToSend);
SeqOffset = WAN_LINKHDR_LEN + LT_SEQ_OFFSET(pArapConn);
pMnpSendBuf->Buffer[SeqOffset] = SeqNum;
pMnpSendBuf->SeqNum = SeqNum;
RemoveEntryList(&pMnpSendBuf->Linkage);
InsertTailList(&pArapConn->HighPriSendQ, &pMnpSendBuf->Linkage);
fMovedSomething = TRUE;
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapRefill: moved %s seq %lx size = %d wait = %d (%d SRPs)\n",
(pSendHead == &pArapConn->MedPriSendQ)? "MED":"LOW",
pMnpSendBuf->SeqNum,pMnpSendBuf->DataSize,TimeWaited,
pMnpSendBuf->NumSends));
}
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
return(fMovedSomething);
}
//***
//
// Function: ArapUnblockSelect
// This function is called when the DLL (ARAP Engine) tells us that
// it's going away. In ideal world, select irp is the only thing
// that should get completed here. If any connection was left
// or any irp wasn't complete yet, this is where we would cleanup!
//
// Parameters: None
//
// Return: result of the operation (ARAPERR_....)
//
//***$
DWORD
ArapUnblockSelect(
IN VOID
)
{
KIRQL OldIrql;
PIRP pIrp;
PIRP pSniffIrp;
PARAP_SEND_RECV_INFO pSndRcvInfo;
NTSTATUS ReturnStatus=STATUS_SUCCESS;
ARAPTRACE(("Entered ArapUnblockSelect\n"));
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql);
pIrp = ArapSelectIrp;
ArapSelectIrp = NULL;
#if DBG
pSniffIrp = ArapSniffIrp;
ArapSniffIrp = NULL;
#endif
if (ArapStackState == ARAP_STATE_ACTIVE)
{
ArapStackState = ARAP_STATE_ACTIVE_WAITING;
}
else if (ArapStackState == ARAP_STATE_INACTIVE)
{
ArapStackState = ARAP_STATE_INACTIVE_WAITING;
}
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
#if DBG
//
// if we had sniffing going, complete the sniff irp since dll is shutting down
//
if (pSniffIrp)
{
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pSniffIrp->AssociatedIrp.SystemBuffer;
pSndRcvInfo->StatusCode = ARAPERR_SHUTDOWN_COMPLETE;
// complete the irp
ARAP_COMPLETE_IRP(pSniffIrp, sizeof(ARAP_SEND_RECV_INFO),
STATUS_SUCCESS, &ReturnStatus);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapUnblockSelect: unblocked the sniff irp\n"));
}
#endif
//
// complete the select irp, now that the engine tells us that it wants to
// shutdown
//
if (pIrp)
{
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pSndRcvInfo->StatusCode = ARAPERR_SHUTDOWN_COMPLETE;
// complete the irp
ARAP_COMPLETE_IRP(pIrp, sizeof(ARAP_SEND_RECV_INFO), STATUS_SUCCESS,
&ReturnStatus);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapUnblockSelect: unblocked the select irp\n"));
}
else
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapUnblockSelect: select irp not yet unblocked\n"));
}
return(ARAPERR_NO_ERROR);
}
//***
//
// Function: ArapReleaseResources
// This routine frees up any global resources we had allocated
//
// Parameters: None
//
// Return: result of the operation (ARAPERR_....)
//
//***$
DWORD
ArapReleaseResources(
IN VOID
)
{
KIRQL OldIrql;
PLIST_ENTRY pList;
PADDRMGMT pAddrMgmt;
PADDRMGMT pNextAddrMgmt;
PARAPCONN pArapConn;
ARAPTRACE(("Entered ArapReleaseResources\n"));
if (RasPortDesc == NULL)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapReleaseResources: arap engine never initialized; returning\n"));
return(ARAPERR_NO_ERROR);
}
#if ARAP_STATIC_MODE
//
// if we were in the static mode of network address allocation, we
// allocated memory for our network address "bitmap": free that here.
//
if (!(ArapGlobs.DynamicMode))
{
pAddrMgmt = ArapGlobs.pAddrMgmt;
ASSERT(pAddrMgmt);
while (pAddrMgmt)
{
pNextAddrMgmt = pAddrMgmt->Next;
AtalkFreeMemory(pAddrMgmt);
pAddrMgmt = pNextAddrMgmt;
}
}
#endif
//
// By the time this routine is called, all the connections should have been
// completely closed. If, however, some connection got stuck in some weird
// state, at least make sure that we don't have a memory leak
//
ASSERT(IsListEmpty(&RasPortDesc->pd_ArapConnHead));
while (1)
{
ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
if (IsListEmpty(&RasPortDesc->pd_ArapConnHead))
{
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
break;
}
pList = RasPortDesc->pd_ArapConnHead.Flink;
pArapConn = CONTAINING_RECORD(pList, ARAPCONN, Linkage);
ACQUIRE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
// protect against it going away while we do this
pArapConn->RefCount++;
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
ArapCleanup(pArapConn);
ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
RemoveEntryList(&pArapConn->Linkage);
InitializeListHead(&pArapConn->Linkage);
// force this connection to be freed
pArapConn->RefCount = 1;
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
DerefArapConn(pArapConn);
}
return(ARAPERR_NO_ERROR);
}
//***
//
// Function: AtalkReferenceRasDefPort
// This routine puts a refcount on the Default adapter so it doesn't go away
// with PnP while we are busy doing some ARAP/PPP connection setup
// It also makes sure that RasPortDesc is indeed present
//
// Parameters: None
//
// Return: TRUE (most cases) if port is all ok, FALSE if PnP in progress or something
//
//***$
BOOLEAN
AtalkReferenceRasDefPort(
IN VOID
)
{
KIRQL OldIrql;
BOOLEAN fDefPortOk = FALSE;
ACQUIRE_SPIN_LOCK(&AtalkPortLock, &OldIrql);
if ((RasPortDesc != NULL) && (!(RasPortDesc->pd_Flags & PD_PNP_RECONFIGURE)))
{
if (AtalkDefaultPort)
{
ACQUIRE_SPIN_LOCK_DPC(&AtalkDefaultPort->pd_Lock);
if ((AtalkDefaultPort->pd_Flags &
(PD_ACTIVE | PD_PNP_RECONFIGURE | PD_CLOSING)) == PD_ACTIVE)
{
// put a IrpProcess refcount, so AtalkDefaultPort doesn't go away in PnP
AtalkDefaultPort->pd_RefCount++;
fDefPortOk = TRUE;
}
else
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("AtalkReferenceRasDefPort: port going away, no can do (%lx)\n",
AtalkDefaultPort->pd_Flags));
}
RELEASE_SPIN_LOCK_DPC(&AtalkDefaultPort->pd_Lock);
}
else
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("AtalkReferenceRasDefPort: no default adapter configured!\n"));
}
}
else
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("AtalkReferenceRasDefPort: RasPortDesc not configured\n"));
}
RELEASE_SPIN_LOCK(&AtalkPortLock, OldIrql);
return(fDefPortOk);
}
VOID
AtalkPnPInformRas(
IN BOOLEAN fEnableRas
)
{
PIRP pIrp=NULL;
PARAP_SEND_RECV_INFO pSndRcvInfo;
DWORD StatusCode;
KIRQL OldIrql;
NTSTATUS ReturnStatus=STATUS_SUCCESS;
//
// fEnableRas = TRUE: we are asked to inform RAS (aka dll, engine) that the stack
// is now "active" (i.e. available for RAS connections)
//
if (fEnableRas)
{
//
// make sure both the adapters are ready. We don't really need spinlock to
// check the flag since all PnP operations are guaranteed to be serialized
//
if ( (AtalkDefaultPort == NULL) ||
(AtalkDefaultPort->pd_Flags & PD_PNP_RECONFIGURE) ||
(RasPortDesc == NULL) ||
(RasPortDesc->pd_Flags & PD_PNP_RECONFIGURE) )
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("AtalkPnPInformRas: not both adapters are ready %lx %lx, returning\n",
AtalkDefaultPort,RasPortDesc));
return;
}
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql);
//
// if we are already active, nothing to do!
//
if (ArapStackState == ARAP_STATE_ACTIVE)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("AtalkPnPInformRas: stack already active, nothing to do\n"));
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
return;
}
pIrp = ArapSelectIrp;
ArapSelectIrp = NULL;
if (pIrp)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("AtalkPnPInformRas: informing dll that stack is ready\n"));
ArapStackState = ARAP_STATE_ACTIVE;
StatusCode = ARAPERR_STACK_IS_ACTIVE;
}
else
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("AtalkPnPInformRas: no select irp. stack ready, but dll not knoweth\n"));
ArapStackState = ARAP_STATE_ACTIVE_WAITING;
}
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
}
//
// fEnableRas = FALSE: we are asked to inform RAS (aka dll, engine) that the stack
// is now "inactive" (i.e. not available for RAS connections)
//
else
{
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql);
//
// if we are already inactive, nothing to do!
//
if (ArapStackState == ARAP_STATE_INACTIVE)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("AtalkPnPInformRas: stack already inactive, nothing to do\n"));
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
return;
}
pIrp = ArapSelectIrp;
ArapSelectIrp = NULL;
if (pIrp)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("AtalkPnPInformRas: informing dll that stack is unavailable\n"));
ArapStackState = ARAP_STATE_INACTIVE;
StatusCode = ARAPERR_STACK_IS_NOT_ACTIVE;
}
else
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("AtalkPnPInformRas: no select irp. stack unavailable, but dll not knoweth\n"));
ArapStackState = ARAP_STATE_INACTIVE_WAITING;
}
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
}
if (pIrp)
{
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pSndRcvInfo->StatusCode = StatusCode;
// complete the irp
ARAP_COMPLETE_IRP(pIrp, sizeof(ARAP_SEND_RECV_INFO), STATUS_SUCCESS,
&ReturnStatus);
}
}
#if ARAP_STATIC_MODE
//***
//
// Function: ArapReleaseAddr
// This routine releases the network address that was being used
// by the client (corresponding to this connection). In case of
// dynamic mode, we don't do anything.
// In case of static mode, we clear the bit corresponding to this
// particular network address.
//
// Parameters: pArapConn - connection element in question
//
// Return: none
//
//***$
VOID
ArapReleaseAddr(
IN PARAPCONN pArapConn
)
{
KIRQL OldIrql;
PADDRMGMT pAddrMgmt;
BYTE Node;
BYTE ByteNum;
BYTE BitMask;
DWORD i;
DBG_ARAP_CHECK_PAGED_CODE();
//
// if we are in static mode, we need to "release" the node so that someone
// else can use it. Find the bit for this node and clear it (i.e. "release")
//
if (!(ArapGlobs.DynamicMode))
{
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql);
// first, find the right pAddrMgmt (1st one if we have <255 clients)
pAddrMgmt = ArapGlobs.pAddrMgmt;
ASSERT(pAddrMgmt);
while (pAddrMgmt->Network != pArapConn->NetAddr.atn_Network)
{
pAddrMgmt = pAddrMgmt->Next;
ASSERT(pAddrMgmt);
}
Node = pArapConn->NetAddr.atn_Node;
// find out which of the 32 bytes we should be looking at
ByteNum = Node/8;
Node -= (ByteNum*8);
// generate the bitmask to represent the node
BitMask = 0x1;
for (i=0; i<Node; i++ )
{
BitMask <<= 1;
}
// now, clear that bit!
pAddrMgmt->NodeBitMap[ByteNum] &= ~BitMask;
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
}
}
#endif //ARAP_STATIC_MODE
//
// not used anymore: leave them in, in case we need to use them sometime...
//
#if 0
DWORD
ArapScheduleWorkerEvent(
IN DWORD Action,
IN PVOID Context1,
IN PVOID Context2
)
{
PARAPQITEM pArapQItem;
if ((pArapQItem = AtalkAllocMemory(sizeof(ARAPQITEM))) == NULL)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapScheduleWorkerEvent: mem alloc failed!\n"));
return(ARAPERR_OUT_OF_RESOURCES);
}
pArapQItem->Action = Action;
pArapQItem->Context1 = Context1;
pArapQItem->Context2 = Context2;
ExInitializeWorkItem(&pArapQItem->WorkQItem,
(PWORKER_THREAD_ROUTINE)ArapDelayedEventHandler,
pArapQItem);
ExQueueWorkItem(&pArapQItem->WorkQItem, CriticalWorkQueue);
return(ARAPERR_NO_ERROR);
}
VOID
ArapDelayedEventHandler(
IN PARAPQITEM pArapQItem
)
{
DWORD Action;
PIRP pIrp;
PIO_STACK_LOCATION pIrpSp;
ULONG IoControlCode;
PMNPSENDBUF pMnpSendBuf;
DWORD StatusCode;
NTSTATUS status;
KIRQL OldIrq;
Action = pArapQItem->Action;
switch (Action)
{
case ARAPACTION_COMPLETE_IRP:
pIrp = (PIRP)pArapQItem->Context1;
status = (NTSTATUS)pArapQItem->Context2;
ASSERT(pIrp != NULL);
#if DBG
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
IoControlCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapDelayedEventHandler: completing irp %lx, Ioctl %lx, Status=%lx at irql=%d\n",
pIrp,IoControlCode,status,KeGetCurrentIrql()));
#endif
//TdiCompleteRequest(pIrp, status);
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
break;
case ARAPACTION_CALL_COMPLETION:
pMnpSendBuf = (PMNPSENDBUF )pArapQItem->Context1;
StatusCode = (DWORD)pArapQItem->Context2;
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapDelayedEventHandler: calling compl. %lx with %lx, %ld\n",
pMnpSendBuf->ComplRoutine,pMnpSendBuf, StatusCode));
(pMnpSendBuf->ComplRoutine)(pMnpSendBuf, StatusCode);
break;
default:
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("ArapDelayedEventHandler: invalid action %ld\n",Action));
}
AtalkFreeMemory(pArapQItem);
}
#endif // #if 0