|
|
/*++
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
|