/*++

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; i<IDB_NUM_OF_INTF_HASH_BUCKETS; i++)
		InitializeListHead (&InterfaceTable.IT_IntfHash[i]);
	for (i=0; i<IDB_NUM_OF_ADPT_HASH_BUCKETS; i++)
		InitializeListHead (&InterfaceTable.IT_AdptHash[i]);

#if DBG
	InitializeListHead (&InterfaceTable.IT_DetachedIntf);
#endif
	InterfaceTable.IT_NumOfActiveInterfaces = 0;

	return NO_ERROR;
	}

/*++
*******************************************************************
		S h u t d o w n I n t e r f a c e s

Routine Description:
	Initiates orderly shutdown of SAP interfaces
	Stop reception of new packets
Arguments:
	None
Return Value:
	None

*******************************************************************
--*/
VOID
ShutdownInterfaces (
	HANDLE			doneEvent
	) {
	INT			i;


		// Now for each active interface in the table
		// we'll start shoutdown worker which will broadcast
		// all 'deleted' servers and dispose of interface control block
	EnterCriticalSection (&InterfaceList.IL_Lock);
	EnterCriticalSection (&InterfaceTable.IT_Lock);
	ShutdownDoneEvent = doneEvent;
	if (InterfaceTable.IT_NumOfActiveInterfaces==0) {
		Trace (DEBUG_INTERFACES, "All interfaces have been shut down.");
		if (doneEvent!=NULL) {
			BOOL res = SetEvent (doneEvent);
			ASSERTERRMSG ("Could not set shutdown done event ", res);
			}
		}
	else {
		ShutdownDoneEvent = doneEvent;


		for (i=0; i<IDB_NUM_OF_INTF_HASH_BUCKETS; i++) {
			while (!IsListEmpty (&InterfaceTable.IT_IntfHash[i])) {
				PINTERFACE_NODE node = CONTAINING_RECORD (
									InterfaceTable.IT_IntfHash[i].Flink,
									INTERFACE_NODE,
									IN_IntfLink);
				if (node->IN_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; i<IDB_NUM_OF_INTF_HASH_BUCKETS; i++) {
		while (!IsListEmpty (&InterfaceTable.IT_IntfHash[i])) {
			PINTERFACE_NODE node = CONTAINING_RECORD (
								InterfaceTable.IT_IntfHash[i].Flink,
								INTERFACE_NODE,
								IN_IntfLink);
			if (node->IN_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 (InterfaceIndex<CONTAINING_RECORD (
						        cur,
						        INTERFACE_NODE,
						        IN_ListLink)->IN_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 (*InterfaceIndex<node->IN_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; i<newTotal; i++) {
						newNodes[i].FN_Index = InterfaceIndex;
						newNodes[i].FN_Filter = &newFilters->ServiceFilter[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;
	}