windows-nt/Source/XPSP1/NT/net/sfm/atalk/sys/atkind.c
2020-09-26 16:20:57 +08:00

670 lines
17 KiB
C

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