1893 lines
49 KiB
C
1893 lines
49 KiB
C
/*++
|
||
|
||
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
|
||
|
||
|