/*++ Copyright (c) 1995 Microsoft Corporation Module Name: rtrmgr.c Abstract: The major router management functions Author: Stefan Solomon 03/22/1995 Revision History: --*/ #include "precomp.h" #pragma hdrstop // *********** Local Variables *********** HINSTANCE IpxCpModuleInstance; ULONG WorkerWaitTimeout; LPVOID RouterGlobalInfop; ULONG RouterGlobalInfoSize; TCHAR ModuleName[MAX_PATH+1]; HINSTANCE hModuleReference; VOID RoutesUpdateNotification(VOID); VOID ServicesUpdateNotification(VOID); VOID RouterStopNotification(VOID); VOID RoutingProtocolsNotification(VOID); DWORD GetRegistryParameters(VOID); typedef VOID (*EVENTHANDLER)(VOID); EVENTHANDLER evhdlr[MAX_EVENTS] = { AdapterNotification, ForwarderNotification, RoutingProtocolsNotification, RouterStopNotification }; DWORD StopRouter(VOID); DWORD RouterBootComplete( VOID ); VOID RouterManagerWorker(VOID); DWORD GetGlobalInfo(OUT LPVOID GlobalInfop, IN OUT LPDWORD GlobalInfoSizep); // These prototypes allow us to specify when ipxcp will be initialized DWORD InitializeIpxCp (HINSTANCE hInstDll); DWORD CleanupIpxCp (HINSTANCE hInstDll); BOOL WINAPI IpxRtrMgrDllEntry(HINSTANCE hInstDll, DWORD fdwReason, LPVOID pReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: GetModuleFileName (hInstDll, ModuleName, sizeof (ModuleName)/sizeof (ModuleName[0])); SS_DBGINITIALIZE; StartTracing(); break; case DLL_PROCESS_DETACH: StopTracing(); // Close the database mutex DeleteCriticalSection (&DatabaseLock); break; default: break; } return TRUE; } const static WCHAR pszIpxStackService[] = L"NwlnkIpx"; // // Verifies that the ipx stack is started and attempts to start the stack // if not. // DWORD VerifyOrStartIpxStack() { SC_HANDLE hSC = NULL, hStack = NULL; SERVICE_STATUS Status; DWORD dwErr; Trace(INIT_TRACE, "VerifyOrStartIpxStack: entered."); __try { // Get a handle to the service controller if ((hSC = OpenSCManager (NULL, NULL, GENERIC_READ | GENERIC_EXECUTE)) == NULL) return GetLastError(); // Get a handle to the ipx stack service hStack = OpenServiceW (hSC, pszIpxStackService, SERVICE_START | SERVICE_QUERY_STATUS); if (!hStack) return GetLastError(); // Find out if the service is running if (QueryServiceStatus (hStack, &Status) == 0) return GetLastError(); // See if the service is running if (Status.dwCurrentState != SERVICE_RUNNING) { // If it's stopped, start it if (Status.dwCurrentState == SERVICE_STOPPED) { if (StartService (hStack, 0, NULL) == 0) return GetLastError(); // Warn that the stack has been started IF_LOG (EVENTLOG_WARNING_TYPE) { RouterLogErrorDataW (RMEventLogHdl, ROUTERLOG_IPX_WRN_STACK_STARTED, 0, NULL, 0, NULL); } Trace(INIT_TRACE, "VerifyOrStartIpxStack: Starting ipx stack..."); // Make sure that the service started. StartService is not supposed // to return until the driver is started. if (QueryServiceStatus (hStack, &Status) == 0) return GetLastError(); if (Status.dwCurrentState != SERVICE_RUNNING) return ERROR_CAN_NOT_COMPLETE; } // If it's not stopped, don't worry about it. else return NO_ERROR; } } __finally { if (hSC) CloseServiceHandle (hSC); if (hStack) CloseServiceHandle (hStack); } return NO_ERROR; } /*++ Function: StartRouter Descr: Initializes the router manager database of interfaces and adapters, starts the other IPX router modules, creates the IPX router manager worker thread. --*/ DWORD StartRouter(PDIM_ROUTER_INTERFACE rifp, BOOL fLANModeOnly, LPVOID GlobalInfop) { HANDLE threadhandle; DWORD threadid, rc; int i; BOOL ThisMachineOnly, bInternalNetNumOk; IPXCP_INTERFACE IpxcpInterface; PIPX_GLOBAL_INFO IpxGlobalInfop; PIPX_INFO_BLOCK_HEADER globalhp; // These flags get set to true when their corrosponding components // get started. They are used to properly clean up. BOOL bEventsCreated = FALSE; BOOL bRoutTableCreated = FALSE; BOOL bRtmStaticObtained = FALSE; BOOL bRtmLocalObtained = FALSE; BOOL bFwdStarted = FALSE; BOOL bAdpManStarted = FALSE; BOOL bProtsStarted = FALSE; BOOL bGlobalRouteCreated = FALSE; BOOL bIpxcpStarted = FALSE; BOOL bIpxcpInitted = FALSE; BOOL bWorkerThreadCreated = FALSE; // Initialize Trace(INIT_TRACE, "StartRouter: Entered\n"); RouterOperState = OPER_STATE_DOWN; // [pmay] // We need to make sure that the stack is started before westart. if (VerifyOrStartIpxStack() != NO_ERROR) { IF_LOG (EVENTLOG_ERROR_TYPE) { RouterLogErrorDataW (RMEventLogHdl, ROUTERLOG_IPX_STACK_DISABLED, 0, NULL, 0, NULL); } Trace(INIT_TRACE, "StartRouter: Unable to start ipx stack."); return ERROR_SERVICE_DEPENDENCY_FAIL; } // [pmay] // We use this scheme to automatically select the internal network // number of the machine we're running on. If the net number is configured // as zero, this function will automatically select a random net num and // verify it's uniqueness on the net that this machine is attached to. if (AutoValidateInternalNetNum(&bInternalNetNumOk, INIT_TRACE) == NO_ERROR) { if (!bInternalNetNumOk) { if (PnpAutoSelectInternalNetNumber(INIT_TRACE) != NO_ERROR) { IF_LOG (EVENTLOG_ERROR_TYPE) { RouterLogErrorDataW (RMEventLogHdl, ROUTERLOG_IPX_AUTO_NETNUM_FAILURE, 0, NULL, 0, NULL); } Trace(INIT_TRACE, "StartRouter: Auto selection of net number failed."); return ERROR_CAN_NOT_COMPLETE; } } } // This try block will be used to automatically cleanup in the case that // something doesn't start right __try { // Make sure the parameters are ok if(GlobalInfop == NULL) { IF_LOG (EVENTLOG_ERROR_TYPE) { RouterLogErrorDataW (RMEventLogHdl, ROUTERLOG_IPX_BAD_GLOBAL_CONFIG, 0, NULL, 0, NULL); } Trace(INIT_TRACE, "StartRouter: invalid global info\n"); return ERROR_CAN_NOT_COMPLETE; } // Read config from registry GetRegistryParameters(); globalhp = (PIPX_INFO_BLOCK_HEADER)GlobalInfop; RouterGlobalInfop = GlobalAlloc(GPTR, globalhp->Size); RouterGlobalInfoSize = globalhp->Size; memcpy(RouterGlobalInfop, GlobalInfop, RouterGlobalInfoSize); IpxGlobalInfop = (PIPX_GLOBAL_INFO)GetInfoEntry((PIPX_INFO_BLOCK_HEADER)GlobalInfop, IPX_GLOBAL_INFO_TYPE); // Initialize the hash table size if(IpxGlobalInfop != NULL) { switch (IpxGlobalInfop->RoutingTableHashSize) { case IPX_SMALL_ROUTING_TABLE_HASH_SIZE: case IPX_MEDIUM_ROUTING_TABLE_HASH_SIZE: case IPX_LARGE_ROUTING_TABLE_HASH_SIZE: RoutingTableHashSize = IpxGlobalInfop->RoutingTableHashSize; Trace(INIT_TRACE, "Setting routing table hash size to %ld\n", RoutingTableHashSize); break; default: Trace(INIT_TRACE, "Using default routing table hash size of %ld\n", RoutingTableHashSize); break; } RMEventLogMask = IpxGlobalInfop->EventLogMask; } // Create router database mutex try { InitializeCriticalSection (&DatabaseLock); } except (EXCEPTION_EXECUTE_HANDLER) { // !!! cannot create database mutex !!! Trace(INIT_TRACE, "InitializeRouter: cannot initialize database lock.\n"); return(ERROR_CAN_NOT_COMPLETE); } // Create the adapter and forwarder notification events for (i=0; i < MAX_EVENTS; i++) { g_hEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL); if (g_hEvents[i] == NULL) { // !!! Log a problem with event creation return (ERROR_CAN_NOT_COMPLETE); } } bEventsCreated = TRUE; // Initialize the interfaces and adapters databases InitIfDB(); InitAdptDB(); // create the IPX routing table if(CreateRouteTable() != NO_ERROR) { Trace(INIT_TRACE, "InitializeRouter: cannot create route table\n"); return(ERROR_CAN_NOT_COMPLETE); } bRoutTableCreated = TRUE; // Get a handle to use later when calling into rtm for static routes if((RtmStaticHandle = RtmRegisterClient(RTM_PROTOCOL_FAMILY_IPX, IPX_PROTOCOL_STATIC, NULL, // not interested in change notif 0)) == NULL) { Trace(INIT_TRACE, "InitializeRouter: cannot register RTM client\n"); return(ERROR_CAN_NOT_COMPLETE); } bRtmStaticObtained = TRUE; // Get a handle to use when calling into the rtm later for local routes if((RtmLocalHandle = RtmRegisterClient(RTM_PROTOCOL_FAMILY_IPX, IPX_PROTOCOL_LOCAL, NULL, // not interested in change notif 0)) == NULL) { Trace(INIT_TRACE, "InitializeRouter: cannot register RTM client\n"); return(ERROR_CAN_NOT_COMPLETE); } bRtmLocalObtained = TRUE; // tell the IPXCP that router has started so we can accept calls from it LanOnlyMode = fLANModeOnly; // Bind with ipxcp if we are a wan router if(!LanOnlyMode) { // Load ipxcp IpxCpModuleInstance = LoadLibrary(IPXCPDLLNAME); if(IpxCpModuleInstance == NULL) { IF_LOG (EVENTLOG_ERROR_TYPE) RouterLogErrorA (RMEventLogHdl, ROUTERLOG_IPX_CANT_LOAD_IPXCP,0, NULL, GetLastError ()); Trace(INIT_TRACE, "StartRouter: cannot load IPXCP DLL\n"); return ERROR_CAN_NOT_COMPLETE; } // Initialize it if ((rc = InitializeIpxCp (IpxCpModuleInstance)) != NO_ERROR) { Trace(INIT_TRACE, "StartRouter: cannot get IpxcpInit Entry Point"); return rc; } bIpxcpInitted = TRUE; // Bind to it if(!(IpxcpBind = (PIPXCP_BIND)GetProcAddress(IpxCpModuleInstance, IPXCP_BIND_ENTRY_POINT_STRING))) { Trace(INIT_TRACE, "StartRouter: cannot get IpxcpBind Entry Point\n"); return ERROR_CAN_NOT_COMPLETE; } IpxcpInterface.RmCreateGlobalRoute = RmCreateGlobalRoute; IpxcpInterface.RmAddLocalWkstaDialoutInterface = RmAddLocalWkstaDialoutInterface; IpxcpInterface.RmDeleteLocalWkstaDialoutInterface = RmDeleteLocalWkstaDialoutInterface; IpxcpInterface.RmGetIpxwanInterfaceConfig = RmGetIpxwanInterfaceConfig; IpxcpInterface.RmIsRoute = RmIsRoute; IpxcpInterface.RmGetInternalNetNumber = RmGetInternalNetNumber; IpxcpInterface.RmUpdateIpxcpConfig = RmUpdateIpxcpConfig; (*IpxcpBind)(&IpxcpInterface); ThisMachineOnly = IpxcpInterface.Params.ThisMachineOnly; WanNetDatabaseInitialized = IpxcpInterface.Params.WanNetDatabaseInitialized; EnableGlobalWanNet = IpxcpInterface.Params.EnableGlobalWanNet; memcpy(GlobalWanNet, IpxcpInterface.Params.GlobalWanNet, 4); IpxcpRouterStarted = IpxcpInterface.IpxcpRouterStarted; IpxcpRouterStopped = IpxcpInterface.IpxcpRouterStopped; } // check that the forwarder module exists and is ready to run if(FwStart(RoutingTableHashSize, ThisMachineOnly)) { // got a problem initializing the forwarder IF_LOG (EVENTLOG_ERROR_TYPE) { RouterLogErrorDataW (RMEventLogHdl, ROUTERLOG_IPX_CANT_LOAD_FORWARDER,0, NULL, 0, NULL); } // !!! log an error !!! Trace(INIT_TRACE, "InitializeRouter: cannot initialize the Forwarder\n"); return(ERROR_CAN_NOT_COMPLETE); } bFwdStarted = TRUE; // start getting the adapter configuration from the IPX stack // this will start adding adapters to the forwader if(StartAdapterManager()) { Trace(INIT_TRACE, "InitializeRouter: cannot get the adapters configuration\n"); return (ERROR_CAN_NOT_COMPLETE); } bAdpManStarted = TRUE; // set the timeout wait for the router worker thread WorkerWaitTimeout = INFINITE; // start the routing protocols (rip/sap or nlsp) if(StartRoutingProtocols(GlobalInfop,g_hEvents[ROUTING_PROTOCOLS_NOTIFICATION_EVENT])) { Trace(INIT_TRACE, "InitializeRouter: cannot initialize routing protocols\n"); return(ERROR_CAN_NOT_COMPLETE); } bProtsStarted = TRUE; // send an IOCTl to the Forwarder to notify the router manager of connection // requests ConnReqOverlapped.hEvent = g_hEvents[FORWARDER_NOTIFICATION_EVENT]; ConnRequest = (PFW_DIAL_REQUEST)GlobalAlloc (GPTR, DIAL_REQUEST_BUFFER_SIZE); if (ConnRequest==NULL) { Trace(INIT_TRACE, "InitializeRouter: Cannot allocate Connecttion Request buffer.\n"); return(ERROR_CAN_NOT_COMPLETE); } rc = FwNotifyConnectionRequest(ConnRequest,DIAL_REQUEST_BUFFER_SIZE,&ConnReqOverlapped); if(rc != NO_ERROR) { Trace(INIT_TRACE, "InitializeRouter: cannot post FwNotifyConnectionRequest IOCtl\n"); return(ERROR_CAN_NOT_COMPLETE); } // exchange function table with the DDM // first, fill in with our entry points rifp->dwProtocolId = PID_IPX; rifp->InterfaceConnected = InterfaceConnected; rifp->StopRouter = StopRouter; rifp->RouterBootComplete = RouterBootComplete; rifp->AddInterface = AddInterface; rifp->DeleteInterface = DeleteInterface; rifp->GetInterfaceInfo = GetInterfaceInfo; rifp->SetInterfaceInfo = SetInterfaceInfo; rifp->InterfaceNotReachable = InterfaceNotReachable; rifp->InterfaceReachable = InterfaceReachable; rifp->UpdateRoutes = RequestUpdate; rifp->GetUpdateRoutesResult = GetDIMUpdateResult; rifp->SetGlobalInfo = SetGlobalInfo; rifp->GetGlobalInfo = GetGlobalInfo; rifp->MIBEntryCreate = MibCreate; rifp->MIBEntryDelete = MibDelete; rifp->MIBEntrySet = MibSet; rifp->MIBEntryGet = MibGet; rifp->MIBEntryGetFirst = MibGetFirst; rifp->MIBEntryGetNext = MibGetNext; // get its entry points ConnectInterface = rifp->ConnectInterface; DisconnectInterface = rifp->DisconnectInterface; SaveInterfaceInfo = rifp->SaveInterfaceInfo; RestoreInterfaceInfo = rifp->RestoreInterfaceInfo; RouterStopped = rifp->RouterStopped; InterfaceEnabled = rifp->InterfaceEnabled; // Tell ipxcp that we have started if appropriate if(!LanOnlyMode) { if(WanNetDatabaseInitialized &&EnableGlobalWanNet) { CreateGlobalRoute(GlobalWanNet); bGlobalRouteCreated = TRUE; } (*IpxcpRouterStarted)(); bIpxcpStarted = TRUE; } // start the Router Manager Worker thread if ((threadhandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) RouterManagerWorker, NULL, 0, &threadid)) == NULL) { // !!! log error cannot create the worker thread !!! return (ERROR_CAN_NOT_COMPLETE); } bWorkerThreadCreated = TRUE; // all started -> the router is ready to accept interface management // apis from DDM, SNMP agent and Sysmon. RouterOperState = OPER_STATE_UP; } // Whenever the above try block exists, the code in this finally block will // be executed. If at that time, the router state is not up, then you know // an error condition exists. This is the time to cleanup in this case. __finally { if (RouterOperState == OPER_STATE_DOWN) { if (bWorkerThreadCreated) CloseHandle(threadhandle); if (bIpxcpStarted) (*IpxcpRouterStopped)(); if (bIpxcpInitted) CleanupIpxCp (IpxCpModuleInstance); if (bGlobalRouteCreated) DeleteGlobalRoute(GlobalWanNet); if (bProtsStarted) StopRoutingProtocols(); if (bAdpManStarted) StopAdapterManager(); if (bFwdStarted) FwStop(); if (bRtmLocalObtained) RtmDeregisterClient (RtmLocalHandle); if (bRtmStaticObtained) RtmDeregisterClient (RtmStaticHandle); if (bRoutTableCreated) DeleteRouteTable(); if (bEventsCreated) { for(i=0; iInterfaceIndex); DeleteAllStaticServices(icbp->InterfaceIndex); // check if oper state UP and admin enabled if((icbp->AdminState == ADMIN_STATE_ENABLED) && (icbp->OperState == OPER_STATE_UP)) { if(memcmp(icbp->acbp->AdapterInfo.Network, nullnet, 4)) { DeleteLocalRoute(icbp); } } lep = lep->Flink; } RELEASE_DATABASE_LOCK; // tell ipxcp that the router is stopping so that it will end calling us if(!LanOnlyMode) { (*IpxcpRouterStopped)(); if(EnableGlobalWanNet) { DeleteGlobalRoute(GlobalWanNet); } } // initiate the stopping of the routing protocols StopRoutingProtocols(); return; } /*++ Function: RouterManagerWorker Descr: the WORKER THREAD --*/ VOID RouterManagerWorker(VOID) { DWORD rc; DWORD signaled_event; hModuleReference = LoadLibrary (ModuleName); while(TRUE) { rc = WaitForMultipleObjectsEx( MAX_EVENTS, g_hEvents, FALSE, // wait any INFINITE, // timeout TRUE // wait alertable, so we can run APCs ); signaled_event = rc - WAIT_OBJECT_0; if(signaled_event < MAX_EVENTS) { // invoke the event handler (*evhdlr[signaled_event])(); } } } VOID RoutingProtocolsNotification(VOID) { PLIST_ENTRY lep; PRPCB rpcbp; ROUTING_PROTOCOL_EVENTS RpEvent; MESSAGE RpMessage; int i; DWORD rc; BOOL RoutingProtocolStopped; Trace(INIT_TRACE, " RoutingProtocolsNotification: Entered\n"); // for each routing protocol get the events and the messages associated // with each event lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); lep = lep->Flink; RoutingProtocolStopped = FALSE; while((rc = (*rpcbp->RP_GetEventMessage)(&RpEvent, &RpMessage)) == NO_ERROR) { switch(RpEvent) { case ROUTER_STOPPED: Trace(INIT_TRACE, "RoutingProtocolNotification: Protocol %x has stopped\n", rpcbp->RP_ProtocolId); RoutingProtocolStopped = TRUE; // remove the routing protocol CB from the list and free it DestroyRoutingProtocolCB(rpcbp); // check if there are still routing protocols to wait for if(IsListEmpty(&RoutingProtocolCBList)) { // // All Routing Protocols stopped -> Stop the Router // // Close the Forwarder. This will complete the Forwarder pending // connect request IOCTl. FwStop(); // set the current state RouterOperState = OPER_STATE_DOWN; // Close the IPX stack config port. StopAdapterManager(); // Clean-up the database ACQUIRE_DATABASE_LOCK; // Remove all adapter control blocks DestroyAllAdapters(); // Remove all interface control blocks DestroyAllInterfaces(); RELEASE_DATABASE_LOCK; // Deregister as RTM clients - this will delete all static and // local routes RtmDeregisterClient(RtmStaticHandle); RtmDeregisterClient(RtmLocalHandle); DeleteRouteTable(); // Close notification events for(i=0; iRP_ProtocolId); UpdateCompleted(&RpMessage.UpdateCompleteMessage); break; default: Trace(INIT_TRACE, "RoutingProtocolNotification: Protocol %x signaled invalid event %d\n", rpcbp->RP_ProtocolId, RpEvent); break; } if(RoutingProtocolStopped) { break; } } } } DWORD SetGlobalInfo(IN LPVOID GlobalInfop) { DWORD rc; PIPX_INFO_BLOCK_HEADER globalhp; PIPX_GLOBAL_INFO IpxGlobalInfop; if(GlobalInfop == NULL) { return ERROR_CAN_NOT_COMPLETE; } GlobalFree(RouterGlobalInfop); globalhp = (PIPX_INFO_BLOCK_HEADER)GlobalInfop; RouterGlobalInfoSize = globalhp->Size; RouterGlobalInfop = GlobalAlloc(GPTR, RouterGlobalInfoSize); if(RouterGlobalInfop == NULL) { return ERROR_CAN_NOT_COMPLETE; } memcpy(RouterGlobalInfop, GlobalInfop, RouterGlobalInfoSize); IpxGlobalInfop = (PIPX_GLOBAL_INFO)GetInfoEntry((PIPX_INFO_BLOCK_HEADER)GlobalInfop, IPX_GLOBAL_INFO_TYPE); if(IpxGlobalInfop != NULL) { // Can only be set at startup // RoutingTableHashSize = IpxGlobalInfop->RoutingTableHashSize; RMEventLogMask = IpxGlobalInfop->EventLogMask; } rc = SetRoutingProtocolsGlobalInfo((PIPX_INFO_BLOCK_HEADER)GlobalInfop); return rc; } DWORD GetGlobalInfo(OUT LPVOID GlobalInfop, IN OUT LPDWORD GlobalInfoSizep) { if((GlobalInfop == NULL) || (*GlobalInfoSizep == 0)) { *GlobalInfoSizep = RouterGlobalInfoSize; return ERROR_INSUFFICIENT_BUFFER; } if(RouterGlobalInfoSize > *GlobalInfoSizep) { *GlobalInfoSizep = RouterGlobalInfoSize; return ERROR_INSUFFICIENT_BUFFER; } memcpy(GlobalInfop, RouterGlobalInfop, RouterGlobalInfoSize); *GlobalInfoSizep = RouterGlobalInfoSize; return NO_ERROR; } //*** // // Function: GetRegistryParameters // // Descr: Reads the parameters from the registry and sets them // //*** DWORD GetRegistryParameters(VOID) { NTSTATUS Status; PWSTR RouterManagerParametersPath = L"RemoteAccess\\RouterManagers\\IPX\\Parameters"; RTL_QUERY_REGISTRY_TABLE paramTable[2]; // table size = nr of params + 1 RtlZeroMemory(¶mTable[0], sizeof(paramTable)); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[0].Name = L"MaxRoutingTableSize"; paramTable[0].EntryContext = &MaxRoutingTableSize; paramTable[0].DefaultType = REG_DWORD; paramTable[0].DefaultData = &MaxRoutingTableSize; paramTable[0].DefaultLength = sizeof(ULONG); Status = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES, RouterManagerParametersPath, paramTable, NULL, NULL); return Status; }