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