|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
arap.c
Abstract:
This module implements routines specific to ARAP
Author:
Shirish Koti
Revision History: 15 Nov 1996 Initial Version
--*/
#include <atalk.h>
#pragma hdrstop
#define FILENUM ARAP
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ArapProcessIoctl)
#pragma alloc_text(PAGE_ARAP, ArapMarkConnectionUp)
#pragma alloc_text(PAGE_ARAP, ArapIoctlRecv)
#pragma alloc_text(PAGE_ARAP, ArapExchangeParms)
#pragma alloc_text(PAGE_ARAP, ArapConnect)
#pragma alloc_text(PAGE_ARAP, ArapConnectComplete)
#pragma alloc_text(PAGE_ARAP, ArapDisconnect)
#pragma alloc_text(PAGE_ARAP, ArapGetAddr)
#pragma alloc_text(PAGE_ARAP, ArapGetStats)
#pragma alloc_text(PAGE_ARAP, ArapIoctlSend)
#pragma alloc_text(PAGE_ARAP, ArapSendPrepare)
#pragma alloc_text(PAGE_ARAP, ArapMnpSendComplete)
#pragma alloc_text(PAGE_ARAP, ArapIoctlSendComplete)
#pragma alloc_text(PAGE_ARAP, ArapDataToDll)
#pragma alloc_text(PAGE_ARAP, MnpSendAckIfReqd)
#pragma alloc_text(PAGE_ARAP, MnpSendLNAck)
#pragma alloc_text(PAGE_ARAP, ArapSendLDPacket)
#pragma alloc_text(PAGE_ARAP, ArapRetryTimer)
#endif
//***
//
// Function: ArapProcessIoctl
// Process all ioctls coming from the Ras-ARAP module
//
// Parameters: pIrp - the irp to process
//
// Return: result of the operation
//
//***$
NTSTATUS ArapProcessIoctl( IN PIRP pIrp ) { NTSTATUS status=STATUS_SUCCESS; PIO_STACK_LOCATION pIrpSp; ULONG IoControlCode; PARAP_SEND_RECV_INFO pSndRcvInfo=NULL; PATCPCONN pAtcpConn=NULL; PARAPCONN pArapConn=NULL; ATALK_NODEADDR ClientNode; DWORD dwBytesToDll; DWORD dwOrgIrql; DWORD dwFlags; DWORD dwInputBufLen; DWORD dwOutputBufLen; BOOLEAN fDerefDefPort=FALSE; NTSTATUS ReturnStatus=STATUS_SUCCESS;
PAGED_CODE ();
dwOrgIrql = KeGetCurrentIrql();
ASSERT(dwOrgIrql < DISPATCH_LEVEL);
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
IoControlCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
dwInputBufLen = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
dwOutputBufLen = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
ARAPTRACE(("Entered ArapProcessIoctl (%lx %lx)\n",pIrp, IoControlCode));
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
if (!pSndRcvInfo) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessIoctl: SystemBuffer is NULL!! (ioctl = %lx,pIrp = %lx)\n", pIrp,IoControlCode));
ARAP_COMPLETE_IRP(pIrp, 0, STATUS_INVALID_PARAMETER, &ReturnStatus); return( ReturnStatus ); }
if (dwInputBufLen < sizeof(ARAP_SEND_RECV_INFO)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessIoctl: irp %lx, too small input buffer (%d bytes)!\n", pIrp,dwInputBufLen));
ARAP_COMPLETE_IRP(pIrp, 0, STATUS_INVALID_PARAMETER, &ReturnStatus); return( ReturnStatus ); }
if (dwOutputBufLen < sizeof(ARAP_SEND_RECV_INFO)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessIoctl: irp %lx, too small output buffer (%d bytes)!\n", pIrp,dwOutputBufLen));
ARAP_COMPLETE_IRP(pIrp, 0, STATUS_INVALID_PARAMETER, &ReturnStatus); return( ReturnStatus ); }
//
// handle PPP (ATCP) ioctls separately
//
if ((IoControlCode == IOCTL_ATCP_SETUP_CONNECTION) || (IoControlCode == IOCTL_ATCP_SUPPRESS_BCAST) || (IoControlCode == IOCTL_ATCP_CLOSE_CONNECTION)) { if (IoControlCode == IOCTL_ATCP_SETUP_CONNECTION) { AtalkLockPPPIfNecessary(); ReturnStatus = PPPProcessIoctl(pIrp, pSndRcvInfo, IoControlCode, NULL); return (ReturnStatus); } else { ClientNode.atn_Network = pSndRcvInfo->ClientAddr.ata_Network; ClientNode.atn_Node = (BYTE)pSndRcvInfo->ClientAddr.ata_Node;
if (ClientNode.atn_Node != 0) { // find the right connection
pAtcpConn = FindAndRefPPPConnByAddr(ClientNode, &dwFlags); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessIoctl: excuse me? Node is 0! Irp=%lx\n",pIrp)); ASSERT(0); }
if (pAtcpConn) { PPPProcessIoctl(pIrp, pSndRcvInfo, IoControlCode, pAtcpConn);
// remove the refcount put in by FindAndRefPPPConnByAddr
DerefPPPConn(pAtcpConn); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessIoctl: PPP Ioctl %lx but can't find conn %x.%x\n", IoControlCode,pSndRcvInfo->ClientAddr.ata_Network, pSndRcvInfo->ClientAddr.ata_Node));
pSndRcvInfo->StatusCode = ARAPERR_NO_SUCH_CONNECTION; ARAP_COMPLETE_IRP(pIrp, sizeof(ARAP_SEND_RECV_INFO), STATUS_SUCCESS, &ReturnStatus); return (ReturnStatus); } }
return( STATUS_SUCCESS); }
//
// NOTE: ALL THE ARAP CODE IS NOW DEFUNCT. To minimize code-churn, only small changes
// are done to disable ARAP. At some point in time, all the code needs to be cleaned up
// so ARAP-specific stuff is completely removed
//
else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessIoctl: ARAP not supported anymore!!\n")); ASSERT(0);
ARAP_COMPLETE_IRP(pIrp, 0, STATUS_INVALID_PARAMETER, &ReturnStatus); return( ReturnStatus ); }
if (!ArapAcceptIrp(pIrp, IoControlCode, &fDerefDefPort)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessIoctl: irp %lx not accepted (%lx)\n", pIrp,IoControlCode));
ARAP_COMPLETE_IRP(pIrp, sizeof(ARAP_SEND_RECV_INFO), STATUS_SUCCESS, &ReturnStatus);
// remove that IrpProcess refcount
if (fDerefDefPort) { AtalkPortDereference(AtalkDefaultPort); }
return( ReturnStatus); }
if ((IoControlCode != IOCTL_ARAP_EXCHANGE_PARMS) && (IoControlCode != IOCTL_ARAP_GET_ZONE_LIST)) { pArapConn = pSndRcvInfo->AtalkContext; }
//
// if ths irp is for a specific connection, validate the connection first!
//
if ((pArapConn != NULL) && (!ArapConnIsValid(pArapConn))) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessIoctl: conn %lx is gone! (ioctl = %lx)\n", pArapConn,IoControlCode));
pSndRcvInfo->StatusCode = ARAPERR_NO_SUCH_CONNECTION;
ARAP_COMPLETE_IRP(pIrp, sizeof(ARAP_SEND_RECV_INFO), STATUS_SUCCESS, &ReturnStatus);
// remove that IrpProcess refcount
if (fDerefDefPort) { AtalkPortDereference(AtalkDefaultPort); }
return( ReturnStatus); }
dwBytesToDll = sizeof(ARAP_SEND_RECV_INFO);
// in most likelihood, we're going to return pending: mark it so
IoMarkIrpPending(pIrp);
switch (IoControlCode) { //
// receive parameters from the user-level, and return some of our own
//
case IOCTL_ARAP_EXCHANGE_PARMS:
// exchange of parms: if not already done, lock arap pages
AtalkLockArapIfNecessary();
status = ArapExchangeParms( pIrp ); dwBytesToDll = sizeof(EXCHGPARMS);
// exchange of parms done: unlock if possible
AtalkUnlockArapIfNecessary(); break;
case IOCTL_ARAP_SETUP_CONNECTION:
// new connection being established: if not already done, lock arap pages
AtalkLockArapIfNecessary(); pSndRcvInfo->StatusCode = ARAPERR_NO_ERROR; status = STATUS_SUCCESS; break;
//
// setup the low level arap connection link (aka Point-to-Point Link)
// (first time client dials in, we respond. At callback, we initiate;
// at callback time, we initiate the connection here)
//
case IOCTL_ARAP_MNP_CONN_RESPOND: case IOCTL_ARAP_MNP_CONN_INITIATE:
status = ArapConnect( pIrp, IoControlCode ); break;
//
// obtain (or make up) an appletalk address for the client and return it
//
case IOCTL_ARAP_GET_ADDR:
status = ArapGetAddr( pIrp ); break;
//
// just mark the Arap connection as being established
//
case IOCTL_ARAP_CONNECTION_UP:
status = ArapMarkConnectionUp( pIrp ); break;
//
// dll wants the connection blown away: disconnect it
//
case IOCTL_ARAP_DISCONNECT:
status = ArapDisconnect( pIrp ); break;
//
// send the buffer given by the dll
//
case IOCTL_ARAP_SEND:
status = ArapIoctlSend( pIrp ); break;
//
// "direct irp": get data for the connection specified
//
case IOCTL_ARAP_RECV:
status = ArapIoctlRecv( pIrp ); break;
//
// "select irp": get data if there is for any connection
//
case IOCTL_ARAP_SELECT:
status = ArapProcessSelect( pIrp ); break;
#if DBG
//
// "sniff irp": return all the sniff info
//
case IOCTL_ARAP_SNIFF_PKTS:
status = ArapProcessSniff( pIrp ); break; #endif
//
// engine wants the select irp unblocked (either because it's shutting
// down or because we want to shutdown)
//
case IOCTL_ARAP_CONTINUE_SHUTDOWN:
ArapUnblockSelect(); status = STATUS_SUCCESS; break;
//
// get names of all the zones in the entire network
//
case IOCTL_ARAP_GET_ZONE_LIST:
ArapZipGetZoneStat( (PZONESTAT)pSndRcvInfo );
// (-4 to avoid 4 bytes from ZoneNames[1] field)
dwBytesToDll = ((PZONESTAT)pSndRcvInfo)->BufLen + sizeof(ZONESTAT) - 4; status = STATUS_SUCCESS; break;
default:
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessIoctl: Invalid Request %lx\n", IoControlCode));
status = STATUS_INVALID_PARAMETER; }
if( status != STATUS_PENDING ) { pIrpSp->Control &= ~SL_PENDING_RETURNED;
ARAP_COMPLETE_IRP(pIrp, dwBytesToDll, STATUS_SUCCESS, &ReturnStatus); status = ReturnStatus; }
//
// if this irp was for a specific connection, validation refcount was put
// on it: take that away
//
if (pArapConn) { DerefArapConn(pArapConn); }
// remove that IrpProcess refcount
if (fDerefDefPort) { AtalkPortDereference(AtalkDefaultPort); }
ASSERT(KeGetCurrentIrql() == dwOrgIrql);
return( status );
}
//***
//
// Function: ArapMarkConnectionUp
// Set the flags in our connection to mark that Arap connection
// has been established (by the dll) (we don't route until this
// happens)
//
// Parameters: pIrp - the irp to process
//
// Return: result of the operation
//
//***$
NTSTATUS ArapMarkConnectionUp( IN PIRP pIrp ) {
PARAPCONN pArapConn; PARAP_SEND_RECV_INFO pSndRcvInfo; KIRQL OldIrql;
DBG_ARAP_CHECK_PAGED_CODE();
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pArapConn = (PARAPCONN)pSndRcvInfo->AtalkContext;
if (pArapConn == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapMarkConnectionUp: null conn\n"));
pSndRcvInfo->StatusCode = ARAPERR_NO_SUCH_CONNECTION; return(STATUS_SUCCESS); }
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, (" Yippeee! %s connection is up! (%x.%x @%lx)\n", (pArapConn->Flags & ARAP_V20_CONNECTION)? "ARAP v2.0":"ARAP v1.0", pArapConn->NetAddr.atn_Network,pArapConn->NetAddr.atn_Node,pArapConn));
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
pArapConn->Flags |= ARAP_CONNECTION_UP;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
pSndRcvInfo->StatusCode = ARAPERR_NO_ERROR;
return( STATUS_SUCCESS ); }
//***
//
// Function: ArapIoctlRecv
// Try to get data for the specified connection. If there is no
// data available, irp is just "queued"
//
// Parameters: pIrp - the irp to process
//
// Return: result of the operation
//
//***$
NTSTATUS ArapIoctlRecv( IN PIRP pIrp ) {
PARAPCONN pArapConn; PARAP_SEND_RECV_INFO pSndRcvInfo; KIRQL OldIrql;
DBG_ARAP_CHECK_PAGED_CODE();
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pArapConn = (PARAPCONN)pSndRcvInfo->AtalkContext;
if (pArapConn == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapIoctlRecv: null conn\n"));
pSndRcvInfo->StatusCode = ARAPERR_NO_SUCH_CONNECTION; return(STATUS_SUCCESS); }
ARAPTRACE(("Entered ArapIoctlRecv (%lx %lx)\n",pIrp,pArapConn));
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
if (pArapConn->State >= MNP_LDISCONNECTING) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapIoctlRecv: rejecting recv ioctl recvd during disconnect %lx\n", pArapConn));
pSndRcvInfo->StatusCode = ARAPERR_DISCONNECT_IN_PROGRESS; RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return(STATUS_SUCCESS); }
// we only allow one irp to be in progress at a time
if (pArapConn->pRecvIoctlIrp != NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapIoctlRecv: rejecting recv \
(irp already in progress) %lx\n", pArapConn));
pSndRcvInfo->StatusCode = ARAPERR_IRP_IN_PROGRESS; RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return(STATUS_SUCCESS); }
pArapConn->pRecvIoctlIrp = pIrp;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
// see if we can satisfy this request
ArapDataToDll( pArapConn );
return( STATUS_PENDING ); }
//***
//
// Function: ArapExchangeParms
// Get configuration parameters from the dll, and return some info
//
// Parameters: pIrp - the irp to process
//
// Return: result of the operation
//
//***$
NTSTATUS ArapExchangeParms( IN PIRP pIrp ) { ZONESTAT ZoneStat; KIRQL OldIrql; PADDRMGMT pAddrMgmt; PEXCHGPARMS pExchgParms;
DBG_ARAP_CHECK_PAGED_CODE();
ARAPTRACE(("Entered ArapExchangeParms (%lx)\n",pIrp));
pExchgParms = (PEXCHGPARMS)pIrp->AssociatedIrp.SystemBuffer;
// we enter this routine only if AtalkDefaultPort is referenced
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql);
ArapGlobs.LowVersion = pExchgParms->Parms.LowVersion; ArapGlobs.HighVersion = pExchgParms->Parms.HighVersion; ArapGlobs.MnpInactiveTime = pExchgParms->Parms.MnpInactiveTime; ArapGlobs.V42bisEnabled = pExchgParms->Parms.V42bisEnabled; ArapGlobs.SmartBuffEnabled = pExchgParms->Parms.SmartBuffEnabled; ArapGlobs.NetworkAccess = pExchgParms->Parms.NetworkAccess; ArapGlobs.DynamicMode = pExchgParms->Parms.DynamicMode; ArapGlobs.NetRange.LowEnd = pExchgParms->Parms.NetRange.LowEnd; ArapGlobs.NetRange.HighEnd = pExchgParms->Parms.NetRange.HighEnd; ArapGlobs.MaxLTFrames = (BYTE)pExchgParms->Parms.MaxLTFrames; ArapGlobs.SniffMode = pExchgParms->Parms.SniffMode;
ArapGlobs.pAddrMgmt = NULL;
// we only support dynamic mode
ASSERT(ArapGlobs.DynamicMode);
#if ARAP_STATIC_MODE
//
// allocate and initialize the bitmap for node allocation
//
if (!(ArapGlobs.DynamicMode)) { RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
if (!ArapValidNetrange(ArapGlobs.NetRange)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapExchangeParms: Netrange %lx - %lx is invalid\n", ArapGlobs.NetRange.LowEnd,ArapGlobs.NetRange.HighEnd));
pExchgParms->StatusCode = ARAPERR_BAD_NETWORK_RANGE;
return (STATUS_SUCCESS); }
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql);
if ( (pAddrMgmt = AtalkAllocZeroedMemory(sizeof(ADDRMGMT))) == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapExchangeParms: alloc for pAddrMgmt failed\n"));
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql); pExchgParms->StatusCode = ARAPERR_OUT_OF_RESOURCES;
return (STATUS_SUCCESS); }
//
// node numbers 0 and 255 are reserved, so mark them as occupied.
//
pAddrMgmt->NodeBitMap[0] |= 0x1; pAddrMgmt->NodeBitMap[31] |= 0x80;
pAddrMgmt->Network = ArapGlobs.NetRange.LowEnd;
ArapGlobs.pAddrMgmt = pAddrMgmt; } #endif //ARAP_STATIC_MODE
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
//
// now, time to return some stack info to the dll.
//
// just an initial guess: dll will figure out the real number when an
// actual connection comes in
//
pExchgParms->Parms.NumZones = 50;
pExchgParms->Parms.ServerAddr.ata_Network = AtalkDefaultPort->pd_Nodes->an_NodeAddr.atn_Network;
pExchgParms->Parms.ServerAddr.ata_Node = AtalkDefaultPort->pd_Nodes->an_NodeAddr.atn_Node;
// copy the server zone in Pascal string format
if (AtalkDesiredZone) { pExchgParms->Parms.ServerZone[0] = AtalkDesiredZone->zn_ZoneLen;
RtlCopyMemory( &pExchgParms->Parms.ServerZone[1], &AtalkDesiredZone->zn_Zone[0], AtalkDesiredZone->zn_ZoneLen ); } else if (AtalkDefaultPort->pd_DefaultZone) { pExchgParms->Parms.ServerZone[0] = AtalkDefaultPort->pd_DefaultZone->zn_ZoneLen;
RtlCopyMemory( &pExchgParms->Parms.ServerZone[1], &AtalkDefaultPort->pd_DefaultZone->zn_Zone[0], AtalkDefaultPort->pd_DefaultZone->zn_ZoneLen ); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("Arap: Server not in any zone?? Client won't see any zones!!\n"));
pExchgParms->Parms.ServerZone[0] = 0; }
ArapGlobs.OurNetwkNum = AtalkDefaultPort->pd_Nodes->an_NodeAddr.atn_Network;
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("Arap: ready to accept connections (Router net=%x node=%x)\n", AtalkDefaultPort->pd_Nodes->an_NodeAddr.atn_Network, AtalkDefaultPort->pd_Nodes->an_NodeAddr.atn_Node));
pExchgParms->StatusCode = ARAPERR_NO_ERROR;
// complete the irp successfully
return (STATUS_SUCCESS);
}
//***
//
// Function: ArapConnect
// Setup the MNP level connection with the client
//
// Parameters: pIrp - the irp to process
//
// Return: result of the operation
//
//***$
NTSTATUS ArapConnect( IN PIRP pIrp, IN ULONG IoControlCode ) { KIRQL OldIrql; PBYTE pFrame; SHORT MnpLen; SHORT FrameLen; DWORD StatusCode=ARAPERR_NO_ERROR; PMNPSENDBUF pMnpSendBuf=NULL; PARAP_SEND_RECV_INFO pSndRcvInfo; PARAPCONN pArapConn; PNDIS_PACKET ndisPacket; NDIS_STATUS ndisStatus;
DBG_ARAP_CHECK_PAGED_CODE();
ARAPTRACE(("Entered ArapConnect (%lx)\n",pIrp));
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pArapConn = FindArapConnByContx(pSndRcvInfo->pDllContext);
if (pArapConn == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapConnect: couldn't find pArapConn!\n")); ASSERT(0); pSndRcvInfo->StatusCode = ARAPERR_NO_SUCH_CONNECTION; return(STATUS_SUCCESS); }
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql); ArapConnections++; RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
#if ARAP_STATIC_MODE
// This will add a route (one-time only) for the ARAP network range
ArapAddArapRoute(); #endif //ARAP_STATIC_MODE
// first, write stack's context for dll's future use
pSndRcvInfo->AtalkContext = (PVOID)pArapConn;
//
// put a refcount for the connection (deref only when the connection gets
// disconnected *and* the dll is told about it).
// Also, initialize the v42bis stuff for this connection
//
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
if (pArapConn->pIoctlIrp != NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapConnect: rejecting connect \
(irp already in progress) %lx\n", pArapConn));
pSndRcvInfo->StatusCode = ARAPERR_IRP_IN_PROGRESS; RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return(STATUS_SUCCESS); }
ASSERT(pArapConn->State == MNP_IDLE);
if (IoControlCode == IOCTL_ARAP_MNP_CONN_RESPOND) { pArapConn->State = MNP_RESPONSE; }
//
// we're doing callback: do some fixing up
//
else { pArapConn->State = MNP_REQUEST; pArapConn->Flags |= ARAP_CALLBACK_MODE; pArapConn->MnpState.SendCredit = 8; }
// Connect refcount: remove only after we tell dll that connection died
pArapConn->RefCount++;
// put MNPSend refcount
pArapConn->RefCount++;
pArapConn->pIoctlIrp = pIrp;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
KeInitializeEvent(&pArapConn->NodeAcquireEvent, NotificationEvent, FALSE);
StatusCode = ARAPERR_NO_ERROR;
//
// allocate buf to send out the connection response/request
//
if ((pMnpSendBuf = AtalkBPAllocBlock(BLKID_MNP_SMSENDBUF)) != NULL) { // get an ndis packet for this puppy
StatusCode = ArapGetNdisPacket(pMnpSendBuf); }
if ((pMnpSendBuf == NULL) || (StatusCode != ARAPERR_NO_ERROR)) { if (pMnpSendBuf) { ArapNdisFreeBuf(pMnpSendBuf); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapConnect: AtalkBPAllocBlock failed on %lx\n", pArapConn)); }
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql); pArapConn->State = MNP_IDLE; pSndRcvInfo->StatusCode = ARAPERR_OUT_OF_RESOURCES; pSndRcvInfo->AtalkContext = ARAP_INVALID_CONTEXT; pArapConn->pIoctlIrp = NULL;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
// didn't succeed: remove that connection refcount
DerefArapConn(pArapConn);
// and the MNPSend refcount
DerefArapConn(pArapConn);
// return success: we have already set our StatusCode to the right thing
return( STATUS_SUCCESS ); }
#if DBG
pMnpSendBuf->Signature = MNPSMSENDBUF_SIGNATURE; #endif
// yes, we need this, in case we bail out
InitializeListHead(&pMnpSendBuf->Linkage);
pMnpSendBuf->SeqNum = 0; // Indication code expects this to be 0
pMnpSendBuf->RetryCount = 1; pMnpSendBuf->RefCount = 1; // 1 MNP refcount
pMnpSendBuf->pArapConn = pArapConn; pMnpSendBuf->ComplRoutine = ArapConnectComplete; pMnpSendBuf->Flags = 1;
// when should we retransmit this pkt?
pMnpSendBuf->RetryTime = pArapConn->SendRetryTime + AtalkGetCurrentTick();
pFrame = &pMnpSendBuf->Buffer[0];
AtalkNdisBuildARAPHdr(pFrame, pArapConn);
pFrame += WAN_LINKHDR_LEN;
FrameLen = WAN_LINKHDR_LEN;
//
// are we just responding to a connection request?
//
if (IoControlCode == IOCTL_ARAP_MNP_CONN_RESPOND) { //
// pSndRcvInfo contains the client's connect request. Parse it and
// prepare a response as appropriate
//
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql);
StatusCode = PrepareConnectionResponse( pArapConn, &pSndRcvInfo->Data[0], pSndRcvInfo->DataLen, pFrame, &MnpLen);
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql);
if (StatusCode != ARAPERR_NO_ERROR) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapConnect: (%lx) response prep failed %ld\n", pArapConn,StatusCode));
ArapConnectComplete(pMnpSendBuf, StatusCode);
return( STATUS_PENDING ); }
FrameLen += MnpLen; }
//
// no, actually we are initiating a connection (callback time)
// copy the frame we used in earlier setup (that dll kindly saved for us)
//
else { RtlCopyMemory(pFrame, (PBYTE)&pSndRcvInfo->Data[0], pSndRcvInfo->DataLen);
FrameLen += (USHORT)pSndRcvInfo->DataLen;
#if DBG
pArapConn->MnpState.SynByte = pSndRcvInfo->Data[0]; pArapConn->MnpState.DleByte = pSndRcvInfo->Data[1]; pArapConn->MnpState.StxByte = pSndRcvInfo->Data[2]; pArapConn->MnpState.EtxByte = MNP_ETX; #endif
}
AtalkSetSizeOfBuffDescData(&pMnpSendBuf->sb_BuffDesc, FrameLen);
pMnpSendBuf->RefCount++; // 1 ndis count, since we'll send now
pMnpSendBuf->DataSize = FrameLen;
NdisAdjustBufferLength(pMnpSendBuf->sb_BuffHdr.bh_NdisBuffer, pMnpSendBuf->DataSize);
// put this connection response on the retransmission queue
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
InsertTailList(&pArapConn->RetransmitQ, &pMnpSendBuf->Linkage);
pArapConn->SendsPending += pMnpSendBuf->DataSize;
pArapConn->MnpState.UnAckedSends++;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
ndisPacket = pMnpSendBuf->sb_BuffHdr.bh_NdisPkt;
NdisSend(&ndisStatus, RasPortDesc->pd_NdisBindingHandle, ndisPacket);
// if there was a problem sending, call the completion routine here
// retransmit logic will send it again
//
if (ndisStatus != NDIS_STATUS_PENDING) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("MnpSendAck: NdisSend failed %lx\n",ndisStatus));
ArapNdisSendComplete(ARAPERR_SEND_FAILED, (PBUFFER_DESC)pMnpSendBuf, NULL); }
//
// done. we'll complete the irp when the client responds (acks or response)
//
return( STATUS_PENDING ); }
//***
//
// Function: ArapConnectComplete
// Completion routine for the ArapConnect routine. This routine
// is called by the ArapRcvComplete routine when we get an ack
// for our Connection response (LR) frame
//
// Parameters: pMnpSendBuf - the send buff that contained the LR response
// StatusCode - how did it go?
//
// Return: none
//
//***$
VOID ArapConnectComplete( IN PMNPSENDBUF pMnpSendBuf, IN DWORD StatusCode ) { KIRQL OldIrql; PIRP pIrp; PARAPCONN pArapConn; PARAP_SEND_RECV_INFO pSndRcvInfo; NTSTATUS ReturnStatus=STATUS_SUCCESS;
DBG_ARAP_CHECK_PAGED_CODE();
pArapConn = pMnpSendBuf->pArapConn;
if (StatusCode != ARAPERR_NO_ERROR) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapConnectComplete: (%x): conn setup failed (%d)!\n", pArapConn,StatusCode)); //
// BUGBUG: change ArapCleanup to accept StatusCode as a parm (currently,
// the real reason behind disconnect is lost, so dll doesn't get it)
//
ArapCleanup(pArapConn);
DerefMnpSendBuf(pMnpSendBuf, FALSE);
return; }
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
pIrp = pArapConn->pIoctlIrp;
pArapConn->pIoctlIrp = NULL;
pArapConn->SendsPending -= pMnpSendBuf->DataSize;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
ARAPTRACE(("Entered ArapConnectComplete (%lx %lx)\n",pIrp,pArapConn));
// if there is an irp (under normal conditions, should be), complete it
if (pIrp) { pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pSndRcvInfo->StatusCode = StatusCode;
if (StatusCode != ARAPERR_NO_ERROR) { pSndRcvInfo->StatusCode = ARAPERR_DISCONNECT_IN_PROGRESS; }
//
// copy the frame we used to establish the connection. In case of
// callback, dll will pass this back to initiate the connection
//
if (pSndRcvInfo->IoctlCode == IOCTL_ARAP_MNP_CONN_RESPOND) { pSndRcvInfo->DataLen = (DWORD)pMnpSendBuf->DataSize;
RtlCopyMemory((PBYTE)&pSndRcvInfo->Data[0], (PBYTE)&pMnpSendBuf->Buffer[0], (DWORD)pMnpSendBuf->DataSize); }
ARAP_COMPLETE_IRP(pIrp, pSndRcvInfo->DataLen + sizeof(ARAP_SEND_RECV_INFO), STATUS_SUCCESS, &ReturnStatus); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapConnectComplete: (%x): no irp available!\n",pArapConn)); }
DerefMnpSendBuf(pMnpSendBuf, FALSE);
}
//***
//
// Function: ArapDisconnect
// Disconnect the connection
//
// Parameters: pIrp - the irp to process
//
// Return: result of the operation
//
//***$
NTSTATUS ArapDisconnect( IN PIRP pIrp ) { PARAPCONN pArapConn; PARAP_SEND_RECV_INFO pSndRcvInfo;
DBG_ARAP_CHECK_PAGED_CODE();
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pArapConn = (PARAPCONN)pSndRcvInfo->AtalkContext;
if ((pArapConn == NULL) || (pArapConn == ARAP_INVALID_CONTEXT)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapDisconnect: null conn\n"));
pSndRcvInfo->StatusCode = ARAPERR_NO_SUCH_CONNECTION; return(STATUS_SUCCESS); }
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapDisconnect: rcvd DISCONNECT on %lx (irp=%lx)\n",pArapConn,pIrp));
// UserCode = 0xFF
pSndRcvInfo->StatusCode = ArapSendLDPacket(pArapConn, 0xFF);
ArapCleanup(pArapConn);
//
// done. let this irp complete: we'll notify the dll of
// 'disconnect-complete' (via select irp) when our cleanup completes
//
return(STATUS_SUCCESS); }
//***
//
// Function: ArapGetAddr
// Get a network address for the remote client
// (if doing dynamic addressing, go to the net; otherwise, get one
// from the table we maintain)
//
// Parameters: pIrp - the irp to process
//
// Return: result of the operation
//
//***$
NTSTATUS ArapGetAddr( IN PIRP pIrp ) { PARAPCONN pArapConn; PARAP_SEND_RECV_INFO pSndRcvInfo; DWORD StatusCode = ARAPERR_NO_NETWORK_ADDR;
DBG_ARAP_CHECK_PAGED_CODE();
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pArapConn = (PARAPCONN)pSndRcvInfo->AtalkContext;
ARAPTRACE(("Entered ArapGetAddr (%lx %lx)\n",pIrp,pArapConn));
if (pArapConn == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapGetAddr: null conn\n"));
pSndRcvInfo->StatusCode = ARAPERR_NO_SUCH_CONNECTION; return(STATUS_SUCCESS); }
if (ArapGlobs.DynamicMode) { StatusCode = ArapGetDynamicAddr(pArapConn); }
#if ARAP_STATIC_MODE
else { StatusCode = ArapGetStaticAddr(pArapConn); } #endif //ARAP_STATIC_MODE
pSndRcvInfo->StatusCode = StatusCode;
if (StatusCode == ARAPERR_NO_ERROR) { pSndRcvInfo->ClientAddr.ata_Network = pArapConn->NetAddr.atn_Network; pSndRcvInfo->ClientAddr.ata_Node = pArapConn->NetAddr.atn_Node; } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapGetAddr: returning %d\n", StatusCode)); }
return( STATUS_SUCCESS );
}
// we don't really support this: why have the code!
#if 0
//***
//
// Function: ArapGetStats
// Return statistics (bytes in, bytes out, compressed etc.) about
// the specified connection
//
// Parameters: pIrp - the irp to process
//
// Return: result of the operation
//
//***$
NTSTATUS ArapGetStats( IN PIRP pIrp ) {
PARAPCONN pArapConn; PARAP_SEND_RECV_INFO pSndRcvInfo; KIRQL OldIrql; PSTAT_INFO pStatInfo;
DBG_ARAP_CHECK_PAGED_CODE();
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pArapConn = (PARAPCONN)pSndRcvInfo->AtalkContext;
if (pArapConn == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapGetStats: null conn\n"));
pSndRcvInfo->StatusCode = ARAPERR_NO_SUCH_CONNECTION; return(STATUS_SUCCESS); }
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapGetStats: returning stats for (%lx)\n",pArapConn));
pStatInfo = (PSTAT_INFO)&pSndRcvInfo->Data[0];
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
*pStatInfo = pArapConn->StatInfo;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
pSndRcvInfo->StatusCode = ARAPERR_NO_ERROR;
return( STATUS_SUCCESS ); }
#endif // #if 0 around ArapGetStats
//***
//
// Function: ArapIoctlSend
// Send the buffer given by the dll to the remote client.
// This routine calls the routine to prepare the send (v42bis
// compression and MNP bookkeeping) and then sends it out
//
// Parameters: pIrp - the irp to process
//
// Return: result of the operation
//
//***$
NTSTATUS ArapIoctlSend( IN PIRP pIrp ) { KIRQL OldIrql; BUFFER_DESC OrgBuffDesc; PARAPCONN pArapConn; PARAP_SEND_RECV_INFO pSndRcvInfo; DWORD StatusCode=ARAPERR_NO_ERROR;
DBG_ARAP_CHECK_PAGED_CODE();
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pArapConn = (PARAPCONN)pSndRcvInfo->AtalkContext;
if (pArapConn == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapIoctlSend: null conn\n"));
pSndRcvInfo->StatusCode = ARAPERR_NO_SUCH_CONNECTION; return(STATUS_SUCCESS); }
ARAPTRACE(("Entered ArapIoctlSend (%lx %lx)\n",pIrp,pArapConn));
// save the irp so we can complete it in the completion routine
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
// we only allow one irp to be in progress at a time
if (pArapConn->pIoctlIrp != NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapIoctlSend: rejecting send \
(irp already in progress) %lx\n", pArapConn));
pSndRcvInfo->StatusCode = ARAPERR_IRP_IN_PROGRESS; RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return(STATUS_SUCCESS); }
pArapConn->pIoctlIrp = pIrp;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
ASSERT(pSndRcvInfo->DataLen <= 618);
DBGDUMPBYTES("Dll send:", &pSndRcvInfo->Data[0],pSndRcvInfo->DataLen,1);
//
// get the send ready (compression, MNP bookkeeping etc.)
//
OrgBuffDesc.bd_Next = NULL; OrgBuffDesc.bd_Length = (SHORT)pSndRcvInfo->DataLen; OrgBuffDesc.bd_CharBuffer = &pSndRcvInfo->Data[0]; OrgBuffDesc.bd_Flags = BD_CHAR_BUFFER;
StatusCode = ArapSendPrepare( pArapConn, &OrgBuffDesc, ARAP_SEND_PRIORITY_HIGH );
if (StatusCode == ARAPERR_NO_ERROR) { //
// now, send that send over. Note that we don't care about the return
// code here: if this particular send fails, we still tell the dll that
// the send succeeded because our retransmission logic will take care
// of ensuring that the send gets there.
//
ArapNdisSend( pArapConn, &pArapConn->HighPriSendQ ); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapIoctlSend (%lx): ArapSendPrepare failed (%ld)\n", pArapConn,StatusCode)); }
ArapIoctlSendComplete(StatusCode, pArapConn);
return( STATUS_PENDING ); }
//***
//
// Function: ArapProcessSelect
// Process the select irp issued by the dll
// This routine saves the select irp so that any connection that
// needs it can take it. Also, it sees if any of the connections
// is waiting for an irp to indicate a disconnect-complete or
// data to the dll. If so, that is completed here.
//
// Parameters: pIrp - the select irp to process
//
// Return: result of the operation
//
//***$
NTSTATUS ArapProcessSelect( IN PIRP pIrp ) { KIRQL OldIrql; KIRQL OldIrql2; PARAPCONN pDiscArapConn=NULL; PARAPCONN pRcvArapConn=NULL; PARAP_SEND_RECV_INFO pSndRcvInfo; PLIST_ENTRY pList; DWORD dwBytesToDll; DWORD StatusCode; NTSTATUS ReturnStatus;
ARAPTRACE(("Entered ArapProcessSelect (%lx)\n",pIrp));
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pDiscArapConn = NULL; pRcvArapConn = NULL;
//
// it's possible that between the time the last select irp completed and
// this select came down, some activity that needs a select irp occured
// (e.g. a disconnect). See if we have hit such a condition
//
ArapDelayedNotify(&pDiscArapConn, &pRcvArapConn);
//
// if we found an arapconn that was waiting for a select irp to notify the
// dll of disconnect, do the good deed!
//
if (pDiscArapConn) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessSelect: completing delayed disconnect on %lx!\n", pDiscArapConn));
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 (pDiscArapConn->pDbgTraceBuffer && pDiscArapConn->SniffedBytes > 0) { dwBytesToDll = ArapFillIrpWithSniffInfo(pDiscArapConn,pIrp); } #endif
dwBytesToDll += sizeof(ARAP_SEND_RECV_INFO);
//
// no need for spinlock here
//
if (pDiscArapConn->Flags & ARAP_REMOTE_DISCONN) { StatusCode = ARAPERR_RDISCONNECT_COMPLETE; } else { StatusCode = ARAPERR_LDISCONNECT_COMPLETE; }
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer; pSndRcvInfo->pDllContext = pDiscArapConn->pDllContext;
pSndRcvInfo->StatusCode = StatusCode;
pSndRcvInfo->DataLen = dwBytesToDll;
// we told (rather, will very shortly tell) dll: remove this link
pDiscArapConn->pDllContext = NULL;
// now that we told dll, remove 1 refcount
DerefArapConn( pDiscArapConn );
return(STATUS_SUCCESS); }
IoAcquireCancelSpinLock(&OldIrql);
if (pIrp->Cancel) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessSelect: select irp %lx already cancelled!\n", pIrp));
IoReleaseCancelSpinLock(OldIrql); ARAP_COMPLETE_IRP(pIrp, 0, STATUS_CANCELLED, &ReturnStatus); return(ReturnStatus); }
ACQUIRE_SPIN_LOCK(&ArapSpinLock, &OldIrql2);
if (ArapSelectIrp != NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessSelect: select irp %lx already in progress!\n", ArapSelectIrp)); ASSERT(0);
pSndRcvInfo->StatusCode = ARAPERR_IRP_IN_PROGRESS; RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql2); IoReleaseCancelSpinLock(OldIrql); return( STATUS_SUCCESS ); }
//
// does arap engine need to be told about some change?
//
if ( (ArapStackState == ARAP_STATE_ACTIVE_WAITING) || (ArapStackState == ARAP_STATE_INACTIVE_WAITING) ) { if (ArapStackState == ARAP_STATE_ACTIVE_WAITING) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessSelect: delayed notify: stack is now active!\n"));
ArapStackState = ARAP_STATE_ACTIVE; pSndRcvInfo->StatusCode = ARAPERR_STACK_IS_ACTIVE; } else if (ArapStackState == ARAP_STATE_INACTIVE_WAITING) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapProcessSelect: delayed notify: stack is now inactive!\n"));
ArapStackState = ARAP_STATE_INACTIVE; pSndRcvInfo->StatusCode = ARAPERR_STACK_IS_NOT_ACTIVE; }
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql2); IoReleaseCancelSpinLock(OldIrql); return( STATUS_SUCCESS ); }
//
// ok, most common case: we just need to stash this select irp!
//
ArapSelectIrp = pIrp;
RELEASE_SPIN_LOCK(&ArapSpinLock, OldIrql2);
IoSetCancelRoutine(pIrp, (PDRIVER_CANCEL)AtalkTdiCancel);
IoReleaseCancelSpinLock(OldIrql);
//
// if there was an arapconn waiting for a select irp, pass on the data!
//
if (pRcvArapConn) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapProcessSelect: getting delayed data on %lx at %ld\n", pRcvArapConn,AtalkGetCurrentTick()));
ARAP_DBG_TRACE(pRcvArapConn,11105,0,0,0,0);
ArapDataToDll( pRcvArapConn ); }
return(STATUS_PENDING); }
//***
//
// Function: ArapDelayedNotify
// This routine checks to see if any of the arap connections was
// waiting for a select irp to come down to notify the dll that
// either the connection went away, or if there was any data waiting on a
// connection.
//
// Parameters: ppDiscArapConn - if a "disconnected" connection exists, it's returned
// in this pointer. If many exist, the first one lucks out.
// If none exists, null is returned here
// ppRecvArapConn - same as above except that the connection returned is
// the one where some data is waiting
//
// Return: none
//
//***$
VOID ArapDelayedNotify( OUT PARAPCONN *ppDiscArapConn, OUT PARAPCONN *ppRecvArapConn ) {
KIRQL OldIrql; PARAPCONN pArapConn=NULL; PLIST_ENTRY pList; PARAPCONN pDiscArapConn=NULL; PARAPCONN pRecvArapConn=NULL;
*ppDiscArapConn = NULL; *ppRecvArapConn = NULL;
if (!RasPortDesc) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapDelayedNotify: RasPortDesc is NULL!\n")); ASSERT(0);
return; }
ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
pList = RasPortDesc->pd_ArapConnHead.Flink;
while (pList != &RasPortDesc->pd_ArapConnHead) { pArapConn = CONTAINING_RECORD(pList, ARAPCONN, Linkage); pList = pArapConn->Linkage.Flink;
ACQUIRE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
//
// if a connection has been disconnected and is waiting for a select
// irp to show up, find out who that is and let the caller know
//
if ((pArapConn->State == MNP_DISCONNECTED) && (pArapConn->Flags & DISCONNECT_NO_IRP)) { pArapConn->Flags &= ~DISCONNECT_NO_IRP; pDiscArapConn = pArapConn;
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock); break; }
//
// if a connection has some data come in on it while select irp wasn't
// down yet, note down this connection
//
if ((pArapConn->State == MNP_UP) && (pArapConn->Flags & ARAP_CONNECTION_UP) && (!IsListEmpty(&pArapConn->ArapDataQ))) { pRecvArapConn = pArapConn; }
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock); }
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock,OldIrql);
if (pDiscArapConn) { *ppDiscArapConn = pDiscArapConn; } else if (pRecvArapConn) { *ppRecvArapConn = pRecvArapConn; }
}
//***
//
// Function: ArapSendPrepare
// This routine takes an incoming buffer descriptor, compresses
// each of the buffers in it and passes the compressed data on to
// another routine which splits (or stuffs) the compressed bytes
// into MNP-level packets.
//
// Parameters: pArapConn - the connection in question
// pOrgBuffDesc - the buffer descriptor containing data buffer(s)
// Priority - how important is the data (highest priority = 1)
// 1 - directed DDP dgrams (all except NBP)
// 2 - directed DDP dgrams (NBP)
// 3 - all DDP-level broadcast (NBP only)
//
// Return: ARAPERR_NO_ERROR if things go well, otherwise errorcode
//
//***$
DWORD ArapSendPrepare( IN PARAPCONN pArapConn, IN PBUFFER_DESC pOrgBuffDesc, IN DWORD Priority ) {
KIRQL OldIrql; DWORD StatusCode=ARAPERR_NO_ERROR; SHORT EthLen, MnpLen; PBYTE pCurrBuff; DWORD CurrBuffLen; DWORD UncompressedDataLen; PBYTE pCompressedData; PBYTE pCompressedDataBuffer; DWORD CompressedDataLen; DWORD CDataLen; PBUFFER_DESC pBuffDesc; DWORD CompBufDataSize;
DBG_ARAP_CHECK_PAGED_CODE();
// BUGBUG: this line put in for now: remove it and make sure priority queue stuff works
Priority = ARAP_SEND_PRIORITY_HIGH;
ARAPTRACE(("Entered ArapSendPrepare (%lx %lx)\n",pArapConn,pOrgBuffDesc));
//
// it's essential to hold this lock until the entire send is compressed and
// put on the queue (Otherwise, we risk mixing up different sends!)
//
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
// are we disconnecting (or not up yet)? if so, don't accept this send
if (pArapConn->State != MNP_UP) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapSendPrepare: (%lx) state=%d, rejecting send\n", pArapConn,pArapConn->State));
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return( ARAPERR_DISCONNECT_IN_PROGRESS ); }
// do we have too many sends queued up? if so, just drop this send
if (pArapConn->SendsPending > ARAP_SENDQ_UPPER_LIMIT) { // make sure it's not gone negative..
ASSERT(pArapConn->SendsPending < 0x100000);
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return( ARAPERR_OUT_OF_RESOURCES ); }
//
// allocate memory to store the compressed data
//
pCompressedDataBuffer = AtalkBPAllocBlock(BLKID_ARAP_SNDPKT);
if (pCompressedDataBuffer == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapSendPrepare: alloc for compressing data failed (%lx)\n", pArapConn));
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return( ARAPERR_OUT_OF_RESOURCES ); }
pBuffDesc = pOrgBuffDesc; // first buffer
CompressedDataLen = 0; // length of compressed data
CompBufDataSize = ARAP_SENDBUF_SIZE; // size of buffer in which to compress
pCompressedData = pCompressedDataBuffer; // ptr to buffer in which to compress
UncompressedDataLen = 0; // size of uncompressed data
#if DBG
//
// put in a guard signature to catch buffer overrun
//
*((DWORD *)&(pCompressedDataBuffer[ARAP_SENDBUF_SIZE-4])) = 0xdeadbeef; #endif
//
// first, walk through the buffer descriptor chain and compress all the
// buffers.
//
while (pBuffDesc) { //
// is this a buffer?
//
if (pBuffDesc->bd_Flags & BD_CHAR_BUFFER) { pCurrBuff = pBuffDesc->bd_CharBuffer; CurrBuffLen = pBuffDesc->bd_Length; }
//
// nope, it's an mdl!
//
else { pCurrBuff = MmGetSystemAddressForMdlSafe( pBuffDesc->bd_OpaqueBuffer, NormalPagePriority);
if (pCurrBuff == NULL) { ASSERT(0); RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); AtalkBPFreeBlock(pCompressedDataBuffer); return( ARAPERR_OUT_OF_RESOURCES ); }
CurrBuffLen = MmGetMdlByteCount(pBuffDesc->bd_OpaqueBuffer); }
DBGDUMPBYTES("ArapSendPrepare (current buffer): ",pCurrBuff,CurrBuffLen,2);
UncompressedDataLen += CurrBuffLen;
ASSERT(UncompressedDataLen <= ARAP_LGPKT_SIZE);
// exclude the 2 srp length bytes
if (UncompressedDataLen > ARAP_MAXPKT_SIZE_OUTGOING+2) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapSendPrepare (%lx): send pkt exceeds limit\n",pArapConn));
ASSERT(0); }
//
// compress the packet (if v42bis is on, that is)
//
if (pArapConn->Flags & MNP_V42BIS_NEGOTIATED) { StatusCode = v42bisCompress(pArapConn, pCurrBuff, CurrBuffLen, pCompressedData, CompBufDataSize, &CDataLen);
}
//
// hmmm, no v42bis! just copy it as is and skip compression!
//
else { ASSERT(CompBufDataSize >= CurrBuffLen);
RtlCopyMemory(pCompressedData, pCurrBuff, CurrBuffLen);
CDataLen = CurrBuffLen;
StatusCode = ARAPERR_NO_ERROR; }
#if DBG
// ... and, check our guard signature
ASSERT (*((DWORD *)&(pCompressedDataBuffer[ARAP_SENDBUF_SIZE-4])) == 0xdeadbeef); #endif
if (StatusCode != ARAPERR_NO_ERROR) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapSendPrepare (%lx):\
v42bisCompress returned %ld\n", pArapConn,StatusCode));
ASSERT(0);
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
AtalkBPFreeBlock(pCompressedDataBuffer); return(StatusCode); }
pCompressedData += CDataLen; CompressedDataLen += CDataLen; CompBufDataSize -= CDataLen;
pBuffDesc = pBuffDesc->bd_Next; }
// we are about to send so many uncompressed bytes: update stats
pArapConn->StatInfo.BytesTransmittedUncompressed += UncompressedDataLen;
// this is how many bytes will go out on the wire: update stats
pArapConn->StatInfo.BytesTransmittedCompressed += CompressedDataLen;
//
// this is how many bytes will go out on the wire: update stats
// Note that we will be adding the start/stop etc. bytes to this count somewhere else
//
pArapConn->StatInfo.BytesSent += CompressedDataLen;
#if DBG
ArapStatistics.SendPreCompMax = (UncompressedDataLen > ArapStatistics.SendPreCompMax)? UncompressedDataLen : ArapStatistics.SendPreCompMax;
ArapStatistics.SendPostCompMax = (CompressedDataLen > ArapStatistics.SendPostCompMax)? CompressedDataLen : ArapStatistics.SendPostCompMax;
ArapStatistics.SendPreCompMin = (UncompressedDataLen < ArapStatistics.SendPreCompMin)? UncompressedDataLen : ArapStatistics.SendPreCompMin;
ArapStatistics.SendPostCompMin = (CompressedDataLen < ArapStatistics.SendPostCompMin)? CompressedDataLen : ArapStatistics.SendPostCompMin; #endif
ARAP_DBG_TRACE(pArapConn,11205,pOrgBuffDesc,Priority,0,0);
// now go put the send on the queue (And yes: hold that lock)
StatusCode = ArapQueueSendBytes(pArapConn, pCompressedDataBuffer, CompressedDataLen, Priority);
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
AtalkBPFreeBlock(pCompressedDataBuffer);
return(StatusCode); }
//***
//
// Function: ArapMnpSendComplete
// Free up the buffer used for the MNP send. If the send failed
// then kill the connection (remember, it's not just one send
// failing but a failure after all the retransmission jing-bang)
//
// Parameters: pMnpSendBuf - the send buff that contained the LR response
// StatusCode - how did it go?
//
// Return: none
//
//***$
VOID ArapMnpSendComplete( IN PMNPSENDBUF pMnpSendBuf, IN DWORD StatusCode ) { PARAPCONN pArapConn; DWORD State; KIRQL OldIrql;
DBG_ARAP_CHECK_PAGED_CODE();
pArapConn = pMnpSendBuf->pArapConn;
ARAPTRACE(("Entered ArapMnpSendComplete (%lx %lx %lx)\n", pMnpSendBuf,StatusCode,pArapConn));
// the send buffer is getting freed up: update the counter
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
State = pArapConn->State;
pArapConn->SendsPending -= pMnpSendBuf->DataSize;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
if ((StatusCode != ARAPERR_NO_ERROR) && (State < MNP_LDISCONNECTING)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapMnpSendComplete (%lx %lx): bad link? Tearing down connection\n", StatusCode,pArapConn));
// link must have gone down: kill the connection!
ArapCleanup(pArapConn); }
// mark that compl. routine has run
#if DBG
pMnpSendBuf->Signature -= 0x100; #endif
// the send has been acked: take away the MNP refcount on the send
DerefMnpSendBuf(pMnpSendBuf, FALSE); }
//***
//
// Function: ArapIoctlSendComplete
// This routine is called right after the send is done in
// ArapIoctlSend, to let the dll know what happened to the send.
//
// Parameters: Status - did the send actually succeed
// pArapConn - the connection in quesion
//
// Return: none
//
//***$
VOID ArapIoctlSendComplete( DWORD StatusCode, PARAPCONN pArapConn ) {
PIRP pIrp; KIRQL OldIrql; PARAP_SEND_RECV_INFO pSndRcvInfo; NTSTATUS ReturnStatus=STATUS_SUCCESS;
DBG_ARAP_CHECK_PAGED_CODE();
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
pIrp = pArapConn->pIoctlIrp; pArapConn->pIoctlIrp = NULL;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
ARAPTRACE(("Entered ArapIoctlSendComplete (%lx %lx)\n",pArapConn,pIrp));
//
// if there is a user-level irp pending, complete it here
//
if (pIrp) { pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pSndRcvInfo->StatusCode = StatusCode;
// complete the irp (irp always completes successfully!)
ARAP_COMPLETE_IRP(pIrp, sizeof(ARAP_SEND_RECV_INFO), STATUS_SUCCESS, &ReturnStatus); }
}
//***
//
// Function: ArapDataToDll
// This routine tries to complete a receive posted on a connection.
// When data arrives, if the Arap connection is established then
// this routine tries to complete a receive via the "select" irp.
// If the Arap connection is not yet established, then a receive
// is completed via a "direct" irp.
//
// Parameters: pArapConn - connection element in question
//
// Return: Number of bytes transferred to the dll
//
//***$
DWORD ArapDataToDll( IN PARAPCONN pArapConn ) {
KIRQL OldIrql; PLIST_ENTRY pRcvList; PARAPBUF pArapBuf; PARAP_SEND_RECV_INFO pSndRcvInfo=NULL; PIRP pIrp; USHORT SrpModLen; DWORD dwBytesToDll; DWORD StatusCode; NTSTATUS ReturnStatus=STATUS_SUCCESS;
DBG_ARAP_CHECK_PAGED_CODE();
ARAPTRACE(("Entered ArapDataToDll (%lx)\n",pArapConn));
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
if (IsListEmpty(&pArapConn->ArapDataQ)) { RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return( 0 ); }
pRcvList = pArapConn->ArapDataQ.Flink;
pArapBuf = CONTAINING_RECORD(pRcvList, ARAPBUF, Linkage);
//
// if the ARAP connection is established, we can hand the data only
// to a select irp (dll won't post direct rcv's anymore)
//
if ( pArapConn->Flags & ARAP_CONNECTION_UP ) { ArapGetSelectIrp(&pIrp); StatusCode = ARAPERR_DATA; }
//
// if the ARAP connection is not established yet, we must guarantee that
// we hand the data only to a direct rcv irp for this connection
//
else { pIrp = pArapConn->pRecvIoctlIrp; pArapConn->pRecvIoctlIrp = NULL; StatusCode = ARAPERR_NO_ERROR; }
// no irp? just have to wait then
if (!pIrp) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapDataToDll: no select irp, data waiting %lx at %ld\n", pArapConn,AtalkGetCurrentTick()));
ARAP_DBG_TRACE(pArapConn,11505,0,0,0,0);
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return( 0 ); }
//
// now that we have irp, fill in the info, after unlinking the recv buf
//
RemoveEntryList(&pArapBuf->Linkage);
ASSERT(pArapConn->RecvsPending >= pArapBuf->DataSize); pArapConn->RecvsPending -= pArapBuf->DataSize;
ARAP_ADJUST_RECVCREDIT(pArapConn);
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
ASSERT(pSndRcvInfo->DataLen >= pArapBuf->DataSize);
SrpModLen = pArapBuf->DataSize;
// ok, copy the data in
RtlCopyMemory( &pSndRcvInfo->Data[0], pArapBuf->CurrentBuffer, pArapBuf->DataSize );
// set the info (contexts need to be set each time in case of select)
pSndRcvInfo->AtalkContext = pArapConn; pSndRcvInfo->pDllContext = pArapConn->pDllContext; pSndRcvInfo->DataLen = SrpModLen; pSndRcvInfo->StatusCode = StatusCode;
dwBytesToDll = SrpModLen + sizeof(ARAP_SEND_RECV_INFO);
DBGDUMPBYTES("Dll recv:", &pSndRcvInfo->Data[0],pSndRcvInfo->DataLen,1);
// ok, complete that irp now!
ARAP_COMPLETE_IRP(pIrp, dwBytesToDll, STATUS_SUCCESS, &ReturnStatus);
// done with that buffer: free it here
ARAP_FREE_RCVBUF(pArapBuf);
return(SrpModLen); }
//***
//
// Function: MnpSendAckIfReqd
// This routine sends an ack to the remote client after making
// sure that condition(s) do exist warranting sending of an Ack.
//
// Parameters: pArapConn - connection element in question
//
// Return: none
//
//***$
VOID MnpSendAckIfReqd( IN PARAPCONN pArapConn, IN BOOLEAN fForceAck ) { KIRQL OldIrql; BYTE SeqToAck; BYTE RecvCredit; PMNPSENDBUF pMnpSendBuf; PBYTE pFrame, pFrameStart; BOOLEAN fOptimized=TRUE; USHORT FrameLen; PNDIS_PACKET ndisPacket; NDIS_STATUS ndisStatus; DWORD StatusCode; BOOLEAN fMustSend;
DBG_ARAP_CHECK_PAGED_CODE();
ARAPTRACE(("Entered MnpSendAckIfReqd (%lx)\n",pArapConn));
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
//
// if we are not up yet (or are disconnecting), forget about this ack
//
if (pArapConn->State != MNP_UP) { RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return; }
fMustSend = FALSE;
//
// first, find out if we need to send an ack at all
//
//
// if we are told to send, just send it: don't question the decision!
//
if (fForceAck) { fMustSend = TRUE; }
//
// spec says if we have one or more unacked pkts and there is no user data
// to send, then send it (what's user data got to do with this??)
//
// BUGBUG: for now, don't check for IsListEmpty(&pArapConn->HighPriSendQ)
#if 0
else if ( (pArapConn->MnpState.UnAckedRecvs > 0) && (IsListEmpty(&pArapConn->HighPriSendQ)) ) { fMustSend = TRUE; } #endif
else if (pArapConn->MnpState.UnAckedRecvs > 0) { fMustSend = TRUE; }
//
// if we haven't acked for a while (i.e. have received more than the
// acceptable number of unacked packets) then send it
//
else if (pArapConn->MnpState.UnAckedRecvs >= pArapConn->MnpState.UnAckedLimit) { fMustSend = TRUE; }
if (!fMustSend) { RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return; }
StatusCode = ARAPERR_NO_ERROR;
// first, allocate a buff descriptor (before we change the state variables)
if ((pMnpSendBuf = AtalkBPAllocBlock(BLKID_MNP_SMSENDBUF)) != NULL) { StatusCode = ArapGetNdisPacket(pMnpSendBuf); }
if ((pMnpSendBuf == NULL) || (StatusCode != ARAPERR_NO_ERROR)) { if (pMnpSendBuf) { ArapNdisFreeBuf(pMnpSendBuf); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("MnpSendAckIfReqd: AtalkBPAllocBlock failed on %lx\n", pArapConn)) }
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); return; }
// BUGBUG: for now, always send full size
//RecvCredit = pArapConn->MnpState.RecvCredit;
RecvCredit = pArapConn->MnpState.WindowSize;
// tell the client which is the last packet we got successfully
SeqToAck = pArapConn->MnpState.LastSeqRcvd;
#if DBG
if ((SeqToAck == pArapConn->MnpState.LastAckSent) && (pArapConn->MnpState.HoleInSeq++ > 1)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("MnpSendAckIfReqd: ack %x already sent earlier\n",SeqToAck)); } #endif
pArapConn->MnpState.LastAckSent = pArapConn->MnpState.LastSeqRcvd;
// with this ack, we will be acking all the outstanding recv's
pArapConn->MnpState.UnAckedRecvs = 0;
// "stop" the 402 timer
pArapConn->LATimer = 0;
// reset the flow-control timer
pArapConn->FlowControlTimer = AtalkGetCurrentTick() + pArapConn->T404Duration;
if (!(pArapConn->Flags & MNP_OPTIMIZED_DATA)) { fOptimized = FALSE; }
ARAP_DBG_TRACE(pArapConn,11605,0,SeqToAck,RecvCredit,0);
MNP_DBG_TRACE(pArapConn,SeqToAck,MNP_LA);
// put MNPSend refcount
pArapConn->RefCount++;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
#if DBG
pMnpSendBuf->Signature = MNPSMSENDBUF_SIGNATURE; InitializeListHead(&pMnpSendBuf->Linkage); #endif
pMnpSendBuf->RetryCount = 0; // not relevant here
pMnpSendBuf->RefCount = 1; // remove when send completes
pMnpSendBuf->pArapConn = pArapConn; pMnpSendBuf->ComplRoutine = NULL; pMnpSendBuf->Flags = 1;
pFrame = pFrameStart = &pMnpSendBuf->Buffer[0];
AtalkNdisBuildARAPHdr(pFrame, pArapConn);
pFrame += WAN_LINKHDR_LEN;
//
// put the start flags
//
*pFrame++ = pArapConn->MnpState.SynByte; *pFrame++ = pArapConn->MnpState.DleByte; *pFrame++ = pArapConn->MnpState.StxByte;
//
// now, put the body of the Ack frame
//
if (fOptimized) { *pFrame++ = 3; // length indication
*pFrame++ = 5; // type indication
*pFrame++ = SeqToAck; // Receive seq number ( N(R) )
*pFrame++ = RecvCredit; // receive credit ( N(k) )
} else { *pFrame++ = 7; // length indication
*pFrame++ = 5; // type indication
*pFrame++ = 1; // var type
*pFrame++ = 1; // var len
*pFrame++ = SeqToAck; // receive seq number ( N(R) )
*pFrame++ = 2; // var type
*pFrame++ = 1; // var len
*pFrame++ = RecvCredit; // receive credit ( N(k) )
}
//
// now finally, put the stop flags (no need for spinlock: this won't change!)
//
*pFrame++ = pArapConn->MnpState.DleByte; *pFrame++ = pArapConn->MnpState.EtxByte;
FrameLen = (USHORT)(pFrame - pFrameStart);
AtalkSetSizeOfBuffDescData(&pMnpSendBuf->sb_BuffDesc, FrameLen);
NdisAdjustBufferLength(pMnpSendBuf->sb_BuffHdr.bh_NdisBuffer,FrameLen);
//
// send the packet over. We need to go directly, and not via ArapNdisSend
// because this packet needs to be delivered just once, regardless of
// whether send window is open
//
ndisPacket = pMnpSendBuf->sb_BuffHdr.bh_NdisPkt;
NdisSend(&ndisStatus, RasPortDesc->pd_NdisBindingHandle, ndisPacket);
// if there was a problem sending, call the completion routine here
if (ndisStatus != NDIS_STATUS_PENDING) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("MnpSendAckIfReqd: NdisSend failed %lx\n",ndisStatus));
ArapNdisSendComplete(ARAPERR_SEND_FAILED, (PBUFFER_DESC)pMnpSendBuf, NULL); } }
//***
//
// Function: MnpSendLNAck
// This routine sends an LN ack to the remote client, acknowleding
// receipt of an LN frame
//
// Parameters: pArapConn - connection element in question
//
// Return: none
//
//***$
VOID MnpSendLNAck( IN PARAPCONN pArapConn, IN BYTE LnSeqToAck ) { KIRQL OldIrql; PMNPSENDBUF pMnpSendBuf; PBYTE pFrame, pFrameStart; USHORT FrameLen; PNDIS_PACKET ndisPacket; NDIS_STATUS ndisStatus; DWORD StatusCode;
DBG_ARAP_CHECK_PAGED_CODE();
ARAPTRACE(("Entered MnpSendLNAck (%lx), %d\n",pArapConn,LnSeqToAck));
StatusCode = ARAPERR_NO_ERROR;
// first, allocate a buff descriptor
if ((pMnpSendBuf = AtalkBPAllocBlock(BLKID_MNP_SMSENDBUF)) != NULL) { StatusCode = ArapGetNdisPacket(pMnpSendBuf); }
if (pMnpSendBuf == NULL || (StatusCode != ARAPERR_NO_ERROR)) { if (pMnpSendBuf) { ArapNdisFreeBuf(pMnpSendBuf); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("MnpSendLNAck: AtalkBPAllocBlock failed on %lx\n", pArapConn)) } return; }
#if DBG
pMnpSendBuf->Signature = MNPSMSENDBUF_SIGNATURE; InitializeListHead(&pMnpSendBuf->Linkage); #endif
pMnpSendBuf->RetryCount = 0; // not relevant here
pMnpSendBuf->RefCount = 1; // remove when send completes
pMnpSendBuf->pArapConn = pArapConn; pMnpSendBuf->ComplRoutine = NULL; pMnpSendBuf->Flags = 1;
pFrame = pFrameStart = &pMnpSendBuf->Buffer[0];
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
// if we are disconnecting, forget about sending this ack
if (pArapConn->State >= MNP_LDISCONNECTING) { RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); ArapNdisFreeBuf(pMnpSendBuf); return; }
// put MNPSend refcount
pArapConn->RefCount++;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
AtalkNdisBuildARAPHdr(pFrame, pArapConn);
pFrame += WAN_LINKHDR_LEN;
//
// put the start flags
//
*pFrame++ = pArapConn->MnpState.SynByte; *pFrame++ = pArapConn->MnpState.DleByte; *pFrame++ = pArapConn->MnpState.StxByte;
//
// now, put the body of the Ack frame
//
*pFrame++ = 4; // length indication
*pFrame++ = 7; // type indication
*pFrame++ = 1; // var type
*pFrame++ = 1; // var len
*pFrame++ = LnSeqToAck; //
//
// now finally, put the stop flags
//
*pFrame++ = pArapConn->MnpState.DleByte; *pFrame++ = pArapConn->MnpState.EtxByte;
FrameLen = (USHORT)(pFrame - pFrameStart);
AtalkSetSizeOfBuffDescData(&pMnpSendBuf->sb_BuffDesc, FrameLen);
NdisAdjustBufferLength(pMnpSendBuf->sb_BuffHdr.bh_NdisBuffer,FrameLen);
//
// send the packet over. We need to go directly, and not via ArapNdisSend
// because this packet needs to be delivered just once, regardless of
// whether send window is open
//
ndisPacket = pMnpSendBuf->sb_BuffHdr.bh_NdisPkt;
NdisSend(&ndisStatus, RasPortDesc->pd_NdisBindingHandle, ndisPacket);
// if there was a problem sending, call the completion routine here
if (ndisStatus != NDIS_STATUS_PENDING) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("MnpSendLNAck: NdisSend failed %lx\n",ndisStatus));
ArapNdisSendComplete(ARAPERR_SEND_FAILED, (PBUFFER_DESC)pMnpSendBuf, NULL); } }
//***
//
// Function: ArapSendLDPacket
// This routine sends a Disconnect (LD) packet to the client
//
// Parameters: pArapConn - the connection
//
// Return: result of the operation
//
//***$
DWORD ArapSendLDPacket( IN PARAPCONN pArapConn, IN BYTE UserCode ) { PBYTE pFrame, pFrameStart; USHORT FrameLen; PNDIS_PACKET ndisPacket; NDIS_STATUS ndisStatus; PMNPSENDBUF pMnpSendBuf; KIRQL OldIrql; DWORD StatusCode;
DBG_ARAP_CHECK_PAGED_CODE();
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapSendLDPacket: sending DISCONNECT on %lx\n",pArapConn));
StatusCode = ARAPERR_NO_ERROR;
//
// allocate buf to send out the disconnection request
//
if ((pMnpSendBuf = AtalkBPAllocBlock(BLKID_MNP_SMSENDBUF)) != NULL) { StatusCode = ArapGetNdisPacket(pMnpSendBuf); }
if ((pMnpSendBuf == NULL) || (StatusCode != ARAPERR_NO_ERROR)) { if (pMnpSendBuf) { ArapNdisFreeBuf(pMnpSendBuf); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapSendLDPacket: AtalkBPAllocBlock failed on %lx\n", pArapConn)); }
return(ARAPERR_OUT_OF_RESOURCES); }
#if DBG
pMnpSendBuf->Signature = MNPSMSENDBUF_SIGNATURE; InitializeListHead(&pMnpSendBuf->Linkage); #endif
pMnpSendBuf->RetryCount = 0; // not relevant here
pMnpSendBuf->RefCount = 1; // remove when send completes
pMnpSendBuf->pArapConn = pArapConn; pMnpSendBuf->ComplRoutine = NULL; pMnpSendBuf->Flags = 1;
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
//
// if we are already disconnecting (say remote disconnected), just say ok
//
if (pArapConn->State >= MNP_LDISCONNECTING) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapSendLDPacket: silently \
discarding disconnect (already in progress) %lx\n", pArapConn));
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql); ArapNdisFreeBuf(pMnpSendBuf); return(ARAPERR_DISCONNECT_IN_PROGRESS); }
// Disconnect refcount: protect pArapConn until disconnect is complete
pArapConn->RefCount++;
// put MNPSend refcount
pArapConn->RefCount++;
pArapConn->State = MNP_LDISCONNECTING;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
pFrame = pFrameStart = &pMnpSendBuf->Buffer[0];
AtalkNdisBuildARAPHdr(pFrame, pArapConn);
pFrame += WAN_LINKHDR_LEN;
//
// put the start flags
//
*pFrame++ = pArapConn->MnpState.SynByte; *pFrame++ = pArapConn->MnpState.DleByte; *pFrame++ = pArapConn->MnpState.StxByte;
//
// now, put the body of the LD frame
//
*pFrame++ = 7; // length indication
*pFrame++ = 2; // type indication for LD
*pFrame++ = 1; // var type
*pFrame++ = 1; // var len
*pFrame++ = 0xFF; // User-initiated disconnect
*pFrame++ = 2; // var type
*pFrame++ = 1; // var len
*pFrame++ = UserCode;
//
// now finally, put the stop flags
//
*pFrame++ = pArapConn->MnpState.DleByte; *pFrame++ = pArapConn->MnpState.EtxByte;
FrameLen = (USHORT)(pFrame - pFrameStart);
AtalkSetSizeOfBuffDescData(&pMnpSendBuf->sb_BuffDesc, FrameLen);
NdisAdjustBufferLength(pMnpSendBuf->sb_BuffHdr.bh_NdisBuffer,FrameLen);
ARAP_SET_NDIS_CONTEXT(pMnpSendBuf, NULL);
ndisPacket = pMnpSendBuf->sb_BuffHdr.bh_NdisPkt;
// send the packet over (and don't bother checking return code!)
NdisSend(&ndisStatus, RasPortDesc->pd_NdisBindingHandle, ndisPacket);
// if there was a problem sending, call the completion routine here
if (ndisStatus != NDIS_STATUS_PENDING) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapSendLDPacket: NdisSend failed %lx\n",ndisStatus));
ArapNdisSendComplete(ARAPERR_SEND_FAILED, (PBUFFER_DESC)pMnpSendBuf, NULL); }
// remove the disconnect refcount
DerefArapConn(pArapConn);
return(ARAPERR_NO_ERROR); }
//***
//
// Function: ArapRetryTimer
// This is the general purpose timer routine for ARAP.
// It checks
// if the ack timer (LATimer) has expired (if yes, send ack)
// if the flowcontrol timer has expired (if yes, send ack)
// if the inactivity timer has expired (if yes, send ack)
// if the retransmit timer has expired (if yes, retransmit)
//
// Parameters: pTimer - the context for the timer that just fired
// TimerShuttingDown - this is TRUE if timer is shutting down
//
// Return: none
//
//***$
LONG FASTCALL ArapRetryTimer( IN PTIMERLIST pTimer, IN BOOLEAN TimerShuttingDown ) {
PARAPCONN pArapConn; PLIST_ENTRY pList; PMNPSENDBUF pMnpSendBuf; BOOLEAN fRetransmit=FALSE; BOOLEAN fMustSendAck = FALSE; BOOLEAN fKill=FALSE; BOOLEAN fMustFlowControl=FALSE; BOOLEAN fInactiveClient=FALSE; LONG CurrentTime; PIRP pIrp=NULL; NTSTATUS ReturnStatus=STATUS_SUCCESS;
DBG_ARAP_CHECK_PAGED_CODE();
pArapConn = CONTAINING_RECORD(pTimer, ARAPCONN, RetryTimer);
ARAPTRACE(("Entered ArapRetryTimer (%lx)\n",pArapConn));
ACQUIRE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
//
// if the global timer is shutting down, or if the connection isn't in the
// right state (e.g. it is disconnecting), then don't requeue the timer
//
if ( TimerShuttingDown || (pArapConn->State <= MNP_IDLE) || (pArapConn->State > MNP_UP) ) { pArapConn->Flags &= ~RETRANSMIT_TIMER_ON;
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
if (TimerShuttingDown) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRetryTimer: timer shut down, killing conn (%lx)\n",pArapConn));
ArapCleanup(pArapConn); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRetryTimer: (%lx) invalid state (%d), not requeing timer\n", pArapConn,pArapConn->State)); }
// remove the timer refcount
DerefArapConn(pArapConn);
return ATALK_TIMER_NO_REQUEUE; }
CurrentTime = AtalkGetCurrentTick();
//
// has the 402 timer expired? if yes, we must send an ack
// (a value of 0 signifies that the 402 timer is not "running")
//
if ( (pArapConn->LATimer != 0) && (CurrentTime >= pArapConn->LATimer) ) { //
// make sure there is a receive that needs to be acked (if sent the ack
// just before this timer fired, don't send the ack again)
//
if (pArapConn->MnpState.UnAckedRecvs) { fMustSendAck = TRUE;
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRetryTimer: 402 timer fired, forcing ack out (%d, now %d)\n", pArapConn->LATimer,CurrentTime)); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRetryTimer: saved on on ack (UnAckedRecvs = 0)\n",pArapConn)); } }
//
// has the flow control timer "expired"? If so, we must send an ack and
// reset the timer
// (a value of 0 signifies that the flow control timer is not "running")
//
else if ( (pArapConn->FlowControlTimer != 0) && (CurrentTime >= pArapConn->FlowControlTimer) ) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapRetryTimer: flow-control timer, forcing ack (%d, now %d)\n", pArapConn->FlowControlTimer,CurrentTime));
fMustFlowControl = TRUE;
pArapConn->FlowControlTimer = CurrentTime + pArapConn->T404Duration; }
//
// if the client been inactive for a long time, we must tell dll
//
else if (CurrentTime >= pArapConn->InactivityTimer) { // first make sure we can get the select irp
ArapGetSelectIrp(&pIrp);
// if we managed to get a select irp, reset the timer so we don't keep
// informing the dll after every tick after this point!
//
if (pIrp) { pArapConn->InactivityTimer = pArapConn->T403Duration + CurrentTime;
fInactiveClient = TRUE; } }
//
// Has the retransmit timer expired? If so, we need to retransmit
//
else { //
// look at the first entry of the retransmit queue. If it's time is up, do
// the retransmit thing. Otherwise, we are done. (All others in the queue
// will be retransmitted after this first one is acked, so ignore for now)
//
pList = pArapConn->RetransmitQ.Flink;
// no entries on the retransmit queue? we're done!
if (pList == &pArapConn->RetransmitQ) { RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock); return ATALK_TIMER_REQUEUE; }
pMnpSendBuf = CONTAINING_RECORD(pList, MNPSENDBUF, Linkage);
// is it time to retransmit yet?
if (CurrentTime >= pMnpSendBuf->RetryTime) { if (pMnpSendBuf->RetryCount >= ARAP_MAX_RETRANSMITS) { fKill = TRUE; RemoveEntryList(&pMnpSendBuf->Linkage);
ASSERT(pArapConn->MnpState.UnAckedSends >= 1);
// not really important, since we're about to disconnect!
pArapConn->MnpState.UnAckedSends--;
ASSERT(pArapConn->SendsPending >= pMnpSendBuf->DataSize);
InitializeListHead(&pMnpSendBuf->Linkage); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapRetryTimer: timer fired, retransmitting....%x (%ld now %ld)\n", pMnpSendBuf->SeqNum,pMnpSendBuf->RetryTime,CurrentTime));
if (pMnpSendBuf->RetryCount > 8) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRetryTimer: buf %lx of %lx retransmitted %d times!\n", pMnpSendBuf,pArapConn,pMnpSendBuf->RetryCount)); }
pArapConn->MnpState.RetransmitMode = TRUE; pArapConn->MnpState.MustRetransmit = TRUE;
fRetransmit = TRUE; } } }
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
// force an ack out (that's what TRUE does)
if (fMustSendAck || fMustFlowControl) { MnpSendAckIfReqd(pArapConn, TRUE); }
// if we must retransmit, go for it.
//
else if (fRetransmit) { ArapNdisSend(pArapConn, &pArapConn->RetransmitQ); }
//
// if we retransmitted too many times, let the completion routine know.
// (it will probably kill the connection)
//
else if (fKill) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRetryTimer: too many retransmits (%lx), disconnecting %lx\n", pMnpSendBuf,pArapConn));
(pMnpSendBuf->ComplRoutine)(pMnpSendBuf, ARAPERR_SEND_FAILED); }
//
// if the connection has been inactive for longer than the limit (given to
// us by the dll), then tell dll about it
//
else if (fInactiveClient) { PARAP_SEND_RECV_INFO pSndRcvInfo;
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRetryTimer: (%lx) inactive, telling dll (%lx)\n",pArapConn, pIrp));
ASSERT(pIrp != NULL);
pSndRcvInfo = (PARAP_SEND_RECV_INFO)pIrp->AssociatedIrp.SystemBuffer;
pSndRcvInfo->pDllContext = pArapConn->pDllContext; pSndRcvInfo->AtalkContext = pArapConn; pSndRcvInfo->StatusCode = ARAPERR_CONN_INACTIVE;
ARAP_COMPLETE_IRP(pIrp, sizeof(ARAP_SEND_RECV_INFO), STATUS_SUCCESS, &ReturnStatus); return ReturnStatus; }
return ATALK_TIMER_REQUEUE; }
|