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

320 lines
7.6 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
router.c
Abstract:
This module contains
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 FILENUM ROUTER
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE_RTR, AtalkDdpRouteInPkt)
#endif
VOID
AtalkDdpRouteInPkt(
IN PPORT_DESCRIPTOR pPortDesc,
IN PATALK_ADDR pSrc,
IN PATALK_ADDR pDest,
IN BYTE ProtoType,
IN PBYTE pPkt,
IN USHORT PktLen,
IN USHORT HopCnt
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PDDP_ADDROBJ pDdpAddr;
PRTE pRte;
PPORT_DESCRIPTOR pDestPort;
PATALK_NODE pRouterNode;
ATALK_ADDR actualDest;
BUFFER_DESC BufDesc;
PBUFFER_DESC pBufCopy = NULL;
USHORT bufLen = 0;
BOOLEAN delivered = FALSE;
ATALK_ERROR error = ATALK_NO_ERROR;
SEND_COMPL_INFO SendInfo;
// AtalkDdpRouteOutBufDesc() will have already passed us a
// copy if bcast is going to be TRUE, and AtalkDdpRouteInPkt() will
// never call AtalkDdpRouter() for a bcast packet. They will also set
// the completion routines to be different if they passed us a copy.
// Those will free up the buffer descriptors.
//
// The completion routines are optional in the sense that the buffer
// will never be freed if they are not set!
// This algorithm is taken from the "Appletalk Phase 2 Specification".
// If the destination network number is within the range of the reception
// port's network range and the destination node number is broadcast, then
// we can drop the packet on the floor -- it is a network specific broadcast
// not for this router. Note that we've already delivered the packet, and
// thus not gotten here, if it was really addressed to the network of any
// node owned by the reception port (in AtalkDdpPacketIn).
// Also:
// Try to find an entry in the routing table that contains the target
// network. If not found, discard the packet.
if (((WITHIN_NETWORK_RANGE(pDest->ata_Network,
&pPortDesc->pd_NetworkRange)) &&
(pDest->ata_Node == ATALK_BROADCAST_NODE)) ||
((pRte = AtalkRtmpReferenceRte(pDest->ata_Network)) == NULL))
{
DBGPRINT(DBG_COMP_ROUTER, DBG_LEVEL_FATAL,
("AtalkDdpRouter: %lx RtmpRte/Not in ThisCableRange\n",
pDest->ata_Network));
return;
}
do
{
// Get the port descriptor for this port number.
pDestPort = pRte->rte_PortDesc;
ASSERT(VALID_PORT(pDestPort));
DBGPRINT(DBG_COMP_ROUTER, DBG_LEVEL_WARN,
("ROUTER: Routing pkt from port %Z.%lx to %Z.%lx\n",
&pPortDesc->pd_AdapterKey, pSrc->ata_Network,
&pDestPort->pd_AdapterKey, pDest->ata_Network));
SendInfo.sc_TransmitCompletion = atalkDdpRouteComplete;
SendInfo.sc_Ctx1 = pDestPort;
// SendInfo.sc_Ctx3 = NULL;
// If the target network's hop count is non-zero, we really need to send
// the beast, so, just do it!
if (pRte->rte_NumHops != 0)
{
// Too many hops?
error = ATALK_FAILURE;
if (HopCnt < RTMP_MAX_HOPS)
{
// We own the data. Call AtalkTransmitDdp() with the buffer descriptor.
// Make a copy! Caller will free our current packet.
// Alloc a buffer descriptor, copy data from packet to buffdesc.
if ((pBufCopy = AtalkAllocBuffDesc(NULL,
PktLen,
(BD_FREE_BUFFER | BD_CHAR_BUFFER))) == NULL)
{
error = ATALK_RESR_MEM;
break;
}
AtalkCopyBufferToBuffDesc(pPkt, PktLen, pBufCopy, 0);
SendInfo.sc_Ctx2 = pBufCopy;
error = AtalkDdpTransmit(pDestPort,
pSrc,
pDest,
ProtoType,
pBufCopy,
NULL,
0,
(USHORT)(HopCnt+1),
NULL, // pZoneMcastAddr,
&pRte->rte_NextRouter,
&SendInfo);
}
INTERLOCKED_INCREMENT_LONG_DPC(
&pDestPort->pd_PortStats.prtst_NumPktRoutedOut,
&AtalkStatsLock.SpinLock);
break;
}
// If the destination node is zero, the packet is really destined for the
// router's node on this port.
if (pDest->ata_Node == ANY_ROUTER_NODE)
{
// Grab the port lock and read the router node address.
// No need to reference, just ensure its not null.
ACQUIRE_SPIN_LOCK_DPC(&pDestPort->pd_Lock);
pRouterNode = pDestPort->pd_RouterNode;
if (pRouterNode != NULL)
{
actualDest.ata_Network = pRouterNode->an_NodeAddr.atn_Network;
actualDest.ata_Node = pRouterNode->an_NodeAddr.atn_Node;
// Set the actual destination socket.
actualDest.ata_Socket = pDest->ata_Socket;
}
else
{
ASSERTMSG("AtalkDdpRouter: pRouter node is null!\n", 0);
error = ATALK_DDP_NOTFOUND;
}
if (ATALK_SUCCESS(error))
{
AtalkDdpRefByAddrNode(pDestPort,
&actualDest,
pRouterNode,
&pDdpAddr,
&error);
}
RELEASE_SPIN_LOCK_DPC(&pDestPort->pd_Lock);
if (ATALK_SUCCESS(error))
{
AtalkDdpInvokeHandler(pDestPort,
pDdpAddr,
pSrc,
pDest, // Pass in the actual destination
ProtoType,
pPkt,
PktLen);
// Remove the reference on the socket
AtalkDdpDereferenceDpc(pDdpAddr);
}
else
{
ASSERTMSG("AtalkDdpRouter: pSocket on router node is null!\n", 0);
}
break;
}
// Okay, now walk through the nodes on the target port, looking for a
// home for this packet.
BufDesc.bd_Next = NULL;
BufDesc.bd_Flags = BD_CHAR_BUFFER;
BufDesc.bd_Length = PktLen;
BufDesc.bd_CharBuffer = pPkt;
AtalkDdpOutBufToNodesOnPort(pDestPort,
pSrc,
pDest,
ProtoType,
&BufDesc,
NULL,
0,
&delivered);
error = ATALK_NO_ERROR;
if (!delivered)
{
// We need to deliver this packet to a local ports network.
// delivered would have been set true *EVEN* if broadcast
// were set, so we need to ensure it was delivered to a specific
// socket by making sure broadcast was not true.
if (HopCnt < RTMP_MAX_HOPS)
{
// We own the data. Call AtalkTransmitDdp() with the buffer descriptor.
// Make a copy! Caller will free our current packet.
// Alloc a buffer descriptor, copy data from packet to buffdesc.
if ((pBufCopy = AtalkAllocBuffDesc(NULL,
PktLen,
(BD_FREE_BUFFER | BD_CHAR_BUFFER))) == NULL)
{
error = ATALK_RESR_MEM;
break;
}
AtalkCopyBufferToBuffDesc(pPkt, PktLen, pBufCopy, 0);
SendInfo.sc_Ctx2 = pBufCopy;
error = AtalkDdpTransmit(pDestPort,
pSrc,
pDest,
ProtoType,
pBufCopy,
NULL,
0,
(USHORT)(HopCnt+1),
NULL, // pZoneMcastAddr
NULL,
&SendInfo);
INTERLOCKED_INCREMENT_LONG_DPC(
&pDestPort->pd_PortStats.prtst_NumPktRoutedOut,
&AtalkStatsLock.SpinLock);
}
else error = ATALK_FAILURE;
break;
}
} while (FALSE);
if ((error != ATALK_PENDING) && (pBufCopy != NULL))
{
// Free the copied buffer descriptor if a copy was made.
AtalkFreeBuffDesc(pBufCopy);
}
AtalkRtmpDereferenceRte(pRte, FALSE); // Lock held?
INTERLOCKED_INCREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_NumPktRoutedIn,
&AtalkStatsLock.SpinLock);
}
VOID FASTCALL
atalkDdpRouteComplete(
IN NDIS_STATUS Status,
IN PSEND_COMPL_INFO pSendInfo
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PPORT_DESCRIPTOR pPortDesc = (PPORT_DESCRIPTOR)(pSendInfo->sc_Ctx1);
PBUFFER_DESC pBuffDesc = (PBUFFER_DESC)(pSendInfo->sc_Ctx2);
if (pBuffDesc != NULL)
{
ASSERT(pBuffDesc->bd_Flags & (BD_CHAR_BUFFER | BD_FREE_BUFFER));
AtalkFreeBuffDesc(pBuffDesc);
}
}