summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/tcpip/tcp/addr.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/tcpip/tcp/addr.c2061
1 files changed, 2061 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/tcp/addr.c b/private/ntos/tdi/tcpip/tcp/addr.c
new file mode 100644
index 000000000..eec1997db
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/addr.c
@@ -0,0 +1,2061 @@
+/********************************************************************/
+/** 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