|
|
/*++
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 ARAPNDIS
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE_ARAP, ArapRcvIndication)
#pragma alloc_text(PAGE_ARAP, ArapNdisSend)
#pragma alloc_text(PAGE_ARAP, ArapNdisSendComplete)
#pragma alloc_text(PAGE_ARAP, ArapGetNdisPacket)
#endif
//***
//
// Function: ArapRcvIndication
// This routine is called whenever Ndis calls the stack to indicate
// data on a port. We find out our context (pArapConn) from the
// the 'fake' ethernet header that NdisWan cooks up.
//
// Parameters: pArapConn - connection element for whom data has come in
// LkBuf - buffer containing the (most likely, compressed) data
// LkBufSize - size of the lookahead buffer
//
// Return: none
//
// NOTE: NdisWan always gives the entire buffer as the lookahead buffer,
// and we rely on that fact!
//***$
VOID ArapRcvIndication( IN PARAPCONN pArapConn, IN PVOID LkBuf, IN UINT LkBufSize ) {
BYTE MnpFrameType; BYTE SeqNum = (BYTE)-1; BYTE LastAckRcvd; BOOLEAN fCopyPacket; BOOLEAN fValidPkt; BOOLEAN fMustAck; PLIST_ENTRY pSendList; PLIST_ENTRY pRecvList; PMNPSENDBUF pMnpSendBuf; PARAPBUF pArapBuf; PARAPBUF pFirstArapBuf=NULL; BOOLEAN fLessOrEqual; BOOLEAN fAcceptPkt; BOOLEAN fGreater; DWORD DecompressedDataLen; DWORD dwDataOffset=0; DWORD dwFrameOverhead=0; DWORD dwMaxAcceptableLen; DWORD StatusCode; BOOLEAN fSendLNAck=FALSE; BYTE ClientCredit; BYTE AttnType; BYTE LNSeqToAck; DWORD BufSizeEstimate; DWORD DecompSize; DWORD BytesDecompressed; DWORD BytesToDecompress; DWORD BytesRemaining; PBYTE CompressedDataBuf; BYTE BitMask; BYTE RelSeq;
DBG_ARAP_CHECK_PAGED_CODE();
DBGDUMPBYTES("ArapRcvInd pkt rcvd: ",LkBuf,LkBufSize,3);
//
// we're at indicate time, so dpc
//
ACQUIRE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
//
// if the connection is going away, drop the packet
//
if ( (pArapConn->State == MNP_IDLE) || (pArapConn->State > MNP_UP) ) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcvIndication: invalid state = %d, returning (%lx %lx)\n", pArapConn,pArapConn->State));
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock); return; }
// one more frame came in
pArapConn->StatInfo.FramesRcvd++;
//
// if a dup LR comes in, SynByte, DleByte etc. aren't set yet, and we hit
// this assert. just check to see if we are in MNP_RESPONSE state
//
ASSERT( ((((PUCHAR)LkBuf)[0] == pArapConn->MnpState.SynByte) && (((PUCHAR)LkBuf)[1] == pArapConn->MnpState.DleByte) && (((PUCHAR)LkBuf)[2] == pArapConn->MnpState.StxByte) && (((PUCHAR)LkBuf)[LkBufSize-4] == pArapConn->MnpState.DleByte) && (((PUCHAR)LkBuf)[LkBufSize-3] == pArapConn->MnpState.EtxByte)) || (pArapConn->State == MNP_RESPONSE) );
ARAP_DBG_TRACE(pArapConn,30105,LkBuf,LkBufSize,0,0);
// we just heard from the client: "reset" the inactivity timer
pArapConn->InactivityTimer = pArapConn->T403Duration + AtalkGetCurrentTick();
MnpFrameType = ((PUCHAR)LkBuf)[MNP_FRMTYPE_OFFSET];
if ( MnpFrameType == MNP_LT_V20CLIENT ) { MnpFrameType = (BYTE)MNP_LT; }
fCopyPacket = FALSE; fValidPkt = TRUE; fMustAck = FALSE;
dwDataOffset = 3; // at the least, we'll ignore the 3 start flag bytes
dwFrameOverhead = 7; // at the least, ignore 3 start, 2 stop, 2 crc bytes
switch(MnpFrameType) { //
// if this is a duplicate LT frame, don't waste time decompressing and
// copying (also, make sure we have room to accept this packet!)
//
case MNP_LT:
fValidPkt = FALSE;
if (LkBufSize < (UINT)LT_MIN_LENGTH(pArapConn)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcv: (%lx) LT pkt, but length invalid: dropping!\n",pArapConn)); ASSERT(0); break; }
SeqNum = LT_SEQ_NUM((PBYTE)LkBuf, pArapConn);
MNP_DBG_TRACE(pArapConn,SeqNum,(0x10|MNP_LT));
dwMaxAcceptableLen = (pArapConn->BlockId == BLKID_MNP_SMSENDBUF) ? MNP_MINPKT_SIZE : MNP_MAXPKT_SIZE;
if ((pArapConn->State == MNP_UP) && (pArapConn->MnpState.RecvCredit > 0) ) { LT_OK_TO_ACCEPT(SeqNum, pArapConn, fAcceptPkt);
if (fAcceptPkt) { fCopyPacket = TRUE;
fValidPkt = TRUE;
dwDataOffset = LT_SRP_OFFSET(pArapConn); dwFrameOverhead = LT_OVERHEAD(pArapConn);
// make sure the packet isn't too big (in other words,invalid!)
if (LkBufSize-dwFrameOverhead > dwMaxAcceptableLen) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapInd: (%lx) too big a pkt (%d vs %d), dropped\n", pArapConn,LkBufSize-dwFrameOverhead,dwMaxAcceptableLen));
fValidPkt = FALSE; }
pArapConn->MnpState.HoleInSeq = FALSE; } else { //
// packet is valid, just not in the right sequence (make note
// of that or else we'll send out an ack!)
//
fValidPkt = TRUE;
//
// did we get an out-of-sequence packet (e.g. we lost B5,
// so we're still expecting B5 but B6 came in)
//
LT_GREATER_THAN(SeqNum, pArapConn->MnpState.NextToReceive, fGreater); if (fGreater) { //
// we have already sent an ack out when we first got
// this hole: don't send ack again
//
if (pArapConn->MnpState.HoleInSeq) { fMustAck = FALSE; } else { pArapConn->MnpState.HoleInSeq = TRUE;
fMustAck = TRUE;
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapRcvInd: (%lx) got a hole, dropping seq=%x vs. %x\n", pArapConn, SeqNum, pArapConn->MnpState.NextToReceive)); }
break; }
//
// spec says that we must ignore the first duplicate of any
// seq. What happens most often when we receive a duplicate is
// that the Mac sends the whole window full of dups. e.g. if
// seq num B1 is retransmitted then Mac will also retransmit
// B2, .. B8. We should ignore all of them, but if we get B1
// (or anything upto B8) again, then we must send out an ack.
//
//
// is this the first time (since we successfully received a
// new frame) that we are getting a dup? If so, we must find
// out what's the smallest seq number we can get as a dup
//
if (!pArapConn->MnpState.ReceivingDup) { //
// e.g. if we're expecting seq 79 then 0x71 is the
// smallest dup that we can get (for window size = 8)
//
if (pArapConn->MnpState.NextToReceive >= pArapConn->MnpState.WindowSize) { pArapConn->MnpState.FirstDupSeq = (pArapConn->MnpState.NextToReceive - pArapConn->MnpState.WindowSize); }
//
// e.g. if we're expecting seq 3 then 0xfb is the
// smallest dup that we can get (for window size = 8)
//
else { pArapConn->MnpState.FirstDupSeq = (0xff - (pArapConn->MnpState.WindowSize - pArapConn->MnpState.NextToReceive) + 1); }
pArapConn->MnpState.ReceivingDup = TRUE; pArapConn->MnpState.DupSeqBitMap = 0; RelSeq = 0; }
//
// find the relative seq number (relative to the first dup)
//
if (SeqNum >= pArapConn->MnpState.FirstDupSeq) { RelSeq = (SeqNum - pArapConn->MnpState.FirstDupSeq); } else { RelSeq = (0xff - pArapConn->MnpState.FirstDupSeq) + SeqNum; }
//
// 8-frame window: relseq can be 0 through 7
//
if (RelSeq >= 8) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcvInd: (%lx) RelSeq > 8!! (%x %x %x)\n", pArapConn, SeqNum, pArapConn->MnpState.FirstDupSeq, pArapConn->MnpState.DupSeqBitMap));
fMustAck = TRUE; break; }
BitMask = (1 << RelSeq);
//
// is this a second (or more) retransmission of this seq num?
//
if (pArapConn->MnpState.DupSeqBitMap & BitMask) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapRcvInd: (%lx) dup pkt, seq=%x vs. %x (%x)\n", pArapConn, SeqNum, pArapConn->MnpState.FirstDupSeq, pArapConn->MnpState.DupSeqBitMap));
fMustAck = TRUE; }
//
// no, this is the first time: don't send out an ack
//
else { pArapConn->MnpState.DupSeqBitMap |= BitMask;
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapRcvInd: (%lx) first dup pkt, seq=%x vs. %x (%x)\n", pArapConn, SeqNum, pArapConn->MnpState.FirstDupSeq, pArapConn->MnpState.DupSeqBitMap)); } } } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapInd: (%lx) pkt dropped (state %ld, credit %ld)\n", pArapConn,pArapConn->State,pArapConn->MnpState.RecvCredit)); }
break;
//
// we got an ACK: process it
//
case MNP_LA:
if (LkBufSize < (UINT)LA_MIN_LENGTH(pArapConn)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcv: (%lx) LA pkt, but length invalid: dropping!\n",pArapConn)); ASSERT(0); break; }
// client's receive credit (it's our send credit)
ClientCredit = LA_CREDIT((PBYTE)LkBuf, pArapConn);
ASSERT((pArapConn->State == MNP_UP) || (pArapConn->State == MNP_RESPONSE));
// last pkt the client recvd successfully from us
LastAckRcvd = LA_SEQ_NUM((PBYTE)LkBuf, pArapConn);
MNP_DBG_TRACE(pArapConn,LastAckRcvd,(0x10|MNP_LA));
//
// in the normal case, the ack we got should be for a bigger seq num
// than the one we got earlier.
// (special case the MNP_RESPONSE state to complete conn setup)
//
LT_GREATER_THAN(LastAckRcvd,pArapConn->MnpState.LastAckRcvd,fGreater);
if (fGreater || (pArapConn->State == MNP_RESPONSE)) { pArapConn->MnpState.LastAckRcvd = LastAckRcvd;
//
// remove all the sends upto and including LastAckRcvd and put
// them on SendAckedQ so that RcvCompletion can finish up the job
//
ASSERT(!IsListEmpty(&pArapConn->RetransmitQ));
ASSERT(pArapConn->SendsPending > 0);
//
// if we sent a response to LR and were waiting for client's
// ack, this is it! (RcvCompletion will do the remaining work)
//
if (pArapConn->State == MNP_RESPONSE) { pArapConn->State = MNP_UP; pArapConn->MnpState.NextToReceive = 1; pArapConn->MnpState.NextToProcess = 1; pArapConn->MnpState.NextToSend = 1;
pArapConn->FlowControlTimer = AtalkGetCurrentTick() + pArapConn->T404Duration; }
//
// remove all the sends that are now acked with this ack from the
// retransmit queue
//
while (1) { pSendList = pArapConn->RetransmitQ.Flink;
// no more sends left on the retransmit queue? if so, done
if (pSendList == &pArapConn->RetransmitQ) { break; }
pMnpSendBuf = CONTAINING_RECORD(pSendList,MNPSENDBUF,Linkage);
LT_LESS_OR_EQUAL(pMnpSendBuf->SeqNum,LastAckRcvd,fLessOrEqual);
if (fLessOrEqual) { ASSERT(pArapConn->SendsPending >= pMnpSendBuf->DataSize);
RemoveEntryList(&pMnpSendBuf->Linkage);
InsertTailList(&pArapConn->SendAckedQ, &pMnpSendBuf->Linkage);
ASSERT(pArapConn->MnpState.UnAckedSends >= 1);
pArapConn->MnpState.UnAckedSends--; } else { // all other sends have higher seq nums: done here
break; } }
//
// if we were in the retransmit mode and the retransmit Q is
// now empty, get out of retransmit mode!
//
if (pArapConn->MnpState.RetransmitMode) { if (pArapConn->MnpState.UnAckedSends == 0) { pArapConn->MnpState.RetransmitMode = FALSE; pArapConn->MnpState.MustRetransmit = FALSE;
// in case we had gone on a "exponential backoff", reset it
pArapConn->SendRetryTime = pArapConn->SendRetryBaseTime;
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapRcvInd: ack %x for xmitted pkt, out of re-xmit mode\n", LastAckRcvd)); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapRcvInd: ack %x for xmitted pkt, still %d more\n", LastAckRcvd,pArapConn->MnpState.UnAckedSends));
pArapConn->MnpState.MustRetransmit = TRUE; } } }
//
// the ack we got is for the same seq num as we got earlier: we need
// to retransmit the send we were hoping this ack was for!
//
else { if (!IsListEmpty(&pArapConn->RetransmitQ)) { pArapConn->MnpState.RetransmitMode = TRUE; pArapConn->MnpState.MustRetransmit = TRUE;
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("\nArapRcvInd: ack %x again, (%d pkts) entering re-xmit mode\n", LastAckRcvd,pArapConn->MnpState.UnAckedSends)); } }
ASSERT(pArapConn->MnpState.UnAckedSends <= pArapConn->MnpState.WindowSize);
//
// spec says our credit is what the client tells us minus the number
// of unacked sends on our Q.
//
//
if (ClientCredit > pArapConn->MnpState.UnAckedSends) { ASSERT((ClientCredit - pArapConn->MnpState.UnAckedSends) <= pArapConn->MnpState.WindowSize);
pArapConn->MnpState.SendCredit = (ClientCredit - pArapConn->MnpState.UnAckedSends); }
//
// But if the client tells us say 3 and we have 4 sends pending,
// be conservative and close the window until sends get cleared up
//
else { pArapConn->MnpState.SendCredit = 0; }
break;
//
// if we sent an LR response, this must be a retry by client: retransmit
// our response. If we sent in the request (in case of callback) then
// this is the response: send the ack
//
case MNP_LR:
MNP_DBG_TRACE(pArapConn,0,(0x10|MNP_LR));
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcvInd: got LR pkt on %lx, state=%d\n", pArapConn,pArapConn->State));
if (pArapConn->State == MNP_RESPONSE) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcvInd: excuse me? got an LR! (setting reX)\n"));
pArapConn->MnpState.RetransmitMode = TRUE; pArapConn->MnpState.MustRetransmit = TRUE; } else if (pArapConn->State == MNP_REQUEST) { //
// we got an LR response to our LR request (we are doing callback)
// Make sure all the parms that the dial-in client gives are ok
// with us, and configure pArapConn appropriately
//
StatusCode = PrepareConnectionResponse( pArapConn, LkBuf, LkBufSize, NULL, NULL); if (StatusCode == ARAPERR_NO_ERROR) { pArapConn->State = MNP_UP; pArapConn->MnpState.NextToReceive = 1; pArapConn->MnpState.NextToProcess = 1; pArapConn->MnpState.NextToSend = 1;
pArapConn->FlowControlTimer = AtalkGetCurrentTick() + pArapConn->T404Duration;
pSendList = pArapConn->RetransmitQ.Flink;
// treat the connection request as a send here
if (pSendList != &pArapConn->RetransmitQ) { pMnpSendBuf = CONTAINING_RECORD(pSendList, MNPSENDBUF, Linkage);
RemoveEntryList(&pMnpSendBuf->Linkage);
InsertTailList(&pArapConn->SendAckedQ, &pMnpSendBuf->Linkage);
ASSERT(pArapConn->MnpState.UnAckedSends >= 1);
pArapConn->MnpState.UnAckedSends--; } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcvInd: (%lx) can't find LR request\n",pArapConn)); ASSERT(0); }
fMustAck = TRUE; } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcvInd: (%lx) invalid LR response %ld\n", pArapConn,StatusCode)); } } else { fValidPkt = FALSE; }
break;
//
// remote sent a disconnect request. Though we'll process it at
// RcvCompletion time, mark it so that we don't attempt send/recv anymore
//
case MNP_LD:
MNP_DBG_TRACE(pArapConn,0,(0x10|MNP_LD));
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcvIndication: recvd disconnect from remote on (%lx)\n",pArapConn));
pArapConn->State = MNP_RDISC_RCVD; fCopyPacket = TRUE;
break;
//
// remote sent a Link Attention request. See what we need to do
//
case MNP_LN:
if (LkBufSize < (dwDataOffset+LN_MIN_LENGTH)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcv: (%lx) LN pkt, but length invalid: dropping!\n",pArapConn)); ASSERT(0); break; }
MNP_DBG_TRACE(pArapConn,0,(0x10|MNP_LN));
AttnType = LN_ATTN_TYPE((PBYTE)LkBuf+dwDataOffset);
LNSeqToAck = LN_ATTN_SEQ((PBYTE)LkBuf+dwDataOffset);
//
// is this a destructive type LN frame? Treat this as a LD frame so
// that we disconnect and cleanup the connection
//
if (AttnType == LN_DESTRUCTIVE) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcv: (%lx) got an LN pkt, sending LNAck!\n",pArapConn));
pArapConn->State = MNP_RDISC_RCVD; }
//
// ok, he just wants to know if we are doing ok: tell him so
//
else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcv: (%lx) got an LN pkt, sending LNAck!\n",pArapConn));
fSendLNAck = TRUE; }
break;
//
// we only ack an LN packet, but never generate an LN packet
// so we should never get this LNA packet. Quietly drop it.
//
case MNP_LNA:
MNP_DBG_TRACE(pArapConn,0,(0x10|MNP_LNA));
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcv: (%lx) got LNA. Now, when did we send LN??\n",pArapConn));
break;
default:
MNP_DBG_TRACE(pArapConn,0,MnpFrameType);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcvIndication: (%lx) dropping packet with unknown type %d\n", pArapConn,MnpFrameType));
break; }
//
// if it's a packet that we don't need to copy (e.g. ack) then we're done here.
// Also, if it's an invalid pkt (e.g. out of seq packet) then we must send an ack
//
if ((!fCopyPacket) || (!fValidPkt)) { RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock)
//
// if we got an invalid packet or if we have a condition where we must ack,
// do the needful
//
if (!fValidPkt || fMustAck) { MnpSendAckIfReqd(pArapConn, TRUE); } else if (fSendLNAck) { MnpSendLNAck(pArapConn, LNSeqToAck); }
return; }
//
// if it's not an LT packet, treat it separately
//
if (MnpFrameType != MNP_LT) { // right now LD is the only packet we put on the Misc Q
ASSERT(MnpFrameType == MNP_LD);
ARAP_GET_RIGHTSIZE_RCVBUF((LkBufSize-dwFrameOverhead), &pArapBuf); if (pArapBuf == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapInd: (%lx) alloc failed, dropping packet (type=%x, seq=%x)\n", pArapConn,MnpFrameType,SeqNum));
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock) return; }
TdiCopyLookaheadData( &pArapBuf->Buffer[0], (PUCHAR)LkBuf+dwDataOffset, LkBufSize-dwFrameOverhead, TDI_RECEIVE_COPY_LOOKAHEAD);
pArapBuf->MnpFrameType = MnpFrameType; pArapBuf->DataSize = (USHORT)(LkBufSize-dwFrameOverhead);
InsertTailList(&pArapConn->MiscPktsQ, &pArapBuf->Linkage);
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock) return; }
//
// ok, we're dealing with the LT packet (the most common packet)
//
// reset the flow control timer
pArapConn->FlowControlTimer = AtalkGetCurrentTick() + pArapConn->T404Duration;
// update the receive state...
ASSERT(pArapConn->MnpState.UnAckedRecvs <= pArapConn->MnpState.WindowSize);
pArapConn->MnpState.UnAckedRecvs++;
// set LastSeqRcvd to what we received successfully just now
pArapConn->MnpState.LastSeqRcvd = pArapConn->MnpState.NextToReceive;
// successfully rcvd the expected packet. Update to next expected
ADD_ONE(pArapConn->MnpState.NextToReceive);
//
// if the 402 timer isn't already "running", "start" it
// Also, shut the flow-control timer: starting T402 timer here will ensure
// that ack goes out, and at that time we'll restart the flow-control timer
//
if (pArapConn->LATimer == 0) { pArapConn->LATimer = pArapConn->T402Duration + AtalkGetCurrentTick(); pArapConn->FlowControlTimer = 0; }
//
// 0-length data is not permissible
// (for some reason, Mac sends a 0-datalength frame: for now, we'll
// "accept" the frame, though we can't do anything with it!)
//
if ((LkBufSize-dwFrameOverhead) == 0) { ARAP_DBG_TRACE(pArapConn,30106,LkBuf,LkBufSize,0,0);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapInd: (%lx) is the client on drugs? it's sending 0-len data!\n",pArapConn));
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock)
return; }
pFirstArapBuf = NULL;
BytesToDecompress = (DWORD)LkBufSize-dwFrameOverhead; CompressedDataBuf = (PUCHAR)LkBuf+dwDataOffset;
DecompressedDataLen = 0;
//
// for now, assume decompressed data will be 4 times the compressed size
// (if that assumption isn't true, we'll alloc more again)
//
BufSizeEstimate = (BytesToDecompress << 2);
if (!(pArapConn->Flags & MNP_V42BIS_NEGOTIATED)) { BufSizeEstimate = BytesToDecompress; }
while (1) { // get a receive buffer for this size
ARAP_GET_RIGHTSIZE_RCVBUF(BufSizeEstimate, &pArapBuf);
if (pArapBuf == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapInd: (%lx) %d bytes alloc failed, dropping seq=%x\n", pArapConn,BufSizeEstimate,SeqNum));
//
// if we put any stuff on the queue for this MNP packet, remove
// them all: we can't have a partially decompressed packet!
//
if (pFirstArapBuf) { pRecvList = &pFirstArapBuf->Linkage;
while (pRecvList != &pArapConn->ReceiveQ) { RemoveEntryList(pRecvList);
pArapBuf = CONTAINING_RECORD(pRecvList,ARAPBUF,Linkage);
ARAP_FREE_RCVBUF(pArapBuf);
pRecvList = pRecvList->Flink; } }
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock)
// force ack so the client gets a hint that we dropped a pkt
MnpSendAckIfReqd(pArapConn, TRUE);
return; }
if (!pFirstArapBuf) { pFirstArapBuf = pArapBuf; }
//
// ok, do that v42bis decompression thing if v42bis is negotiated
//
if (pArapConn->Flags & MNP_V42BIS_NEGOTIATED) { StatusCode = v42bisDecompress( pArapConn, CompressedDataBuf, BytesToDecompress, pArapBuf->CurrentBuffer, pArapBuf->BufferSize, &BytesRemaining, &DecompSize); }
//
// v42bis is not negotiated: skip decompression
//
else { if (BytesToDecompress) { TdiCopyLookaheadData( &pArapBuf->Buffer[0], (PUCHAR)LkBuf+dwDataOffset, BytesToDecompress, TDI_RECEIVE_COPY_LOOKAHEAD); }
DecompSize = BytesToDecompress; StatusCode = ARAPERR_NO_ERROR; }
ASSERT((StatusCode == ARAPERR_NO_ERROR) || (StatusCode == ARAPERR_BUF_TOO_SMALL));
if ((StatusCode != ARAPERR_NO_ERROR) && (StatusCode != ARAPERR_BUF_TOO_SMALL)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapInd: (%lx) v42bisDecompress returned %lx, dropping pkt\n", pArapConn,StatusCode));
ASSERT(0);
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock)
// force ack so the client gets a hint that we dropped a pkt
MnpSendAckIfReqd(pArapConn, TRUE);
return; }
//
// if we got any bytes decompressed, put them on the queue
//
if (DecompSize > 0) { ASSERT(pArapBuf->BufferSize >= DecompSize);
pArapBuf->DataSize = (USHORT)DecompSize;
// Debug only: make sure q looks ok before we put this stuff on the q
ARAP_CHECK_RCVQ_INTEGRITY(pArapConn);
// queue these bytes on to the ReceiveQ
InsertTailList(&pArapConn->ReceiveQ, &pArapBuf->Linkage);
// Debug only: make sure q looks ok after we put this stuff on the q
ARAP_CHECK_RCVQ_INTEGRITY(pArapConn); }
DecompressedDataLen += DecompSize;
// are we done decompressing?
if (StatusCode == ARAPERR_NO_ERROR) { //
// if there was no output data and there was no error, we didn't
// really need this buffer.
//
if (DecompSize == 0) { ARAP_FREE_RCVBUF(pArapBuf); }
break; }
//
// ok, we're here because our assumption about how big a buffer we
// needed for decompression wasn't quite right: we must decompress the
// remaining bytes now
//
BytesDecompressed = (BytesToDecompress - BytesRemaining); BytesToDecompress = BytesRemaining; CompressedDataBuf += BytesDecompressed;
//
// we ran out of room:double our initial estimate
//
BufSizeEstimate <<= 1; }
ARAP_DBG_TRACE(pArapConn,30110,pFirstArapBuf,DecompressedDataLen,0,0);
// update statitics on incoming bytes:
pArapConn->StatInfo.BytesRcvd += (DWORD)LkBufSize; pArapConn->StatInfo.BytesReceivedCompressed += ((DWORD)LkBufSize-dwFrameOverhead); pArapConn->StatInfo.BytesReceivedUncompressed += DecompressedDataLen;
#if DBG
ArapStatistics.RecvPostDecompMax = (DecompressedDataLen > ArapStatistics.RecvPostDecompMax)? DecompressedDataLen : ArapStatistics.RecvPostDecompMax;
ArapStatistics.RecvPostDecomMin = (DecompressedDataLen < ArapStatistics.RecvPostDecomMin)? DecompressedDataLen : ArapStatistics.RecvPostDecomMin; #endif
// we successfully received a brand new packet, so we aren't getting dup's
pArapConn->MnpState.ReceivingDup = FALSE;
// we have these many bytes more waiting to be processed
pArapConn->RecvsPending += DecompressedDataLen;
ARAP_ADJUST_RECVCREDIT(pArapConn);
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
// see if ack needs to be sent for the packet we just received
MnpSendAckIfReqd(pArapConn, FALSE);
}
//***
//
// Function: ArapRcvComplete
// This is the RcvComplete routine for the Arap port.
// We look through all the clients on this port (i.e. all the
// Arap clients) to see who needs work done, and finish it.
//
// Parameters: none
//
// Return: none
//
//***$
VOID ArapRcvComplete( IN VOID ) { PARAPCONN pArapConn; PARAPCONN pPrevArapConn; PLIST_ENTRY pConnList; PLIST_ENTRY pSendAckedList; PLIST_ENTRY pList; KIRQL OldIrql; BOOLEAN fRetransmitting; BOOLEAN fReceiveQEmpty; PMNPSENDBUF pMnpSendBuf=NULL; PMNPSENDBUF pRetransmitBuf=NULL; PARAPBUF pArapBuf=NULL; DWORD BytesProcessed=0; BOOLEAN fArapDataWaiting; BOOLEAN fArapConnUp=FALSE;
//
// walk through all the Arap clients to see if anyone has data to be
// processed.
// Start from the head of the list
// 1 if the connection state is not ok, try the next connection
// else up the refcount (to make sure it stays around until we're done)
// 2 see if we need to disconnect: if yes, do so and move on
// 3 see if retransmits are needed
// 4 see if ack needs to be sent
// 5 see if any sends need to be completed
// 6 see if any receives need to be completed
// 7 Find the next connection which we will move to next
// 8 remove the refcount on the previous connection that we put in step 1
//
pArapConn = NULL; pPrevArapConn = NULL;
while (1) { ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
//
// first, let's find the right connection to work on
//
while (1) { // if we're in the middle of the list, get to the next guy
if (pArapConn != NULL) { pConnList = pArapConn->Linkage.Flink; } // we're just starting: get the guy at the head of the list
else { pConnList = RasPortDesc->pd_ArapConnHead.Flink; }
// finished all?
if (pConnList == &RasPortDesc->pd_ArapConnHead) { RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
if (pPrevArapConn) { DerefArapConn(pPrevArapConn); } return; }
pArapConn = CONTAINING_RECORD(pConnList, ARAPCONN, Linkage);
// make sure this connection needs rcv processing
ACQUIRE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
//
// if this connection is being disconnected, skip it (unless we
// just received disconnect from remote, in which case we need to
// process that)
//
if ((pArapConn->State >= MNP_LDISCONNECTING) && (pArapConn->State != MNP_RDISC_RCVD)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapRcvComplete: (%lx) invalid state %d, no rcv processing done\n", pArapConn,pArapConn->State));
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
// go try the next connection
continue; }
// let's make sure this connection stays around till we finish
pArapConn->RefCount++;
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
break; }
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
//
// remove the refcount on the previous connection we put in for the Rcv
//
if (pPrevArapConn) { DerefArapConn(pPrevArapConn); }
ASSERT(pPrevArapConn != pArapConn);
pPrevArapConn = pArapConn;
fRetransmitting = FALSE; fArapConnUp = FALSE;
// if our sniff buffer has enough bytes, give them to dll and make room
ARAP_DUMP_DBG_TRACE(pArapConn);
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
//
// if we got a disconnect from remote (LD frame), we have cleanup to do
//
if (pArapConn->State == MNP_RDISC_RCVD) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcvComplete: (%lx) disconnect rcvd from remote, calling cleanup\n", pArapConn));
pArapConn->State = MNP_RDISCONNECTING;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
ArapCleanup(pArapConn);
// go process the next connection
continue; }
//
// do we need to retransmit the sends queued up on the retransmit Q?
//
if (pArapConn->MnpState.MustRetransmit) { pList = pArapConn->RetransmitQ.Flink;
if (pList != &pArapConn->RetransmitQ) { pRetransmitBuf = CONTAINING_RECORD(pList, MNPSENDBUF, Linkage);
fRetransmitting = TRUE;
if (pRetransmitBuf->RetryCount >= ARAP_MAX_RETRANSMITS) { RemoveEntryList(&pRetransmitBuf->Linkage);
ASSERT(pArapConn->MnpState.UnAckedSends >= 1);
// not really important, since we're about to disconnect!
pArapConn->MnpState.UnAckedSends--;
ASSERT(pArapConn->SendsPending >= pRetransmitBuf->DataSize);
#if DBG
InitializeListHead(&pRetransmitBuf->Linkage); #endif
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcvComplete: (%lx) too many retransmits (%lx). Killing %lx\n", pRetransmitBuf,pArapConn));
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
(pRetransmitBuf->ComplRoutine)(pRetransmitBuf,ARAPERR_SEND_FAILED);
continue; } } }
//
// See if any sends can be completed as a result of an ack coming in
// (now that we have the spinlock, move the list away and mark the list as
// empty before we release the lock. Idea is to avoid grab-release-grab..
// of spinlock as we complete all the sends).
//
pSendAckedList = pArapConn->SendAckedQ.Flink; InitializeListHead(&pArapConn->SendAckedQ);
// is ARAP connection up yet? we'll use this fact very soon...
if (pArapConn->Flags & ARAP_CONNECTION_UP) { fArapConnUp = TRUE; }
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
//
// next, handle any retransmissions if needed
//
if (fRetransmitting) { ArapNdisSend(pArapConn, &pArapConn->RetransmitQ); }
//
// next, complete all our sends for which we received ack(s)
//
while (pSendAckedList != &pArapConn->SendAckedQ) { pMnpSendBuf = CONTAINING_RECORD(pSendAckedList,MNPSENDBUF,Linkage);
pSendAckedList = pSendAckedList->Flink;
InitializeListHead(&pMnpSendBuf->Linkage);
//
// call the completion routine for this send buffer
//
(pMnpSendBuf->ComplRoutine)(pMnpSendBuf,ARAPERR_NO_ERROR); }
// see if ack needs to be sent, for any packets we received
MnpSendAckIfReqd(pArapConn, FALSE);
//
// and finally, process all the packets on the recieve queue!
//
BytesProcessed = 0; while (1) { if ((pArapBuf = ArapExtractAtalkSRP(pArapConn)) == NULL) { // no more data left (or no complete SRP yet): done here
break; }
// is ARAP connection up? route only if it's up, otherwise drop it!
if (fArapConnUp) { ArapRoutePacketFromWan( pArapConn, pArapBuf ); }
// we received AppleTalk data but connection wasn't/isn't up! Drop pkt
else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapRcvComplete: (%lx) AT data, but conn not up\n",pArapConn)); }
BytesProcessed += pArapBuf->DataSize;
// done with this buffer
ARAP_FREE_RCVBUF(pArapBuf); }
//
// ok, we freed up space: update the counters
//
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
ASSERT(pArapConn->RecvsPending >= BytesProcessed); pArapConn->RecvsPending -= BytesProcessed;
ARAP_ADJUST_RECVCREDIT(pArapConn);
#if DBG
if ((IsListEmpty(&pArapConn->RetransmitQ)) && (IsListEmpty(&pArapConn->HighPriSendQ)) && (IsListEmpty(&pArapConn->MedPriSendQ)) && (IsListEmpty(&pArapConn->LowPriSendQ)) && (IsListEmpty(&pArapConn->SendAckedQ)) ) { ASSERT(pArapConn->SendsPending == 0); } #endif
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
ArapDataToDll(pArapConn);
//
// see if any more packets can/should be sent
//
ArapNdisSend(pArapConn, &pArapConn->HighPriSendQ); } }
//***
//
// Function: ArapNdisSend
// This routine is called when we need to send data out to the
// client, whether it's a fresh send or a retransmit.
//
// Parameters: pArapConn - connection element for whom data has come in
// pSendHead - from which queue (new send or retransmit) to send
//
// Return: none
//
//***$
VOID ArapNdisSend( IN PARAPCONN pArapConn, IN PLIST_ENTRY pSendHead ) {
KIRQL OldIrql; PMNPSENDBUF pMnpSendBuf=NULL; PNDIS_PACKET ndisPacket; NDIS_STATUS ndisStatus; PLIST_ENTRY pSendList; BOOLEAN fGreaterThan; BYTE SendCredit; BYTE PrevSeqNum; BOOLEAN fFirstSend=TRUE; BOOLEAN fRetransmitQ; DWORD StatusCode;
DBG_ARAP_CHECK_PAGED_CODE();
//
// before we begin, let's see if any of the lower priority queue sends
// can be moved ("refilled") on to the high priority queue (the real queue)
//
ArapRefillSendQ(pArapConn);
fRetransmitQ = (pSendHead == &pArapConn->RetransmitQ);
//
// while we have sends queued up and send-credits available,
// keep sending
//
while (1) { ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
pSendList = pSendHead->Flink;
if (pArapConn->MnpState.RetransmitMode) { //
// if we are asked to retransmit, we only retransmit the first
// packet (until it is acked)
//
if (!fFirstSend) { goto ArapNdisSend_Exit; }
//
// if we are in the retransmit mode, we can't accept any fresh sends
//
if (!fRetransmitQ) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapNdisSend: in retransmit mode, dropping fresh send\n"));
goto ArapNdisSend_Exit; }
// we will go down and retransmit (if we can): turn this off here
pArapConn->MnpState.MustRetransmit = FALSE; }
#if 0
//
// if this is a retransmit, find the next send that we must retransmit
//
if ((fRetransmitQ) && (!fFirstSend)) { while (pSendList != pSendHead) { pMnpSendBuf = CONTAINING_RECORD(pSendList,MNPSENDBUF,Linkage);
// find the seq number larger than the one we just retransmitted
LT_GREATER_THAN(pMnpSendBuf->SeqNum,PrevSeqNum,fGreaterThan);
if (fGreaterThan) { break; }
pSendList = pSendList->Flink; } } #endif
// no more to send? then we're done
if (pSendList == pSendHead) { goto ArapNdisSend_Exit; }
pMnpSendBuf = CONTAINING_RECORD(pSendList,MNPSENDBUF,Linkage);
ASSERT( (pMnpSendBuf->Signature == MNPSMSENDBUF_SIGNATURE) || (pMnpSendBuf->Signature == MNPLGSENDBUF_SIGNATURE) );
fFirstSend = FALSE; PrevSeqNum = pMnpSendBuf->SeqNum;
SendCredit = pArapConn->MnpState.SendCredit;
//
// if we are disconnecting, don't send
//
if (pArapConn->State >= MNP_LDISCONNECTING) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapNdisSend: disconnecting, or link-down: dropping send\n"));
ARAP_DBG_TRACE(pArapConn,30305,NULL,pArapConn->State,0,0);
goto ArapNdisSend_Exit; }
//
// if this is a fresh send (i.e. not a retransmit) then make sure we have
// send credits available
//
if ( (SendCredit == 0) && (!fRetransmitQ) ) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapNdisSend: send credit 0, dropping send\n"));
ARAP_DBG_TRACE(pArapConn,30310,NULL,0,0,0);
goto ArapNdisSend_Exit; }
//
// if this send is already in NDIS (rare case, but can happen) then return
//
if (pMnpSendBuf->Flags != 0) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapNdisSend: send %lx already in NDIS!!! (seq=%x, %d times)\n", pMnpSendBuf,pMnpSendBuf->SeqNum,pMnpSendBuf->RetryCount));
goto ArapNdisSend_Exit; }
// Mark that this send is in Ndis
pMnpSendBuf->Flags = 1;
//
// Move it to the RetransmitQ for that "reliable" thing to work
// and set the length so that ndis knows how much to send!
//
if (!fRetransmitQ) { ASSERT(pMnpSendBuf->DataSize <= MNP_MAXPKT_SIZE);
DBGTRACK_SEND_SIZE(pArapConn,pMnpSendBuf->DataSize);
//
// get ndis packet for this send, since this is the first time we
// are sending this send out
//
StatusCode = ArapGetNdisPacket(pMnpSendBuf);
if (StatusCode != ARAPERR_NO_ERROR) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapNdisSend: (%lx) couldn't alloc NdisPacket\n",pArapConn));
pMnpSendBuf->Flags = 0; goto ArapNdisSend_Exit; }
// one more frame going outcame in
pArapConn->StatInfo.FramesSent++;
RemoveEntryList(&pMnpSendBuf->Linkage);
InsertTailList(&pArapConn->RetransmitQ, &pMnpSendBuf->Linkage);
pArapConn->MnpState.UnAckedSends++;
ASSERT(pArapConn->MnpState.UnAckedSends <= pArapConn->MnpState.WindowSize);
ASSERT( (pArapConn->MnpState.SendCredit > 0) && (pArapConn->MnpState.SendCredit <= pArapConn->MnpState.WindowSize));
// we are going to use up one send credit now
pArapConn->MnpState.SendCredit--;
NdisAdjustBufferLength(pMnpSendBuf->sb_BuffHdr.bh_NdisBuffer, (pMnpSendBuf->DataSize + MNP_OVERHD(pArapConn)));
ASSERT( (pMnpSendBuf->Buffer[14] == pArapConn->MnpState.SynByte) && (pMnpSendBuf->Buffer[15] == pArapConn->MnpState.DleByte) && (pMnpSendBuf->Buffer[16] == pArapConn->MnpState.StxByte));
ASSERT((pMnpSendBuf->Buffer[20 + pMnpSendBuf->DataSize] == pArapConn->MnpState.DleByte) && (pMnpSendBuf->Buffer[20 + pMnpSendBuf->DataSize+1] == pArapConn->MnpState.EtxByte)); }
//
// this is a retransmit
//
else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO, ("ArapNdisSend: (%lx) retransmitting %x size=%d\n", pArapConn,pMnpSendBuf->SeqNum,pMnpSendBuf->DataSize));
//
// reset it: it's possible we had changed it for an earlier retransmit
//
if (pMnpSendBuf->RetryCount < ARAP_HALF_MAX_RETRANSMITS) { pArapConn->SendRetryTime = pArapConn->SendRetryBaseTime; }
//
// hmmm: we have retransmitted quite a few times. Time to increase
// our retry time so we do some exponential back off. Increase the
// retry time by 50%, with an upper bound of 5 seconds
//
else { pArapConn->SendRetryTime += (pArapConn->SendRetryTime>>1);
if (pArapConn->SendRetryTime > ARAP_MAX_RETRY_INTERVAL) { pArapConn->SendRetryTime = ARAP_MAX_RETRY_INTERVAL; } } }
// bump this to note our attempt to send this pkt
pMnpSendBuf->RetryCount++;
// put an ndis refcount (remove when ndis completes this send)
pMnpSendBuf->RefCount++;
// when should we retransmit this pkt?
pMnpSendBuf->RetryTime = pArapConn->SendRetryTime + AtalkGetCurrentTick();
// reset the flow-control timer: we're sending something over
pArapConn->FlowControlTimer = AtalkGetCurrentTick() + pArapConn->T404Duration;
ARAP_DBG_TRACE(pArapConn,30320,pMnpSendBuf,fRetransmitQ,0,0);
MNP_DBG_TRACE(pArapConn,pMnpSendBuf->SeqNum,MNP_LT);
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
ndisPacket = pMnpSendBuf->sb_BuffHdr.bh_NdisPkt;
DBGDUMPBYTES("ArapNdisSend sending pkt: ", &pMnpSendBuf->Buffer[0],(pMnpSendBuf->DataSize + MNP_OVERHD(pArapConn)),3);
// Now send the packet descriptor
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, ("ArapNdisSend: NdisSend failed (%lx %lx)\n", pArapConn,ndisStatus));
ArapNdisSendComplete(ARAPERR_SEND_FAILED, (PBUFFER_DESC)pMnpSendBuf, NULL);
// might as well stop here for now if we are having trouble sending!
goto ArapNdisSend_Exit_NoLock; } }
ArapNdisSend_Exit:
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
ArapNdisSend_Exit_NoLock:
;
// if our sniff buffer has enough bytes, give them to dll and make room
ARAP_DUMP_DBG_TRACE(pArapConn);
}
//***
//
// Function: ArapNdisSendComplete
// This routine is the completion routine called by Ndis to tell
// us that the send completed (i.e. just went out on the wire)
//
// Parameters: Status - did it go out on wire?
// pMnpSendBuf - the buffer that was sent out. We dereference the
// buffer here. When this send gets acked, that's
// when the other deref happens.
//
// Return: none
//
//***$
VOID ArapNdisSendComplete( IN NDIS_STATUS Status, IN PBUFFER_DESC pBufferDesc, IN PSEND_COMPL_INFO pSendInfo ) {
PARAPCONN pArapConn; PMNPSENDBUF pMnpSendBuf;
DBG_ARAP_CHECK_PAGED_CODE();
pMnpSendBuf = (PMNPSENDBUF)pBufferDesc; pArapConn = pMnpSendBuf->pArapConn;
ARAPTRACE(("Entered ArapNdisSendComplete (%lx %lx %lx)\n", Status,pMnpSendBuf,pArapConn));
if (Status != NDIS_STATUS_SUCCESS) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("ArapNdisSendComplete (%lx): send failed %lx\n",pArapConn,Status)); }
// ndis send completed: take away the ndis refcount
DerefMnpSendBuf(pMnpSendBuf, TRUE); }
//***
//
// Function: ArapGetNdisPacket
// This function gets an Ndis Packet for the ARAP send buffer
//
// Parameters: pMnpSendBuf - the send buffer for which we need Ndis Packet
//
// Return: error code
//
//***$
DWORD ArapGetNdisPacket( IN PMNPSENDBUF pMnpSendBuf ) {
PBUFFER_HDR pBufHdr; NDIS_STATUS ndisStatus;
DBG_ARAP_CHECK_PAGED_CODE();
pBufHdr = (PBUFFER_HDR)pMnpSendBuf;
pBufHdr->bh_NdisPkt = NULL;
// Allocate an NDIS packet descriptor from the global packet pool
NdisAllocatePacket(&ndisStatus, &pBufHdr->bh_NdisPkt, AtalkNdisPacketPoolHandle); if (ndisStatus != NDIS_STATUS_SUCCESS) { LOG_ERROR(EVENT_ATALK_NDISRESOURCES, ndisStatus, NULL, 0);
DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_ERR, ("ArapGetNdisPacket: Ndis Out-of-Resource condition hit\n"));
ASSERT(0);
return(ARAPERR_OUT_OF_RESOURCES); }
// Link the buffer descriptor into the packet descriptor
RtlZeroMemory(pBufHdr->bh_NdisPkt->ProtocolReserved, sizeof(PROTOCOL_RESD)); NdisChainBufferAtBack(pBufHdr->bh_NdisPkt, pBufHdr->bh_NdisBuffer); ((PPROTOCOL_RESD)(pBufHdr->bh_NdisPkt->ProtocolReserved))->Receive.pr_BufHdr = pBufHdr;
ARAP_SET_NDIS_CONTEXT(pMnpSendBuf, NULL);
return(ARAPERR_NO_ERROR); }
//***
//
// Function: RasStatusIndication
// This is the status indication routine for the Arap port.
// When line-up, line-down indications come from NdisWan, we
// execute this routine.
//
// Parameters: GeneralStatus - what is this indication for
// StatusBuf - the buffer containig the indication info
// StatusBufLen - length of this buffer
//
// Return: none
//
//***$
VOID RasStatusIndication( IN NDIS_STATUS GeneralStatus, IN PVOID StatusBuf, IN UINT StatusBufLen ) {
KIRQL OldIrql; PNDIS_WAN_LINE_UP pLineUp; PNDIS_WAN_LINE_DOWN pLineDown; PNDIS_WAN_FRAGMENT pFragment; ATALK_NODEADDR ClientNode; PARAPCONN pArapConn; PATCPCONN pAtcpConn; PARAP_BIND_INFO pArapBindInfo; PNDIS_WAN_GET_STATS pWanStats; DWORD dwFlags; BOOLEAN fKillConnection=FALSE;
switch (GeneralStatus) { case NDIS_STATUS_WAN_LINE_UP:
if (StatusBufLen < sizeof(NDIS_WAN_LINE_UP)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication:\
line-up buff too small (%ld)\n", StatusBufLen)); break; }
pLineUp = (PNDIS_WAN_LINE_UP)StatusBuf;
pArapBindInfo = (PARAP_BIND_INFO)pLineUp->ProtocolBuffer;
//
// is this a PPP connection?
//
if (pArapBindInfo->fThisIsPPP) { ClientNode.atn_Network = pArapBindInfo->ClientAddr.ata_Network; ClientNode.atn_Node = (BYTE)pArapBindInfo->ClientAddr.ata_Node;
pAtcpConn = FindAndRefPPPConnByAddr(ClientNode, &dwFlags);
ASSERT(pAtcpConn == pArapBindInfo->AtalkContext);
if (pAtcpConn) { ASSERT(pAtcpConn->Signature == ATCPCONN_SIGNATURE);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("PPP: Line-Up received on %lx: link-speed = %lx, net addr %x.%x\n", pAtcpConn,pLineUp->LinkSpeed,ClientNode.atn_Network,ClientNode.atn_Node));
if ((dwFlags & ATCP_LINE_UP_DONE) || (dwFlags & ATCP_CONNECTION_UP)) { // remove the refcount put in by FindAndRefPPPConnByAddr
DerefPPPConn(pAtcpConn);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("PPP: Line-Up received on connection already up")); break; }
ACQUIRE_SPIN_LOCK(&pAtcpConn->SpinLock, &OldIrql);
ASSERT(!((dwFlags & ATCP_LINE_UP_DONE) || (dwFlags & ATCP_CONNECTION_UP))); pAtcpConn->Flags |= ATCP_LINE_UP_DONE; pAtcpConn->Flags |= ATCP_CONNECTION_UP;
// put NDISWAN refcount
pAtcpConn->RefCount++;
//
// put our context for ndiswan
//
// mark that this is a PPP connection
pLineUp->LocalAddress[0] = PPP_ID_BYTE1; pLineUp->LocalAddress[1] = PPP_ID_BYTE2;
pLineUp->LocalAddress[2] = 0x0; pLineUp->LocalAddress[3] = ClientNode.atn_Node; *((USHORT UNALIGNED *)(&pLineUp->LocalAddress[4])) = ClientNode.atn_Network;
//
// copy the header since this is what we'll use throughout the
// life of the connection
//
RtlCopyMemory( &pAtcpConn->NdiswanHeader[0], pLineUp->RemoteAddress, 6 );
RtlCopyMemory( &pAtcpConn->NdiswanHeader[6], pLineUp->LocalAddress, 6 );
// these two bytes don't really mean much, but might as well set'em
pAtcpConn->NdiswanHeader[12] = 0x80; pAtcpConn->NdiswanHeader[13] = 0xf3;
RELEASE_SPIN_LOCK(&pAtcpConn->SpinLock, OldIrql);
// remove the refcount put in by FindAndRefPPPConnByAddr
DerefPPPConn(pAtcpConn);
// tell dll we bound ok
pArapBindInfo->ErrorCode = ARAPERR_NO_ERROR; } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication: PPP line-up, but no conn for %ld.%ld\n", ClientNode.atn_Network,ClientNode.atn_Node)); ASSERT(0); pArapBindInfo->ErrorCode = ARAPERR_NO_SUCH_CONNECTION; } }
//
// nope, this is an ARAP connection!
//
else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("Arap: Line-Up received: link-speed = %lx, dll context = %lx\n", pLineUp->LinkSpeed,pArapBindInfo->pDllContext));
ASSERT(FindArapConnByContx(pArapBindInfo->pDllContext) == NULL);
//
// alloc a connection. If we fail, tell dll sorry
//
pArapConn = AllocArapConn(pLineUp->LinkSpeed); if (pArapConn == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication: AllocArapConn failed\n"));
pArapBindInfo->AtalkContext = NULL; pArapBindInfo->ErrorCode = ARAPERR_OUT_OF_RESOURCES; break; }
// do the legendary "binding" (exchange contexts!)
pArapConn->pDllContext = pArapBindInfo->pDllContext;
pArapBindInfo->AtalkContext = pArapConn;
//
// insert this connection in the list of all Arap connections
//
ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql);
InsertTailList(&RasPortDesc->pd_ArapConnHead, &pArapConn->Linkage);
RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql);
// mark that this is an ARAP connection
pLineUp->LocalAddress[0] = ARAP_ID_BYTE1; pLineUp->LocalAddress[1] = ARAP_ID_BYTE2;
// put our context for ndiswan
*((ULONG UNALIGNED *)(&pLineUp->LocalAddress[2])) = *((ULONG UNALIGNED *)(&pArapConn));
//
// copy the header since this is what we'll use throughout the
// life of the connection
//
RtlCopyMemory( &pArapConn->NdiswanHeader[0], pLineUp->RemoteAddress, 6 );
RtlCopyMemory( &pArapConn->NdiswanHeader[6], pLineUp->LocalAddress, 6 );
// these two bytes don't really mean much, but might as well set'em
pArapConn->NdiswanHeader[12] = 0x80; pArapConn->NdiswanHeader[13] = 0xf3;
// tell dll we bound ok
pArapBindInfo->ErrorCode = ARAPERR_NO_ERROR;
} // if (pArapBindInfo->fThisIsPPP)
break;
case NDIS_STATUS_WAN_LINE_DOWN:
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication: Line-Down received\n"));
if (StatusBufLen < sizeof(NDIS_WAN_LINE_DOWN)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication: line-down buff too small (%ld)\n", StatusBufLen)); break; }
pLineDown = (PNDIS_WAN_LINE_DOWN)StatusBuf;
//
// is this a PPP connection?
//
if ((pLineDown->LocalAddress[0] == PPP_ID_BYTE1) && (pLineDown->LocalAddress[1] == PPP_ID_BYTE2)) { ClientNode.atn_Node = pLineDown->LocalAddress[3]; ClientNode.atn_Network = *((USHORT UNALIGNED *)(&pLineDown->LocalAddress[4]));
pAtcpConn = FindAndRefPPPConnByAddr(ClientNode, &dwFlags); if (pAtcpConn) { ASSERT(pAtcpConn->Signature == ATCPCONN_SIGNATURE);
ACQUIRE_SPIN_LOCK(&pAtcpConn->SpinLock, &OldIrql);
ASSERT(dwFlags & ATCP_LINE_UP_DONE);
pAtcpConn->Flags &= ~(ATCP_CONNECTION_UP | ATCP_LINE_UP_DONE); RELEASE_SPIN_LOCK(&pAtcpConn->SpinLock, OldIrql);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("PPP line-down: killing %lx in line-down\n",pAtcpConn));
// line-down: take away the NDISWAN refcount
DerefPPPConn(pAtcpConn);
// remove the refcount put in by FindAndRefPPPConnByAddr
DerefPPPConn(pAtcpConn); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication: PPP line-down, but no conn for %ld.%ld\n", ClientNode.atn_Network,ClientNode.atn_Node)); } }
//
// no, this is an ARAP connection
//
else {
ASSERT((pLineDown->LocalAddress[0] == ARAP_ID_BYTE1) && (pLineDown->LocalAddress[1] == ARAP_ID_BYTE2));
*((ULONG UNALIGNED *)(&pArapConn)) = *((ULONG UNALIGNED *)(&pLineDown->LocalAddress[2]));
// this had better be a line-down for an existing connection!
if (ArapConnIsValid(pArapConn)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("Arap line-down: killing %lx in line-down\n",pArapConn));
ArapCleanup(pArapConn);
// remove validation refcount
DerefArapConn(pArapConn);
// remove line-up refcount
DerefArapConn(pArapConn); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication: line-down, can't find pArapConn\n")); } }
break;
case NDIS_STATUS_WAN_GET_STATS:
if (StatusBufLen < sizeof(NDIS_WAN_GET_STATS)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication: GetStats buff too small (%ld)\n", StatusBufLen)); break; }
pWanStats = (PNDIS_WAN_GET_STATS)StatusBuf;
//
// is this a PPP connection? If so, ignore it: we don't keep stats
// for PPP connection
//
if ((pWanStats->LocalAddress[0] == PPP_ID_BYTE1) && (pWanStats->LocalAddress[1] == PPP_ID_BYTE2)) { break; }
//
// no, this is an ARAP connection
//
else { ASSERT((pWanStats->LocalAddress[0] == ARAP_ID_BYTE1) && (pWanStats->LocalAddress[1] == ARAP_ID_BYTE2));
*((ULONG UNALIGNED *)(&pArapConn)) = *((ULONG UNALIGNED *)(&pWanStats->LocalAddress[2]));
// the connection had better be a valid one!
if (ArapConnIsValid(pArapConn)) { //
// copy those stats in!
//
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
pWanStats->BytesSent = pArapConn->StatInfo.BytesSent; pWanStats->BytesRcvd = pArapConn->StatInfo.BytesRcvd; pWanStats->FramesSent = pArapConn->StatInfo.FramesSent; pWanStats->FramesRcvd = pArapConn->StatInfo.FramesRcvd; pWanStats->BytesTransmittedUncompressed = pArapConn->StatInfo.BytesTransmittedUncompressed; pWanStats->BytesReceivedUncompressed = pArapConn->StatInfo.BytesReceivedUncompressed; pWanStats->BytesTransmittedCompressed = pArapConn->StatInfo.BytesTransmittedCompressed; pWanStats->BytesReceivedCompressed = pArapConn->StatInfo.BytesReceivedCompressed;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
// remove validation refcount
DerefArapConn(pArapConn); } else { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication: GetStats on bad connection %lx\n",pArapConn)); } }
break;
case NDIS_STATUS_WAN_FRAGMENT:
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication: Wan-Fragment received\n"));
if (StatusBufLen < sizeof(NDIS_WAN_FRAGMENT)) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication:\
fragment: buff too small (%ld)\n", StatusBufLen)); break; }
pFragment = (PNDIS_WAN_FRAGMENT)StatusBuf;
*((ULONG UNALIGNED *)(&pArapConn)) = *((ULONG UNALIGNED *)(&pFragment->LocalAddress[2]));
if (pArapConn == NULL) { DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication:\
fragment, can't find pArapConn\n"));
break; }
//
// a frame got fragmented (wrong crc or resync or something bad)
// Send an ack to the remote client so he might recover quickly
//
MnpSendAckIfReqd(pArapConn, TRUE);
default:
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR, ("RasStatusIndication: unknown status %lx\n", GeneralStatus)); break; }
}
//***
//
// Function: ArapAdapterInit
// This routine, called at init time, sets up protocol type info
// etc. with ndiswan
//
// Parameters: pPortDesc - the port descriptor corresponding to the "Adapter"
//
// Return: none
//
//***$
ATALK_ERROR ArapAdapterInit( IN OUT PPORT_DESCRIPTOR pPortDesc ) { ATALK_ERROR error; NDIS_REQUEST request; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; UCHAR WanProtocolId[6] = { 0x80, 0x00, 0x00, 0x00, 0x80, 0xf3 }; ULONG WanHeaderFormat; NDIS_WAN_PROTOCOL_CAPS WanProtCap;
//
// set the protocol info
//
request.RequestType = NdisRequestSetInformation; request.DATA.QUERY_INFORMATION.Oid = OID_WAN_PROTOCOL_TYPE; request.DATA.QUERY_INFORMATION.InformationBuffer = WanProtocolId; request.DATA.QUERY_INFORMATION.InformationBufferLength = 6;
ndisStatus = AtalkNdisSubmitRequest(pPortDesc, &request, TRUE, NULL, NULL); if (ndisStatus != NDIS_STATUS_SUCCESS) { LOG_ERRORONPORT(pPortDesc, EVENT_ARAP_BIND_FAIL, ndisStatus, NULL, 0); }
//
// set the protocol caps
//
WanProtCap.Flags = WAN_PROTOCOL_KEEPS_STATS; request.RequestType = NdisRequestSetInformation; request.DATA.QUERY_INFORMATION.Oid = OID_WAN_PROTOCOL_CAPS; request.DATA.QUERY_INFORMATION.InformationBuffer = &WanProtCap; request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(NDIS_WAN_PROTOCOL_CAPS);
ndisStatus = AtalkNdisSubmitRequest(pPortDesc, &request, TRUE, NULL, NULL); if (ndisStatus != NDIS_STATUS_SUCCESS) { LOG_ERRORONPORT(pPortDesc, EVENT_ARAP_BIND_FAIL, ndisStatus, NULL, 0); }
//
// set the header info
//
WanHeaderFormat = NdisWanHeaderEthernet; request.RequestType = NdisRequestSetInformation; request.DATA.QUERY_INFORMATION.Oid = OID_WAN_HEADER_FORMAT; request.DATA.QUERY_INFORMATION.InformationBuffer = &WanHeaderFormat; request.DATA.QUERY_INFORMATION.InformationBufferLength = 4;
ndisStatus = AtalkNdisSubmitRequest(pPortDesc, &request, TRUE, NULL, NULL); if (ndisStatus != NDIS_STATUS_SUCCESS) { LOG_ERRORONPORT(pPortDesc, EVENT_ARAP_BIND_FAIL, ndisStatus, NULL, 0); }
//
// Now query the line count.
//
request.RequestType = NdisRequestQueryInformation; request.DATA.QUERY_INFORMATION.Oid = OID_WAN_LINE_COUNT; request.DATA.QUERY_INFORMATION.InformationBuffer = &pPortDesc->pd_RasLines, request.DATA.QUERY_INFORMATION.InformationBufferLength = 4;
ndisStatus = AtalkNdisSubmitRequest(pPortDesc, &request, TRUE, NULL, NULL);
if (ndisStatus != NDIS_STATUS_SUCCESS) { pPortDesc->pd_RasLines = 1; }
if (pPortDesc->pd_RasLines == 0) {
LOG_ERRORONPORT(pPortDesc, EVENT_ARAP_NO_RESRC, ndisStatus, NULL, 0); }
return AtalkNdisToAtalkError(ndisStatus); }
|