|
|
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
atkind.c
Abstract:
This module contains the Appletalk Internal Indication support.
Author:
Nikhil Kamkolkar (nikhilk@microsoft.com) Jameel Hyder (jameelh@microsoft.com)
Revision History: 22 Oct 1993 Initial Version
Notes: Tab stop: 4 --*/
#include <atalk.h>
#pragma hdrstop
#define FILENUM ATKIND
ATALK_ERROR AtalkIndAtpPkt( IN PPORT_DESCRIPTOR pPortDesc, IN PBYTE pLookahead, IN USHORT PktLen, IN OUT PUINT pXferOffset, IN PBYTE pLinkHdr, IN BOOLEAN ShortDdpHdr, OUT PBYTE pSubType, OUT PBYTE * ppPacket, OUT PNDIS_PACKET * pNdisPkt ) /*++
Routine Description:
This routine clumps together DDP and ATP packet in functionality for optimizing response packet reception.
Arguments:
Return Value:
--*/ { USHORT dgramLen, hopCnt; ATALK_ADDR destAddr, srcAddr; PBYTE pAtpHdr, pDdpPkt;
// Only for localtalk
BYTE alapSrcNode=0; BYTE alapDestNode=0; NDIS_STATUS ndisStatus; PNDIS_PACKET ndisPkt; PNDIS_BUFFER ndisBuffer=NULL; USHORT atpDataSize, DataSize; USHORT seqNum, tid, startOffset; BYTE controlInfo, function, eomFlag; BYTE RespType; PPROTOCOL_RESD protocolResd; // Protocolresd field in ndisPkt
PATP_ADDROBJ pAtpAddrObj; PATP_REQ pAtpReq;
BOOLEAN refAtpAddr = FALSE, refAtpReq = FALSE; BOOLEAN Deref = FALSE; ATALK_ERROR error = ATALK_NO_ERROR; #ifdef PROFILING
LARGE_INTEGER TimeS, TimeE, TimeD;
TimeS = KeQueryPerformanceCounter(NULL); #endif
do { #if 0 // Receive indication has already verified this !!
// If we dont have atleast the ddp header and atp header, we
// cant figure much out.
if (LookaheadLen < ((ShortDdpHdr ? SDDP_HDR_LEN : LDDP_HDR_LEN) + ATP_HEADER_SIZE)) { error = ATALK_FAILURE; break; }
if (PktLen > (MAX_DGRAM_SIZE + LDDP_HDR_LEN)) { error = ATALK_INVALID_PKT; break; } #endif
// Short and long header formats have the length in the same place,
pDdpPkt = pLookahead; dgramLen = DDP_GET_LEN(pDdpPkt); hopCnt = DDP_GET_HOP_COUNT(pDdpPkt);
// Is the packet too long?
if ((hopCnt > RTMP_MAX_HOPS) || (dgramLen > PktLen)) { error = ATALK_INVALID_PKT; break; }
switch (pPortDesc->pd_NdisPortType) { case NdisMedium802_5: case NdisMedium802_3: case NdisMediumFddi:
// Check the length.
if ((dgramLen < LDDP_HDR_LEN) || (dgramLen > (MAX_DGRAM_SIZE + LDDP_HDR_LEN))) { error = ATALK_INVALID_PKT; break; }
pAtpHdr = pDdpPkt + LDDP_HDR_LEN; atpDataSize = dgramLen - (LDDP_HDR_LEN + ATP_HEADER_SIZE); break; case NdisMediumLocalTalk: if (ShortDdpHdr) { // Short DDP header! If we are not the default port, dont indicate
// packet, as we shouldn't be routing it to the default port, on
// which all our cached sockets reside.
if (!DEF_PORT(pPortDesc)) { error = ATALK_FAILURE; break; } if ((alapDestNode = *(pLinkHdr + ALAP_DEST_OFFSET)) == ATALK_BROADCAST_NODE) { error = ATALK_FAILURE; break; } else if (alapDestNode != NODE_ON_NONEXTPORT(pPortDesc)) { error = ATALK_FAILURE; break; }
alapSrcNode = *(pLinkHdr + ALAP_SRC_OFFSET);
if ((dgramLen < SDDP_HDR_LEN) || (dgramLen > MAX_DGRAM_SIZE + SDDP_HDR_LEN)) { error = ATALK_INVALID_PKT; break; } pAtpHdr = pDdpPkt + SDDP_HDR_LEN; atpDataSize = dgramLen - (SDDP_HDR_LEN + ATP_HEADER_SIZE); } else { pAtpHdr = pDdpPkt + LDDP_HDR_LEN; atpDataSize = dgramLen - (LDDP_HDR_LEN + ATP_HEADER_SIZE); } break; default: KeBugCheck(0); break; }
if (!ATALK_SUCCESS(error)) { break; }
DataSize = atpDataSize + ATP_HEADER_SIZE; pDdpPkt += 2; if (ShortDdpHdr) { destAddr.ata_Node = alapDestNode; srcAddr.ata_Network = destAddr.ata_Network = NET_ON_NONEXTPORT(pPortDesc); srcAddr.ata_Node = alapSrcNode;
// Get the socket numbers from the ddp header.
destAddr.ata_Socket = *pDdpPkt++; srcAddr.ata_Socket = *pDdpPkt;
DBGPRINT(DBG_COMP_DDP, DBG_LEVEL_WARN, ("AtalkDdpPacketIn: NonExtended Dest Net.Node %lx.%lx\n", destAddr.ata_Network, destAddr.ata_Node));
// Now the destination node address could be
// ALAP_BROADCAST_NODE (0xFF).
if ((srcAddr.ata_Node < MIN_USABLE_ATALKNODE) || (srcAddr.ata_Node > MAX_USABLE_ATALKNODE) || (destAddr.ata_Node == UNKNOWN_NODE)) { error = ATALK_INVALID_PKT; break; }
if (destAddr.ata_Node == ATALK_BROADCAST_NODE) { error = ATALK_FAILURE; break; } } else { // If we have a checksum, we cannot optimize.
if ((*pDdpPkt++ != 0) || (*pDdpPkt++ != 0)) { error = ATALK_FAILURE; break; }
// Build full source and destination AppleTalk address structures
// from our DDP header.
GETSHORT2SHORT(&destAddr.ata_Network, pDdpPkt); pDdpPkt += 2;
GETSHORT2SHORT(&srcAddr.ata_Network, pDdpPkt); pDdpPkt += 2;
destAddr.ata_Node = *pDdpPkt++; srcAddr.ata_Node = *pDdpPkt++; destAddr.ata_Socket = *pDdpPkt++; srcAddr.ata_Socket = *pDdpPkt;
if (destAddr.ata_Node == ATALK_BROADCAST_NODE) { error = ATALK_FAILURE; break; }
// Do we like what we see? Note "nnnn00" is now allowed and used by NBP.
if ((srcAddr.ata_Network > LAST_VALID_NETWORK) || (srcAddr.ata_Network < FIRST_VALID_NETWORK) || (srcAddr.ata_Node < MIN_USABLE_ATALKNODE) || (srcAddr.ata_Node > MAX_USABLE_ATALKNODE)) { error = ATALK_INVALID_PKT; break; } } // Long DDP header
} while (FALSE);
if (!ATALK_SUCCESS(error)) { DBGPRINT(DBG_COMP_DDP, DBG_LEVEL_WARN, ("AtalkDdpPacketIn: drop packet in indication%lx\n", error));
return error; }
// Now for the ATP processing. We need to copy header into ndispkt.
// Get the static fields from the ATP header.
controlInfo = pAtpHdr[ATP_CMD_CONTROL_OFF]; function = (controlInfo & ATP_FUNC_MASK); eomFlag = ((controlInfo & ATP_EOM_MASK) != 0);
// Get the sequence number
seqNum = (USHORT)(pAtpHdr[ATP_SEQ_NUM_OFF]);
// Get the transaction id
GETSHORT2SHORT(&tid, &pAtpHdr[ATP_TRANS_ID_OFF]);
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO, ("AtalkIndAtpPkt: Packet tid %x func %x ctrlinfo %x\n", tid, function, controlInfo));
do { // See if we have a a cached ATP address for this destination address.
AtalkIndAtpCacheLkUpSocket(&destAddr, &pAtpAddrObj, &error); if (!ATALK_SUCCESS(error)) { error = ATALK_FAILURE; DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO, ("AtalkIndAtpPkt: CacheLkup failed - tid %x, func %x, ctrlinfo %x\n", tid, function, controlInfo)); break; }
refAtpAddr = TRUE;
if (function != ATP_RESPONSE) // Is this a request or a release?
{ PBYTE packet; PBUFFER_HDR pBufferHdr = NULL; PPROTOCOL_RESD protocolResd; BLKID BlkId;
// Allocate a small or large ddp buffer as appropriate.
BlkId = BLKID_DDPSM; if (DataSize > (sizeof(DDP_SMBUFFER) - sizeof(BUFFER_HDR))) BlkId = BLKID_DDPLG;
if ((pBufferHdr = (PBUFFER_HDR)AtalkBPAllocBlock(BlkId)) == NULL) { error = ATALK_FAILURE; break; }
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO, ("AtalkIndAtpPkt: Indicating request\n"));
// Setup the ndis packet.
packet = (PBYTE)pBufferHdr + sizeof(BUFFER_HDR); // Get a pointer to the NDIS packet descriptor from the buffer header.
ndisPkt = pBufferHdr->bh_NdisPkt; protocolResd = (PPROTOCOL_RESD)(ndisPkt->ProtocolReserved);
// All set! Set appropriate values in the packet descriptor.
protocolResd->Receive.pr_OptimizeType = INDICATE_ATP; protocolResd->Receive.pr_OptimizeSubType= ATP_ALLOC_BUF; protocolResd->Receive.pr_AtpAddrObj = pAtpAddrObj; protocolResd->Receive.pr_SrcAddr = srcAddr; protocolResd->Receive.pr_DestAddr = destAddr; protocolResd->Receive.pr_DataLength = DataSize; protocolResd->Receive.pr_OptimizeCtx = (PVOID)NULL; protocolResd->Receive.pr_OffCablePkt = (BOOLEAN)(hopCnt > 0);
*pNdisPkt = ndisPkt; *ppPacket = packet; *pSubType = function; *pXferOffset += (ShortDdpHdr ? SDDP_HDR_LEN : LDDP_HDR_LEN);
// Done, break out.
error = ATALK_NO_ERROR; break; }
ASSERT (function == ATP_RESPONSE);
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO, ("AtalkIndAtpPkt: RESPONSE SeqNum %d tid %lx\n", seqNum, tid));
if (seqNum > (ATP_MAX_RESP_PKTS-1)) { error = ATALK_INVALID_PKT; break; }
// See if there is a pending request.
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddrObj->atpao_Lock); atalkAtpReqReferenceByAddrTidDpc(pAtpAddrObj, &srcAddr, tid, &pAtpReq, &error); RELEASE_SPIN_LOCK_DPC(&pAtpAddrObj->atpao_Lock);
if (!ATALK_SUCCESS(error)) { // We dont have a corresponding pending request. Ignore.
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR, ("AtalkIndAtpPkt: NO pending request for tid %lx\n", tid)); error = ATALK_DUP_PKT; // Do not add this to dropped packet statistic
break; }
refAtpReq = TRUE;
do { // Check the request bitmap, which could be zero if the user only
// wanted the user bytes and passed in a null response buffer.
// Do we want to keep this response? Check the corresponding
// bit in our current bitmap set.
ACQUIRE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
pAtpReq->req_Flags |= ATP_REQ_REMOTE;
do { if (((pAtpReq->req_RecdBitmap & AtpBitmapForSeqNum[seqNum]) != 0) || ((pAtpReq->req_Bitmap & AtpBitmapForSeqNum[seqNum]) == 0)) { error = ATALK_DUP_PKT; // Not an error condition
break; }
if (atpDataSize > 0) { startOffset = (USHORT)seqNum * pAtpAddrObj->atpao_MaxSinglePktSize; if (pAtpReq->req_RespBufLen < (startOffset + atpDataSize)) { error = ATALK_FAILURE; break; } }
// If we are the first packet, copy the response user bytes.
if (seqNum == 0) { DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO, ("AtalkIndAtpPkt: Copying user bytes for tid %x\n", tid)); RtlCopyMemory(pAtpReq->req_RespUserBytes, pAtpHdr + ATP_USER_BYTES_OFF, ATP_USERBYTES_SIZE); }
// If this response packet does not cause the req_Bitmap to go to ZERO
// i.e. we have not recvd. all the packets, just copy the data into
// user's buffer, adjust the bitmaps (req_Bitmap & req_RecdBitmap) and
// not indicate this packet up to Atp.
pAtpReq->req_RecdBitmap |= AtpBitmapForSeqNum[seqNum]; pAtpReq->req_Bitmap &= ~AtpBitmapForSeqNum[seqNum]; pAtpReq->req_RespRecdLen += atpDataSize;
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO, ("AtalkIndAtpPkt: Bitmap %x, RecdBitmap %x, RecdLen %d for tid %x\n", pAtpReq->req_Bitmap, pAtpReq->req_RecdBitmap, pAtpReq->req_RespRecdLen, tid));
// Now if eom is set, we need to reset all high order bits
// of the req_Bitmap. req_RecdBitmap should now indicate all
// the buffers we received. The two should be mutually exclusive
// at this point.
if (eomFlag) { pAtpReq->req_Bitmap &= AtpEomBitmapForSeqNum[seqNum]; ASSERT((pAtpReq->req_Bitmap & pAtpReq->req_RecdBitmap) == 0); }
RespType = ATP_USER_BUF; if (pAtpReq->req_Bitmap != 0) { RespType = ATP_USER_BUFX; Deref = TRUE; } else { pAtpReq->req_Flags |= ATP_REQ_RESPONSE_COMPLETE; DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO, ("AtalkIndAtpPkt: LAST Response for tid %x\n", tid)); }
// Allocate an NDIS packet descriptor.
NdisDprAllocatePacket(&ndisStatus, &ndisPkt, AtalkNdisPacketPoolHandle); if (ndisStatus == NDIS_STATUS_SUCCESS) { RtlZeroMemory(ndisPkt->ProtocolReserved, sizeof(PROTOCOL_RESD)); // It will be freed by receive completion now.
ndisBuffer = pAtpReq->req_NdisBuf[seqNum]; pAtpReq->req_NdisBuf[seqNum] = NULL; } else { error = ATALK_FAILURE; break; } } while (FALSE);
RELEASE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
if (!ATALK_SUCCESS(error)) { break; }
// Copy the data into the users buffer. Check if there's room.
if ((atpDataSize > 0) || (ndisBuffer != NULL)) { if (ndisBuffer == NULL) { // Allocate an NDIS buffer descriptor and chain into pkt desc.
NdisCopyBuffer(&ndisStatus, &ndisBuffer, AtalkNdisBufferPoolHandle, (PVOID)pAtpReq->req_RespBuf, startOffset, // Offset
(UINT)atpDataSize); if (ndisStatus != NDIS_STATUS_SUCCESS) { NdisDprFreePacket(ndisPkt); error = ATALK_FAILURE; break; }
ATALK_DBG_INC_COUNT(AtalkDbgMdlsAlloced); }
// Chain in the buffer.
NdisChainBufferAtBack(ndisPkt, ndisBuffer); }
// All set! Set appropriate values in the packet descriptor.
protocolResd = (PPROTOCOL_RESD)&ndisPkt->ProtocolReserved; protocolResd->Receive.pr_OptimizeType = INDICATE_ATP; protocolResd->Receive.pr_OptimizeSubType= RespType; protocolResd->Receive.pr_AtpAddrObj = pAtpAddrObj; protocolResd->Receive.pr_SrcAddr = srcAddr; protocolResd->Receive.pr_DestAddr = destAddr; protocolResd->Receive.pr_DataLength = atpDataSize; protocolResd->Receive.pr_OptimizeCtx = (PVOID)pAtpReq; protocolResd->Receive.pr_OffCablePkt = (BOOLEAN)(hopCnt > 0);
// Do not copy the Atp header unless AtalkAtpPacketIn will be called.
if (RespType == ATP_USER_BUF) { ATALK_RECV_INDICATION_COPY(pPortDesc, protocolResd->Receive.pr_AtpHdr, pAtpHdr, ATP_HEADER_SIZE); DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO, ("AtalkIndAtpPkt: Last packet for request, indicating tid %x\n", tid)); } *pNdisPkt = ndisPkt; *ppPacket = NULL; *pSubType = function; *pXferOffset += ((ShortDdpHdr ? SDDP_HDR_LEN : LDDP_HDR_LEN) + ATP_HEADER_SIZE); } while (FALSE); } while (FALSE);
if (!ATALK_SUCCESS(error) || Deref) { if (refAtpReq) { AtalkAtpReqDereferenceDpc(pAtpReq); }
if (refAtpAddr) { AtalkAtpAddrDereferenceDpc(pAtpAddrObj); } }
#ifdef PROFILING
TimeE = KeQueryPerformanceCounter(NULL); TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR_DPC(&AtalkStatistics.stat_AtpIndicationProcessTime, TimeD, &AtalkStatsLock.SpinLock);
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumIndications, &AtalkStatsLock.SpinLock); #endif
return error; }
ATALK_ERROR AtalkIndAtpCacheSocket( IN PATP_ADDROBJ pAtpAddr, IN PPORT_DESCRIPTOR pPortDesc ) /*++
Routine Description:
Cache ATP socket routine. Have another one for ADSP when that is done.
Arguments:
Return Value:
None --*/ { USHORT i; KIRQL OldIrql; PDDP_ADDROBJ pDdpAddr; ATALK_ERROR error = ATALK_FAILURE;
// Only cache if the net and node match the current net and node.
ACQUIRE_SPIN_LOCK(&AtalkSktCacheLock, &OldIrql); pDdpAddr = pAtpAddr->atpao_DdpAddr;
if ((AtalkSktCache.ac_Network == 0) && (AtalkSktCache.ac_Node == 0) && (AtalkDefaultPort == pPortDesc)) { AtalkSktCache.ac_Network = pDdpAddr->ddpao_Addr.ata_Network; AtalkSktCache.ac_Node = pDdpAddr->ddpao_Addr.ata_Node; }
if ((AtalkSktCache.ac_Network == pDdpAddr->ddpao_Addr.ata_Network) && (AtalkSktCache.ac_Node == pDdpAddr->ddpao_Addr.ata_Node)) { // First try to get a free slot
for (i = 0; i < ATALK_CACHE_SKTMAX; i++) { if (AtalkSktCache.ac_Cache[i].Type == ATALK_CACHE_NOTINUSE) { ASSERT(AtalkSktCache.ac_Cache[i].u.pAtpAddr == NULL);
// Use this slot
AtalkSktCache.ac_Cache[i].Type = (ATALK_CACHE_INUSE | ATALK_CACHE_ATPSKT); AtalkSktCache.ac_Cache[i].Socket = pDdpAddr->ddpao_Addr.ata_Socket; // The caller must have referenced these before calling cache AND
// must called uncache before removing those references. Also, if we
// returned error from this routine, Caller must Dereference them.
AtalkSktCache.ac_Cache[i].u.pAtpAddr = pAtpAddr; error = ATALK_NO_ERROR; break; } } } RELEASE_SPIN_LOCK(&AtalkSktCacheLock, OldIrql);
return error; }
VOID AtalkIndAtpUnCacheSocket( IN PATP_ADDROBJ pAtpAddr ) /*++
Routine Description:
Cache ATP socket routine. Have another one for ADSP when that is done.
Arguments:
Return Value:
None --*/ { USHORT i; KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&AtalkSktCacheLock, &OldIrql); for (i = 0; i < ATALK_CACHE_SKTMAX; i++) { if ((AtalkSktCache.ac_Cache[i].Type == (ATALK_CACHE_INUSE | ATALK_CACHE_ATPSKT)) && (AtalkSktCache.ac_Cache[i].Socket == pAtpAddr->atpao_DdpAddr->ddpao_Addr.ata_Socket)) { ASSERT(AtalkSktCache.ac_Cache[i].u.pAtpAddr == pAtpAddr); AtalkSktCache.ac_Cache[i].Type = ATALK_CACHE_NOTINUSE; AtalkSktCache.ac_Cache[i].u.pAtpAddr = NULL; break; } }
if (i == ATALK_CACHE_SKTMAX) { // We didnt find the socket! References will get all messed up!
ASSERT(0); }
RELEASE_SPIN_LOCK(&AtalkSktCacheLock, OldIrql); }
|