/*++ Copyright (c) 1995 Microsoft Corporation Module Name: ripmain.c Abstract: Contains the rcv and worker threads Author: Stefan Solomon 07/06/1995 Revision History: --*/ #include "precomp.h" #pragma hdrstop DWORD APIENTRY RegisterProtocol( IN OUT PMPR_ROUTING_CHARACTERISTICS pRoutingChar, IN OUT PMPR_SERVICE_CHARACTERISTICS pServiceChar ); DWORD APIENTRY StartProtocol( IN HANDLE hMgrNotifyEvent, IN PSUPPORT_FUNCTIONS pSupportFunctions, IN PVOID pConfig ); DWORD APIENTRY StopProtocol( VOID ); DWORD APIENTRY GetGlobalInfo( IN OUT PVOID pConfig, IN OUT PDWORD pdwSize ); DWORD APIENTRY SetGlobalInfo( IN PVOID pConfig ); DWORD APIENTRY AddInterface( IN PWCHAR pwszInterfaceName, IN DWORD dwIndex, IN NET_INTERFACE_TYPE dwIfType, IN PVOID pConfig ); DWORD APIENTRY DeleteInterface( IN DWORD dwIndex ); DWORD APIENTRY GetEventMessage( OUT ROUTING_PROTOCOL_EVENTS *pEvent, OUT MESSAGE *pResult ); DWORD APIENTRY GetInterfaceConfigInfo( IN DWORD dwIndex, IN OUT PVOID pConfig, IN OUT PDWORD pdwSize ); DWORD APIENTRY SetInterfaceConfigInfo( IN DWORD dwIndex, IN PVOID pConfig ); DWORD APIENTRY BindInterface( IN DWORD dwIndex, IN PVOID pBinding ); DWORD APIENTRY UnbindInterface( IN DWORD dwIndex ); DWORD APIENTRY EnableInterface( IN DWORD dwIndex ); DWORD APIENTRY DisableInterface( IN DWORD dwIndex ); DWORD APIENTRY DoUpdateRoutes( IN DWORD dwIndex ); DWORD APIENTRY MibCreate( IN DWORD dwInputSize, IN PVOID pInputData ); DWORD APIENTRY MibDelete( IN DWORD dwInputSize, IN PVOID pInputData ); DWORD APIENTRY MibGet( IN DWORD dwInputSize, IN PVOID pInputData, IN OUT PDWORD pdwOutputSize, OUT PVOID pOutputData ); DWORD APIENTRY MibSet( IN DWORD dwInputSize, IN PVOID pInputData ); DWORD APIENTRY MibGetFirst( IN DWORD dwInputSize, IN PVOID pInputData, IN OUT PDWORD pdwOutputSize, OUT PVOID pOutputData ); DWORD APIENTRY MibGetNext( IN DWORD dwInputSize, IN PVOID pInputData, IN OUT PDWORD pdwOutputSize, OUT PVOID pOutputData ); // Router Manager Notification Event HANDLE RM_Event; TCHAR ModuleName[MAX_PATH+1]; VOID WorkerThread(VOID); DWORD CreateWorkerThreadObjects(VOID); VOID DestroyWorkerThreadObjects(VOID); VOID ProcessDequeuedIoPacket(DWORD ErrorCode, DWORD BytesTransferred, LPOVERLAPPED Overlappedp); BOOL WINAPI IpxRipDllEntry(HINSTANCE hInstDll, DWORD fdwReason, LPVOID pReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: GetModuleFileName (hInstDll, ModuleName, sizeof (ModuleName)/sizeof (ModuleName[0])); SS_DBGINITIALIZE; RipOperState = OPER_STATE_DOWN; // Create the database lock InitializeCriticalSection(&DbaseCritSec); // Create the queues lock InitializeCriticalSection(&QueuesCritSec); // Create the RIP changed list lock InitializeCriticalSection(&RipChangedListCritSec); break; case DLL_PROCESS_DETACH: // delete the RIP changed list lock DeleteCriticalSection(&RipChangedListCritSec); // delete the database lock DeleteCriticalSection(&DbaseCritSec); // delete the queues lock DeleteCriticalSection(&QueuesCritSec); break; default: break; } return TRUE; } DWORD WINAPI RegisterProtocol( IN OUT PMPR_ROUTING_CHARACTERISTICS pRoutingChar, IN OUT PMPR_SERVICE_CHARACTERISTICS pServiceChar ) { if(pRoutingChar->dwProtocolId != IPX_PROTOCOL_RIP) { return ERROR_NOT_SUPPORTED; } pServiceChar->fSupportedFunctionality = 0; pRoutingChar->fSupportedFunctionality = (ROUTING | DEMAND_UPDATE_ROUTES); pRoutingChar->pfnStartProtocol = StartProtocol; pRoutingChar->pfnStopProtocol = StopProtocol; pRoutingChar->pfnAddInterface = AddInterface; pRoutingChar->pfnDeleteInterface = DeleteInterface; pRoutingChar->pfnGetEventMessage = GetEventMessage; pRoutingChar->pfnGetInterfaceInfo = GetInterfaceConfigInfo; pRoutingChar->pfnSetInterfaceInfo = SetInterfaceConfigInfo; pRoutingChar->pfnBindInterface = BindInterface; pRoutingChar->pfnUnbindInterface = UnbindInterface; pRoutingChar->pfnEnableInterface = EnableInterface; pRoutingChar->pfnDisableInterface = DisableInterface; pRoutingChar->pfnGetGlobalInfo = GetGlobalInfo; pRoutingChar->pfnSetGlobalInfo = SetGlobalInfo; pRoutingChar->pfnMibCreateEntry = MibCreate; pRoutingChar->pfnMibDeleteEntry = MibDelete; pRoutingChar->pfnMibGetEntry = MibGet; pRoutingChar->pfnMibSetEntry = MibSet; pRoutingChar->pfnMibGetFirstEntry = MibGetFirst; pRoutingChar->pfnMibGetNextEntry = MibGetNext; pRoutingChar->pfnUpdateRoutes = DoUpdateRoutes; return NO_ERROR; } DWORD WINAPI StartProtocol(IN HANDLE NotificationEvent, IN PSUPPORT_FUNCTIONS SupportFunctions, IN PVOID GlobalInfo) { #define ripGlobalInfo ((PRIP_GLOBAL_INFO)GlobalInfo) DWORD threadid, i; HANDLE ThreadHandle; RipEventLogMask = ripGlobalInfo->EventLogMask; StartTracing(); Trace(INIT_TRACE, "StartProtocol: Entered\n"); ACQUIRE_DATABASE_LOCK; ACQUIRE_QUEUES_LOCK; RipOperState = OPER_STATE_STARTING; GetIpxRipRegistryParameters(); RM_Event = NotificationEvent; //init the interfaces database InitIfDbase(); // // init all the queues // // InitializeListHead(&WorkersQueue); InitializeListHead(&TimerQueue); InitializeListHead(&RepostRcvPacketsQueue); InitializeListHead(&RipMessageQueue); // create the workers work items heap if(CreateWorkItemsManager() != NO_ERROR) { goto ErrorExit; } // open the RIP socket for I/O if(OpenRipSocket() != NO_ERROR) { Trace(INIT_TRACE, "Cannot open RIP socket\n"); goto ErrorExit; } if(! BindIoCompletionCallback(RipSocketHandle, ProcessDequeuedIoPacket, 0)) { Trace(INIT_TRACE, "Cannot associate IO Completion Port\n"); goto ErrorExit; } // create synchronization objects for the rip threads if(CreateWorkerThreadObjects() != NO_ERROR) { Trace(INIT_TRACE, "Cannot create synchronization objects\n"); goto ErrorExit; } // Open RTM for RIP if(OpenRTM()) { Trace(INIT_TRACE, "Cannot open RTM\n"); goto ErrorExit; } // create the Worker thread if ((ThreadHandle = CreateThread( (LPSECURITY_ATTRIBUTES) NULL, 0, (LPTHREAD_START_ROUTINE) WorkerThread, NULL, 0, &threadid)) == NULL) { // !!! log error cannot create the worker thread !!! goto ErrorExit; } else CloseHandle (ThreadHandle); RipOperState = OPER_STATE_UP; RELEASE_QUEUES_LOCK; RELEASE_DATABASE_LOCK; CreateStartChangesBcastWi(); Trace(INIT_TRACE, "Started successfully\n"); return NO_ERROR; ErrorExit: RELEASE_QUEUES_LOCK; RELEASE_DATABASE_LOCK; return ERROR_CAN_NOT_COMPLETE; #undef ripGlobalInfo } DWORD WINAPI StopProtocol(VOID) { PWORK_ITEM wip; Trace(INIT_TRACE, "StopProtocol: Entered\n"); ACQUIRE_DATABASE_LOCK; if(RipOperState != OPER_STATE_UP) { SS_ASSERT(FALSE); goto ErrorExit; } RipOperState = OPER_STATE_STOPPING; // send interfaces shutdown work item to the workers if((wip = AllocateWorkItem(SHUTDOWN_INTERFACES_TYPE)) == NULL) { goto ErrorExit; } wip->WorkItemSpecific.WIS_ShutdownInterfaces.ShutdownState = SHUTDOWN_START; RtlQueueWorkItem(ProcessWorkItem, wip, 0); RELEASE_DATABASE_LOCK; return NO_ERROR; ErrorExit: RELEASE_DATABASE_LOCK; return ERROR_CAN_NOT_COMPLETE; } VOID WorkerThread(VOID) { DWORD rc; DWORD signaled_event, delay; ULONG dueTime = GetTickCount() + MAXULONG/2; PWORK_ITEM wip; PLIST_ENTRY lep; HANDLE hModuleReference; hModuleReference = LoadLibrary (ModuleName); StartReceiver(); while(TRUE) { delay = dueTime - GetTickCount(); if(delay < MAXULONG/2) { // dueTime is later then present time while((rc = WaitForMultipleObjects( MAX_WORKER_THREAD_OBJECTS, WorkerThreadObjects, FALSE, // wait any delay // timeout )) == WAIT_IO_COMPLETION); } else { // dueTime already happened rc = WAIT_TIMEOUT; } if(rc == WAIT_TIMEOUT) { dueTime = ProcessTimerQueue(); } else { signaled_event = rc - WAIT_OBJECT_0; if(signaled_event < MAX_WORKER_THREAD_OBJECTS) { switch(signaled_event) { case TIMER_EVENT: dueTime = ProcessTimerQueue(); break; case REPOST_RCV_PACKETS_EVENT: RepostRcvPackets(); break; // case WORKERS_QUEUE_EVENT: // dequeue only one item from the work items queue // ACQUIRE_QUEUES_LOCK; // while(!IsListEmpty(&WorkersQueue)) { // lep = RemoveHeadList(&WorkersQueue); // wip = CONTAINING_RECORD(lep, WORK_ITEM, Linkage); // RELEASE_QUEUES_LOCK; // Queue the work item for processing by the // worker threads // RtlQueueWorkItem(ProcessWorkItem, // wip, // WT_EXECUTEINIOTHREAD); // never dieing workers so we can do send submits // and the thread won't die before send completes // ACQUIRE_QUEUES_LOCK; // } // RELEASE_QUEUES_LOCK; // break; case RTM_EVENT: ProcessRTMChanges(); break; case RIP_CHANGES_EVENT: ProcessRIPChanges(); break; case TERMINATE_WORKER_EVENT: // stop the StartChangesBcast work item DestroyStartChangesBcastWi = TRUE; // close the rip socket CloseRipSocket(); FlushTimerQueue(); CloseRTM(); // wait until no more work items while(WorkItemsCount != 0) { Trace(INIT_TRACE, "Terminating: Waiting for work items to be freed: %d outstanding ...\n", WorkItemsCount); Sleep(1000); } // destroy worker thread objects DestroyWorkerThreadObjects(); // destroy workers heap DestroyWorkItemsManager(); // post stop complete message PostEventMessage(ROUTER_STOPPED, NULL); Trace(INIT_TRACE, "Terminating: Stop completed and STOP Event Message posted\n"); FreeLibraryAndExitThread(hModuleReference, 0); break; default: break; } } } } } // table of handlers for work items which keep a reference to the if CB typedef VOID (* IF_WORK_ITEM_HANDLER)(PWORK_ITEM wip); IF_WORK_ITEM_HANDLER IfWorkItemHandler[] = { IfPeriodicBcast, IfCompleteGenResponse, IfChangeBcast, IfCheckUpdateStatus, IfPeriodicGenRequest }; #define MAX_IF_WORK_ITEM_HANDLERS sizeof(IfWorkItemHandler)/sizeof(IF_WORK_ITEM_HANDLER) VOID ProcessWorkItem(PWORK_ITEM wip) { PLIST_ENTRY lep; PICB icbp; switch(wip->Type) { case RECEIVE_PACKET_TYPE: // this work item references the interface via the adapter index ACQUIRE_DATABASE_LOCK; if(RipOperState != OPER_STATE_UP) { RELEASE_DATABASE_LOCK; } else { if((icbp = GetInterfaceByAdapterIndex(wip->AdapterIndex)) != NULL) { wip->icbp = icbp; ACQUIRE_IF_LOCK(icbp); RELEASE_DATABASE_LOCK; ProcessReceivedPacket(wip); RELEASE_IF_LOCK(icbp); } else { RELEASE_DATABASE_LOCK; } } // queue the receive packet back to recv thread for reposting EnqueueRcvPacketToRepostQueue(wip); break; case START_CHANGES_BCAST_TYPE: ACQUIRE_DATABASE_LOCK; StartChangesBcast(wip); RELEASE_DATABASE_LOCK; break; case SHUTDOWN_INTERFACES_TYPE: ACQUIRE_DATABASE_LOCK; ShutdownInterfaces(wip); RELEASE_DATABASE_LOCK; break; case DEBUG_TYPE: FreeWorkItem(wip); break; default: // all these work items reference the interface via an if CB pointer icbp = wip->icbp; ACQUIRE_IF_LOCK(icbp); (*IfWorkItemHandler[wip->Type])(wip); if(icbp->Discarded) { RELEASE_IF_LOCK(icbp); ACQUIRE_DATABASE_LOCK; ACQUIRE_IF_LOCK(icbp); if(--icbp->RefCount == 0) { // remove the if CB from the discarded queue and free it RemoveEntryList(&icbp->IfListLinkage); // free the interface CB Trace(INIT_TRACE, "ProcessWorkItem: Free DISCARDED if CB for if # %d\n", icbp->InterfaceIndex); DestroyInterfaceCB(icbp); } else { RELEASE_IF_LOCK(icbp); } RELEASE_DATABASE_LOCK; } else { icbp->RefCount--; RELEASE_IF_LOCK(icbp); } } } DWORD WINAPI GetEventMessage(ROUTING_PROTOCOL_EVENTS *Event, PMESSAGE Result) { PRIP_MESSAGE emp; PLIST_ENTRY lep; Trace(INIT_TRACE, "GetEventMessage: Entered\n"); ACQUIRE_DATABASE_LOCK; if((RipOperState == OPER_STATE_DOWN) || (RipOperState == OPER_STATE_STARTING)) { RELEASE_DATABASE_LOCK; return ERROR_CAN_NOT_COMPLETE; } ACQUIRE_QUEUES_LOCK; if(IsListEmpty(&RipMessageQueue)) { RELEASE_QUEUES_LOCK; RELEASE_DATABASE_LOCK; return ERROR_NO_MORE_ITEMS; } lep = RemoveHeadList(&RipMessageQueue); emp = CONTAINING_RECORD(lep, RIP_MESSAGE, Linkage); *Event = emp->Event; if(Result != NULL) { *Result = emp->Result; } if(emp->Event == ROUTER_STOPPED) { RipOperState = OPER_STATE_DOWN; StopTracing(); } GlobalFree(emp); RELEASE_QUEUES_LOCK; RELEASE_DATABASE_LOCK; return NO_ERROR; } VOID PostEventMessage(ROUTING_PROTOCOL_EVENTS Event, PMESSAGE Result) { PRIP_MESSAGE emp; if((emp = GlobalAlloc(GPTR, sizeof(RIP_MESSAGE))) == NULL) { return; } emp->Event = Event; if(Result != NULL) { emp->Result = *Result; } ACQUIRE_QUEUES_LOCK; InsertTailList(&RipMessageQueue, &emp->Linkage); RELEASE_QUEUES_LOCK; SetEvent(RM_Event); } DWORD CreateWorkerThreadObjects(VOID) { int i; for(i=0; iEventLogMask; RELEASE_DATABASE_LOCK; return NO_ERROR; #undef ripGlobalInfo } DWORD WINAPI GetGlobalInfo( IN PVOID GlobalInfo, IN OUT PULONG GlobalInfoSize ) { ACQUIRE_DATABASE_LOCK; if(RipOperState != OPER_STATE_UP) { RELEASE_DATABASE_LOCK; return ERROR_CAN_NOT_COMPLETE; } if ((*GlobalInfoSize>=sizeof (RIP_GLOBAL_INFO)) && (GlobalInfo!=NULL)) { #define ripGlobalInfo ((PRIP_GLOBAL_INFO)GlobalInfo) ripGlobalInfo->EventLogMask = RipEventLogMask; #undef ripGlobalInfo } *GlobalInfoSize = sizeof (RIP_GLOBAL_INFO); RELEASE_DATABASE_LOCK; return NO_ERROR; } VOID ProcessDequeuedIoPacket(DWORD ErrorCode, DWORD BytesTransferred, LPOVERLAPPED Overlappedp) { PWORK_ITEM wip; wip = CONTAINING_RECORD(Overlappedp, WORK_ITEM, Overlapped); wip->IoCompletionStatus = (DWORD)Overlappedp->Internal; switch(wip->Type) { case RECEIVE_PACKET_TYPE: ReceiveComplete(wip); break; default: SendComplete(wip); break; } }