/********************************************************************/ /** Microsoft LAN Manager **/ /** Copyright(c) Microsoft Corp., 1990-2000 **/ /********************************************************************/ /* :ts=4 */ //** DGRAM.C - Common datagram protocol code. // // This file contains the code common to both UDP and Raw IP. // #include "precomp.h" #include "tdint.h" #include "addr.h" #include "dgram.h" #include "tlcommon.h" #include "info.h" #include "mdlpool.h" #include "pplasl.h" #define NO_TCP_DEFS 1 #include "tcpdeb.h" extern HANDLE TcpRequestPool; CACHE_LINE_KSPIN_LOCK DGQueueLock; USHORT DGHeaderBufferSize; // Information for maintaining the DG Header structures and // pending queue. Queue DGHeaderPending; Queue DGDelayed; CTEEvent DGDelayedEvent; extern IPInfo LocalNetInfo; #include "tcp.h" #include "udp.h" HANDLE DgHeaderPool; // // All of the init code can be discarded. // #ifdef ALLOC_PRAGMA int InitDG(uint MaxHeaderSize); #pragma alloc_text(INIT, InitDG) #endif //* 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 GetDGHeaderAtDpcLevel(UDPHeader **Header) { PNDIS_BUFFER Buffer; Buffer = MdpAllocateAtDpcLevel(DgHeaderPool, Header); if (Buffer) { ASSERT(*Header); #if BACK_FILL ASSERT(Buffer->ByteOffset >= 40); *Header = (UDPHeader *)((ULONG_PTR)(*Header) + MAX_BACKFILL_HDR_SIZE); Buffer->MappedSystemVa = (PVOID)((ULONG_PTR)Buffer->MappedSystemVa + MAX_BACKFILL_HDR_SIZE); Buffer->ByteOffset += MAX_BACKFILL_HDR_SIZE; Buffer->MdlFlags |= MDL_NETWORK_HEADER; #endif } return Buffer; } PNDIS_BUFFER GetDGHeader(UDPHeader **Header) { #if MILLEN return GetDGHeaderAtDpcLevel(Header); #else KIRQL OldIrql; PNDIS_BUFFER Buffer; OldIrql = KeRaiseIrqlToDpcLevel(); Buffer = GetDGHeaderAtDpcLevel(Header); KeLowerIrql(OldIrql); return Buffer; #endif } //* FreeDGHeader - Free a DG header buffer. // // The free header buffer routine. Called with the SendReqLock held. // // Input: Buffer to be freed. // // Output: Nothing. // __inline VOID FreeDGHeader(PNDIS_BUFFER FreedBuffer) { NdisAdjustBufferLength(FreedBuffer, DGHeaderBufferSize); #if BACK_FILL FreedBuffer->MappedSystemVa = (PVOID)((ULONG_PTR)FreedBuffer->MappedSystemVa - MAX_BACKFILL_HDR_SIZE); FreedBuffer->ByteOffset -= MAX_BACKFILL_HDR_SIZE; #endif MdpFree(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 QueueingAO->ao_lock. // // 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); InterlockedEnqueueAtDpcLevel(&DGHeaderPending, &QueueingAO->ao_pendq, &DGQueueLock.Lock); } } //* 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. // __inline DGSendReq * GetDGSendReq() { DGSendReq *Request; LOGICAL FromList; Request = PplAllocate(TcpRequestPool, &FromList); if (Request != NULL) { #if DBG Request->dsr_sig = dsr_signature; #endif } return Request; } //* 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. // __inline VOID FreeDGSendReq(DGSendReq *Request) { CTEStructAssert(Request, dsr); PplFree(TcpRequestPool, Request); } //* 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. // __inline DGRcvReq * GetDGRcvReq() { DGRcvReq *Request; Request = ExAllocatePoolWithTag(NonPagedPool, sizeof(DGRcvReq), 'dPCT'); #if DBG if (Request!= NULL) { Request->drr_sig = drr_signature; } #endif return Request; } //* FreeDGRcvReq - Free a DG rcv request. // // Called when someone wants to free a DG rcv request. // // Input: RcvReq - RcvReq to be freed. // // Returns: Nothing. // __inline VOID FreeDGRcvReq(DGRcvReq *Request) { CTEStructAssert(Request, drr); ExFreePool(Request); } //* 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) { Queue* Item; AddrObj *SendingAO; DGSendProc SendProc; CTELockHandle AOHandle; while ((Item = InterlockedDequeueIfNotEmpty(&DGDelayed, &DGQueueLock.Lock)) != NULL) { SendingAO = STRUCT_OF(AddrObj, Item, 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); SendingAO->ao_usecnt++; SendProc = SendingAO->ao_dgsend; CTEFreeLock(&SendingAO->ao_lock, AOHandle); (*SendProc) (SendingAO, SendReq); DEREF_AO(SendingAO); } else { CTEFreeLock(&SendingAO->ao_lock, AOHandle); } } else { SET_AO_REQUEST(SendingAO, AO_SEND); CTEFreeLock(&SendingAO->ao_lock, AOHandle); } } } //* 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, IP_STATUS SendStatus) { DGSendReq *FinishedSR = (DGSendReq *) Context; CTELockHandle AOHandle; CTEReqCmpltRtn Callback; // Completion routine. PVOID CallbackContext; // User context. ushort SentSize; AddrObj *AO; Queue* Item; #if TRACE_EVENT PTDI_DATA_REQUEST_NOTIFY_ROUTINE CPCallBack; WMIData WMIInfo; #endif CTEStructAssert(FinishedSR, dsr); 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. Item = InterlockedDequeueIfNotEmpty(&DGHeaderPending, &DGQueueLock.Lock); while (Item) { AO = STRUCT_OF(AddrObj, Item, 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); if (!SendReq->dsr_header) { SendReq->dsr_header = BufferChain; // Give him this buffer. InterlockedEnqueueAtDpcLevel(&DGDelayed, &AO->ao_pendq, &DGQueueLock.Lock); CTEFreeLock(&AO->ao_lock, AOHandle); CTEScheduleEvent(&DGDelayedEvent, NULL); break; } else { CLEAR_AO_OOR(AO); CTEFreeLock(&AO->ao_lock, AOHandle); Item = InterlockedDequeueIfNotEmpty(&DGHeaderPending, &DGQueueLock.Lock ); } } else { // On the pending queue, but no sends! CLEAR_AO_OOR(AO); CTEFreeLock(&AO->ao_lock, AOHandle); Item = InterlockedDequeueIfNotEmpty(&DGHeaderPending, &DGQueueLock.Lock ); } } if (!Item) { FreeDGHeader(BufferChain); } #if TRACE_EVENT if (!(SendStatus == IP_GENERAL_FAILURE)) { WMIInfo.wmi_destaddr = FinishedSR->dsr_addr; WMIInfo.wmi_destport = FinishedSR->dsr_port; WMIInfo.wmi_srcaddr = FinishedSR->dsr_srcaddr; WMIInfo.wmi_srcport = FinishedSR->dsr_srcport; WMIInfo.wmi_context = FinishedSR->dsr_pid; WMIInfo.wmi_size = SentSize; CPCallBack = TCPCPHandlerRoutine; if (CPCallBack!=NULL) { ulong GroupType; GroupType = EVENT_TRACE_GROUP_UDPIP + EVENT_TRACE_TYPE_SEND ; (*CPCallBack)(GroupType, (PVOID)&WMIInfo, sizeof(WMIInfo), NULL); } } #endif FreeDGSendReq(FinishedSR); if (Callback != NULL) { if (SendStatus == IP_GENERAL_FAILURE) (*Callback) (CallbackContext, (uint)TDI_REQ_ABORTED, (uint) SentSize); else if (SendStatus == IP_PACKET_TOO_BIG) (*Callback) (CallbackContext, (uint)TDI_BUFFER_TOO_BIG, (uint) SentSize); else (*Callback) (CallbackContext, TDI_SUCCESS, (uint) SentSize); } } // // 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 inHandle ) { CTELockHandle lockHandle; DGSendReq *sendReq = NULL; Queue *qentry; BOOLEAN found = FALSE; PTCP_CONTEXT tcpContext; PIO_STACK_LOCATION irpSp; VOID *CancelContext, *CancelID; PIRP Irp = Context; irpSp = IoGetCurrentIrpStackLocation(Irp); tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext; 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); CancelContext = Irp->Tail.Overlay.DriverContext[0]; CancelID = Irp->Tail.Overlay.DriverContext[1]; CTEFreeLock(&tcpContext->EndpointLock, inHandle); if (found) { // // Complete the request and free its resources. // (*sendReq->dsr_rtn) (sendReq->dsr_context, (uint) TDI_CANCELLED, 0); if (sendReq->dsr_header != NULL) { FreeDGHeader(sendReq->dsr_header); } FreeDGSendReq(sendReq); } else { //Now try calling ndis cancel routine to complete queued up packets //for this request (*LocalNetInfo.ipi_cancelpackets) (CancelContext, CancelID); } } // TdiCancelSendDatagram VOID TdiCancelReceiveDatagram( AddrObj * SrcAO, PVOID Context, CTELockHandle inHandle ) { CTELockHandle lockHandle; DGRcvReq *rcvReq = NULL; Queue *qentry; BOOLEAN found = FALSE; PTCP_CONTEXT tcpContext; PIO_STACK_LOCATION irpSp; PIRP Irp = Context; irpSp = IoGetCurrentIrpStackLocation(Irp); tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext; 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); CTEFreeLock(&tcpContext->EndpointLock, inHandle); if (found) { // // Complete the request and free its resources. // (*rcvReq->drr_rtn) (rcvReq->drr_context, (uint) TDI_CANCELLED, 0); FreeDGRcvReq(rcvReq); } } // TdiCancelReceiveDatagram //** 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 ConnInfo1, 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; TDI_STATUS ReturnValue = TDI_ADDR_INVALID; DGSendProc SendProc; PTDI_CONNECTION_INFORMATION ConnInfo; // 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. 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. SrcAO = Request->Handle.AddressHandle; if (SrcAO != NULL) { CTEStructAssert(SrcAO, ao); CTEGetLock(&SrcAO->ao_lock, &Handle); if (AO_VALID(SrcAO)) { ConnInfo = ConnInfo1; if ((ConnInfo1 == NULL) && AO_CONNUDP(SrcAO)) { ConnInfo = &SrcAO->ao_udpconn; } // 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 (ConnInfo && 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)) { // AO isn't busy UDPHeader *UH; SendReq->dsr_header = GetDGHeaderAtDpcLevel(&UH); if (SendReq->dsr_header != NULL) { REF_AO(SrcAO); // Lock out exclusive // activities. SendProc = SrcAO->ao_dgsend; CTEFreeLock(&SrcAO->ao_lock, Handle); // Allright, just send it. (*SendProc) (SrcAO, SendReq); 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); CTEFreeLock(&SrcAO->ao_lock, Handle); return 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); } if (SendReq != NULL) FreeDGSendReq(SendReq); return ReturnValue; } //** 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(); RcvAO = Request->Handle.AddressHandle; CTEStructAssert(RcvAO, ao); 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's 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 = (ushort) 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); } // The AddrObj is invalid or non-existent. if (RcvReq != NULL) FreeDGRcvReq(RcvReq); return TDI_ADDR_INVALID; } //* DGFillIpPktInfo - Create an ancillary data object and fill in // IP_PKTINFO information. // // This is a helper function for the IP_PKTINFO socket option supported for // datagram sockets only. The caller provides the destination address as // specified in the IP header of the packet and the IP address of the local // interface the packet was delivered on. This routine will create the // proper ancillary data object and fill in the destination IP address // and the interface number of the local interface. The data object must // be freed by the caller. // // Input: DestAddr - Destination address from IP header of packet. // LocalAddr - IP address of local interface on which packet // arrived. // Size - Buffer that will be filled in with size in bytes // of the ancillary data object. // // Returns: NULL if unsuccessful, an ancillary data object for IP_PKTINFO // if successful. // PTDI_CMSGHDR DGFillIpPktInfo(IPAddr DestAddr, IPAddr LocalAddr, int *Size) { PTDI_CMSGHDR CmsgHdr; *Size = TDI_CMSG_SPACE(sizeof(IN_PKTINFO)); CmsgHdr = ExAllocatePoolWithTag(NonPagedPool, *Size, 'uPCT'); if (CmsgHdr) { IN_PKTINFO *pktinfo = (IN_PKTINFO*)TDI_CMSG_DATA(CmsgHdr); // Fill in the ancillary data object header information. TDI_INIT_CMSGHDR(CmsgHdr, IPPROTO_IP, IP_PKTINFO, sizeof(IN_PKTINFO)); pktinfo->ipi_addr = DestAddr; // Get the index of the local interface on which the packet arrived. pktinfo->ipi_ifindex = (*LocalNetInfo.ipi_getifindexfromaddr) (LocalAddr,IF_CHECK_NONE); } else { *Size = 0; } return CmsgHdr; } #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) { CTEInitLock(&DGQueueLock.Lock); DGHeaderBufferSize = (USHORT)(MaxHeaderSize + LocalNetInfo.ipi_hsize); #if BACK_FILL DGHeaderBufferSize += MAX_BACKFILL_HDR_SIZE; #endif DgHeaderPool = MdpCreatePool ( DGHeaderBufferSize, 'uhCT'); if (!DgHeaderPool) { return FALSE; } INITQ(&DGHeaderPending); INITQ(&DGDelayed); CTEInitEvent(&DGDelayedEvent, DGDelayedEventProc); return TRUE; } #pragma END_INIT