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

1893 lines
49 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
rtmp.c
Abstract:
This module implements the rtmp.
Author:
Jameel Hyder (jameelh@microsoft.com)
Nikhil Kamkolkar (nikhilk@microsoft.com)
Revision History:
26 Feb 1993 Initial Version
Notes: Tab stop: 4
--*/
#include <atalk.h>
#pragma hdrstop
#define FILENUM RTMP
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, AtalkRtmpInit)
#pragma alloc_text(PAGEINIT, AtalkInitRtmpStartProcessingOnPort)
#pragma alloc_text(PAGEINIT, atalkRtmpGetOrSetNetworkNumber)
#pragma alloc_text(PAGEINIT, AtalkRtmpKillPortRtes)
#pragma alloc_text(PAGE_RTR, AtalkRtmpPacketInRouter)
#pragma alloc_text(PAGE_RTR, AtalkRtmpReferenceRte)
#pragma alloc_text(PAGE_RTR, AtalkRtmpDereferenceRte)
#pragma alloc_text(PAGE_RTR, atalkRtmpCreateRte)
#pragma alloc_text(PAGE_RTR, atalkRtmpRemoveRte)
#pragma alloc_text(PAGE_RTR, atalkRtmpSendTimer)
#pragma alloc_text(PAGE_RTR, atalkRtmpValidityTimer)
#pragma alloc_text(PAGE_RTR, atalkRtmpSendRoutingData)
#endif
/*** AtalkRtmpInit
*
*/
ATALK_ERROR
AtalkRtmpInit(
IN BOOLEAN Init
)
{
if (Init)
{
// Allocate space for routing tables and recent routes
AtalkRoutingTable =
(PRTE *)AtalkAllocZeroedMemory(sizeof(PRTE) * NUM_RTMP_HASH_BUCKETS);
AtalkRecentRoutes =
(PRTE *)AtalkAllocZeroedMemory(sizeof(PRTE) * NUM_RECENT_ROUTES);
if ((AtalkRecentRoutes == NULL) || (AtalkRoutingTable == NULL))
{
if (AtalkRoutingTable != NULL)
{
AtalkFreeMemory(AtalkRoutingTable);
AtalkRoutingTable = NULL;
}
return ATALK_RESR_MEM;
}
INITIALIZE_SPIN_LOCK(&AtalkRteLock);
}
else
{
// At this point, we are unloading and there are no race conditions
// or lock contentions. Do not bother locking down the rtmp tables
if (AtalkRoutingTable != NULL)
{
int i;
PRTE pRte;
for (i = 0; i < NUM_RTMP_HASH_BUCKETS; i++)
{
ASSERT(AtalkRoutingTable[i] == NULL);
}
AtalkFreeMemory(AtalkRoutingTable);
AtalkRoutingTable = NULL;
}
if (AtalkRecentRoutes != NULL)
{
AtalkFreeMemory(AtalkRecentRoutes);
AtalkRecentRoutes = NULL;
}
}
return ATALK_NO_ERROR;
}
BOOLEAN
AtalkInitRtmpStartProcessingOnPort(
IN PPORT_DESCRIPTOR pPortDesc,
IN PATALK_NODEADDR pRouterNode
)
{
ATALK_ADDR closeAddr;
ATALK_ERROR Status;
PRTE pRte;
KIRQL OldIrql;
BOOLEAN rc = FALSE;
PDDP_ADDROBJ pRtDdpAddr=NULL;
ASSERT (KeGetCurrentIrql() == LOW_LEVEL);
// For extended networks, the process of acquiring the node has done most of the work
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
do
{
if (EXT_NET(pPortDesc))
{
if ((pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY) &&
(pPortDesc->pd_InitialNetworkRange.anr_FirstNetwork != UNKNOWN_NETWORK))
{
if (!NW_RANGE_EQUAL(&pPortDesc->pd_InitialNetworkRange,
&pPortDesc->pd_NetworkRange))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkRtmpStartProcessingOnPort: Initial range %d-%d, Actual %d-%d\n",
pPortDesc->pd_InitialNetworkRange.anr_FirstNetwork,
pPortDesc->pd_InitialNetworkRange.anr_LastNetwork,
pPortDesc->pd_NetworkRange.anr_FirstNetwork,
pPortDesc->pd_NetworkRange.anr_LastNetwork));
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_INVALID_NETRANGE,
0,
NULL,
0);
// Change InitialNetwork range so that it matches the net
pPortDesc->pd_InitialNetworkRange = pPortDesc->pd_NetworkRange;
}
}
// We are the seed router, so seed if possible
if (!(pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY) &&
!(pPortDesc->pd_Flags & PD_SEED_ROUTER))
{
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
break;
}
if (!(pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY))
{
pPortDesc->pd_NetworkRange = pPortDesc->pd_InitialNetworkRange;
}
}
// For non-extended network either seed or find our network number
else
{
PATALK_NODE pNode;
USHORT SuggestedNetwork;
int i;
SuggestedNetwork = UNKNOWN_NETWORK;
if (pPortDesc->pd_Flags & PD_SEED_ROUTER)
SuggestedNetwork = pPortDesc->pd_InitialNetworkRange.anr_FirstNetwork;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
if (!atalkRtmpGetOrSetNetworkNumber(pPortDesc, SuggestedNetwork))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkRtmpStartProcessingOnPort: atalkRtmpGetOrSetNetworkNumber failed\n"));
break;
}
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
if (!(pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY))
{
pPortDesc->pd_NetworkRange.anr_FirstNetwork =
pPortDesc->pd_NetworkRange.anr_LastNetwork =
pPortDesc->pd_InitialNetworkRange.anr_FirstNetwork;
}
// We'd have allocated a node with network 0, fix it up. Alas the fixup
// also involves all the sockets so far created on this node.
pNode = pPortDesc->pd_Nodes;
ASSERT((pNode != NULL) && (pPortDesc->pd_RouterNode == pNode));
pNode->an_NodeAddr.atn_Network =
pPortDesc->pd_LtNetwork =
pPortDesc->pd_ARouter.atn_Network =
pRouterNode->atn_Network = pPortDesc->pd_NetworkRange.anr_FirstNetwork;
ACQUIRE_SPIN_LOCK_DPC(&pNode->an_Lock);
for (i = 0; i < NODE_DDPAO_HASH_SIZE; i ++)
{
PDDP_ADDROBJ pDdpAddr;
for (pDdpAddr = pNode->an_DdpAoHash[i];
pDdpAddr != NULL;
pDdpAddr = pDdpAddr->ddpao_Next)
{
ACQUIRE_SPIN_LOCK_DPC(&pDdpAddr->ddpao_Lock);
pDdpAddr->ddpao_Addr.ata_Network =
pPortDesc->pd_NetworkRange.anr_FirstNetwork;
RELEASE_SPIN_LOCK_DPC(&pDdpAddr->ddpao_Lock);
}
}
RELEASE_SPIN_LOCK_DPC(&pNode->an_Lock);
}
// We're the router now. Mark it appropriately
pPortDesc->pd_Flags |= (PD_ROUTER_RUNNING | PD_SEEN_ROUTER_RECENTLY);
KeSetEvent(&pPortDesc->pd_SeenRouterEvent, IO_NETWORK_INCREMENT, FALSE);
pPortDesc->pd_ARouter = *pRouterNode;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
// Before creating a Rte for ourselves, check if there is an Rte with
// the same network range already. This will happen, for instance, when
// we are routing on ports which other routers are also seeding and we
// got to know of our port from the other router on another port !!!
do
{
pRte = AtalkRtmpReferenceRte(pPortDesc->pd_NetworkRange.anr_FirstNetwork);
if (pRte != NULL)
{
ACQUIRE_SPIN_LOCK(&pRte->rte_Lock, &OldIrql);
pRte->rte_RefCount --; // Take away creation reference
pRte->rte_Flags |= RTE_DELETE;
RELEASE_SPIN_LOCK(&pRte->rte_Lock, OldIrql);
AtalkRtmpDereferenceRte(pRte, FALSE);
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkRtmpStartProcessing: Invalid Rte for port %Z's range found\n",
&pPortDesc->pd_AdapterKey));
}
} while (pRte != NULL);
// Now we get to really, really create our own Rte !!!
if (!atalkRtmpCreateRte(pPortDesc->pd_NetworkRange,
pPortDesc,
pRouterNode,
0))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkRtmpStartProcessingOnPort: Could not create Rte\n"));
break;
}
// Switch the incoming rtmp handler to the router version
closeAddr.ata_Network = pRouterNode->atn_Network;
closeAddr.ata_Node = pRouterNode->atn_Node;
closeAddr.ata_Socket = RTMP_SOCKET;
ASSERT (KeGetCurrentIrql() == LOW_LEVEL);
AtalkDdpInitCloseAddress(pPortDesc, &closeAddr);
Status = AtalkDdpOpenAddress(pPortDesc,
RTMP_SOCKET,
pRouterNode,
AtalkRtmpPacketInRouter,
NULL,
DDPPROTO_ANY,
NULL,
&pRtDdpAddr);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkRtmpStartProcessingOnPort: AtalkDdpOpenAddress failed %ld\n",
Status));
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkRtmpStartProcessingOnPort: Unable to open the routers rtmp socket %ld\n",
Status));
break;
}
// mark the fact that this is an "internal" socket
pRtDdpAddr->ddpao_Flags |= DDPAO_SOCK_INTERNAL;
// Start the timers now. Reference the port for each timer.
AtalkPortReferenceByPtr(pPortDesc, &Status);
if (!ATALK_SUCCESS(Status))
{
break;
}
AtalkTimerInitialize(&pPortDesc->pd_RtmpSendTimer,
atalkRtmpSendTimer,
RTMP_SEND_TIMER);
AtalkTimerScheduleEvent(&pPortDesc->pd_RtmpSendTimer);
if (!atalkRtmpVdtTmrRunning)
{
AtalkTimerInitialize(&atalkRtmpVTimer,
atalkRtmpValidityTimer,
RTMP_VALIDITY_TIMER);
AtalkTimerScheduleEvent(&atalkRtmpVTimer);
atalkRtmpVdtTmrRunning = TRUE;
}
rc = TRUE;
} while (FALSE);
return rc;
}
// Private data structure used between AtalkRtmpPacketIn and atalkRtmpGetNwInfo
typedef struct _QueuedGetNwInfo
{
WORK_QUEUE_ITEM qgni_WorkQItem;
PPORT_DESCRIPTOR qgni_pPortDesc;
PDDP_ADDROBJ qgni_pDdpAddr;
ATALK_NODEADDR qgni_SenderNode;
ATALK_NETWORKRANGE qgni_CableRange;
BOOLEAN qgni_FreeThis;
} QGNI, *PQGNI;
VOID
atalkRtmpGetNwInfo(
IN PQGNI pQgni
)
{
PPORT_DESCRIPTOR pPortDesc = pQgni->qgni_pPortDesc;
PDDP_ADDROBJ pDdpAddr = pQgni->qgni_pDdpAddr;
KIRQL OldIrql;
ASSERT (KeGetCurrentIrql() == LOW_LEVEL);
AtalkZipGetNetworkInfoForNode(pPortDesc,
&pDdpAddr->ddpao_Node->an_NodeAddr,
FALSE);
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
if (!(pPortDesc->pd_Flags & PD_ROUTER_RUNNING))
{
// Well, we heard from a router. Copy the information. Don't do it
// if we're a router [maybe a proxy node on arouting port] -- we don't
// want "aRouter" to shift away from "us."
pPortDesc->pd_Flags |= PD_SEEN_ROUTER_RECENTLY;
KeSetEvent(&pPortDesc->pd_SeenRouterEvent, IO_NETWORK_INCREMENT, FALSE);
pPortDesc->pd_LastRouterTime = AtalkGetCurrentTick();
pPortDesc->pd_ARouter = pQgni->qgni_SenderNode;
pPortDesc->pd_NetworkRange = pQgni->qgni_CableRange;
}
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
if (pQgni->qgni_FreeThis)
{
AtalkDdpDereference(pDdpAddr);
AtalkFreeMemory(pQgni);
}
}
VOID
AtalkRtmpPacketIn(
IN PPORT_DESCRIPTOR pPortDesc,
IN PDDP_ADDROBJ pDdpAddr,
IN PBYTE pPkt,
IN USHORT PktLen,
IN PATALK_ADDR pSrcAddr,
IN PATALK_ADDR pDstAddr,
IN ATALK_ERROR ErrorCode,
IN BYTE DdpType,
IN PVOID pHandlerCtx,
IN BOOLEAN OptimizedPath,
IN PVOID OptimizeCtx
)
{
ATALK_NODEADDR SenderNode;
ATALK_NETWORKRANGE CableRange;
ATALK_ERROR Status;
TIME TimeS, TimeE, TimeD;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
TimeS = KeQueryPerformanceCounter(NULL);
do
{
if (ErrorCode == ATALK_SOCKET_CLOSED)
break;
else if (ErrorCode != ATALK_NO_ERROR)
{
break;
}
if (DdpType != DDPPROTO_RTMPRESPONSEORDATA)
break;
// we do not care about non-ext tuples on an extended network
if ((EXT_NET(pPortDesc)) && (PktLen < (RTMP_RANGE_END_OFF+2)))
{
break;
}
GETSHORT2SHORT(&SenderNode.atn_Network, pPkt+RTMP_SENDER_NW_OFF);
if (pPkt[RTMP_SENDER_IDLEN_OFF] != 8)
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
SenderNode.atn_Node = pPkt[RTMP_SENDER_ID_OFF];
if (EXT_NET(pPortDesc))
{
GETSHORT2SHORT(&CableRange.anr_FirstNetwork, pPkt+RTMP_RANGE_START_OFF);
GETSHORT2SHORT(&CableRange.anr_LastNetwork, pPkt+RTMP_RANGE_END_OFF);
if (!AtalkCheckNetworkRange(&CableRange))
break;
}
// On a non-extended network, we do not have to do any checking.
// Just copy the information into A-ROUTER and THIS-NET
if (!EXT_NET(pPortDesc))
{
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
pPortDesc->pd_Flags |= PD_SEEN_ROUTER_RECENTLY;
KeSetEvent(&pPortDesc->pd_SeenRouterEvent, IO_NETWORK_INCREMENT, FALSE);
pPortDesc->pd_LastRouterTime = AtalkGetCurrentTick();
pPortDesc->pd_ARouter = SenderNode;
if (pPortDesc->pd_NetworkRange.anr_FirstNetwork == UNKNOWN_NETWORK)
{
PATALK_NODE pNode;
LONG i;
pDdpAddr->ddpao_Node->an_NodeAddr.atn_Network =
pPortDesc->pd_NetworkRange.anr_FirstNetwork =
pPortDesc->pd_NetworkRange.anr_LastNetwork = SenderNode.atn_Network;
pNode = pPortDesc->pd_Nodes;
ASSERT (pNode != NULL);
// Fixup all sockets to have the correct network numbers.
ACQUIRE_SPIN_LOCK_DPC(&pNode->an_Lock);
for (i = 0; i < NODE_DDPAO_HASH_SIZE; i ++)
{
PDDP_ADDROBJ pDdpAddr;
for (pDdpAddr = pNode->an_DdpAoHash[i];
pDdpAddr != NULL;
pDdpAddr = pDdpAddr->ddpao_Next)
{
PREGD_NAME pRegdName;
PPEND_NAME pPendName;
ACQUIRE_SPIN_LOCK_DPC(&pDdpAddr->ddpao_Lock);
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("Setting socket %d to network %d\n",
pDdpAddr->ddpao_Addr.ata_Socket, SenderNode.atn_Network));
pDdpAddr->ddpao_Addr.ata_Network = SenderNode.atn_Network;
// Now all regd/pend name tuples as well
for (pRegdName = pDdpAddr->ddpao_RegNames;
pRegdName != NULL;
pRegdName = pRegdName->rdn_Next)
{
pRegdName->rdn_Tuple.tpl_Address.ata_Network = SenderNode.atn_Network;
}
for (pPendName = pDdpAddr->ddpao_PendNames;
pPendName != NULL;
pPendName = pPendName->pdn_Next)
{
ACQUIRE_SPIN_LOCK_DPC(&pPendName->pdn_Lock);
pPendName->pdn_pRegdName->rdn_Tuple.tpl_Address.ata_Network = SenderNode.atn_Network;
RELEASE_SPIN_LOCK_DPC(&pPendName->pdn_Lock);
}
RELEASE_SPIN_LOCK_DPC(&pDdpAddr->ddpao_Lock);
}
}
RELEASE_SPIN_LOCK_DPC(&pNode->an_Lock);
}
else if (pPortDesc->pd_NetworkRange.anr_FirstNetwork != SenderNode.atn_Network)
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
break;
}
// On extended networks, we may want to reject the information: If we
// already know about a router, the cable ranges must exacly match; If
// we don't know about a router, our node's network number must be
// within the cable range specified by the first tuple. The latter
// test will discard the information if our node is in the startup
// range (which is the right thing to do).
if (pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY)
{
if (!NW_RANGE_EQUAL(&CableRange, &pPortDesc->pd_NetworkRange))
break;
}
// Okay, we've seen a valid Rtmp data, this should allow us to find the
// zone name for the port. We do this outside of the
// "PD_SEEN_ROUTER_RECENTLY" case because the first time a router
// send out an Rtmp data it may not know everything yet, or
// AtalkZipGetNetworkInfoForNode() may really do a
// hard wait and we may need to try it a second time (due to not
// repsonding to Aarp LocateNode's the first time through... the
// second time our addresses should be cached by the remote router
// and he won't need to do a LocateNode again).
if (!(pPortDesc->pd_Flags & PD_VALID_DESIRED_ZONE))
{
if (!WITHIN_NETWORK_RANGE(pDdpAddr->ddpao_Addr.ata_Network,
&CableRange))
break;
// MAKE THIS ASYNCHRONOUS CONDITIONALLY BASED ON THE CURRENT IRQL
// A new router, see if it will tell us our zone name.
if (KeGetCurrentIrql() == LOW_LEVEL)
{
QGNI Qgni;
Qgni.qgni_pPortDesc = pPortDesc;
Qgni.qgni_pDdpAddr = pDdpAddr;
Qgni.qgni_SenderNode = SenderNode;
Qgni.qgni_CableRange = CableRange;
Qgni.qgni_FreeThis = FALSE;
atalkRtmpGetNwInfo(&Qgni);
}
else
{
PQGNI pQgni;
if ((pQgni = AtalkAllocMemory(sizeof(QGNI))) != NULL)
{
pQgni->qgni_pPortDesc = pPortDesc;
pQgni->qgni_pDdpAddr = pDdpAddr;
pQgni->qgni_SenderNode = SenderNode;
pQgni->qgni_CableRange = CableRange;
pQgni->qgni_FreeThis = TRUE;
AtalkDdpReferenceByPtr(pDdpAddr, &Status);
ASSERT (ATALK_SUCCESS(Status));
ExInitializeWorkItem(&pQgni->qgni_WorkQItem,
(PWORKER_THREAD_ROUTINE)atalkRtmpGetNwInfo,
pQgni);
ExQueueWorkItem(&pQgni->qgni_WorkQItem, CriticalWorkQueue);
}
}
break;
}
// Update the fact that we heard from a router
if ((pPortDesc->pd_Flags & PD_ROUTER_RUNNING) == 0)
{
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
pPortDesc->pd_Flags |= PD_SEEN_ROUTER_RECENTLY;
KeSetEvent(&pPortDesc->pd_SeenRouterEvent, IO_NETWORK_INCREMENT, FALSE);
pPortDesc->pd_LastRouterTime = AtalkGetCurrentTick();
pPortDesc->pd_ARouter = SenderNode;
pPortDesc->pd_NetworkRange = CableRange;
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
}
} while (FALSE);
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR_DPC(
&pPortDesc->pd_PortStats.prtst_RtmpPacketInProcessTime,
TimeD,
&AtalkStatsLock.SpinLock);
INTERLOCKED_INCREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_NumRtmpPacketsIn,
&AtalkStatsLock.SpinLock);
}
VOID
AtalkRtmpPacketInRouter(
IN PPORT_DESCRIPTOR pPortDesc,
IN PDDP_ADDROBJ pDdpAddr,
IN PBYTE pPkt,
IN USHORT PktLen,
IN PATALK_ADDR pSrcAddr,
IN PATALK_ADDR pDstAddr,
IN ATALK_ERROR ErrorCode,
IN BYTE DdpType,
IN PVOID pHandlerCtx,
IN BOOLEAN OptimizedPath,
IN PVOID OptimizeCtx
)
{
PBUFFER_DESC pBuffDesc = NULL;
ATALK_NETWORKRANGE CableRange;
ATALK_ERROR Status;
TIME TimeS, TimeE, TimeD;
PRTE pRte = NULL;
BYTE RtmpCmd, NumHops;
PBYTE Datagram;
int i, index;
USHORT RespSize;
BOOLEAN RteLocked;
SEND_COMPL_INFO SendInfo;
TimeS = KeQueryPerformanceCounter(NULL);
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
do
{
if (ErrorCode == ATALK_SOCKET_CLOSED)
break;
if (ErrorCode != ATALK_NO_ERROR)
{
break;
}
if (DdpType == DDPPROTO_RTMPREQUEST)
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("AtalkRtmpPacketInRouter: RtmpRequest\n"));
if (PktLen < RTMP_REQ_DATAGRAM_SIZE)
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
RtmpCmd = pPkt[RTMP_REQ_CMD_OFF];
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("AtalkRtmpPacketInRouter: RtmpRequest %d\n", RtmpCmd));
if ((RtmpCmd == RTMP_DATA_REQUEST) ||
(RtmpCmd == RTMP_ENTIRE_DATA_REQUEST))
{
atalkRtmpSendRoutingData(pPortDesc, pSrcAddr,
(BOOLEAN)(RtmpCmd == RTMP_DATA_REQUEST));
break;
}
else if (RtmpCmd != RTMP_REQUEST)
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("atalkRtmpPacketInRouter: RtmpCmd %d\n", RtmpCmd));
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
// This is a standard Rtmp Request. Do the needfull
// Send an Rtmp response to this guy. Start off by allocating
// a buffer descriptor
pBuffDesc = AtalkAllocBuffDesc(NULL,
RTMP_RESPONSE_MAX_SIZE,
BD_CHAR_BUFFER | BD_FREE_BUFFER);
if (pBuffDesc == NULL)
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkRtmpPacketInRouter: AtalkAllocBuffDesc failed\n"));
break;
}
Datagram = pBuffDesc->bd_CharBuffer;
PUTSHORT2SHORT(Datagram + RTMP_SENDER_NW_OFF,
pPortDesc->pd_ARouter.atn_Network);
Datagram[RTMP_SENDER_IDLEN_OFF] = 8;
Datagram[RTMP_SENDER_ID_OFF] = pPortDesc->pd_ARouter.atn_Node;
// On extended port, we also want to add the initial network
// range tuple
RespSize = RTMP_SENDER_ID_OFF + sizeof(BYTE);
if (EXT_NET(pPortDesc))
{
PUTSHORT2SHORT(Datagram+RTMP_RANGE_START_OFF,
pPortDesc->pd_NetworkRange.anr_FirstNetwork);
PUTSHORT2SHORT(Datagram+RTMP_RANGE_END_OFF,
pPortDesc->pd_NetworkRange.anr_LastNetwork);
Datagram[RTMP_TUPLE_TYPE_OFF] = RTMP_TUPLE_WITHRANGE;
RespSize = RTMP_RANGE_END_OFF + sizeof(USHORT);
}
// Set the length in the buffer descriptor.
AtalkSetSizeOfBuffDescData(pBuffDesc, RespSize);
// Send the response
ASSERT(pBuffDesc->bd_Length > 0);
SendInfo.sc_TransmitCompletion = atalkRtmpSendComplete;
SendInfo.sc_Ctx1 = pBuffDesc;
// SendInfo.sc_Ctx2 = NULL;
// SendInfo.sc_Ctx3 = NULL;
if (!ATALK_SUCCESS(Status = AtalkDdpSend(pDdpAddr,
pSrcAddr,
(BYTE)DDPPROTO_RTMPRESPONSEORDATA,
FALSE,
pBuffDesc,
NULL,
0,
NULL,
&SendInfo)))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkRtmpPacketInRouter: DdpSend failed %ld\n", ErrorCode));
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkRtmpPacketInRouter: AtalkDdpSend Failed %ld\n", Status));
AtalkFreeBuffDesc(pBuffDesc);
}
pBuffDesc = NULL;
break;
}
else if (DdpType != DDPPROTO_RTMPRESPONSEORDATA)
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("AtalkRtmpPacketInRouter: Not ours !!!\n"));
break;
}
ASSERT (DdpType == DDPPROTO_RTMPRESPONSEORDATA);
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("AtalkRtmpPacketInRouter: RtmpResponse\n"));
if ((PktLen < (RTMP_SENDER_IDLEN_OFF + 1)) ||
(pPkt[RTMP_SENDER_IDLEN_OFF] != 8))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkRtmpPacketInRouter: %sExt net, PktLen %d, SenderId %d\n",
EXT_NET(pPortDesc) ? "" : "Non", PktLen, pPkt[RTMP_SENDER_IDLEN_OFF]));
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
// For non-extended networks, we should have a leading version stamp
if (EXT_NET(pPortDesc))
{
// Source could be bad (coming in from a half port) so in this
// case use the source from the rtmp packet
if (pSrcAddr->ata_Network == UNKNOWN_NETWORK)
{
if (PktLen < RTMP_SENDER_ID_OFF + 1)
{
ASSERT(0);
break;
}
GETSHORT2SHORT(&pSrcAddr->ata_Network, pPkt+RTMP_SENDER_NW_OFF);
pSrcAddr->ata_Node = pPkt[RTMP_SENDER_ID_OFF];
}
index = RTMP_SENDER_ID_OFF + 1;
}
else
{
USHORT SenderId;
if (PktLen < RTMP_TUPLE_TYPE_OFF+1)
{
ASSERT(0);
break;
}
GETSHORT2SHORT(&SenderId, pPkt + RTMP_SENDER_ID_OFF + 1);
if ((SenderId != 0) ||
(pPkt[RTMP_TUPLE_TYPE_OFF] != RTMP_VERSION))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
index = RTMP_SENDER_ID_OFF + 4;
}
// Walk though the routing tuples. Ensure we atleast have a
// non-extended tuple
RteLocked = FALSE;
while ((index + sizeof(USHORT) + sizeof(BYTE)) <= PktLen)
{
BOOLEAN FoundOverlap;
// Dereference the previous RTE, if any
if (pRte != NULL)
{
if (RteLocked)
{
RELEASE_SPIN_LOCK_DPC(&pRte->rte_Lock);
RteLocked = FALSE;
}
AtalkRtmpDereferenceRte(pRte, FALSE);
pRte = NULL;
}
GETSHORT2SHORT(&CableRange.anr_FirstNetwork, pPkt+index);
index += sizeof(USHORT);
NumHops = pPkt[index++];
CableRange.anr_LastNetwork = CableRange.anr_FirstNetwork;
if (NumHops & RTMP_EXT_TUPLE_MASK)
{
if ((index + sizeof(USHORT) + sizeof(BYTE)) > PktLen)
{
ASSERT(0);
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
GETSHORT2SHORT(&CableRange.anr_LastNetwork, pPkt+index);
index += sizeof(USHORT);
if (pPkt[index++] != RTMP_VERSION)
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
}
NumHops &= RTMP_NUM_HOPS_MASK;
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("AtalkRtmpPacketInRouter: Response - Port %Z, Hops %d, CableRange %d,%d\n",
&pPortDesc->pd_AdapterKey, NumHops,
CableRange.anr_FirstNetwork, CableRange.anr_LastNetwork));
if (!AtalkCheckNetworkRange(&CableRange))
continue;
// Check if this tuple concerns a network range that we
// already know about
pRte = AtalkRtmpReferenceRte(CableRange.anr_FirstNetwork);
if ((pRte != NULL) &&
NW_RANGE_EQUAL(&pRte->rte_NwRange, &CableRange))
{
ACQUIRE_SPIN_LOCK_DPC(&pRte->rte_Lock);
RteLocked = TRUE;
// Check for "notify neighbor" telling us that an entry is bad
if ((NumHops == RTMP_NUM_HOPS_MASK) &&
(pRte->rte_NextRouter.atn_Network == pSrcAddr->ata_Network) &&
(pRte->rte_NextRouter.atn_Node == pSrcAddr->ata_Node))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("AtalkRtmpPacketInRouter: Notify Neighbor State %d\n",
pRte->rte_State));
if (pRte->rte_State != UGLY)
pRte->rte_State = BAD;
continue;
}
// If we are hearing about one of our directly connected
// nets, we know best. Ignore the information.
if (pRte->rte_NumHops == 0)
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("AtalkRtmpPacketInRouter: Ignoring - hop count 0\n",
pRte->rte_State));
continue;
}
// Check for previously bad entry, and a short enough
// path with this tuple. Also if it shorter or equi-
// distant path to target network. If so, replace the entry
if ((NumHops < RTMP_MAX_HOPS) &&
((pRte->rte_NumHops >= (NumHops + 1)) ||
(pRte->rte_State >= BAD)))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_WARN,
("AtalkRtmpPacketInRouter: Updating Rte from:\n\tRange %d,%d Hops %d Port %Z NextRouter %d.%d\n",
pRte->rte_NwRange.anr_FirstNetwork,
pRte->rte_NwRange.anr_LastNetwork,
pRte->rte_NumHops,
&pRte->rte_PortDesc->pd_AdapterKey,
pRte->rte_NextRouter.atn_Node,
pRte->rte_NextRouter.atn_Network));
pRte->rte_NumHops = NumHops + 1;
pRte->rte_NextRouter.atn_Network = pSrcAddr->ata_Network;
pRte->rte_NextRouter.atn_Node = pSrcAddr->ata_Node;
if (pRte->rte_PortDesc != pPortDesc)
{
ATALK_ERROR Error;
AtalkPortDereference(pRte->rte_PortDesc);
AtalkPortReferenceByPtrDpc(pPortDesc, &Error);
ASSERT (ATALK_SUCCESS(Error));
pRte->rte_PortDesc = pPortDesc;
}
pRte->rte_State = GOOD;
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_WARN,
("to:\tRange %d,%d Hops %d NextRouter %d.%d\n",
pRte->rte_NwRange.anr_FirstNetwork,
pRte->rte_NwRange.anr_LastNetwork,
pRte->rte_NumHops,
pRte->rte_NextRouter.atn_Node,
pRte->rte_NextRouter.atn_Network));
continue;
}
// Check for the same router still thinking it has a path
// to the network, but it is further away now. If so
// update the entry
if ((pRte->rte_PortDesc == pPortDesc) &&
(pRte->rte_NextRouter.atn_Network == pSrcAddr->ata_Network) &&
(pRte->rte_NextRouter.atn_Node == pSrcAddr->ata_Node))
{
pRte->rte_NumHops = NumHops + 1;
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("AtalkRtmpPacketInRouter: NumHops for Rte %lx changed to %d\n",
pRte, pRte->rte_NumHops));
if (pRte->rte_NumHops < 16)
pRte->rte_State = GOOD;
else
{
// atalkRtmpRemoveRte(pRte);
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("AtalkRtmpPacketInRouter: Removing Rte\n"));
pRte->rte_Flags |= RTE_DELETE;
pRte->rte_RefCount --;
}
}
continue;
}
// Dereference any previous RTEs
if (pRte != NULL)
{
if (RteLocked)
{
RELEASE_SPIN_LOCK_DPC(&pRte->rte_Lock);
RteLocked = FALSE;
}
AtalkRtmpDereferenceRte(pRte, FALSE);
pRte = NULL;
}
// Walk thru the entire routing table making sure the current
// tuple does not overlap with anything we already have (since
// it did not match. If we find an overlap, ignore the tuple
// (a network configuration error, no doubt), else add it as
// a new network range !!
ACQUIRE_SPIN_LOCK_DPC(&AtalkRteLock);
FoundOverlap = FALSE;
for (i = 0; !FoundOverlap && (i < NUM_RTMP_HASH_BUCKETS); i++)
{
for (pRte = AtalkRoutingTable[i];
pRte != NULL; pRte = pRte->rte_Next)
{
if (AtalkRangesOverlap(&pRte->rte_NwRange, &CableRange))
{
FoundOverlap = TRUE;
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_WARN,
("AtalkRtmpPacketInRouter: Overlapped ranges %d,%d & %d,%d\n",
pRte->rte_NwRange.anr_FirstNetwork,
pRte->rte_NwRange.anr_LastNetwork,
CableRange.anr_FirstNetwork,
CableRange.anr_LastNetwork));
break;
}
}
}
RELEASE_SPIN_LOCK_DPC(&AtalkRteLock);
pRte = NULL; // We do not want to Dereference this !!!
if (FoundOverlap)
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkRtmpPacketInRouter: Found overlapped ranges\n"));
continue;
}
// Enter this new network range
if (NumHops < RTMP_MAX_HOPS)
{
ATALK_NODEADDR NextRouter;
NextRouter.atn_Network = pSrcAddr->ata_Network;
NextRouter.atn_Node = pSrcAddr->ata_Node;
atalkRtmpCreateRte(CableRange,
pPortDesc,
&NextRouter,
NumHops + 1);
}
}
} while (FALSE);
if (pRte != NULL)
{
if (RteLocked)
{
RELEASE_SPIN_LOCK_DPC(&pRte->rte_Lock);
// RteLocked = FALSE;
}
AtalkRtmpDereferenceRte(pRte, FALSE);
// pRte = NULL;
}
if (pBuffDesc != NULL)
AtalkFreeBuffDesc(pBuffDesc);
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR_DPC(
&pPortDesc->pd_PortStats.prtst_RtmpPacketInProcessTime,
TimeD,
&AtalkStatsLock.SpinLock);
INTERLOCKED_INCREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_NumRtmpPacketsIn,
&AtalkStatsLock.SpinLock);
}
/*** AtalkReferenceRte
*
*/
PRTE
AtalkRtmpReferenceRte(
IN USHORT Network
)
{
int i, index, rindex;
PRTE pRte;
KIRQL OldIrql;
index = (int)((Network >> 4) % NUM_RTMP_HASH_BUCKETS);
rindex = (int)((Network >> 6) % NUM_RECENT_ROUTES);
// First try the recent route cache
ACQUIRE_SPIN_LOCK(&AtalkRteLock, &OldIrql);
if (((pRte = AtalkRecentRoutes[rindex]) == NULL) ||
!IN_NETWORK_RANGE(Network, pRte))
{
// We did not find it in the recent routes cache,
// check in the real table
for (pRte = AtalkRoutingTable[index];
pRte != NULL;
pRte = pRte->rte_Next)
{
if (IN_NETWORK_RANGE(Network, pRte))
break;
}
// If we did not find here. Check all routing tables.
// If we do, cache the info
if (pRte == NULL)
{
for (i = 0; i < NUM_RTMP_HASH_BUCKETS; i++)
{
for (pRte = AtalkRoutingTable[i];
pRte != NULL;
pRte = pRte->rte_Next)
{
if (IN_NETWORK_RANGE(Network, pRte))
{
AtalkRecentRoutes[rindex] = pRte;
break;
}
}
// if we found an entry, search no further.
if (pRte != NULL)
break;
}
}
}
if (pRte != NULL)
{
ASSERT(VALID_RTE(pRte));
ACQUIRE_SPIN_LOCK_DPC(&pRte->rte_Lock);
pRte->rte_RefCount ++;
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("AtalkRtmpReferenceRte: Rte %lx, PostCount %d\n",
pRte, pRte->rte_RefCount));
RELEASE_SPIN_LOCK_DPC(&pRte->rte_Lock);
}
RELEASE_SPIN_LOCK(&AtalkRteLock, OldIrql);
return (pRte);
}
/*** AtalkRtmpDereferenceRte
*
*/
VOID
AtalkRtmpDereferenceRte(
IN PRTE pRte,
IN BOOLEAN LockHeld
)
{
PRTE * ppRte;
int Index;
BOOLEAN KillCache = FALSE, Kill = FALSE;
KIRQL OldIrql;
PPORT_DESCRIPTOR pPortDesc;
ASSERT(VALID_RTE(pRte));
ASSERT(pRte->rte_RefCount > 0);
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("AtalkRtmpDereferenceRte: Rte %lx, PreCount %d\n",
pRte, pRte->rte_RefCount));
ACQUIRE_SPIN_LOCK(&pRte->rte_Lock, &OldIrql);
pRte->rte_RefCount --;
KillCache = (pRte->rte_Flags & RTE_DELETE) ? TRUE : FALSE;
if (pRte->rte_RefCount == 0)
{
ASSERT (pRte->rte_Flags & RTE_DELETE);
KillCache = Kill = TRUE;
}
RELEASE_SPIN_LOCK(&pRte->rte_Lock, OldIrql);
if (KillCache)
{
pPortDesc = pRte->rte_PortDesc;
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_WARN,
("atalkRtmpDereferenceRte: Removing from cache for port %Z, Range %d, %d\n",
&pRte->rte_PortDesc->pd_AdapterKey,
pRte->rte_NwRange.anr_FirstNetwork,
pRte->rte_NwRange.anr_LastNetwork));
if (!LockHeld)
ACQUIRE_SPIN_LOCK(&AtalkRteLock, &OldIrql);
// Walk through the recent routes cache and kill All found
for (Index = 0; Index < NUM_RECENT_ROUTES; Index ++)
{
if (AtalkRecentRoutes[Index] == pRte)
{
AtalkRecentRoutes[Index] = NULL;
}
}
if (Kill)
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_WARN,
("atalkRtmpDereferenceRte: Removing for port %Z, Range %d, %d\n",
&pRte->rte_PortDesc->pd_AdapterKey,
pRte->rte_NwRange.anr_FirstNetwork,
pRte->rte_NwRange.anr_LastNetwork));
Index = (pRte->rte_NwRange.anr_FirstNetwork >> 4) % NUM_RTMP_HASH_BUCKETS;
for (ppRte = &AtalkRoutingTable[Index];
*ppRte != NULL;
ppRte = &(*ppRte)->rte_Next)
{
if (pRte == *ppRte)
{
*ppRte = pRte->rte_Next;
AtalkZoneFreeList(pRte->rte_ZoneList);
AtalkFreeMemory(pRte);
break;
}
}
AtalkPortDereference(pPortDesc);
}
if (!LockHeld)
RELEASE_SPIN_LOCK(&AtalkRteLock, OldIrql);
}
}
/*** atalkCreateRte
*
*/
BOOLEAN
atalkRtmpCreateRte(
IN ATALK_NETWORKRANGE NwRange,
IN PPORT_DESCRIPTOR pPortDesc,
IN PATALK_NODEADDR pNextRouter,
IN int NumHops
)
{
ATALK_ERROR Error;
PRTE pRte;
int index, rindex;
KIRQL OldIrql;
BOOLEAN Success = FALSE;
index = (int)((NwRange.anr_FirstNetwork >> 4) % NUM_RTMP_HASH_BUCKETS);
rindex = (int)((NwRange.anr_FirstNetwork >> 6) % NUM_RECENT_ROUTES);
// First reference the port
AtalkPortReferenceByPtr(pPortDesc, &Error);
if (ATALK_SUCCESS(Error))
{
if ((pRte = AtalkAllocMemory(sizeof(RTE))) != NULL)
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("atalkRtmpCreateRte: Creating for port %Z, Range %d,%d Hops %d index %d\n",
&pPortDesc->pd_AdapterKey,
NwRange.anr_FirstNetwork,
NwRange.anr_LastNetwork,
NumHops,
index));
#if DBG
pRte->rte_Signature = RTE_SIGNATURE;
#endif
INITIALIZE_SPIN_LOCK(&pRte->rte_Lock);
pRte->rte_RefCount = 1; // Creation Reference
pRte->rte_State = GOOD;
pRte->rte_Flags = 0;
pRte->rte_NwRange = NwRange;
pRte->rte_NumHops = (BYTE)NumHops;
pRte->rte_PortDesc = pPortDesc;
pRte->rte_NextRouter = *pNextRouter;
pRte->rte_ZoneList = NULL;
// Link this in the global table
ACQUIRE_SPIN_LOCK(&AtalkRteLock, &OldIrql);
pRte->rte_Next = AtalkRoutingTable[index];
AtalkRoutingTable[index] = pRte;
AtalkRecentRoutes[rindex] = pRte;
RELEASE_SPIN_LOCK(&AtalkRteLock, OldIrql);
Success = TRUE;
}
else
{
AtalkPortDereference(pPortDesc);
}
}
return Success;
}
/*** atalkRtmpRemoveRte
*
*/
BOOLEAN
atalkRtmpRemoveRte(
IN USHORT Network
)
{
PRTE pRte;
KIRQL OldIrql;
if ((pRte = AtalkRtmpReferenceRte(Network)) != NULL)
{
ACQUIRE_SPIN_LOCK(&pRte->rte_Lock, &OldIrql);
pRte->rte_RefCount --; // Take away creation reference
pRte->rte_Flags |= RTE_DELETE;
RELEASE_SPIN_LOCK(&pRte->rte_Lock, OldIrql);
AtalkRtmpDereferenceRte(pRte, FALSE);
}
return (pRte != NULL);
}
/*** AtalkRtmpKillPortRtes
*
*/
VOID FASTCALL
AtalkRtmpKillPortRtes(
IN PPORT_DESCRIPTOR pPortDesc
)
{
// At this point, we are unloading and there are no race conditions
// or lock contentions. Do not bother locking down the rtmp tables
if (AtalkRoutingTable != NULL)
{
int i;
PRTE pRte, pTmp;
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&AtalkRteLock, &OldIrql);
for (i = 0; i < NUM_RTMP_HASH_BUCKETS; i++)
{
for (pRte = AtalkRoutingTable[i];
pRte != NULL;
pRte = pTmp)
{
pTmp = pRte->rte_Next;
if (pRte->rte_PortDesc == pPortDesc)
{
ACQUIRE_SPIN_LOCK_DPC(&pRte->rte_Lock);
pRte->rte_Flags |= RTE_DELETE;
RELEASE_SPIN_LOCK_DPC(&pRte->rte_Lock);
AtalkRtmpDereferenceRte(pRte, TRUE);
}
}
}
RELEASE_SPIN_LOCK(&AtalkRteLock, OldIrql);
}
}
/*** AtalkRtmpAgingTimer
*
*/
LONG FASTCALL
AtalkRtmpAgingTimer(
IN PTIMERLIST pContext,
IN BOOLEAN TimerShuttingDown
)
{
PPORT_DESCRIPTOR pPortDesc;
ATALK_ERROR error;
LONG Now;
pPortDesc = CONTAINING_RECORD(pContext, PORT_DESCRIPTOR, pd_RtmpAgingTimer);
if (TimerShuttingDown ||
(pPortDesc->pd_Flags & PD_CLOSING))
{
AtalkPortDereferenceDpc(pPortDesc);
return ATALK_TIMER_NO_REQUEUE;
}
Now = AtalkGetCurrentTick();
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (((pPortDesc->pd_Flags &
(PD_ACTIVE | PD_ROUTER_RUNNING | PD_SEEN_ROUTER_RECENTLY)) ==
(PD_ACTIVE | PD_SEEN_ROUTER_RECENTLY)) &&
((pPortDesc->pd_LastRouterTime + RTMP_AGING_TIMER) < Now))
{
// Age out A-ROUTER. On extended networks age out THIS-CABLE-RANGE
// and THIS-ZONE too
KeClearEvent(&pPortDesc->pd_SeenRouterEvent);
pPortDesc->pd_Flags &= ~PD_SEEN_ROUTER_RECENTLY;
if (EXT_NET(pPortDesc))
{
pPortDesc->pd_Flags &= ~PD_VALID_DESIRED_ZONE;
pPortDesc->pd_NetworkRange.anr_FirstNetwork = FIRST_VALID_NETWORK;
pPortDesc->pd_NetworkRange.anr_LastNetwork = LAST_STARTUP_NETWORK;
// If we have a zone multicast address that is not broadcast, age it out
if (!AtalkFixedCompareCaseSensitive(pPortDesc->pd_ZoneMulticastAddr,
MAX_HW_ADDR_LEN,
pPortDesc->pd_BroadcastAddr,
MAX_HW_ADDR_LEN))
{
// Release lock before calling in to remove multicast address
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
(*pPortDesc->pd_RemoveMulticastAddr)(pPortDesc,
pPortDesc->pd_ZoneMulticastAddr,
FALSE,
NULL,
NULL);
// Re-acquire the lock now
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
}
RtlZeroMemory(pPortDesc->pd_ZoneMulticastAddr, MAX_HW_ADDR_LEN);
}
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
return ATALK_TIMER_REQUEUE;
}
/*** atalkRtmpSendTimer
*
*/
LOCAL LONG FASTCALL
atalkRtmpSendTimer(
IN PTIMERLIST pContext,
IN BOOLEAN TimerShuttingDown
)
{
PPORT_DESCRIPTOR pPortDesc;
ATALK_ADDR Destination;
ATALK_ERROR error;
pPortDesc = CONTAINING_RECORD(pContext, PORT_DESCRIPTOR, pd_RtmpSendTimer);
if (TimerShuttingDown ||
(pPortDesc->pd_Flags & PD_CLOSING))
{
AtalkPortDereferenceDpc(pPortDesc);
return ATALK_TIMER_NO_REQUEUE;
}
Destination.ata_Network = CABLEWIDE_BROADCAST_NETWORK;
Destination.ata_Node = ATALK_BROADCAST_NODE;
Destination.ata_Socket = RTMP_SOCKET;
if (((pPortDesc->pd_Flags &
(PD_ACTIVE | PD_ROUTER_RUNNING)) == (PD_ACTIVE | PD_ROUTER_RUNNING)))
{
atalkRtmpSendRoutingData(pPortDesc, &Destination, TRUE);
}
return ATALK_TIMER_REQUEUE;
}
/*** atalkValidityTimer
*
*/
LOCAL LONG FASTCALL
atalkRtmpValidityTimer(
IN PTIMERLIST pContext,
IN BOOLEAN TimerShuttingDown
)
{
PRTE pRte, pNext;
int i;
if (TimerShuttingDown)
return ATALK_TIMER_NO_REQUEUE;
ACQUIRE_SPIN_LOCK_DPC(&AtalkRteLock);
for (i = 0; i < NUM_RTMP_HASH_BUCKETS; i++)
{
for (pRte = AtalkRoutingTable[i]; pRte != NULL; pRte = pNext)
{
BOOLEAN Deref;
pNext = pRte->rte_Next;
if (pRte->rte_NumHops == 0)
continue;
Deref = FALSE;
ACQUIRE_SPIN_LOCK_DPC(&pRte->rte_Lock);
switch (pRte->rte_State)
{
case GOOD:
case SUSPECT:
case BAD:
pRte->rte_State++;
break;
case UGLY:
Deref = TRUE;
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_WARN,
("atalkRtmpValidityTimer: Killing pRte %lx\n"));
pRte->rte_Flags |= RTE_DELETE;
break;
default:
// How did we get here ?
ASSERT(0);
KeBugCheck(0);
}
RELEASE_SPIN_LOCK_DPC(&pRte->rte_Lock);
if (Deref)
AtalkRtmpDereferenceRte(pRte, TRUE);
}
}
RELEASE_SPIN_LOCK_DPC(&AtalkRteLock);
return ATALK_TIMER_REQUEUE;
}
/*** atalkRtmpSendRoutingData
*
*/
LOCAL VOID
atalkRtmpSendRoutingData(
IN PPORT_DESCRIPTOR pPortDesc,
IN PATALK_ADDR pDstAddr,
IN BOOLEAN fSplitHorizon
)
{
int i, index;
PRTE pRte;
PBYTE Datagram;
PDDP_ADDROBJ pDdpAddr;
ATALK_ADDR SrcAddr;
PBUFFER_DESC pBuffDesc,
pBuffDescStart = NULL,
*ppBuffDesc = &pBuffDescStart;
SEND_COMPL_INFO SendInfo;
ATALK_ERROR Status;
BOOLEAN AllocNewBuffDesc = TRUE;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
// Compute the source socket: Rtmp socket on our routers node
SrcAddr.ata_Network = pPortDesc->pd_ARouter.atn_Network;
SrcAddr.ata_Node = pPortDesc->pd_ARouter.atn_Node;
SrcAddr.ata_Socket = RTMP_SOCKET;
AtalkDdpReferenceByAddr(pPortDesc, &SrcAddr, &pDdpAddr, &Status);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("atalkRtmpSendRoutingData: AtalkDdpRefByAddr failed %ld for %d.%d\n",
Status, SrcAddr.ata_Network, SrcAddr.ata_Node));
return;
}
// Walk through the rtmp table building a tuple for each network.
// Note: We may have to send multiple-packets. Each packet needs
// to be allocated afresh. The completion routine will free
// it up.
ACQUIRE_SPIN_LOCK_DPC(&AtalkRteLock);
for (i = 0; i < NUM_RTMP_HASH_BUCKETS; i++)
{
for (pRte = AtalkRoutingTable[i];
pRte != NULL;
pRte = pRte->rte_Next)
{
if (AllocNewBuffDesc)
{
if ((pBuffDesc = AtalkAllocBuffDesc(NULL,
MAX_DGRAM_SIZE,
BD_CHAR_BUFFER | BD_FREE_BUFFER)) == NULL)
break;
Datagram = pBuffDesc->bd_CharBuffer;
*ppBuffDesc = pBuffDesc;
pBuffDesc->bd_Next = NULL;
ppBuffDesc = &pBuffDesc->bd_Next;
AllocNewBuffDesc = FALSE;
// Build the static part of the rtmp data packet
PUTSHORT2SHORT(Datagram+RTMP_SENDER_NW_OFF,
pPortDesc->pd_ARouter.atn_Network);
Datagram[RTMP_SENDER_IDLEN_OFF] = 8;
Datagram[RTMP_SENDER_ID_OFF] = pPortDesc->pd_ARouter.atn_Node;
// For non-extended network, we also need the version stamp.
// For extended network, include a initial network range tuple
// as part of the header
if (EXT_NET(pPortDesc))
{
PUTSHORT2SHORT(Datagram + RTMP_RANGE_START_OFF,
pPortDesc->pd_NetworkRange.anr_FirstNetwork);
PUTSHORT2SHORT(Datagram + RTMP_RANGE_END_OFF,
pPortDesc->pd_NetworkRange.anr_LastNetwork);
Datagram[RTMP_TUPLE_TYPE_OFF] = RTMP_TUPLE_WITHRANGE;
Datagram[RTMP_VERSION_OFF_EXT] = RTMP_VERSION;
index = RTMP_VERSION_OFF_EXT + 1; // Beyond version
}
else
{
PUTSHORT2SHORT(Datagram + RTMP_SENDER_ID_OFF + 1, 0);
Datagram[RTMP_VERSION_OFF_NE] = RTMP_VERSION;
index = RTMP_VERSION_OFF_NE + 1; // Beyond version
}
}
// See if we should skip the current tuple due to split horizon
if (fSplitHorizon && (pRte->rte_NumHops != 0) &&
(pPortDesc == pRte->rte_PortDesc))
continue;
// Skip the ports range since we already copied it as the
// first tuple, but only if extended port
if (EXT_NET(pPortDesc) &&
(pPortDesc->pd_NetworkRange.anr_FirstNetwork ==
pRte->rte_NwRange.anr_FirstNetwork) &&
(pPortDesc->pd_NetworkRange.anr_FirstNetwork ==
pRte->rte_NwRange.anr_FirstNetwork))
continue;
// Place the tuple in the packet
PUTSHORT2SHORT(Datagram+index, pRte->rte_NwRange.anr_FirstNetwork);
index += sizeof(SHORT);
// Do 'notify neighbor' if our current state is bad
if (pRte->rte_State >= BAD)
{
Datagram[index++] = RTMP_NUM_HOPS_MASK;
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("atalkRtmpSendRoutingData: Notifying neighbor of bad Rte - port %Z, Range %d.%d\n",
&pRte->rte_PortDesc->pd_AdapterKey,
pRte->rte_NwRange.anr_FirstNetwork,
pRte->rte_NwRange.anr_LastNetwork));
}
else
{
Datagram[index++] = pRte->rte_NumHops;
}
// Send an extended tuple, if the network range isn't ONE or the
// target port is an extended network.
// JH - Changed this so that an extended tuple is sent IFF the range
// isn't ONE
#if EXT_TUPLES_FOR_NON_EXTENDED_RANGE
if ((EXT_NET(pPortDesc)) &&
(pRte->rte_NwRange.anr_FirstNetwork != pRte->rte_NwRange.anr_LastNetwork))
#else
if (pRte->rte_NwRange.anr_FirstNetwork != pRte->rte_NwRange.anr_LastNetwork)
#endif
{
Datagram[index-1] |= RTMP_EXT_TUPLE_MASK;
PUTSHORT2SHORT(Datagram+index, pRte->rte_NwRange.anr_LastNetwork);
index += sizeof(SHORT);
Datagram[index++] = RTMP_VERSION;
}
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_INFO,
("atalkRtmpSendRoutingData: Port %Z, Net '%d:%d', Distance %d\n",
&pPortDesc->pd_AdapterKey,
pRte->rte_NwRange.anr_FirstNetwork,
pRte->rte_NwRange.anr_LastNetwork,
pRte->rte_NumHops));
// Check if this datagram is full.
if ((index + RTMP_EXT_TUPLE_SIZE) >= MAX_DGRAM_SIZE)
{
pBuffDesc->bd_Length = (SHORT)index;
AllocNewBuffDesc = TRUE;
}
}
}
RELEASE_SPIN_LOCK_DPC(&AtalkRteLock);
// Close the current buffdesc
if (!AllocNewBuffDesc)
{
pBuffDesc->bd_Length = (SHORT)index;
}
// We have a bunch of datagrams ready to be fired off. Make it so.
SendInfo.sc_TransmitCompletion = atalkRtmpSendComplete;
// SendInfo.sc_Ctx2 = NULL;
// SendInfo.sc_Ctx3 = NULL;
for (pBuffDesc = pBuffDescStart;
pBuffDesc != NULL;
pBuffDesc = pBuffDescStart)
{
ATALK_ERROR ErrorCode;
pBuffDescStart = pBuffDesc->bd_Next;
// Reset next pointer to be null, length is already correctly set.
pBuffDesc->bd_Next = NULL;
ASSERT(pBuffDesc->bd_Length > 0);
SendInfo.sc_Ctx1 = pBuffDesc;
if (!ATALK_SUCCESS(ErrorCode = AtalkDdpSend(pDdpAddr,
pDstAddr,
DDPPROTO_RTMPRESPONSEORDATA,
FALSE,
pBuffDesc,
NULL,
0,
NULL,
&SendInfo)))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("atalkRtmpSendRoutingData: DdpSend failed %ld\n", ErrorCode));
AtalkFreeBuffDesc(pBuffDesc);
}
}
AtalkDdpDereference(pDdpAddr);
}
/*** atalkRtmpGetOrSetNetworkNumber
*
*/
BOOLEAN
atalkRtmpGetOrSetNetworkNumber(
IN PPORT_DESCRIPTOR pPortDesc,
IN USHORT SuggestedNetwork
)
{
int i;
ATALK_ERROR ErrorCode;
ATALK_ADDR SrcAddr, DstAddr;
PBUFFER_DESC pBuffDesc;
KIRQL OldIrql;
BOOLEAN RetCode = TRUE;
SEND_COMPL_INFO SendInfo;
// If we find the network number of the network, use that and ignore the
// one passed in. Otherwise use the one passed in, unless it is UNKOWN (0)
// in which case it is an error case. This is used only for non-extended
// networks
ASSERT (!EXT_NET(pPortDesc));
SrcAddr.ata_Network = UNKNOWN_NETWORK;
SrcAddr.ata_Node = pPortDesc->pd_RouterNode->an_NodeAddr.atn_Node;
SrcAddr.ata_Socket = RTMP_SOCKET;
DstAddr.ata_Network = UNKNOWN_NETWORK;
DstAddr.ata_Node = ATALK_BROADCAST_NODE;
DstAddr.ata_Socket = RTMP_SOCKET;
// Send off a bunch of broadcasts and see if we get to know the network #
KeClearEvent(&pPortDesc->pd_SeenRouterEvent);
SendInfo.sc_TransmitCompletion = atalkRtmpSendComplete;
// SendInfo.sc_Ctx2 = NULL;
// SendInfo.sc_Ctx3 = NULL;
for (i = 0;
(i < RTMP_NUM_REQUESTS) && !(pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY);
i++)
{
if ((pBuffDesc = AtalkAllocBuffDesc(NULL,
RTMP_REQ_DATAGRAM_SIZE,
BD_CHAR_BUFFER | BD_FREE_BUFFER)) == NULL)
{
RetCode = FALSE;
break;
}
// Set buffer/size
pBuffDesc->bd_CharBuffer[0] = RTMP_REQUEST;
AtalkSetSizeOfBuffDescData(pBuffDesc, RTMP_REQ_DATAGRAM_SIZE);
SendInfo.sc_Ctx1 = pBuffDesc;
ErrorCode = AtalkDdpTransmit(pPortDesc,
&SrcAddr,
&DstAddr,
DDPPROTO_RTMPREQUEST,
pBuffDesc,
NULL,
0,
0,
NULL,
NULL,
&SendInfo);
if (!ATALK_SUCCESS(ErrorCode))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("atalkRtmpGetOrSetNetworkNumber: DdpTransmit failed %ld\n", ErrorCode));
AtalkFreeBuffDesc(pBuffDesc);
RetCode = FALSE;
break;
}
if (AtalkWaitTE(&pPortDesc->pd_SeenRouterEvent, RTMP_REQUEST_WAIT))
break;
}
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
// If we get an answer, we are done
if (pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY)
{
if ((SuggestedNetwork != UNKNOWN_NETWORK) &&
(pPortDesc->pd_NetworkRange.anr_FirstNetwork != SuggestedNetwork))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_NETNUMBERCONFLICT,
0,
NULL,
0);
}
}
// If we did not get an answer, then we better have a good suggested
// network passed in
else if (SuggestedNetwork == UNKNOWN_NETWORK)
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_INVALID_NETRANGE,
0,
NULL,
0);
RetCode = FALSE;
}
else
{
pPortDesc->pd_NetworkRange.anr_FirstNetwork =
pPortDesc->pd_NetworkRange.anr_LastNetwork = SuggestedNetwork;
}
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
return RetCode;
}
/*** atalkRtmpComplete
*
*/
VOID FASTCALL
atalkRtmpSendComplete(
IN NDIS_STATUS Status,
IN PSEND_COMPL_INFO pSendInfo
)
{
AtalkFreeBuffDesc((PBUFFER_DESC)(pSendInfo->sc_Ctx1));
}
#if DBG
PCHAR atalkRteStates[] = { "Eh ?", "GOOD", "SUSPECT", "BAD", "UGLY" };
VOID
AtalkRtmpDumpTable(
VOID
)
{
int i;
PRTE pRte;
if (AtalkRoutingTable == NULL)
return;
ACQUIRE_SPIN_LOCK_DPC(&AtalkRteLock);
DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL, ("RECENT ROUTE CACHE:\n"));
for (i = 0; (AtalkRecentRoutes != NULL) && (i < NUM_RECENT_ROUTES); i ++)
{
if ((pRte = AtalkRecentRoutes[i]) != NULL)
{
DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
("Port %Z Hops %d Range %4d.%4d Router %4d.%3d Flags %x Ref %2d %s\n",
&pRte->rte_PortDesc->pd_AdapterKey,
pRte->rte_NumHops,
pRte->rte_NwRange.anr_FirstNetwork,
pRte->rte_NwRange.anr_LastNetwork,
pRte->rte_NextRouter.atn_Network,
pRte->rte_NextRouter.atn_Node,
pRte->rte_Flags,
pRte->rte_RefCount,
atalkRteStates[pRte->rte_State]));
}
}
DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL, ("ROUTINGTABLE:\n"));
for (i = 0; i < NUM_RTMP_HASH_BUCKETS; i ++)
{
for (pRte = AtalkRoutingTable[i]; pRte != NULL; pRte = pRte->rte_Next)
{
DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
("Port %Z Hops %d Range %4d.%4d Router %4d.%3d Flags %x Ref %2d %s\n",
&pRte->rte_PortDesc->pd_AdapterKey,
pRte->rte_NumHops,
pRte->rte_NwRange.anr_FirstNetwork,
pRte->rte_NwRange.anr_LastNetwork,
pRte->rte_NextRouter.atn_Network,
pRte->rte_NextRouter.atn_Node,
pRte->rte_Flags,
pRte->rte_RefCount,
atalkRteStates[pRte->rte_State]));
}
}
RELEASE_SPIN_LOCK_DPC(&AtalkRteLock);
}
#endif