windows-nt/Source/XPSP1/NT/net/sfm/atalk/sys/arap.c

2905 lines
80 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}