/*++ Copyright (c) 1995 Microsoft Corporation Module Name: net\routing\ipx\sap\intfdb.c Abstract: This module maintains IPX interface configuration and provides interface configuration API for external modules (Router Manager) Author: Vadim Eydelman 05-15-1995 Revision History: --*/ #include "sapp.h" #define IDB_NUM_OF_INTF_HASH_BUCKETS 256 #define IDB_NUM_OF_ADPT_HASH_BUCKETS 32 // Number of additional recv requests to post when binding the interface // that has listen enabled ULONG NewRequestsPerInterface=SAP_REQUESTS_PER_INTF_DEF; // Default filtering mode (for standalone service only) UCHAR FilterOutMode=SAP_DONT_FILTER; #define IntfHashFunction(intf) ((intf)&(IDB_NUM_OF_INTF_HASH_BUCKETS-1)) #define AdptHashFunction(adpt) ((adpt)&(IDB_NUM_OF_ADPT_HASH_BUCKETS-1)) UCHAR INTERNAL_IF_NODE[6] = {0}; UCHAR INTERNAL_IF_NET[4] = {0}; // Interface control block typedef struct _INTERFACE_NODE { INTERFACE_DATA IN_Data; // Externally visible data NET_INTERFACE_TYPE IN_Type; // Interface type PSAP_IF_FILTERS IN_Filters; // Filter description array PFILTER_NODE IN_FilterNodes; // Filter node array hashed in // the filter table LIST_ENTRY IN_IntfLink; // Link in interface table // Used to test if interface block is // is the table (it is not if Flink==Blink) LIST_ENTRY IN_AdptLink; // Link in adapter table LIST_ENTRY IN_ListLink; // Link in interface list LONG IN_RefCount; // Number of times interface data // was referenced, if above 0, interface block // can't be deleted (last client that refers // it will do this). BOOL IN_InUse; // Flag that is set when interface // is bound. It is reset by the last client // that refers to interface after it frees // all resources (if any) that were allocated // at bind time. If reference count is zero // but this flag is set, the last client that // refered to this interface is in process // of releasing resources (waiting on critical // section to lock interface block) and should // be allowed to finish this operation } INTERFACE_NODE, *PINTERFACE_NODE; // Usefull field access macros #define IN_Name IN_Data.name #define IN_IntfIdx IN_Data.index #define IN_Info IN_Data.info #define IN_Adpt IN_Data.adapter #define IN_AdptIdx IN_Data.adapter.AdapterIndex #define IN_Stats IN_Data.stats #define IN_Enabled IN_Data.enabled #define IN_FilterIn IN_Data.filterIn #define IN_FilterOut IN_Data.filterOut // This macro is used to screen interface blocks that were deleted // from the table or replaced and must be disposed of by the last // user that refers to it #define IsInterfaceValid(node) IsListEntry(&node->IN_IntfLink) // This macro must be used to identify interface blocks that // were deleted from the table and must be disposed of by the // last user that refers to it #define InvalidateInterface(node) InitializeListEntry(&node->IN_IntfLink) // Table of interface control blocks typedef struct _INTERFACE_TABLE { LONG IT_NumOfActiveInterfaces; // Number of active (enabled and bound) // interfaces (we close Adapter port when // this number drops to 0) #if DBG LIST_ENTRY IT_DetachedIntf; // List of interfaces that were // removed from the table and await to be // disposed of when the last client releases // reference to them #endif LIST_ENTRY IT_IntfHash[IDB_NUM_OF_INTF_HASH_BUCKETS]; // Interface control blocks hashed by interface // index LIST_ENTRY IT_AdptHash[IDB_NUM_OF_ADPT_HASH_BUCKETS]; // Interface control blocks hashed by adapter // index to which corresponding interface is // bound CRITICAL_SECTION IT_Lock; // Interface table data protection } INTERFACE_TABLE, *PINTERFACE_TABLE; // List of interface blocks in InterfaceIndex order typedef struct _INTERFACE_LIST { CRITICAL_SECTION IL_Lock; // List data protection LIST_ENTRY IL_Head; // List head } INTERFACE_LIST, *PINTERFACE_LIST; INTERFACE_TABLE InterfaceTable; INTERFACE_LIST InterfaceList; HANDLE ShutdownDoneEvent=NULL; // Find if interface control block exists for interface index and // return pointer to it (node), otherwise return place where // new interface block should be inserted (cur) #define if_IsInterfaceNode(InterfaceIndex,node,cur) { \ PLIST_ENTRY HashList=&InterfaceTable.IT_IntfHash[ \ IntfHashFunction(InterfaceIndex)]; \ EnterCriticalSection (&InterfaceTable.IT_Lock); \ cur = HashList->Flink; \ while (cur != HashList) { \ node = CONTAINING_RECORD (cur, INTERFACE_NODE, IN_IntfLink);\ if (InterfaceIndex <= node->IN_IntfIdx) \ break; \ cur = cur->Flink; \ } \ } \ if ((cur==&node->IN_IntfLink) \ && (InterfaceIndex==node->IN_IntfIdx)) // Local prototypes DWORD StartInterface ( PINTERFACE_NODE node ); VOID FreeBindingResources ( PINTERFACE_NODE node ); DWORD StopInterface ( PINTERFACE_NODE node ); #if DBG VOID DumpPacket ( PSAP_BUFFER packet, DWORD count ); #else #define DumpPacket(packet,count) #endif PWCHAR SapDuplicateString (IN PWCHAR pszString) { PWCHAR pszRet; DWORD dwLen; if (!pszString) return NULL; dwLen = wcslen (pszString); pszRet = GlobalAlloc (GMEM_FIXED, (dwLen * sizeof(WCHAR)) + sizeof(WCHAR)); if (pszRet) { wcscpy (pszRet, pszString); } return pszRet; } VOID SapFreeDuplicatedString (IN PWCHAR pszString) { if (pszString) GlobalFree (pszString); } /*++ ******************************************************************* C r e a t e I n t e r f a c e T a b l e Routine Description: Allocates resources for interface table Arguments: None Return Value: NO_ERROR - resources were allocated successfully other - reason of failure (windows error code) ******************************************************************* --*/ DWORD CreateInterfaceTable ( void ) { INT i; DWORD status; InitializeCriticalSection (&InterfaceList.IL_Lock); InitializeListHead (&InterfaceList.IL_Head); InitializeCriticalSection (&InterfaceTable.IT_Lock); for (i=0; iIN_Stats.SapIfOperState==OPER_STATE_UP) { node->IN_Info.Listen = ADMIN_STATE_DISABLED; // This will prevent deletion // of all services associated with // interface when it is stopped (done by the caller) node->IN_Stats.SapIfOperState = OPER_STATE_STOPPING; StopInterface (node); } // Remove interface control block Trace (DEBUG_INTERFACES, "Invalidating interface block: %lX(%d).", node, node->IN_IntfIdx); RemoveEntryList (&node->IN_IntfLink); InvalidateInterface (node); RemoveEntryList (&node->IN_ListLink); // Dispose only if nobody uses it and not waiting on critical // section to dispose of it if ((node->IN_RefCount==0) && !node->IN_InUse) { Trace (DEBUG_INTERFACES, "Releasing interface block: %lX(%d).", node, node->IN_IntfIdx); GlobalFree (node); } // Otherwise, just leave it hang outside of the table // till last client releases reference to it #if DBG else // Keep track of all blocks in debugging mode InsertTailList (&InterfaceTable.IT_DetachedIntf, &node->IN_ListLink); #endif } } } LeaveCriticalSection (&InterfaceTable.IT_Lock); LeaveCriticalSection (&InterfaceList.IL_Lock); } /*++ ******************************************************************* S t o p I n t e r f a c e s Routine Description: Stops all sap interfaces if not already stopped. Arguments: None Return Value: None ******************************************************************* --*/ VOID StopInterfaces ( void ) { INT i; // Delete all interface control blocks EnterCriticalSection (&InterfaceList.IL_Lock); EnterCriticalSection (&InterfaceTable.IT_Lock); for (i=0; iIN_Stats.SapIfOperState==OPER_STATE_UP) { // Stop all bound interfaces node->IN_Info.Listen = ADMIN_STATE_DISABLED; // This will prevent deletion // of all services associated with // interface when it is stopped (done by the caller) node->IN_Stats.SapIfOperState = OPER_STATE_STOPPING; StopInterface (node); } // Remove and dispose of original interface control block Trace (DEBUG_INTERFACES, "Invalidating interface block: %lX(%d).", node, node->IN_IntfIdx); RemoveEntryList (&node->IN_IntfLink); InvalidateInterface (node); RemoveEntryList (&node->IN_ListLink); // Dispose only if nobody uses it and not waiting on critical // section to dispose of it if ((node->IN_RefCount==0) && !node->IN_InUse) { Trace (DEBUG_INTERFACES, "Releasing interface block: %lX(%d).", node, node->IN_IntfIdx); GlobalFree (node); } // Otherwise, just leave it hang outside of the table // till last client releases reference to it #if DBG else // Keep track of all blocks in debugging mode InsertTailList (&InterfaceTable.IT_DetachedIntf, &node->IN_ListLink); #endif } } LeaveCriticalSection (&InterfaceTable.IT_Lock); LeaveCriticalSection (&InterfaceList.IL_Lock); } /*++ ******************************************************************* D e l e t e I n t e r f a c e T a b l e Routine Description: Release all resources associated with interface table Arguments: None Return Value: NO_ERROR - operation completed OK ******************************************************************* --*/ VOID DeleteInterfaceTable ( void ) { DeleteCriticalSection (&InterfaceList.IL_Lock); DeleteCriticalSection (&InterfaceTable.IT_Lock); } /*++ ******************************************************************* A c q u i r e I n t e r f a c e R e f e n c e Routine Description: Increments reference count of interface block. If reference count is greater than 0, the externally visible data in the block are locked (can't be modified) Arguments: intf - pointer to externally visible part of interface control block Return Value: None ******************************************************************* --*/ VOID AcquireInterfaceReference ( IN PINTERFACE_DATA intf ) { PINTERFACE_NODE node = CONTAINING_RECORD(intf, INTERFACE_NODE, IN_Data); InterlockedIncrement(&node->IN_RefCount); } /*++ ******************************************************************* R e l e a s e I n t e r f a c e R e f e n c e Routine Description: Decrements reference count of interface block. When reference count drops to 0, cleanup routine gets called to dispose of all resources allocated at bind time and if interface control block is already removed from the table it gets disposed of as well Arguments: intf - pointer to externally visible part of interface control block Return Value: None ******************************************************************* --*/ VOID ReleaseInterfaceReference ( IN PINTERFACE_DATA intf ) { PINTERFACE_NODE node = CONTAINING_RECORD (intf, INTERFACE_NODE, IN_Data); if (InterlockedDecrement (&node->IN_RefCount)==0) { // This is the last client that refered to this interface block // It should cleanup all resources allocated at bind time // and possibly dispose of interface block itself EnterCriticalSection (&InterfaceTable.IT_Lock); FreeBindingResources (node); if (!IsInterfaceValid(node)) { Trace (DEBUG_INTERFACES, "Releasing interface block: %lX(%d).", node, node->IN_IntfIdx); #if DBG // Debugging mode code keeps all deleted nodes in // detached list RemoveEntryList (&node->IN_ListLink); #endif if (node->IN_Filters!=NULL) { if (node->IN_Filters->SupplyFilterCount>0) ReplaceFilters ( FILTER_TYPE_SUPPLY, &node->IN_FilterNodes[0], node->IN_Filters->SupplyFilterCount, NULL, 0); if (node->IN_Filters->ListenFilterCount>0) ReplaceFilters ( FILTER_TYPE_LISTEN, &node->IN_FilterNodes[node->IN_Filters->SupplyFilterCount], node->IN_Filters->ListenFilterCount, NULL, 0); GlobalFree (node->IN_Filters); } if (node->IN_Name!=NULL) SapFreeDuplicatedString (node->IN_Name); GlobalFree (node); } LeaveCriticalSection (&InterfaceTable.IT_Lock); } } /*++ ******************************************************************* F r e e B i n d i n g R e s o u r c e s Routine Description: Disposes of all resources allocated at bind time and marks interface block as not used. Interface Table must be locked when calling this routine (unless node is already removed from the table) Arguments: node - pointer to interface control block Return Value: None ******************************************************************* --*/ VOID FreeBindingResources ( PINTERFACE_NODE node ) { Trace (DEBUG_INTERFACES, "Releasing binding resources for interface block: %lX(%d).", node, node->IN_IntfIdx); node->IN_InUse = FALSE; if (node->IN_Enabled && (node->IN_Info.AdminState==ADMIN_STATE_ENABLED)) node->IN_Stats.SapIfOperState = OPER_STATE_SLEEPING; else node->IN_Stats.SapIfOperState = OPER_STATE_DOWN; InterfaceTable.IT_NumOfActiveInterfaces -= 1; if (InterfaceTable.IT_NumOfActiveInterfaces==0) { Trace (DEBUG_INTERFACES, "All interfaces have been shut down."); if (ShutdownDoneEvent!=NULL) { BOOL res = SetEvent (ShutdownDoneEvent); ASSERTERRMSG ("Could not set shutdown done event ", res); ShutdownDoneEvent = NULL; } } } /*++ ******************************************************************* G e t I n t e r f a c e R e f e r e n c e Routine Description: Finds interface control block that bound to adapter and increments reference count on it (to prevent it from deletion while it is used). Arguments: AdapterIndex - unique number that indentifies adapter Return Value: Pointer to externally visible part of interface control block NULL if no interface is bound to the adapter ******************************************************************* --*/ PINTERFACE_DATA GetInterfaceReference ( ULONG AdapterIndex ) { PLIST_ENTRY HashList = &InterfaceTable.IT_AdptHash [AdptHashFunction(AdapterIndex)]; PINTERFACE_NODE node; PLIST_ENTRY cur; EnterCriticalSection (&InterfaceTable.IT_Lock); cur = HashList->Flink; while (cur!=HashList) { node = CONTAINING_RECORD (cur, INTERFACE_NODE, IN_AdptLink); if (node->IN_AdptIdx==AdapterIndex) { InterlockedIncrement (&node->IN_RefCount); break; } cur = cur->Flink; } LeaveCriticalSection (&InterfaceTable.IT_Lock); if (cur!=HashList) return &node->IN_Data; else return NULL; } /*++ ******************************************************************* S t a r t I n t e r f a c e Routine Description: Initiate sap on interface Interface Table must be locked when calling this routine Arguments: node - pointer to interface control block Return Value: None ******************************************************************* --*/ DWORD StartInterface ( PINTERFACE_NODE node ) { DWORD status = NO_ERROR; Trace (DEBUG_INTERFACES, "Starting SAP for interface block: %lX(%d,%d).", node, node->IN_IntfIdx, node->IN_AdptIdx); node->IN_Stats.SapIfOperState = OPER_STATE_UP; node->IN_InUse = TRUE; // Create binding reference InterlockedIncrement (&node->IN_RefCount); InsertTailList ( &InterfaceTable.IT_AdptHash[AdptHashFunction(node->IN_AdptIdx)], &node->IN_AdptLink); InterfaceTable.IT_NumOfActiveInterfaces += 1; if ((status==NO_ERROR) && (node->IN_Info.UpdateMode==IPX_STANDARD_UPDATE)) { AddRecvRequests (NewRequestsPerInterface); if (node->IN_Info.Supply==ADMIN_STATE_ENABLED) status = InitBcastItem (&node->IN_Data); if ((status==NO_ERROR) && (node->IN_Info.Listen==ADMIN_STATE_ENABLED)) status = InitSreqItem (&node->IN_Data); } if (status!=NO_ERROR) { node->IN_Stats.SapIfOperState = OPER_STATE_DOWN; RemoveEntryList (&node->IN_AdptLink); if (node->IN_Info.UpdateMode==IPX_STANDARD_UPDATE) { RemoveRecvRequests (NewRequestsPerInterface); } if (InterlockedDecrement (&node->IN_RefCount)==0) // Cleanup binding resources if this is the // last reference to the interface control block FreeBindingResources (node); } return status; } /*++ ******************************************************************* S t o p I n t e r f a c e Routine Description: Stop sap on interface Interface Table must be locked when calling this routine Arguments: node - pointer to interface control block Return Value: None ******************************************************************* --*/ DWORD StopInterface ( PINTERFACE_NODE node ) { DWORD status=NO_ERROR; Trace (DEBUG_INTERFACES, "Stopping SAP for interface block: %lX(%d,%d).", node, node->IN_IntfIdx, node->IN_AdptIdx); if (node->IN_Stats.SapIfOperState==OPER_STATE_UP) { // Set the state of the interface if not already set. if (node->IN_Enabled && (node->IN_Info.AdminState==ADMIN_STATE_ENABLED) && (node->IN_Type!=PERMANENT)) node->IN_Stats.SapIfOperState = OPER_STATE_SLEEPING; else node->IN_Stats.SapIfOperState = OPER_STATE_DOWN; } RemoveEntryList (&node->IN_AdptLink); if (node->IN_Info.UpdateMode==IPX_STANDARD_UPDATE) { RemoveRecvRequests (NewRequestsPerInterface); } if (InterlockedDecrement (&node->IN_RefCount)==0) // Cleanup binding resources if we released the // last reference to the interface control block FreeBindingResources (node); else // Have clients get in sync fast. ExpireLRRequests ((PVOID)UlongToPtr(node->IN_IntfIdx)); // Delete all services obtained through SAP if we were actually // listening to SAP announcements on this interface if (node->IN_Info.Listen==ADMIN_STATE_ENABLED) { HANDLE enumHdl = CreateListEnumerator (SDB_INTF_LIST_LINK, 0xFFFF, NULL, node->IN_IntfIdx, IPX_PROTOCOL_SAP, SDB_DISABLED_NODE_FLAG); // Delete all services obtained through sap if (enumHdl!=NULL) { EnumerateServers (enumHdl, DeleteAllServersCB, enumHdl); DeleteListEnumerator (enumHdl); } else Trace (DEBUG_FAILURES, "File: %s, line %ld." " Could not create enumerator to delete" " sap servers for interface: %ld.", __FILE__, __LINE__, node->IN_IntfIdx); } return status; } DWORD WINAPI UnbindInterface( IN ULONG InterfaceIndex ); SetInterfaceConfigInfo( IN ULONG InterfaceIndex, IN PVOID InterfaceInfo); DWORD UpdateInterfaceState ( PINTERFACE_NODE node ); DWORD SapUpdateLocalServers (); // Makes pnp changes to an interface DWORD SapReconfigureInterface (ULONG InterfaceIndex, PIPX_ADAPTER_BINDING_INFO pAdapter) { PLIST_ENTRY cur; PINTERFACE_NODE node; DWORD dwErr; Trace (DEBUG_INTERFACES, "SapReconfigureInterface: entered for %d", InterfaceIndex); // Lock the interface list and get reference to the // sought after control node. EnterCriticalSection (&InterfaceList.IL_Lock); if_IsInterfaceNode(InterfaceIndex, node, cur) { // Update the information maintained in the interfaces node->IN_Adpt = *pAdapter; UpdateInterfaceState ( node ); } // Unlock LeaveCriticalSection (&InterfaceTable.IT_Lock); LeaveCriticalSection (&InterfaceList.IL_Lock); // If the internal network number was updated, go through all // local servers and update their control blocks accordingly. if (InterfaceIndex == INTERNAL_INTERFACE_INDEX) { if ((dwErr = SapUpdateLocalServers ()) != NO_ERROR) { Trace (DEBUG_INTERFACES, "ERR: SapUpdateLocalServers returned %x", dwErr); } } return NO_ERROR; } /*++ ******************************************************************* S a p C r e a t e S a p I n t e r f a c e Routine Description: Add interface control block for new interface Arguments: InterfaceIndex - unique number that indentifies new interface SapIfConfig - interface configuration info Return Value: NO_ERROR - interface was created OK ERROR_ALREADY_EXISTS - interface with this index already exists other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapCreateSapInterface ( LPWSTR InterfaceName, ULONG InterfaceIndex, NET_INTERFACE_TYPE InterfaceType, PSAP_IF_INFO SapIfConfig ) { PLIST_ENTRY cur; PINTERFACE_NODE node; DWORD status = NO_ERROR; EnterCriticalSection (&InterfaceList.IL_Lock); if_IsInterfaceNode(InterfaceIndex,node,cur) { Trace (DEBUG_INTERFACES, "Interface %ld already exists.",InterfaceIndex); status = ERROR_ALREADY_EXISTS; } else { node = (PINTERFACE_NODE)GlobalAlloc (GMEM_FIXED, sizeof (INTERFACE_NODE)); if (node!=NULL) { node->IN_Name = SapDuplicateString (InterfaceName); if (node->IN_Name!=NULL) { node->IN_RefCount = 0; node->IN_InUse = FALSE; node->IN_Data.name = node->IN_Name; node->IN_IntfIdx = InterfaceIndex; node->IN_AdptIdx = INVALID_ADAPTER_INDEX; node->IN_Enabled = FALSE; node->IN_Type = InterfaceType; node->IN_Filters = NULL; node->IN_FilterNodes = NULL; node->IN_FilterIn = SAP_DONT_FILTER; node->IN_FilterOut = FilterOutMode; node->IN_Stats.SapIfInputPackets = 0; node->IN_Stats.SapIfOutputPackets = 0; if (ARGUMENT_PRESENT(SapIfConfig)) { node->IN_Info = *SapIfConfig; if (node->IN_Enabled && (node->IN_Info.AdminState==ADMIN_STATE_ENABLED)) node->IN_Stats.SapIfOperState = OPER_STATE_SLEEPING; else node->IN_Stats.SapIfOperState = OPER_STATE_DOWN; } else node->IN_Stats.SapIfOperState = OPER_STATE_DOWN; InsertTailList (cur, &node->IN_IntfLink); cur = InterfaceList.IL_Head.Flink; while (cur!=&InterfaceList.IL_Head) { if (InterfaceIndexIN_IntfIdx) break; cur = cur->Flink; } InsertTailList (cur, &node->IN_ListLink); } else { GlobalFree (node); status = ERROR_NOT_ENOUGH_MEMORY; } } else status = ERROR_NOT_ENOUGH_MEMORY; } LeaveCriticalSection (&InterfaceTable.IT_Lock); LeaveCriticalSection (&InterfaceList.IL_Lock); return status; } /*++ ******************************************************************* S a p D e l e t e S a p I n t e r f a c e Routine Description: Delete existing interface control block Arguments: InterfaceIndex - unique number that indentifies the interface Return Value: NO_ERROR - interface was created OK ERROR_INVALID_PARAMETER - interface with this index does not exist other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapDeleteSapInterface ( ULONG InterfaceIndex ) { PLIST_ENTRY cur; PINTERFACE_NODE node; DWORD status; HANDLE enumHdl; EnterCriticalSection (&InterfaceList.IL_Lock); if_IsInterfaceNode (InterfaceIndex,node,cur) { if (node->IN_Stats.SapIfOperState==OPER_STATE_UP) { StopInterface (node); } // Remove and dispose of interface control block Trace (DEBUG_INTERFACES, "Invalidating interface block: %lX(%d).", node, node->IN_IntfIdx); RemoveEntryList (&node->IN_IntfLink); InvalidateInterface (node); RemoveEntryList (&node->IN_ListLink); // Dispose only if nobody uses it and not waiting on critical // section to dispose of it if ((node->IN_RefCount==0) && !node->IN_InUse) { Trace (DEBUG_INTERFACES, "Releasing interface block: %lX(%d).", node, node->IN_IntfIdx); if (node->IN_Filters!=NULL) { if (node->IN_Filters->SupplyFilterCount>0) ReplaceFilters ( FILTER_TYPE_SUPPLY, &node->IN_FilterNodes[0], node->IN_Filters->SupplyFilterCount, NULL, 0); if (node->IN_Filters->ListenFilterCount>0) ReplaceFilters ( FILTER_TYPE_LISTEN, &node->IN_FilterNodes[node->IN_Filters->SupplyFilterCount], node->IN_Filters->ListenFilterCount, NULL, 0); GlobalFree (node->IN_Filters); } if (node->IN_Name!=NULL) SapFreeDuplicatedString (node->IN_Name); GlobalFree (node); } // Otherwise, just leave it hang outside of the table // till last client releases reference to it #if DBG else // Keep track of all blocks in debugging mode InsertTailList (&InterfaceTable.IT_DetachedIntf, &node->IN_ListLink); #endif status = NO_ERROR; } else { Trace (DEBUG_FAILURES, "File: %s, line %ld." " Unknown interface: %ld.", __FILE__, __LINE__, InterfaceIndex); status = ERROR_INVALID_PARAMETER; } LeaveCriticalSection (&InterfaceTable.IT_Lock); LeaveCriticalSection (&InterfaceList.IL_Lock); return status; } /*++ ******************************************************************* U p d a t e I n t e r f a c e S t a t e Routine Description: Performs neccessary operations to syncronize interface operational state with externally set state Arguments: node - interface control block to update Return Value: NO_ERROR - interface was updated OK other - operation failed (windows error code) ******************************************************************* --*/ DWORD UpdateInterfaceState ( PINTERFACE_NODE node ) { DWORD status=NO_ERROR; if (node->IN_IntfIdx!=INTERNAL_INTERFACE_INDEX) { if (node->IN_InUse && (node->IN_AdptIdx!=INVALID_ADAPTER_INDEX) && node->IN_Enabled && (node->IN_Info.AdminState==ADMIN_STATE_ENABLED) ) { // Interface data is in use and it is going to // stay active after the update: THIS IS A CONFIG // CHANGE ON THE FLY!!! We'll have to create a new // block and invalidate the old one PINTERFACE_NODE newNode = GlobalAlloc (GMEM_FIXED, sizeof (INTERFACE_NODE)); if (newNode==NULL) { status = GetLastError (); Trace (DEBUG_FAILURES, "File: %s, line %ld." "Could not allocate memory to replace" " active interface block on set: %ld(gle:%ld).", __FILE__, __LINE__, node->IN_IntfIdx, status); return status; } // Transfer external parameters newNode->IN_Data = node->IN_Data; newNode->IN_Filters = node->IN_Filters; newNode->IN_FilterNodes = node->IN_FilterNodes; // Setup referencing parameters newNode->IN_RefCount = 0; newNode->IN_InUse = FALSE; // Insert in same place in tables InsertTailList (&node->IN_IntfLink, &newNode->IN_IntfLink); InsertTailList (&node->IN_ListLink, &newNode->IN_ListLink); // Will put in adapter table at start InitializeListEntry (&newNode->IN_AdptLink); Trace (DEBUG_INTERFACES, "Replacing interface block on SET: %lX(%d).", newNode, newNode->IN_IntfIdx); status = StartInterface (newNode); if (status != NO_ERROR) node = newNode; // If we failed we'll have to dispose // the new interface block and keep // the old one // Reset this flag to prevent deletion of all services // obtained through SAP (we want to keep them despite // the change to interface parameters) node->IN_Info.Listen = ADMIN_STATE_DISABLED; // Prevent deletion of transferred filters and name node->IN_Filters = NULL; node->IN_Name = NULL; // Shutdown interface if it is still active if (node->IN_Stats.SapIfOperState==OPER_STATE_UP) { node->IN_Stats.SapIfOperState = OPER_STATE_DOWN; StopInterface (node); } // Remove and dispose of original interface control block Trace (DEBUG_INTERFACES, "Invalidating interface block: %lX(%d).", node, node->IN_IntfIdx); RemoveEntryList (&node->IN_IntfLink); InvalidateInterface (node); RemoveEntryList (&node->IN_ListLink); // Dispose only if nobody uses it and not waiting on critical // section to dispose of it if ((node->IN_RefCount==0) && !node->IN_InUse) { Trace (DEBUG_INTERFACES, "Releasing interface block: %lX(%d).", node, node->IN_IntfIdx); GlobalFree (node); } // Otherwise, just leave it hang outside of the table // till last client releases reference to it #if DBG else // Keep track of all blocks in debugging mode InsertTailList (&InterfaceTable.IT_DetachedIntf, &node->IN_ListLink); #endif } else { if ((node->IN_Enabled && (node->IN_Info.AdminState==ADMIN_STATE_ENABLED) && (node->IN_AdptIdx!=INVALID_ADAPTER_INDEX))) { if (node->IN_Stats.SapIfOperState!=OPER_STATE_UP) status = StartInterface (node); } else { if (node->IN_Stats.SapIfOperState==OPER_STATE_UP) status = StopInterface (node); else { if (node->IN_Enabled && (node->IN_Info.AdminState==ADMIN_STATE_ENABLED) && (node->IN_Type!=PERMANENT)) node->IN_Stats.SapIfOperState = OPER_STATE_SLEEPING; else node->IN_Stats.SapIfOperState = OPER_STATE_DOWN; } } } } else { Trace (DEBUG_INTERFACES, "Internal interface info updated."); IpxNetCpy (INTERNAL_IF_NET, node->IN_Adpt.Network); IpxNodeCpy (INTERNAL_IF_NODE, node->IN_Adpt.LocalNode); } return status; } /*++ ******************************************************************* S a p S e t I n t e r f a c e E n a b l e Routine Description: Enables/disables interface Arguments: InterfaceIndex - unique number that indentifies new interface Enable - TRUE-enable, FALSE-disable Return Value: NO_ERROR - config info was changed OK ERROR_INVALID_PARAMETER - interface with this index does not exist other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapSetInterfaceEnable ( ULONG InterfaceIndex, BOOL Enable ) { PLIST_ENTRY cur; PINTERFACE_NODE node; DWORD status=NO_ERROR; EnterCriticalSection (&InterfaceList.IL_Lock); // Don't allow any queries // in interface list // while we are doing this if_IsInterfaceNode (InterfaceIndex,node,cur) { HANDLE enumHdl; if (node->IN_Enabled!=Enable) { node->IN_Enabled = (UCHAR)Enable; status = UpdateInterfaceState (node); } LeaveCriticalSection (&InterfaceTable.IT_Lock); LeaveCriticalSection (&InterfaceList.IL_Lock); if (status==NO_ERROR) { enumHdl = CreateListEnumerator (SDB_INTF_LIST_LINK, 0xFFFF, NULL, node->IN_IntfIdx, 0xFFFFFFFF, Enable ? SDB_DISABLED_NODE_FLAG : 0); // Disable/Reenable all services if (enumHdl!=NULL) { EnumerateServers (enumHdl, Enable ? EnableAllServersCB : DisableAllServersCB, enumHdl); DeleteListEnumerator (enumHdl); } else Trace (DEBUG_FAILURES, "File: %s, line %ld." " Could not create enumerator to enable/disable" " sap servers for interface: %ld.", __FILE__, __LINE__, node->IN_IntfIdx); } } else { LeaveCriticalSection (&InterfaceTable.IT_Lock); LeaveCriticalSection (&InterfaceList.IL_Lock); Trace (DEBUG_FAILURES, "File: %s, line %ld." " Unknown interface: %ld.", __FILE__, __LINE__, InterfaceIndex); status = ERROR_INVALID_PARAMETER; } return status; } /*++ ******************************************************************* S a p S e t S a p I n t e r f a c e Routine Description: Compares existing interface configuration with the new one and performs an update if necessary. Arguments: InterfaceIndex - unique number that indentifies new interface SapIfConfig - new interface configuration info Return Value: NO_ERROR - config info was changed OK ERROR_INVALID_PARAMETER - interface with this index does not exist other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapSetSapInterface ( ULONG InterfaceIndex, PSAP_IF_INFO SapIfConfig ) { PLIST_ENTRY cur; PINTERFACE_NODE node; DWORD status=NO_ERROR; EnterCriticalSection (&InterfaceList.IL_Lock); // Don't allow any queries // in interface list // while we are doing this if_IsInterfaceNode (InterfaceIndex,node,cur) { // memcmp on structures!!! may not work with all compilers // but event if it fails, the result will be just an // set extra operation if (memcmp (&node->IN_Info, SapIfConfig, sizeof (node->IN_Info))!=0) { node->IN_Info = *SapIfConfig; status = UpdateInterfaceState (node); } } else { Trace (DEBUG_FAILURES, "File: %s, line %ld." " Unknown interface: %ld.", __FILE__, __LINE__, InterfaceIndex); status = ERROR_INVALID_PARAMETER; } LeaveCriticalSection (&InterfaceTable.IT_Lock); LeaveCriticalSection (&InterfaceList.IL_Lock); return status; } /*++ ******************************************************************* S a p I s S a p I n t e r f a c e Routine Description: Checks if interface with given index exists Arguments: InterfaceIndex - unique number that indentifies new interface Return Value: TRUE - exist FALSE - does not ******************************************************************* --*/ BOOL SapIsSapInterface ( IN ULONG InterfaceIndex ) { PINTERFACE_NODE node; PLIST_ENTRY cur; BOOL res; if_IsInterfaceNode (InterfaceIndex,node,cur) res = TRUE; else res = FALSE; LeaveCriticalSection (&InterfaceTable.IT_Lock); return res; } /*++ ******************************************************************* S a p G e t S a p I n t e r f a c e Routine Description: Retrieves configuration and statistic info associated with interface Arguments: InterfaceIndex - unique number that indentifies new interface SapIfConfig - buffer to store configuration info SapIfStats - buffer to store statistic info Return Value: NO_ERROR - info was retrieved OK ERROR_INVALID_PARAMETER - interface with this index does not exist other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapGetSapInterface ( IN ULONG InterfaceIndex, OUT PSAP_IF_INFO SapIfConfig OPTIONAL, OUT PSAP_IF_STATS SapIfStats OPTIONAL ) { PINTERFACE_NODE node; DWORD status; PLIST_ENTRY cur; if_IsInterfaceNode (InterfaceIndex,node,cur) { if (ARGUMENT_PRESENT(SapIfConfig)) *SapIfConfig = node->IN_Info; if (ARGUMENT_PRESENT(SapIfStats)) *SapIfStats = node->IN_Stats; status = NO_ERROR; } else { Trace (DEBUG_FAILURES, "File: %s, line %ld." " Unknown interface: %ld.", __FILE__, __LINE__, InterfaceIndex); status = ERROR_INVALID_PARAMETER; } LeaveCriticalSection (&InterfaceTable.IT_Lock); return status; } /*++ ******************************************************************* S a p G e t F i r s t S a p I n t e r f a c e Routine Description: Retrieves configuration and statistic info associated with first interface in InterfaceIndex order Arguments: InterfaceIndex - buffer to store unique number that indentifies interface SapIfConfig - buffer to store configuration info SapIfStats - buffer to store statistic info Return Value: NO_ERROR - info was retrieved OK ERROR_NO_MORE_ITEMS - no interfaces in the table other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapGetFirstSapInterface ( OUT PULONG InterfaceIndex, OUT PSAP_IF_INFO SapIfConfig OPTIONAL, OUT PSAP_IF_STATS SapIfStats OPTIONAL ) { PINTERFACE_NODE node; DWORD status; EnterCriticalSection (&InterfaceList.IL_Lock); if (!IsListEmpty (&InterfaceList.IL_Head)) { node = CONTAINING_RECORD (InterfaceList.IL_Head.Flink, INTERFACE_NODE, IN_ListLink); // Lock the table to make sure nobody modifies data while // we are accessing it EnterCriticalSection (&InterfaceTable.IT_Lock); *InterfaceIndex = node->IN_IntfIdx; if (ARGUMENT_PRESENT(SapIfConfig)) *SapIfConfig = node->IN_Info; if (ARGUMENT_PRESENT(SapIfStats)) *SapIfStats = node->IN_Stats; LeaveCriticalSection (&InterfaceTable.IT_Lock); status = NO_ERROR; } else { Trace (DEBUG_FAILURES, "File: %s, line %ld." " Unknown interface: %ld.", __FILE__, __LINE__, InterfaceIndex); status = ERROR_NO_MORE_ITEMS; } LeaveCriticalSection (&InterfaceList.IL_Lock); return status; } /*++ ******************************************************************* S a p G e t N e x t S a p I n t e r f a c e Routine Description: Retrieves configuration and statistic info associated with first interface in following interface with InterfaceIndex order in interface index order Arguments: InterfaceIndex - on input - interface number to search from on output - interface number of next interface SapIfConfig - buffer to store configuration info SapIfStats - buffer to store statistic info Return Value: NO_ERROR - info was retrieved OK ERROR_NO_MORE_ITEMS - no more interfaces in the table other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapGetNextSapInterface ( IN OUT PULONG InterfaceIndex, OUT PSAP_IF_INFO SapIfConfig OPTIONAL, OUT PSAP_IF_STATS SapIfStats OPTIONAL ) { PINTERFACE_NODE node; PLIST_ENTRY cur; DWORD status=ERROR_NO_MORE_ITEMS; EnterCriticalSection (&InterfaceList.IL_Lock); if_IsInterfaceNode(*InterfaceIndex,node,cur) { if (node->IN_ListLink.Flink!=&InterfaceList.IL_Head) { node = CONTAINING_RECORD (node->IN_ListLink.Flink, INTERFACE_NODE, IN_ListLink); *InterfaceIndex = node->IN_IntfIdx; if (ARGUMENT_PRESENT(SapIfConfig)) *SapIfConfig = node->IN_Info; if (ARGUMENT_PRESENT(SapIfStats)) *SapIfStats = node->IN_Stats; status = NO_ERROR; } LeaveCriticalSection (&InterfaceTable.IT_Lock); } else { LeaveCriticalSection (&InterfaceTable.IT_Lock); cur = InterfaceList.IL_Head.Flink; while (cur!=&InterfaceList.IL_Head) { node = CONTAINING_RECORD (cur, INTERFACE_NODE, IN_ListLink); if (*InterfaceIndexIN_IntfIdx) break; } if (cur!=&InterfaceList.IL_Head) { EnterCriticalSection (&InterfaceTable.IT_Lock); *InterfaceIndex = node->IN_IntfIdx; if (ARGUMENT_PRESENT(SapIfConfig)) *SapIfConfig = node->IN_Info; if (ARGUMENT_PRESENT(SapIfStats)) *SapIfStats = node->IN_Stats; LeaveCriticalSection (&InterfaceTable.IT_Lock); status = NO_ERROR; } } LeaveCriticalSection (&InterfaceList.IL_Lock); return status; } /*++ ******************************************************************* S a p S e t I n t e r f a c e F i l t e r s Routine Description: Compares existing interface configuration with the new one and performs an update if necessary. Arguments: Return Value: NO_ERROR - config info was changed OK ERROR_INVALID_PARAMETER - interface with this index does not exist other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapSetInterfaceFilters ( IN ULONG InterfaceIndex, IN PSAP_IF_FILTERS SapIfFilters ) { PLIST_ENTRY cur; PINTERFACE_NODE node; DWORD status=NO_ERROR; EnterCriticalSection (&InterfaceList.IL_Lock); // Don't allow any queries // in interface list // while we are doing this if_IsInterfaceNode (InterfaceIndex,node,cur) { if ( ((node->IN_Filters!=NULL) && (SapIfFilters!=NULL) // memcmp on structures!!! may not work with all compilers // but event if it fails, the result will be just an // set extra operation && (memcmp (node->IN_Filters, SapIfFilters, FIELD_OFFSET (SAP_IF_FILTERS,ServiceFilter))==0) && (memcmp (&node->IN_Filters->ServiceFilter[0], &SapIfFilters->ServiceFilter[0], sizeof (SAP_SERVICE_FILTER_INFO)* (SapIfFilters->SupplyFilterCount +SapIfFilters->ListenFilterCount))==0)) // Filter info hasn't changed || ((node->IN_Filters==NULL) && ((SapIfFilters==NULL) || (SapIfFilters->SupplyFilterCount +SapIfFilters->ListenFilterCount==0))) ) // There are no filters status = NO_ERROR; else { if ((SapIfFilters!=NULL) && (SapIfFilters->SupplyFilterCount +SapIfFilters->ListenFilterCount>0)) { PFILTER_NODE newNodes; PSAP_IF_FILTERS newFilters; ULONG newTotal = SapIfFilters->SupplyFilterCount +SapIfFilters->ListenFilterCount; newFilters = (PSAP_IF_FILTERS) GlobalAlloc (GMEM_FIXED, FIELD_OFFSET (SAP_IF_FILTERS,ServiceFilter[newTotal]) +sizeof (FILTER_NODE)*newTotal); if (newFilters!=NULL) { ULONG i; memcpy (newFilters, SapIfFilters, FIELD_OFFSET (SAP_IF_FILTERS,ServiceFilter[newTotal])); newNodes = (PFILTER_NODE)&newFilters->ServiceFilter[newTotal]; for (i=0; iServiceFilter[i]; } } else { status = GetLastError (); goto ExitSetFilters; } if (node->IN_Filters) { ReplaceFilters ( FILTER_TYPE_SUPPLY, &node->IN_FilterNodes[0], node->IN_Filters->SupplyFilterCount, &newNodes[0], newFilters->SupplyFilterCount); ReplaceFilters ( FILTER_TYPE_LISTEN, &node->IN_FilterNodes[node->IN_Filters->SupplyFilterCount], node->IN_Filters->ListenFilterCount, &newNodes[newFilters->SupplyFilterCount], newFilters->ListenFilterCount); } else { ReplaceFilters ( FILTER_TYPE_SUPPLY, NULL, 0, &newNodes[0], newFilters->SupplyFilterCount); ReplaceFilters ( FILTER_TYPE_LISTEN, NULL, 0, &newNodes[newFilters->SupplyFilterCount], newFilters->ListenFilterCount); } node->IN_Filters = newFilters; node->IN_FilterNodes = newNodes; node->IN_FilterOut = newFilters->SupplyFilterCount>0 ? (UCHAR)newFilters->SupplyFilterAction : SAP_DONT_FILTER; node->IN_FilterIn = newFilters->ListenFilterCount>0 ? (UCHAR)newFilters->ListenFilterAction : SAP_DONT_FILTER; } else { ReplaceFilters ( FILTER_TYPE_SUPPLY, &node->IN_FilterNodes[0], node->IN_Filters->SupplyFilterCount, NULL, 0); ReplaceFilters ( FILTER_TYPE_LISTEN, &node->IN_FilterNodes[node->IN_Filters->SupplyFilterCount], node->IN_Filters->ListenFilterCount, NULL, 0); GlobalFree (node->IN_Filters); node->IN_Filters = NULL; node->IN_FilterNodes = NULL; node->IN_FilterIn = node->IN_FilterOut = SAP_DONT_FILTER; } status = NO_ERROR; } } else { Trace (DEBUG_FAILURES, "File: %s, line %ld." " Unknown interface: %ld.", __FILE__, __LINE__, InterfaceIndex); status = ERROR_INVALID_PARAMETER; } ExitSetFilters: LeaveCriticalSection (&InterfaceTable.IT_Lock); LeaveCriticalSection (&InterfaceList.IL_Lock); return status; } /*++ ******************************************************************* S a p G e t I n t e r f a c e F i l t e r s Routine Description: Compares existing interface configuration with the new one and performs an update if necessary. Arguments: Return Value: NO_ERROR - config info was changed OK ERROR_INVALID_PARAMETER - interface with this index does not exist other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapGetInterfaceFilters ( IN ULONG InterfaceIndex, OUT PSAP_IF_FILTERS SapIfFilters, OUT PULONG FilterBufferSize ) { PINTERFACE_NODE node; DWORD status; PLIST_ENTRY cur; if_IsInterfaceNode (InterfaceIndex,node,cur) { if (node->IN_Filters!=NULL) { PSAP_IF_FILTERS info = node->IN_Filters; ULONG infoSize = FIELD_OFFSET (SAP_IF_FILTERS, ServiceFilter[info->SupplyFilterCount +info->ListenFilterCount]); if (*FilterBufferSize>=infoSize) { memcpy (SapIfFilters, info, infoSize); status = NO_ERROR; } else status = ERROR_INSUFFICIENT_BUFFER; *FilterBufferSize = infoSize; } else { ULONG infoSize = FIELD_OFFSET (SAP_IF_FILTERS, ServiceFilter); if (*FilterBufferSize>=infoSize) { SapIfFilters->SupplyFilterCount = 0; SapIfFilters->SupplyFilterAction = IPX_SERVICE_FILTER_DENY; SapIfFilters->ListenFilterCount = 0; SapIfFilters->ListenFilterAction = IPX_SERVICE_FILTER_DENY; status = NO_ERROR; } else status = ERROR_INSUFFICIENT_BUFFER; *FilterBufferSize = infoSize; } } else { Trace (DEBUG_FAILURES, "File: %s, line %ld." " Unknown interface: %ld.", __FILE__, __LINE__, InterfaceIndex); status = ERROR_INVALID_PARAMETER; } LeaveCriticalSection (&InterfaceTable.IT_Lock); return status; } /*++ ******************************************************************* S a p B i n d S a p I n t e r f a c e T o A d a p t e r Routine Description: Establishes association between interface and physical adapter and starts sap on the interface if its admin state is enabled Arguments: InterfaceIndex - unique number that indentifies new interface AdapterInfo - info associated with adapter to bind to Return Value: NO_ERROR - interface was bound OK ERROR_INVALID_PARAMETER - interface with this index does not exist other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapBindSapInterfaceToAdapter ( ULONG InterfaceIndex, PIPX_ADAPTER_BINDING_INFO AdptInternInfo ) { PINTERFACE_NODE node; DWORD status=NO_ERROR; PLIST_ENTRY cur; EnterCriticalSection (&InterfaceList.IL_Lock); // Don't allow any queries // in interface list // while we are doing this if_IsInterfaceNode (InterfaceIndex,node,cur) { ASSERTMSG ("Interface is already bound ", node->IN_AdptIdx==INVALID_ADAPTER_INDEX); node->IN_Adpt = *AdptInternInfo; status = UpdateInterfaceState (node); } else { Trace (DEBUG_FAILURES, "File: %s, line %ld." " Unknown interface: %ld.", __FILE__, __LINE__, InterfaceIndex); status = ERROR_INVALID_PARAMETER; } LeaveCriticalSection (&InterfaceTable.IT_Lock); LeaveCriticalSection (&InterfaceList.IL_Lock); return status; } /*++ ******************************************************************* S a p U n b i n d S a p I n t e r f a c e F r o m A d a p t e r Routine Description: Breaks association between interface and physical adapter and stops sap on the interface if it was on Arguments: InterfaceIndex - unique number that indentifies new interface Return Value: NO_ERROR - interface was bound OK ERROR_INVALID_PARAMETER - interface with this index does not exist other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapUnbindSapInterfaceFromAdapter ( ULONG InterfaceIndex ) { PINTERFACE_NODE node; DWORD status; PLIST_ENTRY cur; EnterCriticalSection (&InterfaceList.IL_Lock); // Don't allow any queries // in interface list // while we are doing this if_IsInterfaceNode (InterfaceIndex,node,cur) { node->IN_AdptIdx = INVALID_ADAPTER_INDEX; if (node->IN_Stats.SapIfOperState==OPER_STATE_UP) { status = StopInterface (node); } } else { Trace (DEBUG_FAILURES, "File: %s, line %ld." " Unknown interface: %ld.", __FILE__, __LINE__, InterfaceIndex); status = ERROR_INVALID_PARAMETER; } LeaveCriticalSection (&InterfaceTable.IT_Lock); LeaveCriticalSection (&InterfaceList.IL_Lock); return status; } /*++ ******************************************************************* S a p R e q u e s t U p d a t e Routine Description: Initiates update of services information over the interface Completion of this update will be indicated by signalling NotificationEvent passed at StartProtocol. GetEventMessage can be used then to get the results of autostatic update Arguments: InterfaceIndex - unique index identifying interface to do update on Return Value: NO_ERROR - operation was initiated ok ERROR_CAN_NOT_COMPLETE - the interface does not support updates ERROR_INVALID_PARAMETER - interface with this index does not exist other - operation failed (windows error code) ******************************************************************* --*/ DWORD SapRequestUpdate ( ULONG InterfaceIndex ) { PINTERFACE_NODE node; DWORD status; PLIST_ENTRY cur; if_IsInterfaceNode (InterfaceIndex,node,cur) { if ((node->IN_Info.UpdateMode==IPX_AUTO_STATIC_UPDATE) && (node->IN_Stats.SapIfOperState==OPER_STATE_UP)) { Trace (DEBUG_INTERFACES, "Starting update on interface: %ld.", InterfaceIndex); status = InitTreqItem (&node->IN_Data); } else { Trace (DEBUG_FAILURES, "RequestUpdate called on unbound or" " 'standard update mode' interface: %ld.", InterfaceIndex); status = ERROR_CAN_NOT_COMPLETE; } } else { Trace (DEBUG_FAILURES, "Unknown interface: %ld.", InterfaceIndex); status = ERROR_INVALID_PARAMETER; } LeaveCriticalSection (&InterfaceTable.IT_Lock); return status; }