/*++ Copyright (c) 1995 Microsoft Corporation Module Name: rtmif.c Abstract: Contains the RTM interface functions Author: Stefan Solomon 07/06/1995 Revision History: --*/ #include "precomp.h" #pragma hdrstop // RTM RIP Client Handle HANDLE RtmRipHandle; typedef struct _ROUTE_NODE { LIST_ENTRY Linkage; IPX_ROUTE IpxRoute; } ROUTE_NODE, *PROUTE_NODE; // List of route nodes with RIP route changes LIST_ENTRY RipChangedList; // state of the RipChangedList BOOL RipChangedListOpen = FALSE; // Lock for the RIP changed list CRITICAL_SECTION RipChangedListCritSec; VOID AddRouteToRipChangedList(PIPX_ROUTE IpxRoutep); HANDLE CreateRipRoutesEnumHandle(ULONG InterfaceIndex); DWORD OpenRTM(VOID) { // initialize the variables for the RIP changes list InitializeListHead(&RipChangedList); RipChangedListOpen = TRUE; // register as RTM client if((RtmRipHandle = RtmRegisterClient(RTM_PROTOCOL_FAMILY_IPX, IPX_PROTOCOL_RIP, WorkerThreadObjects[RTM_EVENT], 0)) == NULL) { return ERROR_CAN_NOT_COMPLETE; } else { return NO_ERROR; } } VOID CloseRTM(VOID) { PLIST_ENTRY lep; PROUTE_NODE rnp; // flush the RIP changed list and destroy its critical section ACQUIRE_RIP_CHANGED_LIST_LOCK; while(!IsListEmpty(&RipChangedList)) { lep = RemoveHeadList(&RipChangedList); rnp = CONTAINING_RECORD(lep, ROUTE_NODE, Linkage); GlobalFree(rnp); } RipChangedListOpen = FALSE; RELEASE_RIP_CHANGED_LIST_LOCK; // deregister as RTM client RtmDeregisterClient(RtmRipHandle); } VOID RtmToIpxRoute(PIPX_ROUTE IpxRoutep, PRTM_IPX_ROUTE RtmRoutep) { IpxRoutep->InterfaceIndex = (ULONG)(RtmRoutep->R_Interface); IpxRoutep->Protocol = RtmRoutep->R_Protocol; PUTULONG2LONG(IpxRoutep->Network, RtmRoutep->R_Network); IpxRoutep->TickCount = RtmRoutep->R_TickCount; IpxRoutep->HopCount = RtmRoutep->R_HopCount; memcpy(IpxRoutep->NextHopMacAddress, RtmRoutep->R_NextHopMacAddress, 6); IpxRoutep->Flags = RtmRoutep->R_Flags; } VOID IpxToRtmRoute(PRTM_IPX_ROUTE RtmRoutep, PIPX_ROUTE IpxRoutep) { RtmRoutep->R_Interface = IpxRoutep->InterfaceIndex; RtmRoutep->R_Protocol = IpxRoutep->Protocol; GETLONG2ULONG(&RtmRoutep->R_Network, IpxRoutep->Network); RtmRoutep->R_TickCount = IpxRoutep->TickCount; RtmRoutep->R_HopCount = IpxRoutep->HopCount; memcpy(RtmRoutep->R_NextHopMacAddress, IpxRoutep->NextHopMacAddress, 6); RtmRoutep->R_Flags = IpxRoutep->Flags; } /*++ Function: AddRipRoute Descr: adds a RIP route to RTM --*/ DWORD AddRipRoute(PIPX_ROUTE IpxRoutep, ULONG TimeToLive) { DWORD rc = 0; DWORD flags = 0; RTM_IPX_ROUTE RtmRoute; RTM_IPX_ROUTE CurBestRoute; RTM_IPX_ROUTE PrevBestRoute; IPX_ROUTE PrevBestIpxRoute; IpxRoutep->Protocol = IPX_PROTOCOL_RIP; IpxToRtmRoute(&RtmRoute, IpxRoutep); if((rc = RtmAddRoute( RtmRipHandle, &RtmRoute, TimeToLive, &flags, &CurBestRoute, &PrevBestRoute)) != NO_ERROR) { return rc; } // check the type of change switch(flags) { case RTM_ROUTE_ADDED: AddRouteToRipChangedList(IpxRoutep); break; case RTM_ROUTE_CHANGED: if(CurBestRoute.R_HopCount == 16) { if(PrevBestRoute.R_HopCount < 16) { // advertise that the previous route is down RtmToIpxRoute(&PrevBestIpxRoute, &PrevBestRoute); PrevBestIpxRoute.HopCount = 16; AddRouteToRipChangedList(&PrevBestIpxRoute); } } else { if((CurBestRoute.R_TickCount != PrevBestRoute.R_TickCount) || (CurBestRoute.R_HopCount != PrevBestRoute.R_HopCount)) { AddRouteToRipChangedList(IpxRoutep); } } break; default: break; } return rc; } /*++ Function: DeleteRipRoute Descr: deletes a RIP route from RTM --*/ DWORD DeleteRipRoute(PIPX_ROUTE IpxRoutep) { DWORD rc; DWORD flags = 0; RTM_IPX_ROUTE RtmRoute; RTM_IPX_ROUTE CurBestRoute; IPX_ROUTE CurBestIpxRoute; IpxRoutep->Protocol = IPX_PROTOCOL_RIP; IpxToRtmRoute(&RtmRoute, IpxRoutep); if((rc = RtmDeleteRoute(RtmRipHandle, &RtmRoute, &flags, &CurBestRoute )) != NO_ERROR) { return rc; } switch(flags) { case RTM_ROUTE_DELETED: // bcast that we lost the previous route AddRouteToRipChangedList(IpxRoutep); break; case RTM_ROUTE_CHANGED: // current best route changed RtmToIpxRoute(&CurBestIpxRoute, &CurBestRoute); if(CurBestIpxRoute.HopCount == 16) { // bcast that we lost the previous route AddRouteToRipChangedList(IpxRoutep); } else { // bcast that we have a new best route AddRouteToRipChangedList(&CurBestIpxRoute); } break; default: break; } return rc; } /*++ Function: DeleteAllRipRoutes Descr: deletes all RIP routes for the specified interface --*/ VOID DeleteAllRipRoutes(ULONG InterfaceIndex) { HANDLE EnumHandle; IPX_ROUTE IpxRoute; RTM_IPX_ROUTE RtmCriteriaRoute; DWORD rc; Trace(RTM_TRACE, "DeleteAllRipRoutes: Entered for if # %d\n", InterfaceIndex); // enumerate all the routes for this interface and add them in the rip changed // list if((EnumHandle = CreateRipRoutesEnumHandle(InterfaceIndex)) == NULL) { Trace(RTM_TRACE, "DeleteAllRipRoutes: cannot create enum handle for if # %d\n", InterfaceIndex); goto DeleteRoutes; } while(EnumGetNextRoute(EnumHandle, &IpxRoute) == NO_ERROR) { if(IpxRoute.HopCount < 16) { IpxRoute.HopCount = 16; AddRouteToRipChangedList(&IpxRoute); } } CloseEnumHandle(EnumHandle); DeleteRoutes: // ... and now delete all routes for this interface memset(&RtmCriteriaRoute, 0, sizeof(RTM_IPX_ROUTE)); RtmCriteriaRoute.R_Interface = InterfaceIndex; RtmCriteriaRoute.R_Protocol = IPX_PROTOCOL_RIP; rc = RtmBlockDeleteRoutes(RtmRipHandle, RTM_ONLY_THIS_INTERFACE, &RtmCriteriaRoute); Trace(RTM_TRACE, "DeleteAllRipRoutes: RtmBlockDeleteRoutes returned rc=%d for if # %d\n", rc, InterfaceIndex); } /*++ Function: IsRoute Descr: returns TRUE if a route to the specified net exists --*/ BOOL IsRoute(PUCHAR Network, PIPX_ROUTE IpxRoutep) { DWORD RtmNetwork; RTM_IPX_ROUTE RtmRoute; GETLONG2ULONG(&RtmNetwork, Network); if(RtmIsRoute(RTM_PROTOCOL_FAMILY_IPX, &RtmNetwork, &RtmRoute)) { if (IpxRoutep!=NULL) RtmToIpxRoute(IpxRoutep, &RtmRoute); return TRUE; } else { return FALSE; } } //*********************************************************************** // * // Fast Enumeration Functions * // * //*********************************************************************** HANDLE CreateBestRoutesEnumHandle(VOID) { HANDLE EnumHandle; RTM_IPX_ROUTE CriteriaRoute; EnumHandle = RtmCreateEnumerationHandle(RTM_PROTOCOL_FAMILY_IPX, RTM_ONLY_BEST_ROUTES, &CriteriaRoute); return EnumHandle; } DWORD EnumGetNextRoute(HANDLE EnumHandle, PIPX_ROUTE IpxRoutep) { RTM_IPX_ROUTE RtmRoute; DWORD rc; rc = RtmEnumerateGetNextRoute(EnumHandle, &RtmRoute); if (rc == NO_ERROR) { RtmToIpxRoute(IpxRoutep, &RtmRoute); } return rc; } VOID CloseEnumHandle(HANDLE EnumHandle) { RtmCloseEnumerationHandle(EnumHandle); } HANDLE CreateRipRoutesEnumHandle(ULONG InterfaceIndex) { RTM_IPX_ROUTE EnumCriteriaRoute; HANDLE EnumHandle; memset(&EnumCriteriaRoute, 0, sizeof(RTM_IPX_ROUTE)); EnumCriteriaRoute.R_Interface = InterfaceIndex; EnumCriteriaRoute.R_Protocol = IPX_PROTOCOL_RIP; EnumHandle = RtmCreateEnumerationHandle(RTM_PROTOCOL_FAMILY_IPX, RTM_ONLY_BEST_ROUTES | RTM_ONLY_THIS_INTERFACE | RTM_ONLY_THIS_PROTOCOL, &EnumCriteriaRoute); return EnumHandle; } /*++ Function: GetRipRoutesCount Descr: returns the number of rip routes associated with this interface --*/ ULONG GetRipRoutesCount(ULONG InterfaceIndex) { HANDLE EnumHandle; ULONG RipRoutesCount = 0; IPX_ROUTE IpxRoute; if((EnumHandle = CreateRipRoutesEnumHandle(InterfaceIndex)) == NULL) { return 0; } while(EnumGetNextRoute(EnumHandle, &IpxRoute) == NO_ERROR) { RipRoutesCount++; } CloseEnumHandle(EnumHandle); return RipRoutesCount; } /*++ Function: DequeueRouteChangeFromRip Descr: Remark: >> called with the database & queues lock held << --*/ DWORD DequeueRouteChangeFromRip(PIPX_ROUTE IpxRoutep) { PLIST_ENTRY lep; PROUTE_NODE rnp; if(!IsListEmpty(&RipChangedList)) { lep = RemoveHeadList(&RipChangedList); rnp = CONTAINING_RECORD(lep, ROUTE_NODE, Linkage); *IpxRoutep = rnp->IpxRoute; GlobalFree(rnp); return NO_ERROR; } else { return ERROR_NO_MORE_ITEMS; } } /*++ Function: DequeueRouteChangeFromRtm Descr: Remark: >> called with the database locks held << --*/ DWORD DequeueRouteChangeFromRtm(PIPX_ROUTE IpxRoutep, PBOOL skipitp, PBOOL lastmessagep) { RTM_IPX_ROUTE CurBestRoute, PrevBestRoute; DWORD Flags = 0; DWORD rc; *skipitp = FALSE; *lastmessagep = FALSE; rc = RtmDequeueRouteChangeMessage(RtmRipHandle, &Flags, &CurBestRoute, &PrevBestRoute); switch(rc) { case NO_ERROR: *lastmessagep = TRUE; break; case ERROR_MORE_MESSAGES: break; default: return ERROR_NO_MORE_ITEMS; } switch(Flags) { case RTM_ROUTE_ADDED: RtmToIpxRoute(IpxRoutep, &CurBestRoute); break; case RTM_ROUTE_DELETED: RtmToIpxRoute(IpxRoutep, &PrevBestRoute); IpxRoutep->HopCount = 16; break; case RTM_ROUTE_CHANGED: // if there was a change in metric advertise it. // Else, ignore it. if(CurBestRoute.R_TickCount != PrevBestRoute.R_TickCount) { RtmToIpxRoute(IpxRoutep, &CurBestRoute); } else { *skipitp = TRUE; } break; default: *skipitp = TRUE; break; } return NO_ERROR; } VOID AddRouteToRipChangedList(PIPX_ROUTE IpxRoutep) { PROUTE_NODE rnp; if((rnp = GlobalAlloc(GPTR, sizeof(ROUTE_NODE))) == NULL) { return; } rnp->IpxRoute = *IpxRoutep; ACQUIRE_RIP_CHANGED_LIST_LOCK; if(!RipChangedListOpen) { GlobalFree(rnp); } else { InsertTailList(&RipChangedList, &rnp->Linkage); SetEvent(WorkerThreadObjects[RIP_CHANGES_EVENT]); } RELEASE_RIP_CHANGED_LIST_LOCK; } BOOL IsDuplicateBestRoute(PICB icbp, PIPX_ROUTE IpxRoutep) { RTM_IPX_ROUTE RtmRoute; DWORD rc; GETLONG2ULONG(&RtmRoute.R_Network, IpxRoutep->Network); RtmRoute.R_Interface = icbp->InterfaceIndex; rc = RtmGetFirstRoute( RTM_PROTOCOL_FAMILY_IPX, RTM_ONLY_THIS_NETWORK | RTM_ONLY_THIS_INTERFACE, &RtmRoute); // check if it has the same metric if((rc == NO_ERROR) && ((USHORT)(RtmRoute.R_TickCount) == IpxRoutep->TickCount)) { // duplicate ! return TRUE; } else { return FALSE; } }