/*++ 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 #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