/*++ 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 #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)); 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); }