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

2221 lines
55 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
aarp.c
Abstract:
This module contains the Appletalk Address Resolution Protocol code.
Author:
Jameel Hyder (jameelh@microsoft.com)
Nikhil Kamkolkar (nikhilk@microsoft.com)
Revision History:
19 Jun 1992 Initial Version
Notes: Tab stop: 4
--*/
#include <atalk.h>
#pragma hdrstop
// Define the file number for this module for errorlogging.
#define FILENUM AARP
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGEINIT, AtalkInitAarpForNodeOnPort)
#pragma alloc_text(PAGEINIT, AtalkInitAarpForNodeInRange)
#pragma alloc_text(PAGEINIT, atalkInitAarpForNode)
#endif
VOID
AtalkAarpPacketIn(
IN OUT PPORT_DESCRIPTOR pPortDesc,
IN PBYTE pLinkHdr,
IN PBYTE pPkt, // Only aarp data
IN USHORT Length
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PBYTE srcAddr;
PBYTE startOfPkt;
ATALK_NODEADDR srcNode, dstNode;
PBYTE pRouteInfo = NULL;
USHORT routeInfoLen = 0;
ULONG logEventPlace = 0;
USHORT hardwareLen, protocolLength, aarpCommand;
PBUFFER_DESC pBuffDesc;
ATALK_ERROR error;
PVOID pRasConn;
PATCPCONN pAtcpConn=NULL;
PARAPCONN pArapConn=NULL;
DWORD dwFlags;
BOOLEAN fDialInNode=TRUE;
BOOLEAN fThisIsPPP;
TIME TimeS, TimeE, TimeD;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
TimeS = KeQueryPerformanceCounter(NULL);
if (PORT_CLOSING(pPortDesc))
{
// If we are not active, return!
return;
}
if (pPortDesc->pd_NdisPortType == NdisMedium802_5)
{
if (pLinkHdr[TLAP_SRC_OFFSET] & TLAP_SRC_ROUTING_MASK)
{
routeInfoLen = (pLinkHdr[TLAP_ROUTE_INFO_OFFSET] & TLAP_ROUTE_INFO_SIZE_MASK);
// First, glean any AARP information that we can, then handle the DDP
// packet. This guy also makes sure we have a good 802.2 header...
//
// Need to make a localcopy of the source address and then turn
// the source routing bit off before calling AarpGleanInfo
//
// (HdrBuf)[TLAP_SRC_OFFSET] = ((HdrBuf)[TLAP_SRC_OFFSET] & ~TLAP_SRC_ROUTING_MASK);
pLinkHdr[TLAP_SRC_OFFSET] &= ~TLAP_SRC_ROUTING_MASK;
pRouteInfo = pLinkHdr + TLAP_ROUTE_INFO_OFFSET;
}
}
startOfPkt = pPkt;
ASSERT(routeInfoLen <= TLAP_MAX_ROUTING_BYTES);
// Pull out the information we'll be playing with. All three valid AARP
// commands use the same packet format. But have some variable length
// fields.
// The packet will not include the 802.2 header!
// pPkt += IEEE8022_HDR_LEN;
// Length -= IEEE8022_HDR_LEN;
pPkt += AARP_HW_LEN_OFFSET; // Skip the hardware type
do
{
GETBYTE2SHORT (&hardwareLen, pPkt);
pPkt++ ;
if ((hardwareLen < AARP_MIN_HW_ADDR_LEN ) ||
(hardwareLen > AARP_MAX_HW_ADDR_LEN))
{
logEventPlace = (FILENUM | __LINE__);
break;
}
GETBYTE2SHORT(&protocolLength, pPkt);
pPkt ++;
if (protocolLength != AARP_PROTO_ADDR_LEN)
{
logEventPlace = (FILENUM | __LINE__);
break;
}
GETSHORT2SHORT(&aarpCommand, pPkt);
pPkt += 2;
// Remember where the source address is in the packet for
// entering it into the mapping table
srcAddr = pPkt;
// Skip over the source hardware length
// Skip over to leading null pad on logical address.
pPkt += (hardwareLen + 1);
GETSHORT2SHORT(&srcNode.atn_Network, pPkt);
pPkt += 2;
srcNode.atn_Node = *pPkt++;
// Skip the destination hardware address
// Skip over to leading null pad on logical destination address.
pPkt += (hardwareLen + 1);
GETSHORT2SHORT(&dstNode.atn_Network, pPkt);
pPkt += 2;
dstNode.atn_Node = *pPkt++;
// We should have eaten the whole packet...
if ((ULONG)(pPkt - startOfPkt) != Length)
{
logEventPlace = (FILENUM | __LINE__);
break;
}
// Ignore any AARPs from us.
ASSERT(hardwareLen == TLAP_ADDR_LEN);
if (AtalkFixedCompareCaseSensitive(srcAddr,
hardwareLen,
pPortDesc->pd_PortAddr,
hardwareLen))
{
break;
}
// Handle the Aarp command packets
switch(aarpCommand)
{
case AARP_REQUEST:
// We can get valid mapping info from a request, use it!
// We are guaranteed routing info is positive and is not odd
// (atleast 2 bytes).
ASSERT((routeInfoLen >= 0) && (routeInfoLen != 1));
if (routeInfoLen > 0)
atalkAarpTuneRouteInfo(pPortDesc, pRouteInfo);
atalkAarpEnterIntoAmt(pPortDesc,
&srcNode,
srcAddr,
hardwareLen,
pRouteInfo,
routeInfoLen);
// After that, we can ignore any request not destined for us.
if (!AtalkNodeExistsOnPort(pPortDesc, &dstNode))
{
// our dial-in clients can only be in the network range of the
// default port. If another adapter is plugged into the same net
// as the default adapter, we don't want dial-in clients to
// mess things up: ignore anything not coming on default adapter
// (as far as dial-in clients go)
if (pPortDesc != AtalkDefaultPort)
{
break;
}
//
// is this one of our dial-in "nodes"? If so, we must send out
// a proxy response from our DefaultPort.
//
if ((pRasConn = FindAndRefRasConnByAddr(dstNode,
&dwFlags,
&fThisIsPPP)) != NULL)
{
if (fThisIsPPP)
{
ASSERT(((PATCPCONN)pRasConn)->Signature == ATCPCONN_SIGNATURE);
DerefPPPConn((PATCPCONN)pRasConn);
}
else
{
ASSERT(((PARAPCONN)pRasConn)->Signature == ARAPCONN_SIGNATURE);
DerefArapConn((PARAPCONN)pRasConn);
}
}
//
// nope, a dial-in client with such a node addr doesn't exist either..
//
else
{
break;
}
}
// The're asking about us, speak the truth.
pBuffDesc = BUILD_AARPRESPONSE(pPortDesc,
hardwareLen,
srcAddr,
pRouteInfo,
routeInfoLen,
dstNode,
srcNode);
if (pBuffDesc == NULL)
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_ERR,
("AtalkAarpPacketIn: Mem alloc failed %d\n", __LINE__));
break;
}
if (!ATALK_SUCCESS(AtalkNdisSendPacket(pPortDesc,
pBuffDesc,
AtalkAarpSendComplete,
NULL)))
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_ERR,
("AtalkAarpPacketIn: SendPkt %lx failed %d\n",
pBuffDesc->bd_CharBuffer, __LINE__));
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_AARP_SEND_FAIL,
STATUS_INSUFFICIENT_RESOURCES,
pBuffDesc->bd_CharBuffer,
Length);
// We allocated the packet.
AtalkAarpSendComplete(NDIS_STATUS_FAILURE,
pBuffDesc,
NULL);
}
break;
case AARP_RESPONSE:
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (pPortDesc->pd_Flags & PD_FINDING_NODE)
{
// No doubt, this is a response to our probe, check to make sure
// the address matches, if so set the "used" flag.
if (ATALK_NODES_EQUAL(&dstNode, &pPortDesc->pd_TentativeNodeAddr))
{
pPortDesc->pd_Flags |= PD_NODE_IN_USE;
// Wakeup the blocking thread...
KeSetEvent(&pPortDesc->pd_NodeAcquireEvent, IO_NETWORK_INCREMENT, FALSE);
}
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
// is this one of our dial-in "nodes"? If so, check if we are probing
if ((pRasConn = FindAndRefRasConnByAddr(dstNode,
&dwFlags,
&fThisIsPPP)) != NULL)
{
//
// our dial-in clients can only be in the network range of the
// default port
//
ASSERT(pPortDesc == AtalkDefaultPort);
pAtcpConn = NULL;
pArapConn = NULL;
if (fThisIsPPP)
{
pAtcpConn = (PATCPCONN)pRasConn;
ASSERT(pAtcpConn->Signature == ATCPCONN_SIGNATURE);
}
else
{
pArapConn = (PARAPCONN)pRasConn;
ASSERT(pArapConn->Signature == ARAPCONN_SIGNATURE);
}
// PPP client?
if (pAtcpConn)
{
if (dwFlags & ATCP_FINDING_NODE)
{
ACQUIRE_SPIN_LOCK_DPC(&pAtcpConn->SpinLock);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_WARN,
("AtalkAarpPacketIn: PPP: someone owns %lx %x, retrying\n",
dstNode.atn_Network,dstNode.atn_Node));
pAtcpConn->Flags |= ATCP_NODE_IN_USE;
// Wakeup the blocking thread...
KeSetEvent(&pAtcpConn->NodeAcquireEvent,
IO_NETWORK_INCREMENT,
FALSE);
RELEASE_SPIN_LOCK_DPC(&pAtcpConn->SpinLock);
}
// remove refcount put by FindAndRefRasConnByAddr
DerefPPPConn(pAtcpConn);
}
// nope, ARAP client
else
{
if (dwFlags & ARAP_FINDING_NODE)
{
ACQUIRE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_WARN,
("AtalkAarpPacketIn: ARAP: someone owns %lx %x, retrying\n",
dstNode.atn_Network,dstNode.atn_Node));
pArapConn->Flags |= ARAP_NODE_IN_USE;
// Wakeup the blocking thread...
KeSetEvent(&pArapConn->NodeAcquireEvent,
IO_NETWORK_INCREMENT,
FALSE);
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
}
// remove refcount put by FindAndRefRasConnByAddr
DerefArapConn(pArapConn);
}
}
// This must have been a response to a probe or request... update our
// mapping table.
if (routeInfoLen != 0)
{
atalkAarpTuneRouteInfo(pPortDesc, pRouteInfo);
}
atalkAarpEnterIntoAmt(pPortDesc,
&srcNode,
srcAddr,
hardwareLen,
pRouteInfo,
routeInfoLen);
break;
case AARP_PROBE:
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (pPortDesc->pd_Flags & PD_FINDING_NODE)
{
// If we get a probe for our current tentative address, set the
// "used" flag.
if (ATALK_NODES_EQUAL(&dstNode, &pPortDesc->pd_TentativeNodeAddr))
{
pPortDesc->pd_Flags |= PD_NODE_IN_USE;
KeSetEvent(&pPortDesc->pd_NodeAcquireEvent,
IO_NETWORK_INCREMENT,
FALSE);
}
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (pPortDesc != AtalkDefaultPort)
{
break;
}
fDialInNode = FALSE;
// is the probe asking about one of our dial-in nodes? if so, we
// must defend that address (or, if we are trying to acquire this
// node addr, stop that since someone else is doing the same)
//
// our dial-in clients can only be in the network range of the
// default port. If another adapter is plugged into the same net
// as the default adapter, we don't want dial-in clients to
// mess things up: ignore anything not coming on default adapter
// (as far as dial-in clients go)
if ((pPortDesc == AtalkDefaultPort) &&
((pRasConn = FindAndRefRasConnByAddr(dstNode,
&dwFlags,
&fThisIsPPP)) != NULL))
{
pAtcpConn = NULL;
pArapConn = NULL;
if (fThisIsPPP)
{
pAtcpConn = (PATCPCONN)pRasConn;
ASSERT(pAtcpConn->Signature == ATCPCONN_SIGNATURE);
}
else
{
pArapConn = (PARAPCONN)pRasConn;
ASSERT(pArapConn->Signature == ARAPCONN_SIGNATURE);
}
// PPP client?
if (pAtcpConn)
{
if (dwFlags & ATCP_FINDING_NODE)
{
ACQUIRE_SPIN_LOCK_DPC(&pAtcpConn->SpinLock);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_WARN,
("AtalkAarpPacketIn: PPP: someone trying to acquire %lx %x, retrying\n",
dstNode.atn_Network,dstNode.atn_Node));
pAtcpConn->Flags |= ATCP_NODE_IN_USE;
// Wakeup the blocking thread...
KeSetEvent(&pAtcpConn->NodeAcquireEvent,
IO_NETWORK_INCREMENT,
FALSE);
RELEASE_SPIN_LOCK_DPC(&pAtcpConn->SpinLock);
}
else
{
fDialInNode = TRUE;
}
// remove refcount put by FindAndRefRasConnByAddr
DerefPPPConn(pAtcpConn);
}
// nope, ARAP client
else
{
if (dwFlags & ARAP_FINDING_NODE)
{
ACQUIRE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_WARN,
("AtalkAarpPacketIn: ARAP: someone trying to acquire %lx %x, retrying\n",
dstNode.atn_Network,dstNode.atn_Node));
pArapConn->Flags |= ARAP_NODE_IN_USE;
// Wakeup the blocking thread...
KeSetEvent(&pArapConn->NodeAcquireEvent,
IO_NETWORK_INCREMENT,
FALSE);
RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock);
}
else
{
fDialInNode = TRUE;
}
// remove refcount put by FindAndRefRasConnByAddr
DerefArapConn(pArapConn);
}
}
// If the probe isn't asking about one of our AppleTalk addresses,
// and it's not one of our dial-in nodes either, drop it on the floor.
if (!fDialInNode && !AtalkNodeExistsOnPort(pPortDesc, &dstNode))
{
break;
}
// The're talking to us! Build and send the response.
if (routeInfoLen != 0)
{
atalkAarpTuneRouteInfo(pPortDesc, pRouteInfo);
}
if (fDialInNode)
{
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
("AtalkAarpPacketIn: defending dial-in client's addr %x %x\n",
dstNode.atn_Network,dstNode.atn_Node));
}
pBuffDesc = BUILD_AARPRESPONSE(pPortDesc,
hardwareLen,
srcAddr,
pRouteInfo,
routeInfoLen,
dstNode,
srcNode);
if (pBuffDesc == NULL)
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_ERR,
("AtalkAarpPacketIn: Mem alloc failed %d\n", __LINE__));
break;
}
if (!ATALK_SUCCESS(AtalkNdisSendPacket(pPortDesc,
pBuffDesc,
AtalkAarpSendComplete,
NULL)))
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_ERR,
("AtalkAarpPacketIn: SendPkt %lx failed %d\n",
pBuffDesc->bd_CharBuffer, __LINE__));
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_AARP_SEND_FAIL,
STATUS_INSUFFICIENT_RESOURCES,
pBuffDesc->bd_CharBuffer,
Length);
// We allocated the packet. This will free it up.
AtalkAarpSendComplete(NDIS_STATUS_FAILURE,
pBuffDesc,
NULL);
}
break;
default:
logEventPlace = (FILENUM | __LINE__);
break;
}
} while (FALSE);
if (logEventPlace)
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_INVALIDAARPPACKET,
logEventPlace,
startOfPkt,
Length);
}
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR_DPC(
&pPortDesc->pd_PortStats.prtst_AarpPacketInProcessTime,
TimeD,
&AtalkStatsLock.SpinLock);
INTERLOCKED_INCREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_NumAarpPacketsIn,
&AtalkStatsLock.SpinLock);
}
VOID
AtalkAarpSendComplete(
NDIS_STATUS Status,
PBUFFER_DESC pBuffDesc,
PSEND_COMPL_INFO pSendInfo
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ASSERT(pBuffDesc->bd_Next == NULL);
ASSERT(pBuffDesc->bd_Flags & BD_CHAR_BUFFER);
AtalkNdisFreeBuf(pBuffDesc);
}
#define AtalkAarpUpdateBre(_pPortDesc, \
_Network, \
_SrcAddr, \
_AddrLen, \
_RouteInfo, \
_RouteInfoLen) \
{ \
PBRE pBre, *ppBre; \
int index; \
BLKID BlkId; \
\
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_INFO, \
("AtalkAarpUpdateBre: Entering %x in brc\n", _Network)); \
\
index = (int)((_Network) & (PORT_BRC_HASH_SIZE - 1)); \
\
ACQUIRE_SPIN_LOCK_DPC(&(_pPortDesc)->pd_Lock); \
\
for (ppBre = &(_pPortDesc)->pd_Brc[index]; \
(pBre = *ppBre) != NULL; \
ppBre = &pBre->bre_Next) \
{ \
if (pBre->bre_Network == (_Network)) \
{ \
/* \
* Unlink it from the list since it could potentially \
* be freed if the routeinfolen grew and also we want \
* to link it again at the head of the list \
*/ \
*ppBre = pBre->bre_Next; \
break; \
} \
} \
\
if ((pBre != NULL) && \
(pBre->bre_RouteInfoLen < (BYTE)(_RouteInfoLen))) \
{ \
AtalkBPFreeBlock(pBre); \
pBre = NULL; \
} \
\
if (pBre == NULL) \
{ \
BlkId = BLKID_BRE; \
if ((_RouteInfoLen) != 0) \
BlkId = BLKID_BRE_ROUTE; \
pBre = (PBRE)AtalkBPAllocBlock(BlkId); \
} \
\
if (pBre != NULL) \
{ \
pBre->bre_Age = 0; \
pBre->bre_Network = (_Network); \
\
COPY_NETWORK_ADDR(pBre->bre_RouterAddr, \
_SrcAddr); \
\
pBre->bre_RouteInfoLen =(BYTE)(_RouteInfoLen); \
\
if ((_RouteInfoLen) > 0) \
RtlCopyMemory((PBYTE)pBre + sizeof(BRE), \
_RouteInfo, \
_RouteInfoLen); \
\
pBre->bre_Next = *ppBre; \
*ppBre = pBre; \
} \
\
RELEASE_SPIN_LOCK_DPC(&(_pPortDesc)->pd_Lock); \
}
BOOLEAN
AtalkAarpGleanInfo(
IN OUT PPORT_DESCRIPTOR pPortDesc,
IN PBYTE SrcAddr,
IN SHORT AddrLen,
IN OUT PBYTE RouteInfo,
IN USHORT RouteInfoLen,
IN PBYTE pPkt,
IN USHORT Length
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ATALK_NODEADDR srcNode, dstNode;
PBYTE startOfPkt;
ULONG logEventPlace = 0;
BYTE offCableInfo;
BOOLEAN result = TRUE;
if (PORT_CLOSING(pPortDesc))
{
// If we are not active, return!
return FALSE;
}
// Packet will not include the 802.2 header!
// pPkt += IEEE8022_HDR_LEN;
// Length -= IEEE8022_HDR_LEN;
// Remember the start of the packet
startOfPkt = pPkt;
// Get the off cable information
offCableInfo = *pPkt;
// Skip the datagram length and checksum fields
pPkt += (2 + 2);
// Get the destination network number
GETSHORT2SHORT(&dstNode.atn_Network, pPkt);
pPkt += sizeof(USHORT);
// Get the source network number
GETSHORT2SHORT(&srcNode.atn_Network, pPkt);
pPkt += sizeof(USHORT);
// Get the destination node id
dstNode.atn_Node = *pPkt++;
// Get the source node id
srcNode.atn_Node = *pPkt++;
do
{
// Do a little verification.
if ((srcNode.atn_Node < MIN_USABLE_ATALKNODE) ||
(srcNode.atn_Node > MAX_USABLE_ATALKNODE) ||
(srcNode.atn_Network < FIRST_VALID_NETWORK) ||
(srcNode.atn_Network > LAST_VALID_NETWORK))
{
// Only bother logging this if we are in some routing capacity,
// otherwise, let A-ROUTER worry about it
if (AtalkRouter)
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_ERR,
("AtalkAarpGleanInfo: dstNode invalid %x.%x\n",
srcNode.atn_Network, srcNode.atn_Node));
logEventPlace = FILENUM | __LINE__;
}
break;
}
if (dstNode.atn_Network > LAST_VALID_NETWORK)
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_ERR,
("AtalkAarpGleanInfo: srcNode invalid %x.%x\n",
dstNode.atn_Network, dstNode.atn_Node));
logEventPlace = FILENUM | __LINE__;
break;
}
// Did the packet come from off this cable? Look at the hop count. If so,
// enter it into our best-router cache.
//
// **NOTE** We assume that the RouteInfo buffer can be written to!
if (RouteInfoLen > 0)
atalkAarpTuneRouteInfo(pPortDesc, RouteInfo);
if ((offCableInfo >> 2) & AARP_OFFCABLE_MASK)
{
AtalkAarpUpdateBre(pPortDesc,
srcNode.atn_Network,
SrcAddr,
AddrLen,
RouteInfo,
RouteInfoLen);
}
else
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_INFO,
("AtalkAarpGleanInfo: Entering %x.%x info in Amt tables\n",
srcNode.atn_Network, srcNode.atn_Node));
// "Glean" AARP information from on-cable packets.
atalkAarpEnterIntoAmt(pPortDesc,
&srcNode,
SrcAddr,
AddrLen,
RouteInfo,
RouteInfoLen);
}
} while (FALSE);
if (logEventPlace)
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_INVALIDAARPPACKET,
logEventPlace,
startOfPkt,
Length);
}
return (logEventPlace == 0);
}
VOID
AtalkAarpOptGleanInfo(
IN OUT PPORT_DESCRIPTOR pPortDesc,
IN PBYTE pLinkHdr,
IN PATALK_ADDR pSrcAddr,
IN PATALK_ADDR pDestAddr,
IN BOOLEAN OffCablePkt
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ATALK_NODEADDR srcNode, dstNode;
int index;
PBYTE pRouteInfo;
USHORT routeLen = 0, srcOffset;
if (PORT_CLOSING(pPortDesc))
{
// If we are not active, return!
return;
}
switch (pPortDesc->pd_NdisPortType)
{
case NdisMedium802_5:
if (pLinkHdr[TLAP_SRC_OFFSET] & TLAP_SRC_ROUTING_MASK)
{
routeLen = (pLinkHdr[TLAP_ROUTE_INFO_OFFSET] & TLAP_ROUTE_INFO_SIZE_MASK);
//
// First, glean any AARP information that we can, then handle the DDP
// packet. This guy also makes sure we have a good 802.2 header...
//
// Need to make a localcopy of the source address and then turn
// the source routing bit off before calling AarpGleanInfo
//
// (HdrBuf)[TLAP_SRC_OFFSET] = ((HdrBuf)[TLAP_SRC_OFFSET] & ~TLAP_SRC_ROUTING_MASK);
//
pLinkHdr[TLAP_SRC_OFFSET] &= ~TLAP_SRC_ROUTING_MASK;
pRouteInfo = pLinkHdr + TLAP_ROUTE_INFO_OFFSET;
}
srcOffset = TLAP_SRC_OFFSET;
break;
case NdisMedium802_3:
srcOffset = ELAP_SRC_OFFSET;
break;
case NdisMediumFddi:
srcOffset = FDDI_SRC_OFFSET;
break;
default:
KeBugCheck(0);
break;
}
// Get the destination network number
dstNode.atn_Network = pDestAddr->ata_Network;
dstNode.atn_Node = pDestAddr->ata_Node;
srcNode.atn_Network = pSrcAddr->ata_Network;
srcNode.atn_Node = pSrcAddr->ata_Node;
do
{
// Do a little verification.
if ((srcNode.atn_Node < MIN_USABLE_ATALKNODE) ||
(srcNode.atn_Node > MAX_USABLE_ATALKNODE) ||
(srcNode.atn_Network < FIRST_VALID_NETWORK) ||
(srcNode.atn_Network > LAST_VALID_NETWORK))
{
break;
}
if (dstNode.atn_Network > LAST_VALID_NETWORK)
{
break;
}
// Did the packet come from off this cable? Look at the hop count. If so,
// enter it into our best-router cache.
//
// **NOTE** We assume that the pRouteInfo buffer can be written to!
if (routeLen > 0)
atalkAarpTuneRouteInfo(pPortDesc, pRouteInfo);
if (OffCablePkt)
{
AtalkAarpUpdateBre(pPortDesc,
srcNode.atn_Network,
pLinkHdr + srcOffset,
ELAP_ADDR_LEN,
pRouteInfo,
routeLen);
}
else
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_INFO,
("AtalkAarpGleanInfo: Entering %x.%x info in Amt tables\n",
srcNode.atn_Network, srcNode.atn_Node));
// "Glean" AARP information from on-cable packets.
atalkAarpEnterIntoAmt(pPortDesc,
&srcNode,
pLinkHdr + srcOffset,
ELAP_ADDR_LEN,
pRouteInfo,
routeLen);
}
} while (FALSE);
}
ATALK_ERROR
AtalkInitAarpForNodeOnPort(
IN PPORT_DESCRIPTOR pPortDesc,
IN BOOLEAN AllowStartupRange,
IN ATALK_NODEADDR DesiredNode,
IN OUT PATALK_NODE * ppAtalkNode
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ATALK_ERROR error;
PDDP_ADDROBJ pDdpAddr;
ATALK_NODEADDR newNode;
PATALK_NODE pAtalkNode;
KIRQL OldIrql;
BOOLEAN foundNode = FALSE;
BOOLEAN inStartupRange = FALSE, result = TRUE;
if (!ATALK_SUCCESS(AtalkInitNodeAllocate(pPortDesc, &pAtalkNode)))
{
return ATALK_RESR_MEM;
}
// Try to find a new extended Node on the given port; first try for the
// requested address (if specified), else try in this port's cable range
// (if it's known) or in the default cable range (if any), then try the
// start-up range (if allowed).
do
{
if (DesiredNode.atn_Network != UNKNOWN_NETWORK)
{
if (((pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY) == 0) ||
(WITHIN_NETWORK_RANGE(DesiredNode.atn_Network, &pPortDesc->pd_NetworkRange)))
{
foundNode = atalkInitAarpForNode(pPortDesc,
NULL, // not a dial-in client
FALSE, // don't care
DesiredNode.atn_Network,
DesiredNode.atn_Node);
}
// leave if we found a node.
if (foundNode)
{
newNode = DesiredNode;
break;
}
}
if (pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY)
{
foundNode = AtalkInitAarpForNodeInRange(pPortDesc,
NULL, // not a dial-in client
FALSE, // don't care
pPortDesc->pd_NetworkRange,
&newNode);
// leave if we found a node.
if (foundNode)
{
break;
}
}
if (pPortDesc->pd_InitialNetworkRange.anr_FirstNetwork != UNKNOWN_NETWORK)
{
foundNode = AtalkInitAarpForNodeInRange(pPortDesc,
NULL, // not a dial-in client
FALSE, // don't care
pPortDesc->pd_InitialNetworkRange,
&newNode);
// leave if we found a node.
if (foundNode)
{
break;
}
}
// If no place else to try, try the start-up range. Do this even if
// we don't want to end up there.
//
// The idea is that this happens only when we are starting the router
// on one of our ports. So we do not want the router started in the
// startup range. If we do start in the startup range, and we see later
// that we did not see a router in the process,
// we will release the node. Of course, if we are a seed router, we will
// never be here, as the if statement above will be true.
//
inStartupRange = TRUE;
foundNode = AtalkInitAarpForNodeInRange(pPortDesc,
NULL, // not a dial-in client
FALSE, // don't care
AtalkStartupNetworkRange,
&newNode);
break;
} while (FALSE);
// If we have a tentative Node, go on.
if (foundNode)
{
do
{
// Use the allocated structure to set the info.
// Thread this into the port structure.
pAtalkNode->an_NodeAddr = newNode;
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
// Reference the port for this node.
AtalkPortRefByPtrNonInterlock(pPortDesc, &error);
if (!ATALK_SUCCESS(error))
{
result = FALSE;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
AtalkFreeMemory(pAtalkNode);
break;
}
// Now put it in the port descriptor
pAtalkNode->an_Next = pPortDesc->pd_Nodes;
pPortDesc->pd_Nodes = pAtalkNode;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
// See who's out there. We need to open the ZIP socket in order to be
// able to hear replies.
if (!ATALK_SUCCESS(AtalkDdpOpenAddress(pPortDesc,
ZONESINFORMATION_SOCKET,
&newNode,
AtalkZipPacketIn,
NULL,
DDPPROTO_ANY,
NULL,
&pDdpAddr)))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_OPENZIPSOCKET,
0,
NULL,
0);
AtalkNodeReleaseOnPort(pPortDesc, pAtalkNode);
result = FALSE;
break;
}
// mark the fact that this is an "internal" socket
pDdpAddr->ddpao_Flags |= DDPAO_SOCK_INTERNAL;
if (!(pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY))
{
// Get the default zone
AtalkZipGetNetworkInfoForNode(pPortDesc, &pAtalkNode->an_NodeAddr, TRUE);
// Validate the desired zone
AtalkZipGetNetworkInfoForNode(pPortDesc, &pAtalkNode->an_NodeAddr, FALSE);
}
// If nobody was out there and our tentative Node was in the
// startup range and our caller doesn't want to be there, return
// an error now.
//
// Note: this means that we were trying to start the router on
// a non-seeding port, and since there is not router on the net,
// it means the net is not seeded and so, we exit.
if (inStartupRange &&
!(pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY) &&
!AllowStartupRange)
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_STARTUPRANGENODE,
0,
NULL,
0);
AtalkNodeReleaseOnPort(pPortDesc, pAtalkNode);
result = FALSE;
break;
}
// If we have seen SeenRouterRecently is not true, that means we have
// used the InitialNetworkRange to AARP. If now SeenRouterRecently is
// true that means we have gotten the address in the InitialNetworkRange,
// but now there is a seeded range on the net that we must use. So redo
// the GetNode work.
if ((pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY) &&
!WITHIN_NETWORK_RANGE(newNode.atn_Network,
&pPortDesc->pd_NetworkRange))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_INITIALRANGENODE,
0,
NULL,
0);
// Release the node we obtained.
AtalkNodeReleaseOnPort(pPortDesc, pAtalkNode);
// Get another node and retry in the correct range.
ASSERTMSG("NetworkRange still set to startup!\n",
pPortDesc->pd_NetworkRange.anr_FirstNetwork != UNKNOWN_NETWORK);
foundNode = AtalkInitAarpForNodeInRange(pPortDesc,
NULL, // not a dial-in client
FALSE, // don't care
pPortDesc->pd_NetworkRange,
&newNode);
if (foundNode)
{
ASSERTMSG("New node is not within NetworkRange!\n",
WITHIN_NETWORK_RANGE(newNode.atn_Network,
&pPortDesc->pd_NetworkRange));
if (!ATALK_SUCCESS(AtalkInitNodeAllocate(pPortDesc, &pAtalkNode)))
{
result = FALSE;
break;
}
// Use the allocated structure to set the info.
// Thread this into the port structure.
pAtalkNode->an_NodeAddr = newNode;
// Reference the port for this node.
AtalkPortReferenceByPtr(pPortDesc, &error);
if (!ATALK_SUCCESS(error))
{
result = FALSE;
AtalkFreeMemory(pAtalkNode);
break;
}
// Now put it in the port descriptor
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
pAtalkNode->an_Next = pPortDesc->pd_Nodes;
pPortDesc->pd_Nodes = pAtalkNode;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
// Open the zip socket to be consistent
if (!ATALK_SUCCESS(AtalkDdpOpenAddress(pPortDesc,
ZONESINFORMATION_SOCKET,
&newNode,
AtalkZipPacketIn,
NULL,
DDPPROTO_ANY,
NULL,
&pDdpAddr)))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_OPENZIPSOCKET,
0,
NULL,
0);
AtalkNodeReleaseOnPort(pPortDesc, pAtalkNode);
result = FALSE;
break;
}
// mark the fact that this is an "internal" socket
pDdpAddr->ddpao_Flags |= DDPAO_SOCK_INTERNAL;
}
}
} while (FALSE);
}
else
{
// Free the allocated node structure. This has not yet been
// inserted into the port descriptor, so we can just free it.
AtalkFreeMemory(pAtalkNode);
}
if (foundNode && result)
{
// All set!
ASSERT(ppAtalkNode != NULL);
*ppAtalkNode = pAtalkNode;
// atalkAarpEnterIntoAmt() expects to be called at DISPATCH_LEVEL. Make it so.
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
atalkAarpEnterIntoAmt(pPortDesc,
&newNode,
pPortDesc->pd_PortAddr,
MAX_HW_ADDR_LEN,
NULL,
0);
KeLowerIrql(OldIrql);
}
return ((foundNode && result) ? ATALK_NO_ERROR : ATALK_FAILURE);
}
BOOLEAN
AtalkInitAarpForNodeInRange(
IN PPORT_DESCRIPTOR pPortDesc,
IN PVOID pRasConn,
IN BOOLEAN fThisIsPPP,
IN ATALK_NETWORKRANGE NetworkRange,
OUT PATALK_NODEADDR Node
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
BYTE currentNode;
USHORT currentNetwork;
int firstNode, lastNode;
long netTry;
int nodeTry;
USHORT nodeWidth, nodeChange, nodeIndex;
USHORT netWidth, netChange, netIndex;
BOOLEAN found = FALSE;
// Pick the node number range we'll try for (we do not pay attention to the
// "ServerNode" concept for LocalTalk). Our node is obtained by the
// localtalk driver anyways.
firstNode = MIN_USABLE_ATALKNODE;
lastNode = MAX_EXT_ATALKNODE;
// Okay, now some fun starts. Plow through our options trying to find an
// unused extended node number.
// Compute the width of our network range, and pick a random start point.
netWidth = (USHORT)((NetworkRange.anr_LastNetwork + 1) - NetworkRange.anr_FirstNetwork);
netTry = GET_RANDOM(NetworkRange.anr_FirstNetwork, NetworkRange.anr_LastNetwork);
// Come up with a random decrement, making sure it's odd (to avoid repeats)
// and large enough to appear pretty random.
netChange = (USHORT)(GET_RANDOM(1, netWidth) | 1);
while ((netWidth % netChange == 0) ||
(!AtalkIsPrime((long)netChange)))
{
netChange += 2;
}
// Now walk trough the range decrementing the starting network by the
// choosen change (with wrap, of course) until we find an address or
// we've processed every available network in the range.
for (netIndex = 0; netIndex < netWidth; netIndex ++)
{
currentNetwork = (USHORT) netTry;
// Compute the width of our node range, and pick a random start point.
nodeWidth = (USHORT)((lastNode + 1) - firstNode);
nodeTry = (int)GET_RANDOM(firstNode, lastNode);
// Come up with a random decrement, making sure it's odd (to avoid repeats)
// and large enough to appear pretty random.
nodeChange = (USHORT)(GET_RANDOM(1, nodeWidth) | 1);
while ((nodeWidth % nodeChange == 0) || !(AtalkIsPrime((long)nodeChange)))
nodeChange += 2;
// Now walk trough the range decrementing the starting network by the
// choosen change (with wrap, of course) until we find an address or
// we've processed every available node in the range.
for (nodeIndex = 0; nodeIndex < nodeWidth; nodeIndex ++)
{
currentNode = (BYTE )nodeTry;
// Let AARP have a crack at it.
if ((found = atalkInitAarpForNode(pPortDesc,
pRasConn,
fThisIsPPP,
currentNetwork,
currentNode)))
{
break;
}
// Okay, try again, bump down with wrap.
nodeTry -= nodeChange;
while (nodeTry < firstNode)
nodeTry += nodeWidth;
} // Node number loop
// If we found a node, break on thru to the other side.
if (found)
break;
// Okay, try again, bump down with wrap.
netTry -= netChange;
while (netTry < (long)NetworkRange.anr_FirstNetwork)
netTry += netWidth;
} // Network number loop
// Okay if we found one return all's well, otherwise no luck.
if (found)
{
if (Node != NULL)
{
Node->atn_Network = currentNetwork;
Node->atn_Node = currentNode;
}
}
return found;
} // AarpForNodeInRange
LOCAL BOOLEAN
atalkInitAarpForNode(
IN PPORT_DESCRIPTOR pPortDesc,
IN PVOID pRasConn,
IN BOOLEAN fThisIsPPP,
IN USHORT Network,
IN BYTE Node
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
SHORT probeAttempt;
PBUFFER_DESC pBuffDesc;
PVOID pTmpConn;
PATCPCONN pAtcpConn;
PARAPCONN pArapConn;
ATALK_NODEADDR tryNode;
KIRQL OldIrql;
PKEVENT pWaitEvent;
DWORD dwFlags;
BOOLEAN nodeInUse;
BOOLEAN fNoOneHasResponded=TRUE;
BOOLEAN fPPPConn;
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_INFO,
("atalkAarpForNode: AARPing for %x.%x on port %Z\n",
Network, Node, &pPortDesc->pd_AdapterKey));
// First make sure we don't own this node.
tryNode.atn_Network = Network;
tryNode.atn_Node = Node;
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
nodeInUse = AtalkNodeExistsOnPort(pPortDesc, &tryNode);
KeLowerIrql(OldIrql);
if (nodeInUse)
{
return(FALSE);
}
// is this node used by one of the dial in clients?
if ((pTmpConn = FindAndRefRasConnByAddr(tryNode, &dwFlags, &fPPPConn)) != NULL)
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_INFO,
("atalkAarpForNode: %x.%x already a dial in client (%lx)\n",
Network, Node, pTmpConn));
// our dial-in clients can only be in the network range of the
// default port
ASSERT(pPortDesc == AtalkDefaultPort);
if (fPPPConn)
{
ASSERT(((PATCPCONN)pTmpConn)->Signature == ATCPCONN_SIGNATURE);
DerefPPPConn((PATCPCONN)pTmpConn);
}
else
{
ASSERT(((PARAPCONN)pTmpConn)->Signature == ARAPCONN_SIGNATURE);
DerefArapConn((PARAPCONN)pTmpConn);
}
return(FALSE);
}
pAtcpConn = NULL;
pArapConn = NULL;
//
// if we are acquiring a node addr for a dial-in client...
//
if (pRasConn != NULL)
{
if (fThisIsPPP)
{
pAtcpConn = (PATCPCONN)pRasConn;
}
else
{
pArapConn = (PARAPCONN)pRasConn;
}
}
// PPP client?
if (pAtcpConn)
{
ACQUIRE_SPIN_LOCK(&pAtcpConn->SpinLock, &OldIrql);
pAtcpConn->NetAddr.atn_Network = Network;
pAtcpConn->NetAddr.atn_Node = Node;
pAtcpConn->Flags &= ~ATCP_NODE_IN_USE;
pWaitEvent = &pAtcpConn->NodeAcquireEvent;
RELEASE_SPIN_LOCK(&pAtcpConn->SpinLock, OldIrql);
}
// nope, ARAP client?
else if (pArapConn)
{
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
pArapConn->NetAddr.atn_Network = Network;
pArapConn->NetAddr.atn_Node = Node;
pArapConn->Flags &= ~ARAP_NODE_IN_USE;
pWaitEvent = &pArapConn->NodeAcquireEvent;
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
}
// no, this is node acquisition for one of the server nodes
else
{
// Use AARP to probe for a particular network/node address.
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
pPortDesc->pd_Flags &= ~PD_NODE_IN_USE;
pPortDesc->pd_TentativeNodeAddr.atn_Network = Network;
pPortDesc->pd_TentativeNodeAddr.atn_Node = Node;
pWaitEvent = &pPortDesc->pd_NodeAcquireEvent;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
}
fNoOneHasResponded = TRUE;
// Build the packet and blast it out the specified number of times.
for (probeAttempt = 0;
((probeAttempt < pPortDesc->pd_AarpProbes) && (fNoOneHasResponded));
probeAttempt ++)
{
pBuffDesc = BUILD_AARPPROBE(pPortDesc, MAX_HW_ADDR_LEN, tryNode);
if (pBuffDesc == NULL)
{
RES_LOG_ERROR();
break;
}
if (!ATALK_SUCCESS(AtalkNdisSendPacket(pPortDesc,
pBuffDesc,
AtalkAarpSendComplete,
NULL)))
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_ERR,
("atalkAarpForNode: AtalkNdisSendPacket failed while AARPing for %x.%x\n",
Network, Node));
// We allocated the packet.
AtalkAarpSendComplete(NDIS_STATUS_FAILURE, pBuffDesc, NULL);
break;
}
AtalkWaitTE(pWaitEvent, AARP_PROBE_TIMER_MS);
// node addr for a PPP client?
if (pAtcpConn)
{
ACQUIRE_SPIN_LOCK(&pAtcpConn->SpinLock, &OldIrql);
if (pAtcpConn->Flags & ATCP_NODE_IN_USE)
{
fNoOneHasResponded = FALSE;
}
RELEASE_SPIN_LOCK(&pAtcpConn->SpinLock, OldIrql);
}
// node addr for a ARAP client?
else if (pArapConn)
{
ACQUIRE_SPIN_LOCK(&pArapConn->SpinLock, &OldIrql);
if (pArapConn->Flags & ARAP_NODE_IN_USE)
{
fNoOneHasResponded = FALSE;
}
RELEASE_SPIN_LOCK(&pArapConn->SpinLock, OldIrql);
}
// nope, node addr for one of the server nodes
else
{
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
if (pPortDesc->pd_Flags & PD_NODE_IN_USE)
{
fNoOneHasResponded = FALSE;
}
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
}
} // Probe attempts loop
// We win if the current tentenative node has not been used
// (i.e. no one responds to our probes)
return (fNoOneHasResponded);
} // atalkAarpForNode
LOCAL VOID
atalkAarpEnterIntoAmt(
IN PPORT_DESCRIPTOR pPortDesc,
IN PATALK_NODEADDR pSrcNode,
IN PBYTE SrcAddr,
IN SHORT AddrLen,
IN PBYTE RouteInfo,
IN SHORT RouteInfoLen
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
int index;
PAMT pAmt, *ppAmt;
if ((pSrcNode->atn_Node < MIN_USABLE_ATALKNODE) ||
(pSrcNode->atn_Node > MAX_USABLE_ATALKNODE) ||
(pSrcNode->atn_Network < FIRST_VALID_NETWORK) ||
(pSrcNode->atn_Network > LAST_VALID_NETWORK))
{
UCHAR AtalkAndMacAddress[sizeof(ATALK_NODEADDR) + MAX_HW_ADDR_LEN];
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_ERR,
("atalkAarpEnterIntoAmt: Bad Node %x, %x\n",
pSrcNode->atn_Node, pSrcNode->atn_Network));
RtlCopyMemory(AtalkAndMacAddress, pSrcNode, sizeof(ATALK_NODEADDR));
RtlCopyMemory(AtalkAndMacAddress + sizeof(ATALK_NODEADDR), SrcAddr, MAX_HW_ADDR_LEN);
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_AMT_INVALIDSOURCE,
0,
AtalkAndMacAddress,
sizeof(AtalkAndMacAddress));
return;
}
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_INFO,
("AtalkAarpEnterIntoAmt: Entering %x.%x in amt\n",
pSrcNode->atn_Network, pSrcNode->atn_Node));
// Do we already know about this mapping?
index = HASH_ATALK_NODE(pSrcNode) & (PORT_AMT_HASH_SIZE - 1);
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
for (ppAmt = &pPortDesc->pd_Amt[index];
(pAmt = *ppAmt) != NULL;
ppAmt = &pAmt->amt_Next)
{
ASSERT(VALID_AMT(pAmt));
if (ATALK_NODES_EQUAL(pSrcNode, &pAmt->amt_Target))
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_INFO,
("atalkAarpEnterIntoAmt: Address %x.%x exists in tables\n",
pSrcNode->atn_Network, pSrcNode->atn_Node));
if ((pAmt->amt_RouteInfoLen == 0) ^ (RouteInfoLen == 0))
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_WARN,
("atalkAarpEnterIntoAmt: %x.%x has wrong routing info\n",
pSrcNode->atn_Network, pSrcNode->atn_Node));
*ppAmt = pAmt->amt_Next;
AtalkBPFreeBlock(pAmt);
pAmt = NULL;
}
break;
}
}
// If not, allocate a new mapping Node.
if (pAmt == NULL)
{
BLKID BlkId;
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_WARN,
("atalkAarpEnterIntoAmt: Address %x.%x DOES NOT exist in tables\n",
pSrcNode->atn_Network, pSrcNode->atn_Node));
ASSERT(RouteInfoLen <= TLAP_MAX_ROUTING_BYTES);
BlkId = BLKID_AMT;
if (RouteInfoLen != 0)
BlkId = BLKID_AMT_ROUTE;
if ((pAmt = (PAMT)AtalkBPAllocBlock(BlkId)) != NULL)
{
#if DBG
pAmt->amt_Signature = AMT_SIGNATURE;
#endif
// Link it in. Fill in below
pAmt->amt_Target.atn_Network = pSrcNode->atn_Network;
pAmt->amt_Target.atn_Node = pSrcNode->atn_Node;
pAmt->amt_Next = pPortDesc->pd_Amt[index];
pPortDesc->pd_Amt[index] = pAmt;
}
}
if (pAmt != NULL)
{
// Update mapping table! Do this if we knew about the mapping OR
// if we allocated a new node
ASSERTMSG("HWAddrLen is not right!\n", (AddrLen == MAX_HW_ADDR_LEN));
RtlCopyMemory(pAmt->amt_HardwareAddr, SrcAddr, AddrLen);
ASSERTMSG("RouteLen is not right!\n", (RouteInfoLen <= MAX_ROUTING_BYTES));
if (RouteInfoLen > 0)
RtlCopyMemory((PBYTE)pAmt + sizeof(AMT), RouteInfo, RouteInfoLen);
pAmt->amt_RouteInfoLen = (BYTE)RouteInfoLen;
pAmt->amt_Age = 0;
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
}
VOID
AtalkAarpReleaseAmt(
IN OUT PPORT_DESCRIPTOR pPortDesc
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
int index;
PAMT pAmt, *ppAmt;
// Free up all the AMT entries. No need to acquire spinlock at this
// point. We are unloading and all binding etc are gone.
for (index = 0; index < PORT_AMT_HASH_SIZE; index ++)
{
for (ppAmt = &pPortDesc->pd_Amt[index];
(pAmt = *ppAmt) != NULL;
NOTHING)
{
ASSERT(VALID_AMT(pAmt));
*ppAmt = pAmt->amt_Next;
AtalkBPFreeBlock(pAmt);
}
}
}
VOID
AtalkAarpReleaseBrc(
IN OUT PPORT_DESCRIPTOR pPortDesc
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
int index;
PBRE pBre, *ppBre;
// Free up all the BRC entries. No need to acquire spinlock at this
// point. We are unloading and all binding etc are gone.
for (index = 0; index < PORT_BRC_HASH_SIZE; index ++)
{
for (ppBre = &pPortDesc->pd_Brc[index];
(pBre = *ppBre) != NULL;
NOTHING)
{
*ppBre = pBre->bre_Next;
AtalkBPFreeBlock(pBre);
}
}
}
LONG FASTCALL
AtalkAarpAmtTimer(
IN PTIMERLIST pTimer,
IN BOOLEAN TimerShuttingDown
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PAMT pAmt, *ppAmt;
PPORT_DESCRIPTOR pPortDesc;
int index;
pPortDesc = (PPORT_DESCRIPTOR)CONTAINING_RECORD(pTimer, PORT_DESCRIPTOR, pd_AmtTimer);
ASSERT(VALID_PORT(pPortDesc));
ASSERT(EXT_NET(pPortDesc));
// Walk though all address mapping entries on this port aging the entries.
// We need to protect the mapping tables with critical sections, but don't
// stay in a critical section too long.
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (TimerShuttingDown ||
((pPortDesc->pd_Flags & PD_CLOSING) != 0))
{
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
// Remove the reference we added to this port at the time of
// starting the timer. Return;
AtalkPortDereferenceDpc(pPortDesc);
return ATALK_TIMER_NO_REQUEUE;
}
for (index = 0; index < PORT_AMT_HASH_SIZE; index ++)
{
for (ppAmt = &pPortDesc->pd_Amt[index];
(pAmt = *ppAmt) != NULL;
NOTHING)
{
ASSERT(VALID_AMT(pAmt));
if (pAmt->amt_Age < AMT_MAX_AGE)
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_INFO,
("atalkAarpAmtTimer: Entry for %x.%x %lx OK\n",
pAmt->amt_Target.atn_Network, pAmt->amt_Target.atn_Node,
pAmt));
pAmt->amt_Age ++;
ppAmt = &pAmt->amt_Next;
}
else
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_WARN,
("atalkAarpAmtTimer: Freeing node %x.%x from list\n",
pAmt->amt_Target.atn_Network,
pAmt->amt_Target.atn_Node));
*ppAmt = pAmt->amt_Next;
AtalkBPFreeBlock(pAmt);
}
}
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
return ATALK_TIMER_REQUEUE;
}
LONG FASTCALL
AtalkAarpBrcTimer(
IN PTIMERLIST pTimer,
IN BOOLEAN TimerShuttingDown
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
int index;
PPORT_DESCRIPTOR pPortDesc;
BOOLEAN DerefPort = FALSE;
pPortDesc = (PPORT_DESCRIPTOR)CONTAINING_RECORD(pTimer, PORT_DESCRIPTOR, pd_BrcTimer);
ASSERT(VALID_PORT(pPortDesc));
ASSERT(EXT_NET(pPortDesc));
// Walk though all best router entries on this port aging the entries.
// We need to protect the brc tables with critical sections, but don't
// stay in a critical section too long.
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (TimerShuttingDown ||
((pPortDesc->pd_Flags & PD_CLOSING) != 0))
{
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
// Remove the reference we added to this port at the time of
// starting the timer. Return;
AtalkPortDereferenceDpc(pPortDesc);
return ATALK_TIMER_NO_REQUEUE;
}
for (index = 0; index < PORT_BRC_HASH_SIZE; index ++)
{
PBRE pBre, *ppBre;
for (ppBre = &pPortDesc->pd_Brc[index];
(pBre = *ppBre) != NULL;
NOTHING)
{
if (pBre->bre_Age < BRC_MAX_AGE)
{
pBre->bre_Age ++;
ppBre = &pBre->bre_Next;
}
else
{
*ppBre = pBre->bre_Next;
AtalkBPFreeBlock(pBre);
}
}
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
return ATALK_TIMER_REQUEUE;
}
PBUFFER_DESC
AtalkAarpBuildPacket(
IN PPORT_DESCRIPTOR pPortDesc,
IN USHORT Type,
IN USHORT HardwareLen,
IN PBYTE SrcHardwareAddr,
IN ATALK_NODEADDR SrcLogicalAddr,
IN PBYTE DestHardwareAddr,
IN ATALK_NODEADDR DestLogicalAddr,
IN PBYTE TrueDest,
IN PBYTE RouteInfo,
IN USHORT RouteInfoLen
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PBYTE aarpData;
USHORT linkLen;
PBUFFER_DESC pBuffDesc = NULL;
BYTE protocolLength = AARP_PROTO_ADDR_LEN;
PVOID pRasConn;
DWORD dwFlags;
BOOLEAN fThisIsPPP;
// Read only.
static BYTE zeroAddr[MAX_HW_ADDR_LEN] =
{
0, 0, 0, 0, 0, 0
};
#if DBG
// make sure we aren't sending AARP request/probe for our own dial-in client
if ((Type == AARP_REQUEST) || (Type == AARP_PROBE))
{
pRasConn = FindAndRefRasConnByAddr(DestLogicalAddr, &dwFlags, &fThisIsPPP);
if (pRasConn)
{
if (fThisIsPPP)
{
ASSERT(((PATCPCONN)pRasConn)->Signature == ATCPCONN_SIGNATURE);
if (dwFlags & ATCP_NODE_IN_USE)
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_ERR,
("AtalkAarpBuildPacket: PPP client (%lx) owns %x.%x (Type=%x)\n",
pRasConn,DestLogicalAddr.atn_Network, DestLogicalAddr.atn_Node,Type));
}
DerefPPPConn((PATCPCONN)pRasConn);
}
else
{
ASSERT(((PARAPCONN)pRasConn)->Signature == ARAPCONN_SIGNATURE);
if (dwFlags & ARAP_NODE_IN_USE)
{
DBGPRINT(DBG_COMP_AARP, DBG_LEVEL_ERR,
("AtalkAarpBuildPacket: ARA client (%lx) owns %x.%x (Type=%x)\n",
pRasConn,DestLogicalAddr.atn_Network, DestLogicalAddr.atn_Node,Type));
}
DerefArapConn((PARAPCONN)pRasConn);
}
}
}
#endif
// If no destination hardware address is specified, set it
// to all zeros.
if (DestHardwareAddr == NULL)
{
DestHardwareAddr = zeroAddr;
}
// Get a header buffer allocated from link routines. Tell it we want
// maximum aarp data size as the required size.
AtalkNdisAllocBuf(&pBuffDesc);
if (pBuffDesc == NULL)
{
return(pBuffDesc);
}
// Build the LAP header.
AtalkNdisBuildHdr(pPortDesc,
pBuffDesc->bd_CharBuffer,
linkLen,
AARP_MIN_DATA_SIZE,
TrueDest,
RouteInfo,
RouteInfoLen,
AARP_PROTOCOL);
aarpData = pBuffDesc->bd_CharBuffer + linkLen;
// Build the specified type of AARP packet with the specified information;
PUTSHORT2SHORT((PUSHORT)aarpData,
pPortDesc->pd_AarpHardwareType);
aarpData += sizeof(USHORT);
PUTSHORT2SHORT((PUSHORT)aarpData,
pPortDesc->pd_AarpProtocolType);
aarpData += sizeof(USHORT);
*aarpData++ = (BYTE)HardwareLen;
*aarpData++ = (BYTE)AARP_PROTO_ADDR_LEN;
PUTSHORT2SHORT((PUSHORT)aarpData, Type);
aarpData += sizeof(USHORT);
// Source hardware address.
RtlCopyMemory(aarpData, SrcHardwareAddr, HardwareLen);
aarpData += HardwareLen;
// Source logical address pad
*aarpData++ = 0;
// Network number
PUTSHORT2SHORT(aarpData, SrcLogicalAddr.atn_Network);
aarpData += sizeof(USHORT);
// Node number
*aarpData++ = SrcLogicalAddr.atn_Node;
// Destination hardware address.
RtlCopyMemory(aarpData, DestHardwareAddr, HardwareLen);
aarpData += HardwareLen;
// Destination logical address, null pad
*aarpData++ = 0;
// Network number
PUTSHORT2SHORT(aarpData, DestLogicalAddr.atn_Network);
aarpData += sizeof(USHORT);
// Node number
*aarpData++ = DestLogicalAddr.atn_Node;
// Set length in the buffer descriptor. Pad it to max data size. Some devices seem
// to drop the aarp responses if they see less, Macs dictate their behavior.
// Also zero out the extra space.
AtalkSetSizeOfBuffDescData(pBuffDesc,
(SHORT)(aarpData - pBuffDesc->bd_CharBuffer + AARP_MAX_DATA_SIZE - AARP_MIN_DATA_SIZE));
RtlZeroMemory(aarpData, AARP_MAX_DATA_SIZE - AARP_MIN_DATA_SIZE);
return pBuffDesc;
}
LOCAL VOID FASTCALL
atalkAarpTuneRouteInfo(
IN PPORT_DESCRIPTOR pPortDesc,
IN OUT PBYTE RouteInfo
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
// Given an incoming TokenRing routing info, tune it to make it valid
// for outing routing info. Do this in place!
ASSERT(pPortDesc->pd_PortType == TLAP_PORT);
// Set to "non-broadcast" and invert "direction".
RouteInfo[0] &= TLAP_NON_BROADCAST_MASK;
RouteInfo[1] ^= TLAP_DIRECTION_MASK;
}
#if DBG
VOID
AtalkAmtDumpTable(
VOID
)
{
int j, k;
KIRQL OldIrql;
PPORT_DESCRIPTOR pPortDesc;
PAMT pAmt;
ACQUIRE_SPIN_LOCK(&AtalkPortLock, &OldIrql);
for (pPortDesc = AtalkPortList;
pPortDesc != NULL;
pPortDesc = pPortDesc = pPortDesc->pd_Next)
{
DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
("AMT Table for port %Z\n", &pPortDesc->pd_AdapterKey));
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
for (j = 0; j < PORT_AMT_HASH_SIZE; j++)
{
for (pAmt = pPortDesc->pd_Amt[j];
pAmt != NULL;
pAmt = pAmt->amt_Next)
{
DBGPRINTSKIPHDR(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
("\t%d: %lx.%lx", j,
pAmt->amt_Target.atn_Network, pAmt->amt_Target.atn_Node));
for (k = 0; k < MAX_HW_ADDR_LEN; k++)
{
DBGPRINTSKIPHDR(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
("%02x", pAmt->amt_HardwareAddr[k]));
}
if (pAmt->amt_RouteInfoLen != 0)
{
PBYTE pRouteInfo;
pRouteInfo = (PBYTE)pAmt + sizeof(AMT);
DBGPRINTSKIPHDR(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
(" ("));
for (k = 0; k < pAmt->amt_RouteInfoLen; k++)
{
DBGPRINTSKIPHDR(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
(" %02x", pRouteInfo[k]));
}
DBGPRINTSKIPHDR(DBG_COMP_DUMP, DBG_LEVEL_FATAL, (" )"));
}
DBGPRINTSKIPHDR(DBG_COMP_DUMP, DBG_LEVEL_FATAL, ("\n"));
}
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
DBGPRINTSKIPHDR(DBG_COMP_DUMP, DBG_LEVEL_FATAL, ("\n"));
}
RELEASE_SPIN_LOCK(&AtalkPortLock, OldIrql);
}
#endif