/*++ Copyright (c) 1995 Microsoft Corporation Module Name: rpal.c Abstract: Routing protocols abstraction layer. It abstracts the RIP/SAP or NLSP routing protocols interface functions for the rest of the router manager system. Author: Stefan Solomon 04/24/1995 Revision History: --*/ #include "precomp.h" #pragma hdrstop // // Service Table Manager Entry Points // PIS_SERVICE STM_IsService; PCREATE_SERVICE_ENUMERATION_HANDLE STM_CreateServiceEnumerationHandle; PENUMERATE_GET_NEXT_SERVICE STM_EnumerateGetNextService; PCLOSE_SERVICE_ENUMERATION_HANDLE STM_CloseServiceEnumerationHandle; PGET_SERVICE_COUNT STM_GetServiceCount; PCREATE_STATIC_SERVICE STM_CreateStaticService; PDELETE_STATIC_SERVICE STM_DeleteStaticService; PBLOCK_CONVERT_SERVICES_TO_STATIC STM_BlockConvertServicesToStatic; PBLOCK_DELETE_STATIC_SERVICES STM_BlockDeleteStaticServices; PGET_FIRST_ORDERED_SERVICE STM_GetFirstOrderedService; PGET_NEXT_ORDERED_SERVICE STM_GetNextOrderedService; // // Auto-Static Update Entry Points // ULONG UpdateRoutesProtId; PDO_UPDATE_ROUTES RP_UpdateRoutes; ULONG UpdateServicesProtId; PDO_UPDATE_SERVICES RP_UpdateServices; // // Router Manager Support Functions - Passed to routing protocols at Start // SUPPORT_FUNCTIONS RMSupportFunctions = { RoutingProtocolConnectionRequest, MibCreate, MibDelete, MibSet, MibGet, MibGetFirst, MibGetNext }; //************************************************************************** // * // Routing Protocols Init/Start/Stop Functions * // * //************************************************************************** DWORD StartRoutingProtocols(LPVOID GlobalInfop, HANDLE RoutingProtocolsEvent) { DWORD rc, i, count, namelen, support; HINSTANCE moduleinstance; PRPCB rpcbp; LPVOID ProtocolGlobalInfop; PIPX_GLOBAL_INFO IpxGlobalInfop; MPR_PROTOCOL_0 *RtProtocolsp; DWORD NumRoutingProtocols; MPR_ROUTING_CHARACTERISTICS mrcRouting; MPR_SERVICE_CHARACTERISTICS mscService; // Initialize Routing Protocols List // InitializeListHead(&RoutingProtocolCBList); rc = MprSetupProtocolEnum (PID_IPX, (LPBYTE *)&RtProtocolsp, &NumRoutingProtocols); if (rc!=NO_ERROR) { Trace (RPAL_TRACE, "StartRoutingProtocols: failed to get routing protocol info: %d", rc); return ERROR_CAN_NOT_COMPLETE; } Trace (RPAL_TRACE, "StartRoutingProtocols: %d protocols installed.", NumRoutingProtocols); for (i=0; iRP_DllName = (PWSTR)((PUCHAR)rpcbp + sizeof(RPCB)); rpcbp->RP_DllHandle = moduleinstance; memcpy (rpcbp->RP_DllName, RtProtocolsp[i].wszDLLName, namelen) ; // Loading all entrypoints // rpcbp->RP_RegisterProtocol = (PREGISTER_PROTOCOL)GetProcAddress(moduleinstance, REGISTER_PROTOCOL_ENTRY_POINT_STRING) ; if(rpcbp->RP_RegisterProtocol == NULL) { // // Could not find the RegisterProtocol entry point // Nothing we can do - bail out // Sleep(0); MprSetupProtocolFree (RtProtocolsp); Trace(RPAL_TRACE, "StartRoutingProtocols: Could not find RegisterProtocol for %S", RtProtocolsp[i].wszDLLName); GlobalFree(rpcbp); FreeLibrary(moduleinstance); return ERROR_INVALID_FUNCTION; } ZeroMemory(&mrcRouting, sizeof(MPR_ROUTING_CHARACTERISTICS)); ZeroMemory(&mscService, sizeof(MPR_SERVICE_CHARACTERISTICS)); mrcRouting.dwVersion = MS_ROUTER_VERSION; mrcRouting.dwProtocolId = RtProtocolsp[i].dwProtocolId; mrcRouting.fSupportedFunctionality = ROUTING|DEMAND_UPDATE_ROUTES; mscService.dwVersion = MS_ROUTER_VERSION; mscService.dwProtocolId = RtProtocolsp[i].dwProtocolId; mscService.fSupportedFunctionality = SERVICES|DEMAND_UPDATE_SERVICES; rpcbp->RP_ProtocolId = RtProtocolsp[i].dwProtocolId; rc = rpcbp->RP_RegisterProtocol(&mrcRouting, &mscService); if(rc != NO_ERROR) { Sleep(0); IF_LOG (EVENTLOG_ERROR_TYPE) { CHAR num[8]; LPSTR pnum[1] = {num}; _ultoa (rpcbp->RP_ProtocolId, num, 16); RouterLogErrorA (RMEventLogHdl, ROUTERLOG_IPX_CANT_REGISTER_PROTOCOL, 1, pnum, rc); } FreeLibrary(moduleinstance); GlobalFree(rpcbp); Trace(RPAL_TRACE, "StartRoutingProtocols: %S returned error %d while registering", RtProtocolsp[i].wszDLLName, rc); MprSetupProtocolFree (RtProtocolsp); return rc; } rpcbp->RP_StartProtocol = mrcRouting.pfnStartProtocol; rpcbp->RP_StopProtocol = mrcRouting.pfnStopProtocol; rpcbp->RP_AddInterface = mrcRouting.pfnAddInterface; rpcbp->RP_DeleteInterface = mrcRouting.pfnDeleteInterface; rpcbp->RP_GetEventMessage = mrcRouting.pfnGetEventMessage; rpcbp->RP_GetIfConfigInfo = mrcRouting.pfnGetInterfaceInfo; rpcbp->RP_SetIfConfigInfo = mrcRouting.pfnSetInterfaceInfo; rpcbp->RP_BindInterface = mrcRouting.pfnBindInterface; rpcbp->RP_UnBindInterface = mrcRouting.pfnUnbindInterface; rpcbp->RP_EnableInterface = mrcRouting.pfnEnableInterface; rpcbp->RP_DisableInterface = mrcRouting.pfnDisableInterface; rpcbp->RP_GetGlobalInfo = mrcRouting.pfnGetGlobalInfo; rpcbp->RP_SetGlobalInfo = mrcRouting.pfnSetGlobalInfo; rpcbp->RP_MibCreate = mrcRouting.pfnMibCreateEntry; rpcbp->RP_MibDelete = mrcRouting.pfnMibDeleteEntry; rpcbp->RP_MibGet = mrcRouting.pfnMibGetEntry; rpcbp->RP_MibSet = mrcRouting.pfnMibSetEntry; rpcbp->RP_MibGetFirst = mrcRouting.pfnMibGetFirstEntry; rpcbp->RP_MibGetNext = mrcRouting.pfnMibGetNextEntry; if (!(rpcbp->RP_RegisterProtocol) || !(rpcbp->RP_StartProtocol) || !(rpcbp->RP_StopProtocol) || !(rpcbp->RP_AddInterface) || !(rpcbp->RP_DeleteInterface) || !(rpcbp->RP_GetEventMessage) || !(rpcbp->RP_GetIfConfigInfo) || !(rpcbp->RP_SetIfConfigInfo) || !(rpcbp->RP_BindInterface) || !(rpcbp->RP_UnBindInterface) || !(rpcbp->RP_EnableInterface) || !(rpcbp->RP_DisableInterface) || !(rpcbp->RP_GetGlobalInfo) || !(rpcbp->RP_SetGlobalInfo) || !(rpcbp->RP_MibCreate) || !(rpcbp->RP_MibDelete) || !(rpcbp->RP_MibSet) || !(rpcbp->RP_MibGet) || !(rpcbp->RP_MibGetFirst) || !(rpcbp->RP_MibGetNext)) { Trace(RPAL_TRACE, "StartRoutingProtocols: %ls failed to load entrypoints", RtProtocolsp[i].wszDLLName); GlobalFree(rpcbp); FreeLibrary (moduleinstance); MprSetupProtocolFree (RtProtocolsp); return ERROR_CAN_NOT_COMPLETE; } if(mscService.fSupportedFunctionality & SERVICES) { STM_IsService = mscService.pfnIsService; STM_CreateServiceEnumerationHandle = mscService.pfnCreateServiceEnumerationHandle; STM_EnumerateGetNextService = mscService.pfnEnumerateGetNextService; STM_CloseServiceEnumerationHandle = mscService.pfnCloseServiceEnumerationHandle; STM_GetServiceCount = mscService.pfnGetServiceCount; STM_CreateStaticService = mscService.pfnCreateStaticService; STM_DeleteStaticService = mscService.pfnDeleteStaticService; STM_BlockConvertServicesToStatic = mscService.pfnBlockConvertServicesToStatic; STM_BlockDeleteStaticServices = mscService.pfnBlockDeleteStaticServices; STM_GetFirstOrderedService = mscService.pfnGetFirstOrderedService; STM_GetNextOrderedService = mscService.pfnGetNextOrderedService; if(!(STM_IsService) || !(STM_CreateServiceEnumerationHandle) || !(STM_EnumerateGetNextService) || !(STM_CloseServiceEnumerationHandle) || !(STM_GetServiceCount) || !(STM_CreateStaticService) || !(STM_DeleteStaticService) || !(STM_BlockConvertServicesToStatic) || !(STM_BlockDeleteStaticServices) || !(STM_GetFirstOrderedService) || !(STM_GetNextOrderedService)) { Trace(RPAL_TRACE, "StartRoutingProtocols: %ls failed to get STM entry points\n", RtProtocolsp[i].wszDLLName); GlobalFree(rpcbp); FreeLibrary (moduleinstance); MprSetupProtocolFree (RtProtocolsp); return ERROR_CAN_NOT_COMPLETE; } } if(mrcRouting.fSupportedFunctionality & DEMAND_UPDATE_ROUTES) { RP_UpdateRoutes = mrcRouting.pfnUpdateRoutes; if(!RP_UpdateRoutes) { Trace(RPAL_TRACE, "StartRoutingProtocols: %ls failed to get UpdateRoutes entry points\n", RtProtocolsp[i].wszDLLName); GlobalFree(rpcbp); FreeLibrary (moduleinstance); MprSetupProtocolFree (RtProtocolsp); return ERROR_CAN_NOT_COMPLETE; } UpdateRoutesProtId = rpcbp->RP_ProtocolId; } if(mscService.fSupportedFunctionality & DEMAND_UPDATE_SERVICES) { RP_UpdateServices = mscService.pfnUpdateServices; if(!RP_UpdateServices) { Trace(RPAL_TRACE, "StartRoutingProtocols: %ls failed to get UpdateServices entry points\n", RtProtocolsp[i].wszDLLName); GlobalFree(rpcbp); FreeLibrary (moduleinstance); MprSetupProtocolFree (RtProtocolsp); return ERROR_CAN_NOT_COMPLETE; } UpdateServicesProtId = rpcbp->RP_ProtocolId; } ProtocolGlobalInfop = GetInfoEntry(GlobalInfop, rpcbp->RP_ProtocolId); if ((rc = (*rpcbp->RP_StartProtocol)(RoutingProtocolsEvent, &RMSupportFunctions, ProtocolGlobalInfop)) != NO_ERROR) { IF_LOG (EVENTLOG_ERROR_TYPE) { CHAR num[8]; LPSTR pnum[1] = {num}; _ultoa (rpcbp->RP_ProtocolId, num, 16); RouterLogErrorA (RMEventLogHdl, ROUTERLOG_IPX_CANT_START_PROTOCOL, 1, pnum, rc); } Trace(RPAL_TRACE, "StartRoutingProtocols: %ls failed to start: %d\n", RtProtocolsp[i].wszDLLName, rc); GlobalFree(rpcbp); FreeLibrary (moduleinstance); MprSetupProtocolFree (RtProtocolsp); return ERROR_CAN_NOT_COMPLETE; } // Insert this routing protocol in the list of routing protocols // InsertTailList(&RoutingProtocolCBList, &rpcbp->RP_Linkage); RoutingProtocolActiveCount++; Trace(RPAL_TRACE, "StartRoutingProtocols: %ls successfully initialized", RtProtocolsp[i].wszDLLName); } if(!RP_UpdateRoutes || !RP_UpdateServices) { Trace(RPAL_TRACE, "StartRoutingProtocols: missing update entry point\n"); MprSetupProtocolFree (RtProtocolsp); return ERROR_CAN_NOT_COMPLETE; } MprSetupProtocolFree (RtProtocolsp); Trace(RPAL_TRACE, ""); return NO_ERROR; } VOID StopRoutingProtocols(VOID) { PLIST_ENTRY lep; PRPCB rpcbp; DWORD rc; lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); (*rpcbp->RP_StopProtocol)(); lep = lep->Flink; } } //************************************************************************** // * // Routing Protocols Interface Management Functions * // * //************************************************************************** DWORD CreateRoutingProtocolsInterfaces(PIPX_INFO_BLOCK_HEADER InterfaceInfop, PICB icbp) { PLIST_ENTRY lep; PRPCB rpcbp; LPVOID RpIfInfop; NET_INTERFACE_TYPE NetInterfaceType; DWORD rc; NetInterfaceType = MapIpxToNetInterfaceType(icbp); lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); RpIfInfop = GetInfoEntry(InterfaceInfop, rpcbp->RP_ProtocolId); if(RpIfInfop == NULL) { return ERROR_CAN_NOT_COMPLETE; } rc = (*rpcbp->RP_AddInterface)( icbp->InterfaceNamep, icbp->InterfaceIndex, NetInterfaceType, RpIfInfop); if(rc != NO_ERROR) { return rc; } lep = lep->Flink; } return NO_ERROR; } DWORD DeleteRoutingProtocolsInterfaces(ULONG InterfaceIndex) { PLIST_ENTRY lep; PRPCB rpcbp; lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); (*rpcbp->RP_DeleteInterface)(InterfaceIndex); lep = lep->Flink; } return NO_ERROR; } ULONG SizeOfRoutingProtocolsIfsInfo(ULONG InterfaceIndex) { PLIST_ENTRY lep; PRPCB rpcbp; ULONG TotalInfoSize = 0; ULONG RpIfInfoSize = 0; lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); (*rpcbp->RP_GetIfConfigInfo)(InterfaceIndex, NULL, &RpIfInfoSize); // align size on double DWORD boundary - add two's complement for the last three bits RpIfInfoSize += ((~RpIfInfoSize) + 1) & 0x7; TotalInfoSize += RpIfInfoSize; RpIfInfoSize = 0; lep = lep->Flink; } return TotalInfoSize; } ULONG RoutingProtocolsTocCount(VOID) { return(RoutingProtocolActiveCount); } /*++ Function: CreateRoutingProtocolsTocAndInfoEntries Descr: as it says Arguments: ibhp - ptr to the info block header InterfaceIndex current_tocepp - ptr to the location of the current TOC ptr; you have to increment this to get to the next TOC -> your TOC! current_NextInfoOffsetp - pointer to the location of the next info entry offset in the info block; you have to use this for your info entry and then to increment it for the next user. --*/ DWORD CreateRoutingProtocolsTocAndInfoEntries(PIPX_INFO_BLOCK_HEADER ibhp, ULONG InterfaceIndex, PIPX_TOC_ENTRY *current_tocepp, PULONG current_NextInfoOffsetp) { PIPX_TOC_ENTRY tocep; ULONG NextInfoOffset; PLIST_ENTRY lep; PRPCB rpcbp; ULONG RpIfInfoSize; DWORD rc; lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); // increment the current pointer to table of contents entries so it will // point to the next entry (*current_tocepp)++; tocep = *current_tocepp; // create the routing protocol and info toc entry tocep->InfoType = rpcbp->RP_ProtocolId; tocep->InfoSize = 0; tocep->Count = 1; tocep->Offset = *current_NextInfoOffsetp; rc = (*rpcbp->RP_GetIfConfigInfo)(InterfaceIndex, (LPVOID)((PUCHAR)ibhp + tocep->Offset), &tocep->InfoSize); if(rc != ERROR_INSUFFICIENT_BUFFER) { return rc; } rc = (*rpcbp->RP_GetIfConfigInfo)(InterfaceIndex, (LPVOID)((PUCHAR)ibhp + tocep->Offset), &tocep->InfoSize); if(rc != NO_ERROR) { return rc; } *current_NextInfoOffsetp += tocep->InfoSize; // align the next offset on DWORD boundary *current_NextInfoOffsetp += (~*current_NextInfoOffsetp + 1) & 0x7; lep = lep->Flink; } return NO_ERROR; } DWORD SetRoutingProtocolsInterfaces(PIPX_INFO_BLOCK_HEADER InterfaceInfop, ULONG InterfaceIndex) { PLIST_ENTRY lep; PRPCB rpcbp; LPVOID RpIfInfop; lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); RpIfInfop = GetInfoEntry(InterfaceInfop, rpcbp->RP_ProtocolId); if(RpIfInfop == NULL) { return ERROR_CAN_NOT_COMPLETE; } (*rpcbp->RP_SetIfConfigInfo)(InterfaceIndex, RpIfInfop); lep = lep->Flink; } return NO_ERROR; } DWORD BindRoutingProtocolsIfsToAdapter(ULONG InterfaceIndex, PIPX_ADAPTER_BINDING_INFO abip) { PLIST_ENTRY lep; PRPCB rpcbp; lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); (*rpcbp->RP_BindInterface)(InterfaceIndex, abip); lep = lep->Flink; } return NO_ERROR; } DWORD UnbindRoutingProtocolsIfsFromAdapter(ULONG InterfaceIndex) { PLIST_ENTRY lep; PRPCB rpcbp; lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); (*rpcbp->RP_UnBindInterface)(InterfaceIndex); lep = lep->Flink; } return NO_ERROR; } DWORD RoutingProtocolsEnableIpxInterface(ULONG InterfaceIndex) { PLIST_ENTRY lep; PRPCB rpcbp; lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); (*rpcbp->RP_EnableInterface)(InterfaceIndex); lep = lep->Flink; } return NO_ERROR; } DWORD RoutingProtocolsDisableIpxInterface(ULONG InterfaceIndex) { PLIST_ENTRY lep; PRPCB rpcbp; lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); (*rpcbp->RP_DisableInterface)(InterfaceIndex); lep = lep->Flink; } return NO_ERROR; } //************************************************************************** // * // Routing Protocols Services Management Functions * // * //************************************************************************** DWORD GetFirstService(DWORD OrderingMethod, DWORD ExclusionFlags, PIPX_SERVICE Servicep) { return(*STM_GetFirstOrderedService)(OrderingMethod, ExclusionFlags, Servicep); } DWORD GetNextService(DWORD OrderingMethod, DWORD ExclusionFlags, PIPX_SERVICE Servicep) { return(*STM_GetNextOrderedService)(OrderingMethod, ExclusionFlags, Servicep); } DWORD CreateStaticService(PICB icbp, PIPX_STATIC_SERVICE_INFO ServiceEntry) { DWORD rc; rc = (*STM_CreateStaticService)(icbp->InterfaceIndex, ServiceEntry); return rc; } DWORD DeleteStaticService(ULONG InterfaceIndex, PIPX_STATIC_SERVICE_INFO ServiceEntry) { DWORD rc; rc = (*STM_DeleteStaticService)(InterfaceIndex, ServiceEntry); return rc; } HANDLE CreateStaticServicesEnumHandle(ULONG InterfaceIndex) { IPX_SERVICE CriteriaService; HANDLE EnumHandle; if(STM_CreateServiceEnumerationHandle == NULL) { return NULL; } memset(&CriteriaService, 0, sizeof(IPX_SERVICE)); CriteriaService.InterfaceIndex = InterfaceIndex; CriteriaService.Protocol = IPX_PROTOCOL_STATIC; EnumHandle = (*STM_CreateServiceEnumerationHandle)( STM_ONLY_THIS_INTERFACE | STM_ONLY_THIS_PROTOCOL, &CriteriaService); return EnumHandle; } DWORD GetNextStaticService(HANDLE EnumHandle, PIPX_STATIC_SERVICE_INFO StaticSvInfop) { IPX_SERVICE Service; DWORD rc; if((rc = (*STM_EnumerateGetNextService)(EnumHandle, &Service)) == NO_ERROR) { // fill in the static service structure *StaticSvInfop = Service.Server; } return rc; } DWORD CloseStaticServicesEnumHandle(HANDLE EnumHandle) { DWORD rc = NO_ERROR; if(EnumHandle) { rc = (*STM_CloseServiceEnumerationHandle)(EnumHandle); } return rc; } DWORD DeleteAllStaticServices(ULONG InterfaceIndex) { if(!STM_BlockDeleteStaticServices) { return NO_ERROR; } return((*STM_BlockDeleteStaticServices)(InterfaceIndex)); } DWORD GetServiceCount(VOID) { if(!STM_GetServiceCount) { return 0; } return((*STM_GetServiceCount)()); } //************************************************************************** // * // Routing Protocols Auto-Static Update Functions * // * //************************************************************************** DWORD RtProtRequestRoutesUpdate(ULONG InterfaceIndex) { return((*RP_UpdateRoutes)(InterfaceIndex)); } DWORD RtProtRequestServicesUpdate(ULONG InterfaceIndex) { return((*RP_UpdateServices)(InterfaceIndex)); } VOID DestroyRoutingProtocolCB(PRPCB rpcbp) { RemoveEntryList(&rpcbp->RP_Linkage); FreeLibrary (rpcbp->RP_DllHandle); GlobalFree(rpcbp); RoutingProtocolActiveCount--; } VOID ConvertAllServicesToStatic(ULONG InterfaceIndex) { STM_BlockConvertServicesToStatic(InterfaceIndex); } DWORD GetStaticServicesCount(ULONG InterfaceIndex) { HANDLE EnumHandle; DWORD Count; IPX_STATIC_SERVICE_INFO StaticSvInfo; EnumHandle = CreateStaticServicesEnumHandle(InterfaceIndex); if(EnumHandle == NULL) { return 0; } Count = 0; while(GetNextStaticService(EnumHandle, &StaticSvInfo) == NO_ERROR) { Count++; } CloseStaticServicesEnumHandle(EnumHandle); return Count; } PRPCB GetRoutingProtocolCB(DWORD ProtocolId) { PRPCB rpcbp; PLIST_ENTRY lep; lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); if(rpcbp->RP_ProtocolId == ProtocolId) { return rpcbp; } lep = lep->Flink; } return NULL; } BOOL IsService(USHORT SvType, PUCHAR SvName, PIPX_SERVICE Svp) { return(STM_IsService(SvType, SvName, Svp)); } DWORD SetRoutingProtocolsGlobalInfo(PIPX_INFO_BLOCK_HEADER GlobalInfop) { PLIST_ENTRY lep; PRPCB rpcbp; LPVOID RpGlobalInfop; lep = RoutingProtocolCBList.Flink; while(lep != &RoutingProtocolCBList) { rpcbp = CONTAINING_RECORD(lep, RPCB, RP_Linkage); RpGlobalInfop = GetInfoEntry(GlobalInfop, rpcbp->RP_ProtocolId); if(RpGlobalInfop == NULL) { return NO_ERROR; } (*rpcbp->RP_SetGlobalInfo)(RpGlobalInfop); lep = lep->Flink; } return NO_ERROR; }