/*++
Copyright (c) 1994 Microsoft Corporation
Copyright (c) 1993 Micro Computer Systems, Inc.
Module Name:
net\svcdlls\nwsap\server\wancheck.c
Abstract:
These routines handle the WAN cards going up and down.
Author:
Brian Walker (MCS) 06-15-1993
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
/** Structure of data we pass to IPX for the ADDRESS_NOTIFY **/
typedef struct {
UCHAR Reserved[20]; /* Reserved for IPX use */
IPX_ADDRESS_DATA IpxData; /* Information structure we get */
HANDLE EventHandle; /* Handle for IPX to signal */
} IPX_ADDRESS_DATA_EXTENDED;
/** **/
typedef struct {
LIST_ENTRY ListEntry;
IPX_ADDRESS_DATA_EXTENDED Data;
} SAP_WANTRACK, *PSAP_WANTRACK;
#define SAP_WANTRACK_SIZE sizeof(SAP_WANTRACK)
/** **/
SOCKET SapWanSocket = INVALID_SOCKET;
/** Internal Function Prototypes **/
DWORD WINAPI
SapWanCheckThread(
LPVOID Threadparm);
DWORD WINAPI
SapWanWorkerThread(
LPVOID Threadparm);
VOID
SapWanCardUp(
PIPX_ADDRESS_DATA IpxData);
VOID
SapWanCardDown(
PIPX_ADDRESS_DATA IpxData);
/*++
*******************************************************************
S a p W a n I n i t
Routine Description:
This routine initializes the WAN monitoring portion of the Sap Agent.
Arguments:
None
Return Value:
0 = OK
Else = Error
*******************************************************************
--*/
INT
SapWanInit(
VOID)
{
HANDLE Handle;
DWORD Threadid;
DWORD Error;
INT rc;
INT i;
INT Length;
SOCKADDR_IPX Bindaddr;
/** Initialize the sockets interface **/
WSADATA wsadata;
rc = WSAStartup(0x0101, &wsadata);
if (rc) {
SapEventId = NWSAP_EVENT_WSASTARTUP_FAILED;
return rc;
}
/**
Create an event to use to wait with. We use this
with the ADDRESS_NOTIFY socket option.
**/
SapWanEvent = CreateEvent(
NULL, /* No security */
FALSE, /* Auto reset */
FALSE, /* Initial state = Not signalled */
NULL); /* No name */
if (SapWanEvent == NULL) {
SapError = GetLastError();
SapEventId = NWSAP_EVENT_WANEVENT_ERROR;
return -1;
}
/**
Allocate a block of memory for the
list of handles that we use to store the
NOTIFY threads with.
**/
Length = SapNumWanNotifyThreads * sizeof(HANDLE),
SapWanNotifyHandlesBuf = SAP_MALLOC(
Length,
"SapWanNoifyThreadHandles");
if (SapWanNotifyHandlesBuf == NULL) {
SapError = GetLastError();
SapEventId = NWSAP_EVENT_WANHANDLEMEMORY_ERROR;
return -1;
}
memset(SapWanNotifyHandlesBuf, 0, Length);
/** Create a semaphore for the threads to wait on **/
SapWanCurBackup = 0;
SapWanCurFree = 0;
SapWanSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
if (SapWanSem == NULL) {
SapError = GetLastError();
SapEventId = NWSAP_EVENT_WANSEM_FAIL;
return -1;
}
/** Open a socket to do the getsockopt on **/
SapWanSocket = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
if (SapWanSocket == INVALID_SOCKET) {
SapError = h_errno;
SapEventId = NWSAP_EVENT_WANSOCKET_FAILED;
return -1;
}
/** Bind to any old address **/
memset(&Bindaddr, 0, sizeof(SOCKADDR_IPX));
Bindaddr.sa_family = AF_IPX;
rc = bind(SapWanSocket, (PSOCKADDR)&Bindaddr, sizeof(SOCKADDR_IPX));
if (rc == -1) {
SapError = h_errno;
SapEventId = NWSAP_EVENT_WANBIND_FAILED;
return -1;
}
/**
Create the worker thread.
We count this thread as running before we start it
for synchronization. If the start fails - then we will
dec this count
**/
SAP_INC_THREAD_COUNT("Wan Start 1", NULL);
/** Start the thread **/
Handle = CreateThread(
NULL, /* Security Ptr */
0, /* Initial stack size */
SapWanWorkerThread, /* Thread Function */
(LPVOID)NULL, /* Parm for the thread */
0, /* Creation Flags */
&Threadid); /* Ptr to thread id */
if (Handle == NULL) {
/** **/
Error = GetLastError();
IF_DEBUG(INITIALIZATION_ERRORS) {
SS_PRINT(("NWSAP: Error starting WAN worker thread = %d\n", Error));
}
/** Dec the thread count **/
SAP_DEC_THREAD_COUNT("Wan Start 1 Error", NULL);
/** Log the error **/
SsLogEvent(NWSAP_EVENT_STARTWANWORKER_ERROR, 0, NULL, Error);
/** Return Error **/
return -1;
}
/** We can close this handle **/
CloseHandle(Handle);
/**
Create the wait thread.
We count this thread as running before we start it
for synchronization. If the start fails - then we will
dec this count
**/
for (i = 0 ; i < SapNumWanNotifyThreads ; i++) {
SAP_INC_THREAD_COUNT("Wan Start 2", NULL);
/** Start the thread **/
SapWanNotifyHandlesBuf[i] = CreateThread(
NULL, /* Security Ptr */
0, /* Initial stack size */
SapWanCheckThread, /* Thread Function */
(LPVOID)i, /* Parm for the thread */
0, /* Creation Flags */
&Threadid); /* Ptr to thread id */
if (SapWanNotifyHandlesBuf[i] == NULL) {
/** **/
Error = GetLastError();
IF_DEBUG(INITIALIZATION_ERRORS) {
SS_PRINT(("NWSAP: Error starting WAN check thread = %d\n", Error));
}
/** Dec the thread count **/
SAP_DEC_THREAD_COUNT("Wan Start 2 Error", NULL);
/** Log the error **/
SsLogEvent(NWSAP_EVENT_STARTWANCHECK_ERROR, 0, NULL, Error);
/** Return Error **/
return -1;
}
}
/** Init OK **/
return 0;
}
/*++
*******************************************************************
S a p W a n S h u t d o w n
Routine Description:
When we are terminating, this routine will clean
up everything.
Arguments:
None
Return Value:
Nothing
*******************************************************************
--*/
VOID
SapWanShutdown(
VOID)
{
PLIST_ENTRY Listp;
PSAP_WANTRACK Wanp;
/** Close the semaphore handle **/
if (SapWanSem != NULL)
CloseHandle(SapWanSem);
/** Close the handle **/
if (SapWanSocket != INVALID_SOCKET)
closesocket(SapWanSocket);
/** Cleanup the free list **/
while (!IsListEmpty(&SapWanRecvList)) {
Listp = RemoveHeadList(&SapWanRecvList);
Wanp = CONTAINING_RECORD(Listp, SAP_WANTRACK, ListEntry);
SAP_FREE(Wanp, "Wan Shutdown Recv List");
}
while (!IsListEmpty(&SapWanFreeList)) {
Listp = RemoveHeadList(&SapWanFreeList);
Wanp = CONTAINING_RECORD(Listp, SAP_WANTRACK, ListEntry);
SAP_FREE(Wanp, "Wan Shutdown Free List");
}
/** All Done **/
return;
}
/*++
*******************************************************************
S a p W a n C h e c k T h r e a d
Routine Description:
This thread keeps a getsockopt down to the IPX driver
to look for WANs that are going up or down.
Arguments:
Threadparm = Thread parameter from CreateThread
Return Value:
0 Always
*******************************************************************
--*/
DWORD WINAPI
SapWanCheckThread(
LPVOID Threadparm)
{
PSAP_WANTRACK Trackp;
PLIST_ENTRY Listp;
INT Length;
INT rc;
DWORD Error;
INT Index;
/** **/
Index = (INT)Threadparm;
/** **/
while (1) {
/** Get a buffer to do this with **/
ACQUIRE_WANFREETABLE_LOCK();
if (SapWanCurFree) {
Listp = RemoveHeadList(&SapWanFreeList);
Trackp = CONTAINING_RECORD(Listp, SAP_WANTRACK, ListEntry);
SapWanCurFree--;
}
else {
Trackp = SAP_MALLOC(SAP_WANTRACK_SIZE, "Alloc Trackp");
}
RELEASE_WANFREETABLE_LOCK();
/** If no memory **/
if (Trackp == NULL) {
IF_DEBUG(ERRORS) {
SS_PRINT(("WanCheckThread: No memory\n"));
}
break;
}
/** Set the handle that we use for waiting on the event **/
Trackp->Data.EventHandle = SapWanEvent;
/** Issue the Getsockopt **/
Length = sizeof(IPX_ADDRESS_DATA_EXTENDED);
rc = getsockopt(
SapWanSocket,
NSPROTO_IPX,
IPX_ADDRESS_NOTIFY,
(PVOID)&Trackp->Data,
&Length);
/** If we get an error - just free the entry and bomb out **/
if (rc == -1) {
Error = h_errno;
IF_DEBUG(ERRORS) {
SS_PRINT(("WanCheckThread: terminate w/error 1 = %d\n", Error));
}
SAP_FREE(Trackp, "ADDR NOTIFY ERROR");
break;
}
/** Wait on the event to signal **/
Error = WaitForSingleObject(SapWanEvent, INFINITE);
/** If the wait failed - just free the entry and bomb out **/
if (Error == 0xFFFFFFFF) {
Error = GetLastError();
IF_DEBUG(ERRORS) {
SS_PRINT(("WanCheckThread: terminate w/error 2 = %d\n", Error));
}
SAP_FREE(Trackp, "ADDR WAIT ERROR");
break;
}
/**
If the server is going down - get out now
**/
if (!SsInitialized) {
SAP_FREE(Trackp, "Wan - Server down");
break;
}
/** Put the entry in the list **/
InitializeListHead(&Trackp->ListEntry);
ACQUIRE_WANRECVTABLE_LOCK();
InsertTailList(&SapWanRecvList, &Trackp->ListEntry);
SapWanCurBackup++;
RELEASE_WANRECVTABLE_LOCK();
/** Release the semaphore to run the worker thread **/
ReleaseSemaphore(SapWanSem, 1, NULL);
}
/**
Take us out of the table
**/
ACQUIRE_THREADCOUNT_LOCK();
if (SapWanNotifyHandlesBuf) {
if (SapWanNotifyHandlesBuf[Index]) {
SapWanNotifyHandlesBuf[Index] = NULL;
RELEASE_THREADCOUNT_LOCK();
SAP_DEC_THREAD_COUNT("Wan Check Thread Termination", NULL);
}
else {
RELEASE_THREADCOUNT_LOCK();
}
}
else {
RELEASE_THREADCOUNT_LOCK();
}
/** Just leave **/
return 0;
}
/*++
*******************************************************************
S a p W a n W o r k e r T h r e a d
Routine Description:
This thread takes blocks from the SapWanCheckThread
and processes them.
Arguments:
Threadparm = Thread parameter from CreateThread
Return Value:
0 Always
*******************************************************************
--*/
DWORD WINAPI
SapWanWorkerThread(
LPVOID Threadparm)
{
NTSTATUS Status;
PSAP_WANTRACK Trackp;
PLIST_ENTRY Listp;
/** **/
while (SsInitialized) {
/** Wait for a request to show up **/
Status = WaitForSingleObjectEx(SapWanSem, INFINITE, TRUE);
/** If stopping - just leave **/
if (!SsInitialized) {
IF_DEBUG(TERMINATION) {
SS_PRINT(("NWSAP: Wan Worker Thread: Breaking out for stop\n"));
}
break;
}
/** If error - just ignore it **/
if (!NT_SUCCESS(Status)) {
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: Wan Worker: Wait failed: Status = 0x%lx\n", Status));
}
continue;
}
/** Get ownership of the worker list **/
ACQUIRE_WANRECVTABLE_LOCK();
/** Get an entry from the list **/
if (!IsListEmpty(&SapWanRecvList)) {
SS_ASSERT(SapWanCurBackup != 0);
Listp = RemoveHeadList(&SapWanRecvList);
Trackp = CONTAINING_RECORD(Listp, SAP_WANTRACK, ListEntry);
SapWanCurBackup--;
}
else {
SS_ASSERT(SapWanCurBackup == 0);
Trackp = NULL;
}
/** Release the lock on the list **/
RELEASE_WANRECVTABLE_LOCK();
/** If no packet - error **/
if (Trackp == NULL) {
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: WAN WORKER: Wait came back but no block ready\n"));
}
continue;
}
/** Handle the data here **/
if (Trackp->Data.IpxData.status == TRUE)
SapWanCardUp(&Trackp->Data.IpxData);
else
SapWanCardDown(&Trackp->Data.IpxData);
/**
If there is room in the FREE list - then put
this entry in the free list, else just free
the memory back to heap.
**/
ACQUIRE_WANFREETABLE_LOCK();
if (SapWanCurFree < SapWanMaxFree) {
InsertTailList(&SapWanFreeList, &Trackp->ListEntry);
SapWanCurFree++;
}
else {
SAP_FREE(Trackp, "FREE WAN TRACK 1");
}
RELEASE_WANFREETABLE_LOCK();
}
/** We are terminating - uncount and set event if need to **/
SAP_DEC_THREAD_COUNT("Wan Worker Terminate", NULL);
/** All Done **/
return 0;
}
/*++
*******************************************************************
S a p W a n C a r d U p
Routine Description:
This routine is called when a new adapter is added. We
will handle setting up this new adapter.
Arguments:
IpxData = Ptr to an IPX_ADDRESS_DATA structure that we
got from the transport
Return Value:
Nothing
*******************************************************************
--*/
VOID
SapWanCardUp(
PIPX_ADDRESS_DATA IpxData)
{
PSAP_CARD Cardptr;
INT Retcode;
/** Make sure that this card does not already exist **/
IF_DEBUG(WAN) {
SS_PRINT(("NWSAP: SapWanCardUp: Adapnum = %d\n", IpxData->adapternum));
}
ACQUIRE_CARDLIST_WRITERS_LOCK(Retcode, "Wancheck up 1");
if (Retcode) {
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: SapWanCardUp: Error getting card writers lock\n"));
}
return;
}
Cardptr = SapCardHead;
while (Cardptr) {
if (Cardptr->Number == IpxData->adapternum)
break;
Cardptr = Cardptr->Next;
}
/** If card already here - just leave **/
if (Cardptr) {
RELEASE_CARDLIST_WRITERS_LOCK("Wancheck up 1");
return;
}
/** Allocate an entry for this card **/
Cardptr = SAP_MALLOC(SAP_CARD_SIZE, "SapWanCardUp");
if (Cardptr == NULL) {
/** Release the lock **/
RELEASE_CARDLIST_WRITERS_LOCK("Wancheck up 2");
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: SapWanCardUp: Error allocating memory\n"));
}
/** Log the error **/
SsLogEvent(
NWSAP_EVENT_CARDMALLOC_FAILED,
0,
NULL,
0);
/** And Leave **/
return;
}
/** Fill in this card **/
Cardptr->Next = NULL;
SAP_COPY_NETNUM(Cardptr->Netnum, IpxData->netnum);
SAP_COPY_NODENUM(Cardptr->Nodenum, IpxData->nodenum);
Cardptr->Linkspeed = IpxData->linkspeed;
Cardptr->Wanflag = IpxData->wan;
Cardptr->Maxpkt = IpxData->maxpkt;
Cardptr->Number = (UCHAR)IpxData->adapternum;
Cardptr->ReqCount = 1; /* Send one extra general request */
/** For building with **/
Cardptr->Plist.Curnum = 0;
Cardptr->Plist.Curpkt = NULL;
Cardptr->Plist.Curptr = NULL;
Cardptr->Plist.PktHead = NULL;
Cardptr->Plist.PktTail = NULL;
Cardptr->Plist.NumPkts = 0;
/** Put this card in the list **/
if (SapCardHead)
SapCardTail->Next = Cardptr;
else
SapCardHead = Cardptr;
SapCardTail = Cardptr;
SapNumCards++; /* Count the card */
/**
If we are still starting up - then
we just leave. If not, then we send
a general request on this netnum.
**/
if (SapCardInitDone == 0) {
RELEASE_CARDLIST_WRITERS_LOCK("WanCardUp X1");
return;
}
/**
Send a general request on this network number to
get our table filled up for this network
**/
RELEASE_CARDLIST_WRITERS_LOCK("WanCardUp X2");
SapSendGeneralRequest(FALSE, IpxData->netnum);
/** All Done **/
return;
}
/*++
*******************************************************************
S a p W a n C a r d D o w n
Routine Description:
This routine is called when an adapter is removed from the
transports list. We cleanup everything that had to do with
this adapter.
Arguments:
IpxData = Ptr to an IPX_ADDRESS_DATA structure that we
got from the transport
Return Value:
Nothing
*******************************************************************
--*/
VOID
SapWanCardDown(
PIPX_ADDRESS_DATA IpxData)
{
PSAP_CARD Cardptr;
PSAP_CARD BCardptr;
INT Status;
INT Cardnum;
/** **/
IF_DEBUG(WAN) {
SS_PRINT(("NWSAP: SapWanCardDown: Adapnum = %d\n", IpxData->adapternum));
}
/** Take the entry out of the card list **/
Cardnum = IpxData->adapternum;
ACQUIRE_CARDLIST_WRITERS_LOCK(Status, "WanCardDown");
if (Status) {
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: SapWanCardDown: Error getting cardlist writers lock\n"));
}
return;
}
/** **/
Cardptr = SapCardHead;
BCardptr = NULL;
while (Cardptr) {
/** If this is it - take it out of the list **/
if (Cardptr->Number == Cardnum) {
if (BCardptr)
BCardptr->Next = Cardptr->Next;
else
SapCardHead = Cardptr->Next;
if (Cardptr == SapCardTail)
SapCardTail = BCardptr;
SapNumCards--; /* Uncount the card */
break;
}
/** Goto the next entry **/
BCardptr = Cardptr;
Cardptr = Cardptr->Next;
}
/**
If we are still starting up - then
we just leave. If not, then we need to go
cleanup for this card.
**/
if (SapCardInitDone == 0) {
RELEASE_CARDLIST_WRITERS_LOCK("WanCardDown X1");
return;
}
/** Go shutdown the card now **/
RELEASE_CARDLIST_WRITERS_LOCK("WanCardDown X2");
if (Cardptr)
SapCleanupDownedCard(Cardnum);
/** All Done **/
return;
}
/*++
*******************************************************************
S a p R e c h e c k A l l C a r d s
Routine Description:
This routine goes thru and checks all cards occassionally
to make sure that they are still valid. This is called
from the send thread.
Arguments:
None
Return Value:
Nothing
*******************************************************************
--*/
VOID
SapRecheckAllCards(
VOID)
{
INT Cardnum;
INT rc;
INT Length;
IPX_ADDRESS_DATA Addrdata;
/** Get the list of cards we have **/
Cardnum = 0;
while (Cardnum != SapMaxCardIndex) {
/** Fill in this entry **/
Addrdata.adapternum = Cardnum;
Length = sizeof(IPX_ADDRESS_DATA);
rc = getsockopt(
SapSocket,
NSPROTO_IPX,
IPX_ADDRESS,
(PCHAR)&Addrdata,
&Length);
/**
If this card is active - call to make sure it
is in our list.
If this card is inactive - call to make sure it
is not in our list.
Only check this if the getsockopt was OK.
**/
if (rc == 0) {
if (Addrdata.status == TRUE)
SapWanCardUp(&Addrdata);
else
SapWanCardDown(&Addrdata);
}
/** Goto the next card number **/
Cardnum++;
}
/** All Done **/
return;
}
/*++
*******************************************************************
S a p C h e c k S e n d G e n e r a l R e q u e s t
Routine Description:
This routine is called by the main send thread to check
if we need to send a GENERAL REQUEST out.
When a WAN card comes up, we send a general request.
We send the request multiple times in case it got
missed.
Arguments:
None
Return Value:
Nothing
*******************************************************************
--*/
#define SAPMAX_GENREQBUF 40 /* Max we can do at once */
VOID
SapCheckSendGeneralRequest(
VOID)
{
PSAP_CARD Cardptr;
PUCHAR NetPtr;
UCHAR NetBuf[SAPMAX_GENREQBUF * SAP_NET_LEN];
INT NumNets = 0;
/**
Even though we change the cards ReqCount entry
here, we get a READERS lock because No one else
is allowed to change this.
**/
ACQUIRE_CARDLIST_READERS_LOCK("CheckSendGenReq Entry");
/**
See if any cards need the advertise sent again. We
keep a count in the card structure for this.
If we find one - copy the network number to a
private buffer for us to send out of later.
**/
NetPtr = NetBuf;
Cardptr = SapCardHead;
while (Cardptr) {
if (Cardptr->ReqCount) {
/** Dec the send again count on this adapter **/
Cardptr->ReqCount--;
/** Save off the network number **/
SAP_COPY_NETNUM(NetPtr, Cardptr->Netnum);
NetPtr += SAP_NET_LEN;
/** If we hit max - just dump out **/
NumNets++;
if (NumNets == SAPMAX_GENREQBUF)
break;
}
Cardptr = Cardptr->Next;
}
RELEASE_CARDLIST_READERS_LOCK("CheckSendGenReq X1");
/**
Send a general request on this network number to
get our table filled up for this network
**/
NetPtr = NetBuf;
while (NumNets--) {
SapSendGeneralRequest(FALSE, NetPtr);
NetPtr += SAP_NET_LEN;
}
/** All Done **/
return;
}