summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/tcpip/tcp/dgram.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/tcpip/tcp/dgram.c990
1 files changed, 990 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/tcp/dgram.c b/private/ntos/tdi/tcpip/tcp/dgram.c
new file mode 100644
index 000000000..c0ada18c5
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/dgram.c
@@ -0,0 +1,990 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** DGRAM.C - Common datagram protocol code.
+//
+// This file contains the code common to both UDP and Raw IP.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#include "tdistat.h"
+#ifdef VXD
+#include "tdivxd.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "dgram.h"
+#include "tlcommon.h"
+#include "info.h"
+
+#define NO_TCP_DEFS 1
+#include "tcpdeb.h"
+
+#ifdef NT
+
+#ifdef POOL_TAGGING
+
+#ifdef ExAllocatePool
+#undef ExAllocatePool
+#endif
+
+#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'dPCT')
+
+#ifndef CTEAllocMem
+#error "CTEAllocMem is not already defined - will override tagging"
+#else
+#undef CTEAllocMem
+#endif
+
+#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'dPCT')
+
+#endif // POOL_TAGGING
+
+#endif // NT
+
+#define NUM_DG_HEADERS 5
+
+#ifdef NT
+#define DG_MAX_HDRS 0xffff
+#else
+#define DG_MAX_HDRS 100
+#endif
+
+ulong DGCurrentSendFree = 0;
+ulong DGMaxSendFree = DG_MAX_HDRS;
+
+EXTERNAL_LOCK(AddrObjTableLock)
+
+DGSendReq *DGSendReqFree;
+DEFINE_LOCK_STRUCTURE(DGSendReqLock)
+
+#ifndef NT
+DGRcvReq *DGRcvReqFree;
+#else
+SLIST_HEADER DGRcvReqFree;
+#endif
+
+DEFINE_LOCK_STRUCTURE(DGRcvReqFreeLock)
+
+#ifdef DEBUG
+uint NumSendReq = 0;
+uint NumRcvReq = 0;
+#endif
+
+// Information for maintaining the DG Header structures and
+// pending queue.
+uint DGHeaderSize;
+PNDIS_BUFFER DGHeaderList;
+Queue DGHeaderPending;
+Queue DGDelayed;
+
+CTEEvent DGDelayedEvent;
+
+extern IPInfo LocalNetInfo;
+
+typedef struct DGHdrBPoolEntry {
+ struct DGHdrBPoolEntry *uhe_next;
+ NDIS_HANDLE uhe_handle;
+ uchar *uhe_buffer;
+} DGHdrBPoolEntry;
+
+DGHdrBPoolEntry *DGHdrBPoolList = NULL;
+
+//
+// All of the init code can be discarded.
+//
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+
+int InitDG(uint MaxHeaderSize);
+
+#pragma alloc_text(INIT, InitDG)
+
+#endif // ALLOC_PRAGMA
+#endif
+
+#ifdef CHICAGO
+extern int RegisterAddrChangeHndlr(void *Handler, uint Add);
+extern void AddrChange(IPAddr Addr, IPMask Mask, void *Context,
+ uint Added);
+#endif
+
+
+
+//* GrowDGHeaderList - Try to grow the DG header list.
+//
+// Called when we run out of buffers on the DG header list, and need
+// to grow it. We look to see if we're already at the maximum size, and
+// if not we'll allocate the need structures and free them to the list.
+// This routine must be called with the SendReq lock held.
+//
+// Input: Nothing.
+//
+// Returns: A pointer to a new DG header buffer if we have one, or NULL.
+//
+PNDIS_BUFFER
+GrowDGHeaderList(void)
+{
+ DGHdrBPoolEntry *NewEntry;
+ NDIS_STATUS Status;
+ uint HeaderSize;
+ uchar *DGSendHP;
+ uint i;
+ PNDIS_BUFFER Buffer;
+ PNDIS_BUFFER ReturnBuffer = NULL;
+
+ if (DGCurrentSendFree < DGMaxSendFree) {
+
+ // Still room to grow the list.
+ NewEntry = CTEAllocMem(sizeof(DGHdrBPoolEntry));
+
+ if (NewEntry == NULL) {
+ // Couldn't get the memory.
+ return NULL;
+ }
+
+ NdisAllocateBufferPool(&Status, &NewEntry->uhe_handle,
+ NUM_DG_HEADERS);
+
+ if (Status != NDIS_STATUS_SUCCESS) {
+ // Couldn't get a new set of buffers. Fail.
+ CTEFreeMem(NewEntry);
+ return NULL;
+ }
+
+ HeaderSize = DGHeaderSize + LocalNetInfo.ipi_hsize;
+
+ DGSendHP = CTEAllocMem(HeaderSize * NUM_DG_HEADERS);
+
+ if (DGSendHP == NULL) {
+ NdisFreeBufferPool(NewEntry->uhe_handle);
+ CTEFreeMem(NewEntry);
+ return NULL;
+ }
+
+ NewEntry->uhe_buffer = DGSendHP;
+
+ for (i = 0; i < NUM_DG_HEADERS; i++) {
+ NdisAllocateBuffer(&Status, &Buffer, NewEntry->uhe_handle,
+ DGSendHP + (i * HeaderSize), HeaderSize);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ NdisFreeBufferPool(NewEntry->uhe_handle);
+ CTEFreeMem(NewEntry);
+ CTEFreeMem(DGSendHP);
+ return NULL;
+ }
+ if (i != 0)
+ FreeDGHeader(Buffer);
+ else
+ ReturnBuffer = Buffer;
+ }
+
+ DGCurrentSendFree += NUM_DG_HEADERS;
+ NewEntry->uhe_next = DGHdrBPoolList;
+ DGHdrBPoolList = NewEntry;
+
+ } else {
+ // At the limit already.
+ ReturnBuffer = NULL;
+ }
+
+ return ReturnBuffer;
+
+
+}
+//* GetDGHeader - Get a DG header buffer.
+//
+// The get header buffer routine. Called with the SendReqLock held.
+//
+// Input: Nothing.
+//
+// Output: A pointer to an NDIS buffer, or NULL.
+//
+_inline PNDIS_BUFFER
+GetDGHeader(void)
+{
+ PNDIS_BUFFER NewBuffer;
+
+ NewBuffer = DGHeaderList;
+ if (NewBuffer != NULL)
+ DGHeaderList = NDIS_BUFFER_LINKAGE(NewBuffer);
+ else
+ NewBuffer = GrowDGHeaderList();
+
+ return NewBuffer;
+}
+
+//* FreeDGHeader - Free a DG header buffer.
+//
+// The free header buffer routine. Called with the SendReqLock held.
+//
+// Input: Buffer to be freed.
+//
+// Output: Nothing.
+//
+void
+FreeDGHeader(PNDIS_BUFFER FreedBuffer)
+{
+ NDIS_BUFFER_LINKAGE(FreedBuffer) = DGHeaderList;
+ DGHeaderList = FreedBuffer;
+}
+
+//* PutPendingQ - Put an address object on the pending queue.
+//
+// Called when we've experienced a header buffer out of resources condition,
+// and want to queue an AddrObj for later processing. We put the specified
+// address object on the DGHeaderPending queue, set the OOR flag and clear
+// the 'send request' flag. It is invariant in the system that the send
+// request flag and the OOR flag are not set at the same time.
+//
+// This routine assumes that the caller holds the DGSendReqLock and the
+// lock on the particular AddrObj.
+//
+// Input: QueueingAO - Pointer to address object to be queued.
+//
+// Returns: Nothing.
+//
+void
+PutPendingQ(AddrObj *QueueingAO)
+{
+ CTEStructAssert(QueueingAO, ao);
+
+ if (!AO_OOR(QueueingAO)) {
+ CLEAR_AO_REQUEST(QueueingAO, AO_SEND);
+ SET_AO_OOR(QueueingAO);
+
+ ENQUEUE(&DGHeaderPending, &QueueingAO->ao_pendq);
+ }
+}
+
+//* GetDGSendReq - Get a DG send request.
+//
+// Called when someone wants to allocate a DG send request. We assume
+// the send request lock is held when we are called.
+//
+// Note: This routine and the corresponding free routine might
+// be good candidates for inlining.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to the SendReq, or NULL if none.
+//
+DGSendReq *
+GetDGSendReq()
+{
+ DGSendReq *NewReq;
+
+
+ NewReq = DGSendReqFree;
+ if (NewReq != NULL) {
+ CTEStructAssert(NewReq, dsr);
+ DGSendReqFree = (DGSendReq *)NewReq->dsr_q.q_next;
+ } else {
+ // Couldn't get a request, grow it. This is one area where we'll try
+ // to allocate memory with a lock held. Because of this, we've
+ // got to be careful about where we call this routine from.
+
+ NewReq = CTEAllocMem(sizeof(DGSendReq));
+ if (NewReq != NULL) {
+#ifdef DEBUG
+ NewReq->dsr_sig = dsr_signature;
+ NumSendReq++;
+#endif
+ }
+ }
+
+ return NewReq;
+}
+
+//* FreeDGSendReq - Free a DG send request.
+//
+// Called when someone wants to free a DG send request. It's assumed
+// that the caller holds the SendRequest lock.
+//
+// Input: SendReq - SendReq to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeDGSendReq(DGSendReq *SendReq)
+{
+ CTEStructAssert(SendReq, dsr);
+
+ *(DGSendReq **)&SendReq->dsr_q.q_next = DGSendReqFree;
+ DGSendReqFree = SendReq;
+}
+
+//* GetDGRcvReq - Get a DG receive request.
+//
+// Called when we need to get a DG receive request.
+//
+// Input: Nothing.
+//
+// Returns: Pointer to new request, or NULL if none.
+//
+DGRcvReq *
+GetDGRcvReq()
+{
+ DGRcvReq *NewReq;
+
+#ifdef VXD
+ NewReq = DGRcvReqFree;
+ if (NewReq != NULL) {
+ CTEStructAssert(NewReq, drr);
+ DGRcvReqFree = (DGRcvReq *)NewReq->drr_q.q_next;
+ } else {
+ // Couldn't get a request, grow it.
+ NewReq = CTEAllocMem(sizeof(DGRcvReq));
+ if (NewReq != NULL) {
+#ifdef DEBUG
+ NewReq->drr_sig = drr_signature;
+ NumRcvReq++;
+#endif
+ }
+ }
+
+#endif // VXD
+
+#ifdef NT
+ PSINGLE_LIST_ENTRY BufferLink;
+ Queue *QueuePtr;
+
+ BufferLink = ExInterlockedPopEntrySList(
+ &DGRcvReqFree,
+ &DGRcvReqFreeLock
+ );
+
+ if (BufferLink != NULL) {
+ QueuePtr = STRUCT_OF(Queue, BufferLink, q_next);
+ NewReq = STRUCT_OF(DGRcvReq, QueuePtr, drr_q);
+ CTEStructAssert(NewReq, drr);
+ }
+ else {
+ // Couldn't get a request, grow it.
+ NewReq = CTEAllocMem(sizeof(DGRcvReq));
+ if (NewReq != NULL) {
+#ifdef DEBUG
+ NewReq->drr_sig = drr_signature;
+ ExInterlockedAddUlong(&NumRcvReq, 1, &DGRcvReqFreeLock);
+#endif
+ }
+ }
+
+#endif // NT
+
+ return NewReq;
+}
+
+//* FreeDGRcvReq - Free a DG rcv request.
+//
+// Called when someone wants to free a DG rcv request.
+//
+// Input: RcvReq - RcvReq to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeDGRcvReq(DGRcvReq *RcvReq)
+{
+#ifdef VXD
+
+ CTEStructAssert(RcvReq, drr);
+
+ *(DGRcvReq **)&RcvReq->drr_q.q_next = DGRcvReqFree;
+ DGRcvReqFree = RcvReq;
+
+#endif // VXD
+
+#ifdef NT
+
+ PSINGLE_LIST_ENTRY BufferLink;
+
+ CTEStructAssert(RcvReq, drr);
+
+ BufferLink = STRUCT_OF(SINGLE_LIST_ENTRY, &(RcvReq->drr_q.q_next), Next);
+ ExInterlockedPushEntrySList(
+ &DGRcvReqFree,
+ BufferLink,
+ &DGRcvReqFreeLock
+ );
+
+#endif // NT
+}
+
+
+//* DGDelayedEventProc - Handle a delayed event.
+//
+// This is the delayed event handler, used for out-of-resources conditions
+// on AddrObjs. We pull from the delayed queue, and is the addr obj is
+// not already busy we'll send the datagram.
+//
+// Input: Event - Pointer to the event structure.
+// Context - Nothing.
+//
+// Returns: Nothing
+//
+void
+DGDelayedEventProc(CTEEvent *Event, void *Context)
+{
+ CTELockHandle HeaderHandle, AOHandle;
+ AddrObj *SendingAO;
+ DGSendProc SendProc;
+
+ CTEGetLock(&DGSendReqLock, &HeaderHandle);
+ while (!EMPTYQ(&DGDelayed)) {
+ DEQUEUE(&DGDelayed, SendingAO, AddrObj, ao_pendq);
+ CTEStructAssert(SendingAO, ao);
+
+ CTEGetLock(&SendingAO->ao_lock, &AOHandle);
+
+ CLEAR_AO_OOR(SendingAO);
+ if (!AO_BUSY(SendingAO)) {
+ DGSendReq *SendReq;
+
+ if (!EMPTYQ(&SendingAO->ao_sendq)) {
+ DEQUEUE(&SendingAO->ao_sendq, SendReq, DGSendReq, dsr_q);
+
+ CTEStructAssert(SendReq, dsr);
+ CTEAssert(SendReq->dsr_header != NULL);
+
+ SendingAO->ao_usecnt++;
+ SendProc = SendingAO->ao_dgsend;
+ CTEFreeLock(&SendingAO->ao_lock, AOHandle);
+ CTEFreeLock(&DGSendReqLock, HeaderHandle);
+
+ (*SendProc)(SendingAO, SendReq);
+ DEREF_AO(SendingAO);
+ CTEGetLock(&DGSendReqLock, &HeaderHandle);
+ } else {
+ CTEAssert(FALSE);
+ CTEFreeLock(&SendingAO->ao_lock, AOHandle);
+ }
+
+ } else {
+ SET_AO_REQUEST(SendingAO, AO_SEND);
+ CTEFreeLock(&SendingAO->ao_lock, AOHandle);
+ }
+ }
+
+ CTEFreeLock(&DGSendReqLock, HeaderHandle);
+
+}
+
+//* DGSendComplete - DG send complete handler.
+//
+// This is the routine called by IP when a send completes. We
+// take the context passed back as a pointer to a SendRequest
+// structure, and complete the caller's send.
+//
+// Input: Context - Context we gave on send (really a
+// SendRequest structure).
+// BufferChain - Chain of buffers sent.
+//
+// Returns: Nothing.
+void
+DGSendComplete(void *Context, PNDIS_BUFFER BufferChain)
+{
+ DGSendReq *FinishedSR = (DGSendReq *)Context;
+ CTELockHandle HeaderHandle, AOHandle;
+ CTEReqCmpltRtn Callback; // Completion routine.
+ PVOID CallbackContext; // User context.
+ ushort SentSize;
+ AddrObj *AO;
+
+ CTEStructAssert(FinishedSR, dsr);
+ CTEGetLock(&DGSendReqLock, &HeaderHandle);
+
+ Callback = FinishedSR->dsr_rtn;
+ CallbackContext = FinishedSR->dsr_context;
+ SentSize = FinishedSR->dsr_size;
+
+ // If there's nothing on the header pending queue, just free the
+ // header buffer. Otherwise pull from the pending queue, give him the
+ // resource, and schedule an event to deal with him.
+ if (EMPTYQ(&DGHeaderPending)) {
+ FreeDGHeader(BufferChain);
+ } else {
+ DEQUEUE(&DGHeaderPending, AO, AddrObj, ao_pendq);
+ CTEStructAssert(AO, ao);
+ CTEGetLock(&AO->ao_lock, &AOHandle);
+ if (!EMPTYQ(&AO->ao_sendq)) {
+ DGSendReq *SendReq;
+
+ PEEKQ(&AO->ao_sendq, SendReq, DGSendReq, dsr_q);
+ SendReq->dsr_header = BufferChain; // Give him this buffer.
+
+ ENQUEUE(&DGDelayed, &AO->ao_pendq);
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+ CTEScheduleEvent(&DGDelayedEvent, NULL);
+ } else {
+ // On the pending queue, but no sends!
+ DEBUGCHK;
+ CLEAR_AO_OOR(AO);
+ CTEFreeLock(&AO->ao_lock, AOHandle);
+ }
+
+ }
+
+ FreeDGSendReq(FinishedSR);
+ CTEFreeLock(&DGSendReqLock, HeaderHandle);
+ if (Callback != NULL)
+ (*Callback)(CallbackContext, TDI_SUCCESS, (uint)SentSize);
+
+}
+
+
+#ifdef NT
+//
+// NT supports cancellation of DG send/receive requests.
+//
+
+#define TCP_DEBUG_SEND_DGRAM 0x00000100
+#define TCP_DEBUG_RECEIVE_DGRAM 0x00000200
+
+extern ULONG TCPDebug;
+
+
+VOID
+TdiCancelSendDatagram(
+ AddrObj *SrcAO,
+ PVOID Context
+ )
+{
+ CTELockHandle lockHandle;
+ DGSendReq *sendReq = NULL;
+ Queue *qentry;
+ BOOLEAN found = FALSE;
+
+
+ CTEStructAssert(SrcAO, ao);
+
+ CTEGetLock(&SrcAO->ao_lock, &lockHandle);
+
+ // Search the send list for the specified request.
+ for ( qentry = QNEXT(&(SrcAO->ao_sendq));
+ qentry != &(SrcAO->ao_sendq);
+ qentry = QNEXT(qentry)
+ ) {
+
+ sendReq = STRUCT_OF(DGSendReq, qentry, dsr_q);
+
+ CTEStructAssert(sendReq, dsr);
+
+ if (sendReq->dsr_context == Context) {
+ //
+ // Found it. Dequeue
+ //
+ REMOVEQ(qentry);
+ found = TRUE;
+
+ IF_TCPDBG(TCP_DEBUG_SEND_DGRAM) {
+ TCPTRACE((
+ "TdiCancelSendDatagram: Dequeued item %lx\n",
+ Context
+ ));
+ }
+
+ break;
+ }
+ }
+
+ CTEFreeLock(&SrcAO->ao_lock, lockHandle);
+
+ if (found) {
+ //
+ // Complete the request and free its resources.
+ //
+ (*sendReq->dsr_rtn)(sendReq->dsr_context, (uint) TDI_CANCELLED, 0);
+
+ CTEGetLock(&DGSendReqLock, &lockHandle);
+
+ if (sendReq->dsr_header != NULL) {
+ FreeDGHeader(sendReq->dsr_header);
+ }
+
+ FreeDGSendReq(sendReq);
+
+ CTEFreeLock(&DGSendReqLock, lockHandle);
+ }
+
+} // TdiCancelSendDatagram
+
+
+VOID
+TdiCancelReceiveDatagram(
+ AddrObj *SrcAO,
+ PVOID Context
+ )
+{
+ CTELockHandle lockHandle;
+ DGRcvReq *rcvReq = NULL;
+ Queue *qentry;
+ BOOLEAN found = FALSE;
+
+
+ CTEStructAssert(SrcAO, ao);
+
+ CTEGetLock(&SrcAO->ao_lock, &lockHandle);
+
+ // Search the send list for the specified request.
+ for ( qentry = QNEXT(&(SrcAO->ao_rcvq));
+ qentry != &(SrcAO->ao_rcvq);
+ qentry = QNEXT(qentry)
+ ) {
+
+ rcvReq = STRUCT_OF(DGRcvReq, qentry, drr_q);
+
+ CTEStructAssert(rcvReq, drr);
+
+ if (rcvReq->drr_context == Context) {
+ //
+ // Found it. Dequeue
+ //
+ REMOVEQ(qentry);
+ found = TRUE;
+
+ IF_TCPDBG(TCP_DEBUG_SEND_DGRAM) {
+ TCPTRACE((
+ "TdiCancelReceiveDatagram: Dequeued item %lx\n",
+ Context
+ ));
+ }
+
+ break;
+ }
+ }
+
+ CTEFreeLock(&SrcAO->ao_lock, lockHandle);
+
+ if (found) {
+ //
+ // Complete the request and free its resources.
+ //
+ (*rcvReq->drr_rtn)(rcvReq->drr_context, (uint) TDI_CANCELLED, 0);
+
+ FreeDGRcvReq(rcvReq);
+ }
+
+} // TdiCancelReceiveDatagram
+
+
+#endif // NT
+
+
+//** TdiSendDatagram - TDI send datagram function.
+//
+// This is the user interface to the send datagram function. The
+// caller specified a request structure, a connection info
+// structure containing the address, and data to be sent.
+// This routine gets a DG Send request structure to manage the
+// send, fills the structure in, and calls DGSend to deal with
+// it.
+//
+// Input: Request - Pointer to request structure.
+// ConnInfo - Pointer to ConnInfo structure which points to
+// remote address.
+// DataSize - Size in bytes of data to be sent.
+// BytesSent - Pointer to where to return size sent.
+// Buffer - Pointer to buffer chain.
+//
+// Returns: Status of attempt to send.
+//
+TDI_STATUS
+TdiSendDatagram(PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION ConnInfo,
+ uint DataSize, uint *BytesSent, PNDIS_BUFFER Buffer)
+{
+ AddrObj *SrcAO; // Pointer to AddrObj for src.
+ DGSendReq *SendReq; // Pointer to send req for this request.
+ CTELockHandle Handle, SRHandle; // Lock handles for the AO and the
+ // send request.
+ TDI_STATUS ReturnValue;
+ DGSendProc SendProc;
+
+ // First, get a send request. We do this first because of MP issues
+ // if we port this to NT. We need to take the SendRequest lock before
+ // we take the AddrObj lock, to prevent deadlock and also because
+ // GetDGSendReq might yield, and the state of the AddrObj might
+ // change on us, so we don't want to yield after we've validated
+ // it.
+
+ CTEGetLock(&DGSendReqLock, &SRHandle);
+ SendReq = GetDGSendReq();
+
+ // Now get the lock on the AO, and make sure it's valid. We do this
+ // to make sure we return the correct error code.
+
+#ifdef VXD
+ SrcAO = GetIndexedAO((uint)Request->Handle.AddressHandle);
+
+ if (SrcAO != NULL) {
+#else
+ SrcAO = Request->Handle.AddressHandle;
+#endif
+
+ CTEStructAssert(SrcAO, ao);
+
+ CTEGetLock(&SrcAO->ao_lock, &Handle);
+
+ if (AO_VALID(SrcAO)) {
+
+ // Make sure the size is reasonable.
+ if (DataSize <= SrcAO->ao_maxdgsize) {
+
+ // The AddrObj is valid. Now fill the address into the send request,
+ // if we've got one. If this works, we'll continue with the
+ // send.
+
+ if (SendReq != NULL) { // Got a send request.
+ if (GetAddress(ConnInfo->RemoteAddress, &SendReq->dsr_addr,
+ &SendReq->dsr_port)) {
+
+ SendReq->dsr_rtn = Request->RequestNotifyObject;
+ SendReq->dsr_context = Request->RequestContext;
+ SendReq->dsr_buffer = Buffer;
+ SendReq->dsr_size = (ushort)DataSize;
+
+ // We've filled in the send request. If the AO isn't
+ // already busy, try to get a DG header buffer and send
+ // this. If the AO is busy, or we can't get a buffer, queue
+ // until later. We try to get the header buffer here, as
+ // an optimazation to avoid having to retake the lock.
+
+ if (!AO_OOR(SrcAO)) { // AO isn't out of resources
+ if (!AO_BUSY(SrcAO)) { // or or busy
+
+ if ((SendReq->dsr_header = GetDGHeader()) != NULL) {
+ REF_AO(SrcAO); // Lock out exclusive
+ // activities.
+ SendProc = SrcAO->ao_dgsend;
+
+ CTEFreeLock(&SrcAO->ao_lock, Handle);
+ CTEFreeLock(&DGSendReqLock, SRHandle);
+
+ // Allright, just send it.
+ (*SendProc)(SrcAO, SendReq);
+
+ // See if any pending requests occured during
+ // the send. If so, call the request handler.
+ DEREF_AO(SrcAO);
+
+ return TDI_PENDING;
+ } else {
+ // We couldn't get a header buffer. Put this
+ // guy on the pending queue, and then fall
+ // through to the 'queue request' code.
+ PutPendingQ(SrcAO);
+ }
+ } else {
+ // AO is busy, set request for later
+ SET_AO_REQUEST(SrcAO, AO_SEND);
+ }
+ }
+
+ // AO is busy, or out of resources. Queue the send request
+ // for later.
+ SendReq->dsr_header = NULL;
+ ENQUEUE(&SrcAO->ao_sendq, &SendReq->dsr_q);
+ SendReq = NULL;
+ ReturnValue = TDI_PENDING;
+ }
+ else {
+ // The remote address was invalid.
+ ReturnValue = TDI_BAD_ADDR;
+ }
+ }
+ else {
+ // Send request was null, return no resources.
+ ReturnValue = TDI_NO_RESOURCES;
+ }
+ }
+ else {
+ // Buffer was too big, return an error.
+ ReturnValue = TDI_BUFFER_TOO_BIG;
+ }
+ }
+ else {
+ // The addr object is invalid, possibly because it's deleting.
+ ReturnValue = TDI_ADDR_INVALID;
+ }
+
+ CTEFreeLock(&SrcAO->ao_lock, Handle);
+
+#ifdef VXD
+ }
+ else {
+ ReturnValue = TDI_ADDR_INVALID;
+ }
+#endif
+
+ if (SendReq != NULL)
+ FreeDGSendReq(SendReq);
+
+ CTEFreeLock(&DGSendReqLock, SRHandle);
+
+ return TDI_ADDR_INVALID;
+}
+
+//** TdiReceiveDatagram - TDI receive datagram function.
+//
+// This is the user interface to the receive datagram function. The
+// caller specifies a request structure, a connection info
+// structure that acts as a filter on acceptable datagrams, a connection
+// info structure to be filled in, and other parameters. We get a DGRcvReq
+// structure, fill it in, and hang it on the AddrObj, where it will be removed
+// later by incomig datagram handler.
+//
+// Input: Request - Pointer to request structure.
+// ConnInfo - Pointer to ConnInfo structure which points to
+// remote address.
+// ReturnInfo - Pointer to ConnInfo structure to be filled in.
+// RcvSize - Total size in bytes receive buffer.
+// BytesRcvd - Pointer to where to return size received.
+// Buffer - Pointer to buffer chain.
+//
+// Returns: Status of attempt to receive.
+//
+TDI_STATUS
+TdiReceiveDatagram(PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION ConnInfo,
+ PTDI_CONNECTION_INFORMATION ReturnInfo, uint RcvSize, uint *BytesRcvd,
+ PNDIS_BUFFER Buffer)
+{
+ AddrObj *RcvAO; // AddrObj that is receiving.
+ DGRcvReq *RcvReq; // Receive request structure.
+ CTELockHandle AOHandle;
+ uchar AddrValid;
+
+ RcvReq = GetDGRcvReq();
+
+#ifdef VXD
+ RcvAO = GetIndexedAO((uint)Request->Handle.AddressHandle);
+
+ if (RcvAO != NULL) {
+ CTEStructAssert(RcvAO, ao);
+
+#else
+ RcvAO = Request->Handle.AddressHandle;
+ CTEStructAssert(RcvAO, ao);
+#endif
+
+ CTEGetLock(&RcvAO->ao_lock, &AOHandle);
+ if (AO_VALID(RcvAO)) {
+
+ IF_TCPDBG(TCP_DEBUG_RAW) {
+ TCPTRACE(("posting receive on AO %lx\n", RcvAO));
+ }
+
+ if (RcvReq != NULL) {
+ if (ConnInfo != NULL && ConnInfo->RemoteAddressLength != 0)
+ AddrValid = GetAddress(ConnInfo->RemoteAddress,
+ &RcvReq->drr_addr, &RcvReq->drr_port);
+ else {
+ AddrValid = TRUE;
+ RcvReq->drr_addr = NULL_IP_ADDR;
+ RcvReq->drr_port = 0;
+ }
+
+ if (AddrValid) {
+
+ // Everything'd valid. Fill in the receive request and queue it.
+ RcvReq->drr_conninfo = ReturnInfo;
+ RcvReq->drr_rtn = Request->RequestNotifyObject;
+ RcvReq->drr_context = Request->RequestContext;
+ RcvReq->drr_buffer = Buffer;
+ RcvReq->drr_size = RcvSize;
+ ENQUEUE(&RcvAO->ao_rcvq, &RcvReq->drr_q);
+ CTEFreeLock(&RcvAO->ao_lock, AOHandle);
+
+ return TDI_PENDING;
+ } else {
+ // Have an invalid filter address.
+ CTEFreeLock(&RcvAO->ao_lock, AOHandle);
+ FreeDGRcvReq(RcvReq);
+ return TDI_BAD_ADDR;
+ }
+ } else {
+ // Couldn't get a receive request.
+ CTEFreeLock(&RcvAO->ao_lock, AOHandle);
+ return TDI_NO_RESOURCES;
+ }
+ } else {
+ // The AddrObj isn't valid.
+ CTEFreeLock(&RcvAO->ao_lock, AOHandle);
+ }
+
+#ifdef VXD
+ }
+#endif
+
+ // The AddrObj is invalid or non-existent.
+ if (RcvReq != NULL)
+ FreeDGRcvReq(RcvReq);
+
+ return TDI_ADDR_INVALID;
+}
+
+
+#pragma BEGIN_INIT
+
+//* InitDG - Initialize the DG stuff.
+//
+// Called during init time to initalize the DG code. We initialize
+// our locks and request lists.
+//
+// Input: MaxHeaderSize - The maximum size of a datagram transport header,
+// not including the IP header.
+//
+// Returns: True if we succeed, False if we fail.
+//
+int
+InitDG(uint MaxHeaderSize)
+{
+ PNDIS_BUFFER Buffer;
+ CTELockHandle Handle;
+
+
+ DGHeaderSize = MaxHeaderSize;
+
+ CTEInitLock(&DGSendReqLock);
+ CTEInitLock(&DGRcvReqFreeLock);
+
+ DGSendReqFree = NULL;
+
+#ifndef NT
+ DGRcvReqFree = NULL;
+#else
+ ExInitializeSListHead(&DGRcvReqFree);
+#endif
+
+
+ CTEGetLock(&DGSendReqLock, &Handle);
+
+ Buffer = GrowDGHeaderList();
+
+ if (Buffer != NULL) {
+ FreeDGHeader(Buffer);
+ CTEFreeLock(&DGSendReqLock, Handle);
+ } else {
+ CTEFreeLock(&DGSendReqLock, Handle);
+ return FALSE;
+ }
+
+ INITQ(&DGHeaderPending);
+ INITQ(&DGDelayed);
+
+ CTEInitEvent(&DGDelayedEvent, DGDelayedEventProc);
+
+ return TRUE;
+}
+
+#pragma END_INIT