You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1893 lines
51 KiB
1893 lines
51 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
rtmp.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the rtmp.
|
|
|
|
Author:
|
|
|
|
Jameel Hyder ([email protected])
|
|
Nikhil Kamkolkar ([email protected])
|
|
|
|
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
|
|
|
|
|