NT4/private/ntos/tdi/tcpip/tcp/addr.c
2020-09-30 17:12:29 +02:00

2062 lines
60 KiB
C

/********************************************************************/
/** Microsoft LAN Manager **/
/** Copyright(c) Microsoft Corp., 1990-1993 **/
/********************************************************************/
/* :ts=4 */
//** ADDR.C - TDI address object procedures
//
// This file contains the TDI address object related procedures,
// including TDI open address, TDI close address, etc.
//
// The local address objects are stored in a hash table, protected
// by the AddrObjTableLock. In order to insert or delete from the
// hash table this lock must be held, as well as the address object
// lock. The table lock must always be taken before the object lock.
//
#include "oscfg.h"
#include "ndis.h"
#include "tdi.h"
#include "tdistat.h"
#include "cxport.h"
#include "ip.h"
#ifdef VXD
#include "tdivxd.h"
#endif
#ifdef NT
#include "tdint.h"
#include "tdistat.h"
#endif
#include "queue.h"
#include "addr.h"
#include "udp.h"
#include "raw.h"
#ifndef UDP_ONLY
#include "tcp.h"
#include "tcpconn.h"
#else
#include "tcpdeb.h"
#endif
#include "info.h"
#include "tcpinfo.h"
#include "tcpcfg.h"
extern IPInfo LocalNetInfo; // Information about the local nets.
EXTERNAL_LOCK(DGSendReqLock)
#ifndef UDP_ONLY
EXTERNAL_LOCK(ConnTableLock)
#endif
extern void FreeAORequest(AORequest *Request);
//* Addess object hash table.
AddrObj *AddrObjTable[AO_TABLE_SIZE];
AddrObj *LastAO; // one element lookup cache.
DEFINE_LOCK_STRUCTURE(AddrObjTableLock)
//* AORequest free list.
AORequest *AORequestFree;
ushort NextUserPort = MIN_USER_PORT;
#define NUM_AO_REQUEST 5
DEFINE_LOCK_STRUCTURE(AORequestLock)
#define AO_HASH(a, p) ((*(uint *)&(a) + (uint)(p)) % AO_TABLE_SIZE)
#ifdef VXD
#define DEFAULT_AO_INDEX_SIZE 32
#define AO_INDEX_INCR 16 // How much to grow by.
typedef AddrObj *AOIndexTbl[];
ushort AOInstance; // Global AO instance count.
uint AOIndexSize; // # of entries in AOIndex.
uint NextAOIndex; // Next AO index to use.
AOIndexTbl *AOIndex;
#define AO_INDEX(i) ((i) & 0xffff)
#define AO_INST(i) ((i) >> 16)
#define MAKE_AO_INDEX(s, i) (uint)(((s) << 16) | ((i) & 0xffff))
#define MAX_INDEX_SIZE 256
#define INVALID_INDEX 0xffffffff
#endif
//
// All of the init code can be discarded.
//
#ifdef NT
#ifdef ALLOC_PRAGMA
int InitAddr();
#pragma alloc_text(INIT, InitAddr)
#endif // ALLOC_PRAGMA
#endif
#ifdef VXD
//* GetIndexedAO - Get an AddrObj from an index.
//
// Called by the UDP routines that access an AO to find the AO by it's
// index. We look it up in the table, and compare the high 16 bits against
// the instance # in the AddrObj.
//
// Input: Index - Index of AddrObj.
//
// Returns: Pointer to AO, or NULL if it's not valid.
//
AddrObj *
GetIndexedAO(uint Index)
{
AddrObj *AO;
if (AO_INDEX(Index) < AOIndexSize) {
AO = (*AOIndex)[AO_INDEX(Index)];
if (AO != NULL && AO->ao_inst == AO_INST(Index))
return AO;
}
return NULL;
}
//* GetAOIndex - Get an index value for an AddrObj.
//
// Called when we're creating an index value for an AddrObj. We go through
// the table, looking for a valid index. If we find one, we'll make an index
// out of it and the AOInstance variable, and bump the instance field.
// Otherwise we may grow the table.
//
// Input: AO - AddrObj to put into table.
//
// Returns: Index to use, or INVALID_INDEX if we can't find one.
//
uint
GetAOIndex(AddrObj *AO)
{
uint i; // Index variable.
uint CurrentIndex; // Current index being checked.
for (;;) {
CurrentIndex = NextAOIndex;
for (i = 0; i < AOIndexSize; i++) {
if (CurrentIndex == AOIndexSize)
CurrentIndex = 0; // Set it back to beginning.
if ((*AOIndex)[CurrentIndex] == NULL)
break; // Found the one we needed.
CurrentIndex++;
}
if (i < AOIndexSize) {
uint NewIndex;
// We came out because we found an empty slot.
AO->ao_inst = AOInstance;
AO->ao_index = (uchar)CurrentIndex;
(*AOIndex)[CurrentIndex] = AO;
NextAOIndex = CurrentIndex + 1; // Bump the next one to look at.
NewIndex = MAKE_AO_INDEX(AOInstance, CurrentIndex);
AOInstance++;
return NewIndex;
} else {
// Couldn't find a slot, so grow the table.
if (AOIndexSize != MAX_INDEX_SIZE) {
// Table isn't already at max size. Try and grow it.
uint NewIndexSize;
AOIndexTbl *NewIndexTbl, *OldIndexTbl;
NewIndexSize = MIN(MAX_INDEX_SIZE, AOIndexSize + AO_INDEX_INCR);
NewIndexTbl = CTEAllocMem(sizeof(AddrObj *) * NewIndexSize);
if (NewIndexTbl != NULL) {
// We allocated it.
CTEMemCopy(NewIndexTbl, AOIndex,
AOIndexSize * sizeof(AddrObj *));
OldIndexTbl = AOIndex;
AOIndex = NewIndexTbl;
AOIndexSize = NewIndexSize;
CTEFreeMem(OldIndexTbl); // Loop around, and try again.
} else
return INVALID_INDEX;
} else
return INVALID_INDEX;
}
}
}
#endif
//* ReadNextAO - Read the next AddrObj in the table.
//
// Called to read the next AddrObj in the table. The needed information
// is derived from the incoming context, which is assumed to be valid.
// We'll copy the information, and then update the context value with
// the next AddrObj to be read.
//
// Input: Context - Poiner to a UDPContext.
// Buffer - Pointer to a UDPEntry structure.
//
// Returns: TRUE if more data is available to be read, FALSE is not.
//
uint
ReadNextAO(void *Context, void *Buffer)
{
UDPContext *UContext = (UDPContext *)Context;
UDPEntry *UEntry = (UDPEntry *)Buffer;
AddrObj *CurrentAO;
uint i;
CurrentAO = UContext->uc_ao;
CTEStructAssert(CurrentAO, ao);
UEntry->ue_localaddr = CurrentAO->ao_addr;
UEntry->ue_localport = CurrentAO->ao_port;
// We've filled it in. Now update the context.
CurrentAO = CurrentAO->ao_next;
if (CurrentAO != NULL && CurrentAO->ao_prot == PROTOCOL_UDP) {
UContext->uc_ao = CurrentAO;
return TRUE;
} else {
// The next AO is NULL, or not a UDP AO. Loop through the AddrObjTable
// looking for a new one.
i = UContext->uc_index;
for (;;) {
while (CurrentAO != NULL) {
if (CurrentAO->ao_prot == PROTOCOL_UDP)
break;
else
CurrentAO = CurrentAO->ao_next;
}
if (CurrentAO != NULL)
break; // Get out of for (;;) loop.
CTEAssert(CurrentAO == NULL);
// Didn't find one on this chain. Walk down the table, looking
// for the next one.
while (++i < AO_TABLE_SIZE) {
if (AddrObjTable[i] != NULL) {
CurrentAO = AddrObjTable[i];
break; // Out of while loop.
}
}
if (i == AO_TABLE_SIZE)
break; // Out of for (;;) loop.
}
// If we found one, return it.
if (CurrentAO != NULL) {
UContext->uc_ao = CurrentAO;
UContext->uc_index = i;
return TRUE;
} else {
UContext->uc_index = 0;
UContext->uc_ao = NULL;
return FALSE;
}
}
}
//* ValidateAOContext - Validate the context for reading the AddrObj table.
//
// Called to start reading the AddrObj table sequentially. We take in
// a context, and if the values are 0 we return information about the
// first AddrObj in the table. Otherwise we make sure that the context value
// is valid, and if it is we return TRUE.
// We assume the caller holds the AddrObjTable lock.
//
// Input: Context - Pointer to a UDPContext.
// Valid - Where to return information about context being
// valid.
//
// Returns: TRUE if data in table, FALSE if not. *Valid set to true if the
// context is valid.
//
uint
ValidateAOContext(void *Context, uint *Valid)
{
UDPContext *UContext = (UDPContext *)Context;
uint i;
AddrObj *TargetAO;
AddrObj *CurrentAO;
i = UContext->uc_index;
TargetAO = UContext->uc_ao;
// If the context values are 0 and NULL, we're starting from the beginning.
if (i == 0 && TargetAO == NULL) {
*Valid = TRUE;
do {
if ((CurrentAO = AddrObjTable[i]) != NULL) {
CTEStructAssert(CurrentAO, ao);
while (CurrentAO != NULL && CurrentAO->ao_prot != PROTOCOL_UDP)
CurrentAO = CurrentAO->ao_next;
if (CurrentAO != NULL)
break;
}
i++;
} while (i < AO_TABLE_SIZE);
if (CurrentAO != NULL) {
UContext->uc_index = i;
UContext->uc_ao = CurrentAO;
return TRUE;
} else
return FALSE;
} else {
// We've been given a context. We just need to make sure that it's
// valid.
if (i < AO_TABLE_SIZE) {
CurrentAO = AddrObjTable[i];
while (CurrentAO != NULL) {
if (CurrentAO == TargetAO) {
if (CurrentAO->ao_prot == PROTOCOL_UDP) {
*Valid = TRUE;
return TRUE;
}
break;
} else {
CurrentAO = CurrentAO->ao_next;
}
}
}
// If we get here, we didn't find the matching AddrObj.
*Valid = FALSE;
return FALSE;
}
}
//** GetAddrObj - Find a local address object.
//
// This is the local address object lookup routine. We take as input the local
// address and port and a pointer to a 'previous' address object. The hash
// table entries in each bucket are sorted in order of increasing address, and
// we skip over any object that has an address lower than the 'previous'
// address. To get the first address object, pass in a previous value of NULL.
//
// We assume that the table lock is held while we're in this routine. We don't
// take each object lock, since the local address and port can't change while
// the entry is in the table and the table lock is held so nothing can be
// inserted or deleted.
//
// Input: LocalAddr - Local IP address of object to find (may be NULL);
// LocalPort - Local port of object to find.
// Protocol - Protocol to find.
// PreviousAO - Pointer to last address object found.
//
// Returns: A pointer to the Address object, or NULL if none.
//
AddrObj *
GetAddrObj(IPAddr LocalAddr, ushort LocalPort, uchar Protocol,
AddrObj *PreviousAO)
{
AddrObj *CurrentAO; // Current address object we're examining.
#ifdef DEBUG
if (PreviousAO != NULL)
CTEStructAssert(PreviousAO, ao);
#endif
#if 0
//
// Check our 1-element cache for a match
//
if ((PreviousAO == NULL) && (LastAO != NULL)) {
CTEStructAssert(LastAO, ao);
if ( (LastAO->ao_prot == Protocol) &&
IP_ADDR_EQUAL(LastAO->ao_addr, LocalAddr) &&
(LastAO->ao_port == LocalPort)
)
{
return LastAO;
}
}
#endif
// Find the appropriate bucket in the hash table, and search for a match.
// If we don't find one the first time through, we'll try again with a
// wildcard local address.
for (;;) {
CurrentAO = AddrObjTable[AO_HASH(LocalAddr, LocalPort)];
// While we haven't hit the end of the list, examine each element.
while (CurrentAO != NULL) {
CTEStructAssert(CurrentAO, ao);
// If the current one is greater than one we were given, check it.
//
// #62710: Return only valid AO's since we might have stale AO's lying
// around.
//
if ((CurrentAO > PreviousAO) &&
(AO_VALID(CurrentAO))) {
if (!(CurrentAO->ao_flags & AO_RAW_FLAG)) {
if ( IP_ADDR_EQUAL(CurrentAO->ao_addr, LocalAddr) &&
(CurrentAO->ao_port == LocalPort) &&
(CurrentAO->ao_prot == Protocol)
)
{
LastAO = CurrentAO;
return CurrentAO;
}
}
else {
if ( (Protocol != PROTOCOL_UDP)
#ifndef UDP_ONLY
&& (Protocol != PROTOCOL_TCP)
#endif
)
{
IF_TCPDBG(TCP_DEBUG_RAW) {
TCPTRACE((
"matching <p, a> <%u, %lx> ao %lx <%u, %lx>\n",
Protocol, LocalAddr, CurrentAO,
CurrentAO->ao_prot, CurrentAO->ao_addr
));
}
if ( IP_ADDR_EQUAL(CurrentAO->ao_addr, LocalAddr) &&
( (CurrentAO->ao_prot == Protocol) ||
(CurrentAO->ao_prot == 0)
)
)
{
LastAO = CurrentAO;
return CurrentAO;
}
}
}
}
// Either it was less than the previous one, or they didn't match.
CurrentAO = CurrentAO->ao_next;
}
// When we get here, we've hit the end of the list we were examining.
// If we weren't examining a wildcard address, look for a wild card
// address.
if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) {
LocalAddr = NULL_IP_ADDR;
PreviousAO = NULL;
} else
return NULL; // We looked for a wildcard and couldn't find
// one, so fail.
}
}
//* GetNextAddrObj - Get the next address object in a sequential search.
//
// This is the 'get next' routine, called when we are reading the address
// object table sequentially. We pull the appropriate parameters from the
// search context, call GetAddrObj, and update the search context with what
// we find. This routine assumes the AddrObjTableLock is held by the caller.
//
// Input: SearchContext - Pointer to seach context for search taking place.
//
// Returns: Pointer to AddrObj, or NULL if search failed.
//
AddrObj *
GetNextAddrObj(AOSearchContext *SearchContext)
{
AddrObj *FoundAO; // Pointer to the address object we found.
CTEAssert(SearchContext != NULL);
// Try and find a match.
FoundAO = GetAddrObj(SearchContext->asc_addr, SearchContext->asc_port,
SearchContext->asc_prot, SearchContext->asc_previous);
// Found a match. Update the search context for next time.
if (FoundAO != NULL) {
SearchContext->asc_previous = FoundAO;
SearchContext->asc_addr = FoundAO->ao_addr;
// Don't bother to update port or protocol, they don't change.
}
return FoundAO;
}
//* GetFirstAddrObj - Get the first matching address object.
//
// The routine called to start a sequential read of the AddrObj table. We
// initialize the provided search context and then call GetNextAddrObj to do
// the actual read. We assume that the AddrObjTableLock is held by the caller.
//
// Input: LocalAddr - Local IP address of object to be found.
// LocalPort - Local port of AO to be found.
// Protocol - Protocol to be found.
// SearchContext - Pointer to search context to be used during
// search.
//
// Returns: Pointer to AO found, or NULL if we couldn't find any.
//
AddrObj *
GetFirstAddrObj(IPAddr LocalAddr, ushort LocalPort, uchar Protocol,
AOSearchContext *SearchContext)
{
CTEAssert(SearchContext != NULL);
// Fill in the search context.
SearchContext->asc_previous = NULL; // Haven't found one yet.
SearchContext->asc_addr = LocalAddr;
SearchContext->asc_port = LocalPort;
SearchContext->asc_prot = Protocol;
return GetNextAddrObj(SearchContext);
}
//* InsertAddrObj - Insert an address object into the AddrObj table.
//
// Called to insert an AO into the table, assuming the table lock is held. We
// hash on the addr and port, and then insert in into the correct place
// (sorted by address of the objects).
//
// Input: NewAO - Pointer to AddrObj to be inserted.
//
// Returns: Nothing.
//
void
InsertAddrObj(AddrObj *NewAO)
{
AddrObj *PrevAO; // Pointer to previous address object in hash
// chain.
AddrObj *CurrentAO; // Pointer to current AO in table.
CTEStructAssert(NewAO, ao);
PrevAO = STRUCT_OF(AddrObj,
&AddrObjTable[AO_HASH(NewAO->ao_addr, NewAO->ao_port)], ao_next);
CurrentAO = PrevAO->ao_next;
// Loop through the chain until we hit the end or until we find an entry
// whose address is greater than ours.
while (CurrentAO != NULL) {
CTEStructAssert(CurrentAO, ao);
CTEAssert(CurrentAO != NewAO); // Debug check to make sure we aren't
// inserting the same entry.
if (NewAO < CurrentAO)
break;
PrevAO = CurrentAO;
CurrentAO = CurrentAO->ao_next;
}
// At this point, PrevAO points to the AO before the new one. Insert it
// there.
CTEAssert(PrevAO != NULL);
CTEAssert(PrevAO->ao_next == CurrentAO);
NewAO->ao_next = CurrentAO;
PrevAO->ao_next = NewAO;
if (NewAO->ao_prot == PROTOCOL_UDP)
UStats.us_numaddrs++;
}
//* RemoveAddrObj - Remove an address object from the table.
//
// Called when we need to remove an address object from the table. We hash on
// the addr and port, then walk the table looking for the object. We assume
// that the table lock is held.
//
// The AddrObj may have already been removed from the table if it was
// invalidated for some reason, so we need to check for the case of not
// finding it.
//
// Input: DeletedAO - AddrObj to delete.
//
// Returns: Nothing.
//
void
RemoveAddrObj(AddrObj *RemovedAO)
{
AddrObj *PrevAO; // Pointer to previous address object in hash
// chain.
AddrObj *CurrentAO; // Pointer to current AO in table.
CTEStructAssert(RemovedAO, ao);
PrevAO = STRUCT_OF(AddrObj,
&AddrObjTable[AO_HASH(RemovedAO->ao_addr, RemovedAO->ao_port)],
ao_next);
CurrentAO = PrevAO->ao_next;
// Walk the table, looking for a match.
while (CurrentAO != NULL) {
CTEStructAssert(CurrentAO, ao);
if (CurrentAO == RemovedAO) {
PrevAO->ao_next = CurrentAO->ao_next;
if (CurrentAO->ao_prot == PROTOCOL_UDP) {
UStats.us_numaddrs--;
}
if (CurrentAO == LastAO) {
LastAO = NULL;
}
return;
} else {
PrevAO = CurrentAO;
CurrentAO = CurrentAO->ao_next;
}
}
// If we get here, we didn't find him. This is OK, but we should say
// something about it.
CTEPrint("RemoveAddrObj: Object not found.\r\n");
}
//* FindAnyAddrObj - Find an AO with matching port on any local address.
//
// Called for wildcard address opens. We go through the entire addrobj table,
// and see if anyone has the specified port. We assume that the lock is
// already held on the table.
//
// Input: Port - Port to be looked for.
// Protocol - Protocol on which to look.
//
// Returns: Pointer to AO found, or NULL is noone has it.
//
AddrObj *
FindAnyAddrObj(ushort Port, uchar Protocol)
{
int i; // Index variable.
AddrObj *CurrentAO; // Current AddrObj being examined.
for (i = 0; i < AO_TABLE_SIZE; i++) {
CurrentAO = AddrObjTable[i];
while (CurrentAO != NULL) {
CTEStructAssert(CurrentAO, ao);
if (CurrentAO->ao_port == Port && CurrentAO->ao_prot == Protocol)
return CurrentAO;
else
CurrentAO = CurrentAO->ao_next;
}
}
return NULL;
}
//* GetAddress - Get an IP address and port from a TDI address structure.
//
// Called when we need to get our addressing information from a TDI
// address structure. We go through the structure, and return what we
// find.
//
// Input: AddrList - Pointer to TRANSPORT_ADDRESS structure to search.
// Addr - Pointer to where to return IP address.
// Port - Pointer to where to return Port.
//
// Return: TRUE if we find an address, FALSE if we don't.
//
uchar
GetAddress(TRANSPORT_ADDRESS UNALIGNED *AddrList, IPAddr *Addr, ushort *Port)
{
int i; // Index variable.
TA_ADDRESS UNALIGNED *CurrentAddr; // Address we're examining and may use.
// First, verify that someplace in Address is an address we can use.
CurrentAddr = (TA_ADDRESS UNALIGNED *)AddrList->Address;
for (i = 0; i < AddrList->TAAddressCount; i++) {
if (CurrentAddr->AddressType == TDI_ADDRESS_TYPE_IP) {
if (CurrentAddr->AddressLength >= TDI_ADDRESS_LENGTH_IP) {
TDI_ADDRESS_IP UNALIGNED *ValidAddr =
(TDI_ADDRESS_IP UNALIGNED *)CurrentAddr->Address;
*Port = ValidAddr->sin_port;
*Addr = ValidAddr->in_addr;
return TRUE;
} else
return FALSE; // Wrong length for address.
} else
CurrentAddr = (TA_ADDRESS UNALIGNED *)(CurrentAddr->Address +
CurrentAddr->AddressLength);
}
return FALSE; // Didn't find a match.
}
//* InvalidateAddrs - Invalidate all AOs for a specific address.
//
// Called when we need to invalidate all AOs for a specific address. Walk
// down the table with the lock held, and take the lock on each AddrObj.
// If the address matches, mark it as invalid, pull off all requests,
// and continue. At the end we'll complete all requests with an error.
//
// Input: Addr - Addr to be invalidated.
//
// Returns: Nothing.
//
void
InvalidateAddrs(IPAddr Addr)
{
Queue SendQ;
Queue RcvQ;
AORequest *ReqList;
CTELockHandle TableHandle, AOHandle;
uint i;
AddrObj *AO;
DGSendReq *SendReq;
DGRcvReq *RcvReq;
AOMCastAddr *MA;
INITQ(&SendQ);
INITQ(&RcvQ);
ReqList = NULL;
CTEGetLock(&AddrObjTableLock, &TableHandle);
for (i = 0; i < AO_TABLE_SIZE; i++) {
// Walk down each hash bucket, looking for a match.
AO = AddrObjTable[i];
while (AO != NULL) {
CTEStructAssert(AO, ao);
CTEGetLock(&AO->ao_lock, &AOHandle);
if (IP_ADDR_EQUAL(AO->ao_addr, Addr) && AO_VALID(AO)) {
// This one matches. Mark as invalid, then pull his requests.
SET_AO_INVALID(AO);
// Free any IP options we have.
(*LocalNetInfo.ipi_freeopts)(&AO->ao_opt);
// If he has a request on him, pull him off.
if (AO->ao_request != NULL) {
AORequest *Temp;
Temp = STRUCT_OF(AORequest, &AO->ao_request, aor_next);
do {
Temp = Temp->aor_next;
} while (Temp->aor_next != NULL);
Temp->aor_next = ReqList;
ReqList = AO->ao_request;
AO->ao_request = NULL;
}
// Go down his send list, pulling things off the send q and
// putting them on our local queue.
while (!EMPTYQ(&AO->ao_sendq)) {
DEQUEUE(&AO->ao_sendq, SendReq, DGSendReq, dsr_q);
CTEStructAssert(SendReq, dsr);
ENQUEUE(&SendQ, &SendReq->dsr_q);
}
// Do the same for the receive queue.
while (!EMPTYQ(&AO->ao_rcvq)) {
DEQUEUE(&AO->ao_rcvq, RcvReq, DGRcvReq, drr_q);
CTEStructAssert(RcvReq, drr);
ENQUEUE(&RcvQ, &RcvReq->drr_q);
}
// Free any multicast addresses he may have. IP will have
// deleted them at that level before we get here, so all we need
// to do if free the memory.
MA = AO->ao_mcastlist;
while (MA != NULL) {
AOMCastAddr *Temp;
Temp = MA;
MA = MA->ama_next;
CTEFreeMem(Temp);
}
AO->ao_mcastlist = NULL;
}
CTEFreeLock(&AO->ao_lock, AOHandle);
AO = AO->ao_next; // Go to the next one.
}
}
CTEFreeLock(&AddrObjTableLock, TableHandle);
// OK, now walk what we've collected, complete it, and free it.
while (ReqList != NULL) {
AORequest *Req;
Req = ReqList;
ReqList = Req->aor_next;
(*Req->aor_rtn)(Req->aor_context, (uint) TDI_ADDR_INVALID, 0);
FreeAORequest(Req);
}
// Walk down the rcv. q, completing and freeing requests.
while (!EMPTYQ(&RcvQ)) {
DEQUEUE(&RcvQ, RcvReq, DGRcvReq, drr_q);
CTEStructAssert(RcvReq, drr);
(*RcvReq->drr_rtn)(RcvReq->drr_context, (uint) TDI_ADDR_INVALID, 0);
FreeDGRcvReq(RcvReq);
}
// Now do the same for sends.
while (!EMPTYQ(&SendQ)) {
DEQUEUE(&SendQ, SendReq, DGSendReq, dsr_q);
CTEStructAssert(SendReq, dsr);
(*SendReq->dsr_rtn)(SendReq->dsr_context, (uint) TDI_ADDR_INVALID, 0);
CTEGetLock(&DGSendReqLock, &TableHandle);
if (SendReq->dsr_header != NULL)
FreeDGHeader(SendReq->dsr_header);
FreeDGSendReq(SendReq);
CTEFreeLock(&DGSendReqLock, TableHandle);
}
}
//* RequestEventProc - Handle a deferred request event.
//
// Called when the event scheduled by DelayDerefAO is called.
// We just call ProcessAORequest.
//
// Input: Event - Event that fired.
// Context - Pointer to AddrObj.
//
// Returns: Nothing.
//
void
RequestEventProc(CTEEvent *Event, void *Context)
{
AddrObj *AO = (AddrObj *)Context;
CTEStructAssert(AO, ao);
CTEAssert(AO_BUSY(AO));
ProcessAORequests(AO);
}
//* GetAddrOptions - Get the address options.
//
// Called when we're opening an address. We take in a pointer, and walk
// down it looking for address options we know about.
//
// Input: Ptr - Ptr to search.
// Reuse - Pointer to reuse variable.
// DHCPAddr - Pointer to DHCP addr.
//
// Returns: Nothing.
//
void
GetAddrOptions(void *Ptr, uchar *Reuse, uchar *DHCPAddr)
{
uchar *OptPtr;
*Reuse = 0;
*DHCPAddr = 0;
if (Ptr == NULL)
return;
OptPtr = (uchar *)Ptr;
while (*OptPtr != TDI_OPTION_EOL) {
if (*OptPtr == TDI_ADDRESS_OPTION_REUSE)
*Reuse = 1;
else
if (*OptPtr == TDI_ADDRESS_OPTION_DHCP)
*DHCPAddr = 1;
OptPtr++;
}
}
//* TdiOpenAddress - Open a TDI address object.
//
// This is the external interface to open an address. The caller provides a
// TDI_REQUEST structure and a TRANSPORT_ADDRESS structure, as well a pointer
// to a variable identifying whether or not we are to allow reuse of an
// address while it's still open.
//
// Input: Request - Pointer to a TDI request structure for this request.
// AddrList - Pointer to TRANSPORT_ADDRESS structure describing
// address to be opened.
// Protocol - Protocol on which to open the address. Only the
// least significant byte is used.
// Ptr - Pointer to option buffer.
//
// Returns: TDI_STATUS code of attempt.
//
TDI_STATUS
TdiOpenAddress(PTDI_REQUEST Request, TRANSPORT_ADDRESS UNALIGNED *AddrList,
uint Protocol, void *Ptr)
{
uint i; // Index variable
ushort Port; // Local Port we'll use.
IPAddr LocalAddr; // Actual address we'll use.
AddrObj *NewAO; // New AO we'll use.
AddrObj *ExistingAO; // Pointer to existing AO, if any.
CTELockHandle Handle;
uchar Reuse, DHCPAddr;
if (!GetAddress(AddrList, &LocalAddr, &Port))
return TDI_BAD_ADDR;
// Find the address options we might need.
GetAddrOptions(Ptr, &Reuse, &DHCPAddr);
// Allocate the new addr obj now, assuming that
// we need it, so we don't have to do it with locks held later.
NewAO = CTEAllocMem(sizeof(AddrObj));
if (NewAO != NULL) {
#ifdef VXD
uint NewAOIndex;
#endif
CTEMemSet(NewAO, 0, sizeof(AddrObj));
// Check to make sure IP address is one of our local addresses. This
// is protected with the address table lock, so we can interlock an IP
// address going away through DHCP.
CTEGetLock(&AddrObjTableLock, &Handle);
if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) { // Not a wildcard.
// Call IP to find out if this is a local address.
if ((*LocalNetInfo.ipi_getaddrtype)(LocalAddr) != DEST_LOCAL) {
// Not a local address. Fail the request.
CTEFreeLock(&AddrObjTableLock, Handle);
CTEFreeMem(NewAO);
return TDI_BAD_ADDR;
}
}
// The specified IP address is a valid local address. Now we do
// protocol-specific processing.
switch (Protocol) {
#ifndef UDP_ONLY
case PROTOCOL_TCP:
#endif
case PROTOCOL_UDP:
// If no port is specified we have to assign one. If there is a
// port specified, we need to make sure that the IPAddress/Port
// combo isn't already open (unless Reuse is specified). If the
// input address is a wildcard, we need to make sure the address
// isn't open on any local ip address.
if (Port == WILDCARD_PORT) { // Have a wildcard port, need to assign an
// address.
Port = NextUserPort;
for (i = 0; i < NUM_USER_PORTS; i++, Port++) {
ushort NetPort; // Port in net byte order.
if (Port > MaxUserPort)
Port = MIN_USER_PORT;
NetPort = net_short(Port);
if (IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) // Wildcard IP
// address.
ExistingAO = FindAnyAddrObj(NetPort, (uchar)Protocol);
else
ExistingAO = GetBestAddrObj(LocalAddr, NetPort, (uchar)Protocol);
if (ExistingAO == NULL)
break; // Found an unused port.
}
if (i == NUM_USER_PORTS) { // Couldn't find a free port.
CTEFreeLock(&AddrObjTableLock, Handle);
CTEFreeMem(NewAO);
return TDI_NO_FREE_ADDR;
}
NextUserPort = Port + 1;
Port = net_short(Port);
} else { // Address was specificed
// Don't check if a DHCP address is specified.
if (!DHCPAddr) {
if (IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) // Wildcard IP
ExistingAO = FindAnyAddrObj(Port, (uchar)Protocol); // address.
else
ExistingAO = GetBestAddrObj(LocalAddr, Port, (uchar)Protocol);
if (ExistingAO != NULL) { // We already have this address open.
// If the caller hasn't asked for Reuse, fail the request.
if (!Reuse) {
CTEFreeLock(&AddrObjTableLock, Handle);
CTEFreeMem(NewAO);
return TDI_ADDR_IN_USE;
}
}
}
}
//
// We have a new AO. Set up the protocol specific portions
//
if (Protocol == PROTOCOL_UDP) {
NewAO->ao_dgsend = UDPSend;
NewAO->ao_maxdgsize = 0xFFFF - sizeof(UDPHeader);
}
SET_AO_XSUM(NewAO); // Checksumming defaults to on.
break;
// end case TCP & UDP
default:
//
// All other protocols are opened over Raw IP. For now we don't
// do any duplicate checks.
//
CTEAssert(!DHCPAddr);
//
// We must set the port to zero. This puts all the raw sockets
// in one hash bucket, which is necessary for GetAddrObj to
// work correctly. It wouldn't be a bad idea to come up with
// a better scheme...
//
Port = 0;
NewAO->ao_dgsend = RawSend;
NewAO->ao_maxdgsize = 0xFFFF;
NewAO->ao_flags |= AO_RAW_FLAG;
IF_TCPDBG(TCP_DEBUG_RAW) {
TCPTRACE(("raw open protocol %u AO %lx\n", Protocol, NewAO));
}
break;
}
// When we get here, we know we're creating a brand new address object.
// Port contains the port in question, and NewAO points to the newly
// created AO.
(*LocalNetInfo.ipi_initopts)(&NewAO->ao_opt);
(*LocalNetInfo.ipi_initopts)(&NewAO->ao_mcastopt);
NewAO->ao_mcastopt.ioi_ttl = 1;
NewAO->ao_mcastaddr = NULL_IP_ADDR;
CTEInitLock(&NewAO->ao_lock);
CTEInitEvent(&NewAO->ao_event, RequestEventProc);
INITQ(&NewAO->ao_sendq);
INITQ(&NewAO->ao_rcvq);
INITQ(&NewAO->ao_activeq);
INITQ(&NewAO->ao_idleq);
INITQ(&NewAO->ao_listenq);
NewAO->ao_port = Port;
NewAO->ao_addr = LocalAddr;
NewAO->ao_prot = (uchar)Protocol;
#ifdef DEBUG
NewAO->ao_sig = ao_signature;
#endif
NewAO->ao_flags |= AO_VALID_FLAG; // AO is valid.
if (DHCPAddr)
NewAO->ao_flags |= AO_DHCP_FLAG;
#ifdef VXD
NewAOIndex = GetAOIndex(NewAO);
if (NewAOIndex == INVALID_INDEX) {
CTEFreeLock(&AddrObjTableLock, Handle);
CTEFreeMem(NewAO);
return TDI_NO_RESOURCES;
}
#endif
InsertAddrObj(NewAO);
CTEFreeLock(&AddrObjTableLock, Handle);
#ifdef VXD
Request->Handle.AddressHandle = (PVOID)NewAOIndex;
#else
Request->Handle.AddressHandle = NewAO;
#endif
return TDI_SUCCESS;
} else { // Couldn't allocate an address object.
return TDI_NO_RESOURCES;
}
}
//* DeleteAO - Delete an address object.
//
// The internal routine to delete an address object. We complete any pending
// requests with errors, and remove and free the address object.
//
// Input: DeletedAO - AddrObj to be deleted.
//
// Returns: Nothing.
//
void
DeleteAO(AddrObj *DeletedAO)
{
CTELockHandle TableHandle, AOHandle; // Lock handles we'll use here.
CTELockHandle HeaderHandle;
#ifndef UDP_ONLY
CTELockHandle ConnHandle, TCBHandle;
TCB *TCBHead = NULL, *CurrentTCB;
TCPConn *Conn;
Queue *Temp;
Queue *CurrentQ;
#endif
AOMCastAddr *AMA;
CTEStructAssert(DeletedAO, ao);
CTEAssert(!AO_VALID(DeletedAO));
CTEAssert(DeletedAO->ao_usecnt == 0);
CTEGetLock(&AddrObjTableLock, &TableHandle);
#ifndef UDP_ONLY
CTEGetLock(&ConnTableLock, &ConnHandle);
#endif
CTEGetLock(&DGSendReqLock, &HeaderHandle);
CTEGetLock(&DeletedAO->ao_lock, &AOHandle);
// If he's on an oor queue, remove him.
if (AO_OOR(DeletedAO))
REMOVEQ(&DeletedAO->ao_pendq);
#ifdef VXD
(*AOIndex)[DeletedAO->ao_index] = NULL;
#endif
RemoveAddrObj(DeletedAO);
#ifndef UDP_ONLY
// Walk down the list of associated connections and zap their AO pointers.
// For each connection, we need to shut down the connection if it's active.
// If the connection isn't already closing, we'll put a reference on it
// so that it can't go away while we're dealing with the AO, and put it
// on a list. On our way out we'll walk down that list and zap each
// connection.
CurrentQ = &DeletedAO->ao_activeq;
for (;;) {
Temp = QHEAD(CurrentQ);
while (Temp != QEND(CurrentQ)) {
Conn = QSTRUCT(TCPConn, Temp, tc_q);
CTEStructAssert(Conn, tc);
CurrentTCB = Conn->tc_tcb;
if (CurrentTCB != NULL) {
// We have a TCB.
CTEStructAssert(CurrentTCB, tcb);
CTEGetLock(&CurrentTCB->tcb_lock, &TCBHandle);
if (CurrentTCB->tcb_state != TCB_CLOSED && !CLOSING(CurrentTCB)) {
// It's not closing. Put a reference on it and save it on the
// list.
CurrentTCB->tcb_refcnt++;
CurrentTCB->tcb_aonext = TCBHead;
TCBHead = CurrentTCB;
}
CurrentTCB->tcb_conn = NULL;
CurrentTCB->tcb_rcvind = NULL;
CTEFreeLock(&CurrentTCB->tcb_lock, TCBHandle);
}
// Destroy the pointers to the TCB and the AO.
Conn->tc_ao = NULL;
Conn->tc_tcb = NULL;
Temp = QNEXT(Temp);
}
if (CurrentQ == &DeletedAO->ao_activeq) {
CurrentQ = &DeletedAO->ao_idleq;
} else if (CurrentQ == &DeletedAO->ao_idleq) {
CurrentQ = &DeletedAO->ao_listenq;
} else {
CTEAssert(CurrentQ == &DeletedAO->ao_listenq);
break;
}
}
#endif
// We've removed him from the queues, and he's marked as invalid. Return
// pending requests with errors.
#ifndef UDP_ONLY
CTEFreeLock(&DGSendReqLock, AOHandle);
CTEFreeLock(&ConnTableLock, HeaderHandle);
CTEFreeLock(&AddrObjTableLock, ConnHandle);
#else
CTEFreeLock(&DGSendReqLock, AOHandle);
CTEFreeLock(&AddrObjTableLock, HeaderHandle);
#endif
// We still hold the lock on the AddrObj, although this may not be
// neccessary.
while (!EMPTYQ(&DeletedAO->ao_rcvq)) {
DGRcvReq *Rcv;
DEQUEUE(&DeletedAO->ao_rcvq, Rcv, DGRcvReq, drr_q);
CTEStructAssert(Rcv, drr);
CTEFreeLock(&DeletedAO->ao_lock, TableHandle);
(*Rcv->drr_rtn)(Rcv->drr_context, (uint) TDI_ADDR_DELETED, 0);
FreeDGRcvReq(Rcv);
CTEGetLock(&DeletedAO->ao_lock, &TableHandle);
}
// Now destroy any sends.
while (!EMPTYQ(&DeletedAO->ao_sendq)) {
DGSendReq *Send;
DEQUEUE(&DeletedAO->ao_sendq, Send, DGSendReq, dsr_q);
CTEStructAssert(Send, dsr);
CTEFreeLock(&DeletedAO->ao_lock, TableHandle);
(*Send->dsr_rtn)(Send->dsr_context, (uint) TDI_ADDR_DELETED, 0);
CTEGetLock(&DGSendReqLock, &HeaderHandle);
if (Send->dsr_header != NULL)
FreeDGHeader(Send->dsr_header);
FreeDGSendReq(Send);
CTEFreeLock(&DGSendReqLock, HeaderHandle);
CTEGetLock(&DeletedAO->ao_lock, &TableHandle);
}
CTEFreeLock(&DeletedAO->ao_lock, TableHandle);
// Free any IP options we have.
(*LocalNetInfo.ipi_freeopts)(&DeletedAO->ao_opt);
// Free any associated multicast addresses.
AMA = DeletedAO->ao_mcastlist;
while (AMA != NULL) {
AOMCastAddr *Temp;
(*LocalNetInfo.ipi_setmcastaddr)(AMA->ama_addr, AMA->ama_if, FALSE);
Temp = AMA;
AMA = AMA->ama_next;
CTEFreeMem(Temp);
}
CTEFreeMem(DeletedAO);
#ifndef UDP_ONLY
// Now go down the TCB list, and destroy any we need to.
CurrentTCB = TCBHead;
while (CurrentTCB != NULL) {
TCB *NextTCB;
CTEGetLock(&CurrentTCB->tcb_lock, &TCBHandle);
CurrentTCB->tcb_refcnt--;
CurrentTCB->tcb_flags |= NEED_RST; // Make sure we send a RST.
NextTCB = CurrentTCB->tcb_aonext;
TryToCloseTCB(CurrentTCB, TCB_CLOSE_ABORTED, TCBHandle);
CurrentTCB = NextTCB;
}
#endif
}
//* GetAORequest - Get an AO request structure.
//
// A routine to allocate a request structure from our free list.
//
// Input: Nothing.
//
// Returns: Pointer to request structure, or NULL if we couldn't get one.
//
AORequest *
GetAORequest()
{
AORequest *NewRequest;
CTELockHandle Handle;
CTEGetLock(&AORequestLock, &Handle);
NewRequest = AORequestFree;
if (NewRequest != NULL) {
AORequestFree = (AORequest *)NewRequest->aor_rtn;
CTEStructAssert(NewRequest, aor);
}
CTEFreeLock(&AORequestLock, Handle);
return NewRequest;
}
//* FreeAORequest - Free an AO request structure.
//
// Called to free an AORequest structure.
//
// Input: Request - AORequest structure to be freed.
//
// Returns: Nothing.
//
void
FreeAORequest(AORequest *Request)
{
CTELockHandle Handle;
CTEStructAssert(Request, aor);
CTEGetLock(&AORequestLock, &Handle);
*(AORequest **)&Request->aor_rtn = AORequestFree;
AORequestFree = Request;
CTEFreeLock(&AORequestLock, Handle);
}
//* TDICloseAddress - Close an address.
//
// The user API to delete an address. Basically, we destroy the local address
// object if we can.
//
// This routine is interlocked with the AO busy bit - if the busy bit is set,
// we'll just flag the AO for later deletion.
//
// Input: Request - TDI_REQUEST structure for this request.
//
// Returns: Status of attempt to delete the address - either pending or
// success.
//
TDI_STATUS
TdiCloseAddress(PTDI_REQUEST Request)
{
AddrObj *DeletingAO;
CTELockHandle AOHandle;
#ifdef VXD
DeletingAO = GetIndexedAO((uint)Request->Handle.AddressHandle);
if (DeletingAO == NULL)
return TDI_ADDR_INVALID;
#else
DeletingAO = Request->Handle.AddressHandle;
#endif
CTEStructAssert(DeletingAO, ao);
CTEGetLock(&DeletingAO->ao_lock, &AOHandle);
if (!AO_BUSY(DeletingAO) && !(DeletingAO->ao_usecnt)) {
SET_AO_BUSY(DeletingAO);
SET_AO_INVALID(DeletingAO); // This address object is
// deleting.
CTEFreeLock(&DeletingAO->ao_lock, AOHandle);
DeleteAO(DeletingAO);
return TDI_SUCCESS;
} else {
AORequest *NewRequest, *OldRequest;
CTEReqCmpltRtn CmpltRtn;
PVOID ReqContext;
TDI_STATUS Status;
// Check and see if we already have a delete in progress. If we don't
// allocate and link up a delete request structure.
if (!AO_REQUEST(DeletingAO, AO_DELETE)) {
OldRequest = DeletingAO->ao_request;
NewRequest = GetAORequest();
if (NewRequest != NULL) { // Got a request.
NewRequest->aor_rtn = Request->RequestNotifyObject;
NewRequest->aor_context = Request->RequestContext;
CLEAR_AO_REQUEST(DeletingAO, AO_OPTIONS); // Clear the option
// request,
// if there is one.
SET_AO_REQUEST(DeletingAO, AO_DELETE);
SET_AO_INVALID(DeletingAO); // This address
// object is
// deleting.
DeletingAO->ao_request = NewRequest;
NewRequest->aor_next = NULL;
CTEFreeLock(&DeletingAO->ao_lock, AOHandle);
while (OldRequest != NULL) {
AORequest *Temp;
CmpltRtn = OldRequest->aor_rtn;
ReqContext = OldRequest->aor_context;
(*CmpltRtn)(ReqContext, (uint) TDI_ADDR_DELETED, 0);
Temp = OldRequest;
OldRequest = OldRequest->aor_next;
FreeAORequest(Temp);
}
return TDI_PENDING;
} else
Status = TDI_NO_RESOURCES;
} else // Delete already in progress.
Status = TDI_ADDR_INVALID;
CTEFreeLock(&DeletingAO->ao_lock, AOHandle);
return Status;
}
}
//* FindAOMCastAddr - Find a multicast address on an AddrObj.
//
// A utility routine to find a multicast address on an AddrObj. We also return
// a pointer to it's predecessor, for use in deleting.
//
// Input: AO - AddrObj to search.
// Addr - MCast address to search for.
// IF - IPAddress of interface
// PrevAMA - Pointer to where to return predecessor.
//
// Returns: Pointer to matching AMA structure, or NULL if there is none.
//
AOMCastAddr *
FindAOMCastAddr(AddrObj *AO, IPAddr Addr, IPAddr IF, AOMCastAddr **PrevAMA)
{
AOMCastAddr *FoundAMA, *Temp;
Temp = STRUCT_OF(AOMCastAddr, &AO->ao_mcastlist, ama_next);
FoundAMA = AO->ao_mcastlist;
while (FoundAMA != NULL) {
if (IP_ADDR_EQUAL(Addr, FoundAMA->ama_addr) &&
IP_ADDR_EQUAL(IF, FoundAMA->ama_if))
break;
Temp = FoundAMA;
FoundAMA = FoundAMA->ama_next;
}
*PrevAMA = Temp;
return FoundAMA;
}
//* MCastAddrOnAO - Test to see if a multicast address on an AddrObj.
//
// A utility routine to test to see if a multicast address is on an AddrObj.
//
// Input: AO - AddrObj to search.
// Addr - MCast address to search for.
//
// Returns: TRUE is Addr is on AO.
//
uint
MCastAddrOnAO(AddrObj *AO, IPAddr Addr)
{
AOMCastAddr *FoundAMA;
FoundAMA = AO->ao_mcastlist;
while (FoundAMA != NULL) {
if (IP_ADDR_EQUAL(Addr, FoundAMA->ama_addr))
return(TRUE);
FoundAMA = FoundAMA->ama_next;
}
return(FALSE);
}
//* SetAOOptions - Set AddrObj options.
//
// The set options worker routine, called when we've validated the buffer
// and know that the AddrObj isn't busy.
//
// Input: OptionAO - AddrObj for which options are being set.
// Options - AOOption buffer of options.
//
// Returns: TDI_STATUS of attempt.
//
TDI_STATUS
SetAOOptions(AddrObj *OptionAO, uint ID, uint Length, uchar *Options)
{
IP_STATUS IPStatus; // Status of IP option set request.
CTELockHandle Handle;
TDI_STATUS Status;
AOMCastAddr *AMA, *PrevAMA;
CTEAssert(AO_BUSY(OptionAO));
// First, see if there are IP options.
if (ID == AO_OPTION_IPOPTIONS) {
IF_TCPDBG(TCP_DEBUG_OPTIONS) {
TCPTRACE(("processing IP_IOTIONS on AO %lx\n", OptionAO));
}
// These are IP options. Pass them down.
(*LocalNetInfo.ipi_freeopts)(&OptionAO->ao_opt);
IPStatus = (*LocalNetInfo.ipi_copyopts)(Options, Length,
&OptionAO->ao_opt);
if (IPStatus == IP_SUCCESS)
return TDI_SUCCESS;
else if (IPStatus == IP_NO_RESOURCES)
return TDI_NO_RESOURCES;
else
return TDI_BAD_OPTION;
}
// These are UDP/TCP options.
if (Length == 0)
return TDI_BAD_OPTION;
Status = TDI_SUCCESS;
CTEGetLock(&OptionAO->ao_lock, &Handle);
switch (ID) {
case AO_OPTION_XSUM:
if (Options[0])
SET_AO_XSUM(OptionAO);
else
CLEAR_AO_XSUM(OptionAO);
break;
case AO_OPTION_IP_DONTFRAGMENT:
IF_TCPDBG(TCP_DEBUG_OPTIONS) {
TCPTRACE((
"DF opt %u, initial flags %lx on AO %lx\n",
(int) Options[0], OptionAO->ao_opt.ioi_flags, OptionAO
));
}
if (Options[0])
OptionAO->ao_opt.ioi_flags |= IP_FLAG_DF;
else
OptionAO->ao_opt.ioi_flags &= ~IP_FLAG_DF;
IF_TCPDBG(TCP_DEBUG_OPTIONS) {
TCPTRACE((
"New flags %lx on AO %lx\n",
OptionAO->ao_opt.ioi_flags, OptionAO
));
}
break;
case AO_OPTION_TTL:
IF_TCPDBG(TCP_DEBUG_OPTIONS) {
TCPTRACE((
"setting TTL to %d on AO %lx\n", Options[0], OptionAO
));
}
OptionAO->ao_opt.ioi_ttl = Options[0];
break;
case AO_OPTION_TOS:
IF_TCPDBG(TCP_DEBUG_OPTIONS) {
TCPTRACE((
"setting TOS to %d on AO %lx\n", Options[0], OptionAO
));
}
OptionAO->ao_opt.ioi_tos = Options[0];
break;
case AO_OPTION_MCASTTTL:
OptionAO->ao_mcastopt.ioi_ttl = Options[0];
break;
case AO_OPTION_MCASTIF:
if (Length >= sizeof(UDPMCastIFReq)) {
UDPMCastIFReq *Req;
IPAddr Addr;
Req = (UDPMCastIFReq *)Options;
Addr = Req->umi_addr;
if (!IP_ADDR_EQUAL(Addr, NULL_IP_ADDR) &&
(*LocalNetInfo.ipi_getaddrtype)(Addr) != DEST_LOCAL)
{
Status = TDI_BAD_OPTION;
}
else {
OptionAO->ao_mcastaddr = Addr;
}
} else
Status = TDI_BAD_OPTION;
break;
case AO_OPTION_ADD_MCAST:
case AO_OPTION_DEL_MCAST:
if (Length >= sizeof(UDPMCastReq)) {
UDPMCastReq *Req = (UDPMCastReq *)Options;
AMA = FindAOMCastAddr(OptionAO, Req->umr_addr, Req->umr_if,
&PrevAMA);
if (ID == AO_OPTION_ADD_MCAST) {
if (AMA != NULL) {
Status = TDI_BAD_OPTION;
break;
}
AMA = CTEAllocMem(sizeof(AOMCastAddr));
if (AMA == NULL) {
// Couldn't get the resource we need.
Status = TDI_NO_RESOURCES;
break;
}
AMA->ama_next = OptionAO->ao_mcastlist;
OptionAO->ao_mcastlist = AMA;
AMA->ama_addr = Req->umr_addr;
AMA->ama_if = Req->umr_if;
} else {
// This is a delete request. Fail it if it's not there.
if (AMA == NULL) {
Status = TDI_BAD_OPTION;
break;
}
PrevAMA->ama_next = AMA->ama_next;
CTEFreeMem(AMA);
}
IPStatus = (*LocalNetInfo.ipi_setmcastaddr)(Req->umr_addr,
Req->umr_if, ID == AO_OPTION_ADD_MCAST ? TRUE : FALSE);
if (IPStatus != TDI_SUCCESS) {
// Some problem adding or deleting. If we were adding, we
// need to free the one we just added.
if (ID == AO_OPTION_ADD_MCAST) {
AMA = FindAOMCastAddr(OptionAO, Req->umr_addr,
Req->umr_if, &PrevAMA);
if (AMA != NULL) {
PrevAMA->ama_next = AMA->ama_next;
CTEFreeMem(AMA);
} else
DEBUGCHK;
}
Status = (IPStatus == IP_NO_RESOURCES ? TDI_NO_RESOURCES :
TDI_BAD_OPTION);
}
} else
Status = TDI_BAD_OPTION;
break;
default:
Status = TDI_BAD_OPTION;
break;
}
CTEFreeLock(&OptionAO->ao_lock, Handle);
return Status;
}
//* SetAddrOptions - Set options on an address object.
//
// Called to set options on an address object. We validate the buffer,
// and if everything is OK we'll check the status of the AddrObj. If
// it's OK then we'll set them, otherwise we'll mark it for later use.
//
// Input: Request - Request describing AddrObj for option set.
// ID - ID for option to be set.
// OptLength - Length of options.
// Options - Pointer to options.
//
// Returns: TDI_STATUS of attempt.
//
TDI_STATUS
SetAddrOptions(PTDI_REQUEST Request, uint ID, uint OptLength, void *Options)
{
AddrObj *OptionAO;
TDI_STATUS Status;
CTELockHandle AOHandle;
#ifdef VXD
OptionAO = GetIndexedAO((uint)Request->Handle.AddressHandle);
if (OptionAO == NULL)
return TDI_ADDR_INVALID;
#else
OptionAO = Request->Handle.AddressHandle;
#endif
CTEStructAssert(OptionAO, ao);
CTEGetLock(&OptionAO->ao_lock, &AOHandle);
if (AO_VALID(OptionAO)) {
if (!AO_BUSY(OptionAO) && OptionAO->ao_usecnt == 0) {
SET_AO_BUSY(OptionAO);
CTEFreeLock(&OptionAO->ao_lock, AOHandle);
Status = SetAOOptions(OptionAO, ID, OptLength, Options);
CTEGetLock(&OptionAO->ao_lock, &AOHandle);
if (!AO_PENDING(OptionAO)) {
CLEAR_AO_BUSY(OptionAO);
CTEFreeLock(&OptionAO->ao_lock, AOHandle);
return Status;
} else {
CTEFreeLock(&OptionAO->ao_lock, AOHandle);
ProcessAORequests(OptionAO);
return Status;
}
} else {
AORequest *NewRequest, *OldRequest;
// The AddrObj is busy somehow. We need to get a request, and link
// him on the request list.
NewRequest = GetAORequest();
if (NewRequest != NULL) { // Got a request.
NewRequest->aor_rtn = Request->RequestNotifyObject;
NewRequest->aor_context = Request->RequestContext;
NewRequest->aor_id = ID;
NewRequest->aor_length = OptLength;
NewRequest->aor_buffer = Options;
SET_AO_REQUEST(OptionAO, AO_OPTIONS); // Set the
// option request,
OldRequest = STRUCT_OF(AORequest, &OptionAO->ao_request,
aor_next);
while (OldRequest->aor_next != NULL)
OldRequest = OldRequest->aor_next;
OldRequest->aor_next = NewRequest;
CTEFreeLock(&OptionAO->ao_lock, AOHandle);
return TDI_PENDING;
} else
Status = TDI_NO_RESOURCES;
}
} else
Status = TDI_ADDR_INVALID;
CTEFreeLock(&OptionAO->ao_lock, AOHandle);
return Status;
}
//* TDISetEvent - Set a handler for a particular event.
//
// This is the user API to set an event. It's pretty simple, we just
// grab the lock on the AddrObj and fill in the event.
//
//
// Input: Handle - Pointer to address object.
// Type - Event being set.
// Handler - Handler to call for event.
// Context - Context to pass to event.
//
// Returns: TDI_SUCCESS if it works, an error if it doesn't. This routine
// never pends.
//
TDI_STATUS
TdiSetEvent(PVOID Handle, int Type, PVOID Handler, PVOID Context)
{
AddrObj *EventAO;
CTELockHandle AOHandle;
TDI_STATUS Status;
#ifdef VXD
EventAO = GetIndexedAO((uint)Handle);
CTEStructAssert(EventAO, ao);
if (EventAO == NULL || !AO_VALID(EventAO))
return TDI_ADDR_INVALID;
#else
EventAO = (AddrObj *)Handle;
CTEStructAssert(EventAO, ao);
if (!AO_VALID(EventAO))
return TDI_ADDR_INVALID;
#endif
CTEGetLock(&EventAO->ao_lock, &AOHandle);
Status = TDI_SUCCESS;
switch (Type) {
case TDI_EVENT_CONNECT:
EventAO->ao_connect = Handler;
EventAO->ao_conncontext = Context;
break;
case TDI_EVENT_DISCONNECT:
EventAO->ao_disconnect = Handler;
EventAO->ao_disconncontext = Context;
break;
case TDI_EVENT_ERROR:
EventAO->ao_error = Handler;
EventAO->ao_errcontext = Context;
break;
case TDI_EVENT_RECEIVE:
EventAO->ao_rcv = Handler;
EventAO->ao_rcvcontext = Context;
break;
case TDI_EVENT_RECEIVE_DATAGRAM:
EventAO->ao_rcvdg = Handler;
EventAO->ao_rcvdgcontext = Context;
break;
case TDI_EVENT_RECEIVE_EXPEDITED:
EventAO->ao_exprcv = Handler;
EventAO->ao_exprcvcontext = Context;
break;
default:
Status = TDI_BAD_EVENT_TYPE;
break;
}
CTEFreeLock(&EventAO->ao_lock, AOHandle);
return Status;
}
//* ProcessAORequests - Process pending requests on an AddrObj.
//
// This is the delayed request processing routine, called when we've
// done something that used the busy bit. We examine the pending
// requests flags, and dispatch the requests appropriately.
//
// Input: RequestAO - AddrObj to be processed.
//
// Returns: Nothing.
//
void
ProcessAORequests(AddrObj *RequestAO)
{
CTELockHandle AOHandle;
AORequest *Request;
CTEStructAssert(RequestAO, ao);
CTEAssert(AO_BUSY(RequestAO));
CTEAssert(RequestAO->ao_usecnt == 0);
CTEGetLock(&RequestAO->ao_lock, &AOHandle);
while (AO_PENDING(RequestAO)) {
Request = RequestAO->ao_request;
if (AO_REQUEST(RequestAO, AO_DELETE)) {
CTEAssert(Request != NULL);
CTEAssert(!AO_REQUEST(RequestAO, AO_OPTIONS));
CTEFreeLock(&RequestAO->ao_lock, AOHandle);
DeleteAO(RequestAO);
(*Request->aor_rtn)(Request->aor_context, TDI_SUCCESS, 0);
FreeAORequest(Request);
return; // Deleted him, so get out.
}
// Now handle options request.
while (AO_REQUEST(RequestAO, AO_OPTIONS)) {
TDI_STATUS Status;
// Have an option request.
Request = RequestAO->ao_request;
RequestAO->ao_request = Request->aor_next;
if (RequestAO->ao_request == NULL)
CLEAR_AO_REQUEST(RequestAO, AO_OPTIONS);
CTEAssert(Request != NULL);
CTEFreeLock(&RequestAO->ao_lock, AOHandle);
Status = SetAOOptions(RequestAO, Request->aor_id,
Request->aor_length, Request->aor_buffer);
(*Request->aor_rtn)(Request->aor_context, Status, 0);
FreeAORequest(Request);
CTEGetLock(&RequestAO->ao_lock, &AOHandle);
}
// We've done options, now try sends.
if (AO_REQUEST(RequestAO, AO_SEND)) {
DGSendReq *SendReq;
// Need to send. Clear the busy flag, bump the send count, and
// get the send request.
if (!EMPTYQ(&RequestAO->ao_sendq)) {
DEQUEUE(&RequestAO->ao_sendq, SendReq, DGSendReq, dsr_q);
CLEAR_AO_BUSY(RequestAO);
RequestAO->ao_usecnt++;
CTEFreeLock(&RequestAO->ao_lock, AOHandle);
UDPSend(RequestAO, SendReq);
CTEGetLock(&RequestAO->ao_lock, &AOHandle);
// If there aren't any other pending sends, set the busy bit.
if (!(--RequestAO->ao_usecnt))
SET_AO_BUSY(RequestAO);
else
break; // Still sending, so get out.
} else {
// Had the send request set, but no send! Odd....
DEBUGCHK;
CLEAR_AO_REQUEST(RequestAO, AO_SEND);
}
}
}
// We're done here.
CLEAR_AO_BUSY(RequestAO);
CTEFreeLock(&RequestAO->ao_lock, AOHandle);
}
//* DelayDerefAO - Derefrence an AddrObj, and schedule an event.
//
// Called when we are done with an address object, and need to
// derefrence it. We dec the usecount, and if it goes to 0 and
// if there are pending actions we'll schedule an event to deal
// with them.
//
// Input: RequestAO - AddrObj to be processed.
//
// Returns: Nothing.
//
void
DelayDerefAO(AddrObj *RequestAO)
{
CTELockHandle Handle;
CTEGetLock(&RequestAO->ao_lock, &Handle);
RequestAO->ao_usecnt--;
if (!RequestAO->ao_usecnt && !AO_BUSY(RequestAO)) {
if (AO_PENDING(RequestAO)) {
SET_AO_BUSY(RequestAO);
CTEFreeLock(&RequestAO->ao_lock, Handle);
CTEScheduleEvent(&RequestAO->ao_event, RequestAO);
return;
}
}
CTEFreeLock(&RequestAO->ao_lock, Handle);
}
//* DerefAO - Derefrence an AddrObj.
//
// Called when we are done with an address object, and need to
// derefrence it. We dec the usecount, and if it goes to 0 and
// if there are pending actions we'll call the process AO handler.
//
// Input: RequestAO - AddrObj to be processed.
//
// Returns: Nothing.
//
void
DerefAO(AddrObj *RequestAO)
{
CTELockHandle Handle;
CTEGetLock(&RequestAO->ao_lock, &Handle);
RequestAO->ao_usecnt--;
if (!RequestAO->ao_usecnt && !AO_BUSY(RequestAO)) {
if (AO_PENDING(RequestAO)) {
SET_AO_BUSY(RequestAO);
CTEFreeLock(&RequestAO->ao_lock, Handle);
ProcessAORequests(RequestAO);
return;
}
}
CTEFreeLock(&RequestAO->ao_lock, Handle);
}
#pragma BEGIN_INIT
//* InitAddr - Initialize the address object stuff.
//
// Called during init time to initalize the address object stuff.
//
// Input: Nothing
//
// Returns: True if we succeed, False if we fail.
//
int
InitAddr()
{
AORequest *RequestPtr;
int i;
CTEInitLock(&AddrObjTableLock);
CTEInitLock(&AORequestLock);
#ifdef VXD
AOInstance = 1;
AOIndexSize = DEFAULT_AO_INDEX_SIZE;
NextAOIndex = 0;
AOIndex = CTEAllocMem(sizeof(AddrObj *) * DEFAULT_AO_INDEX_SIZE);
if (AOIndex == NULL)
return FALSE;
#endif
RequestPtr = CTEAllocMem(sizeof(AORequest)*NUM_AO_REQUEST);
if (RequestPtr == NULL) {
#ifdef VXD
CTEFreeMem(AOIndex);
#endif
return FALSE;
}
AORequestFree = NULL;
for (i = 0; i < NUM_AO_REQUEST; i++, RequestPtr++) {
#ifdef DEBUG
RequestPtr->aor_sig = aor_signature;
#endif
FreeAORequest(RequestPtr);
}
for (i = 0; i < AO_TABLE_SIZE; i++)
AddrObjTable[i] = NULL;
LastAO = NULL;
return TRUE;
}
#pragma END_INIT