2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: transport/datagram/datagram.c
5 * PURPOSE: Routines for sending and receiving datagrams
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
8 * CSH 01/08-2000 Created
19 /* Pending request queue */
20 LIST_ENTRY DGPendingListHead;
21 KSPIN_LOCK DGPendingListLock;
22 /* Work queue item for pending requests */
23 WORK_QUEUE_ITEM DGWorkItem;
29 * FUNCTION: Handles pending send requests
31 * Context = Pointer to context information (unused)
33 * This routine is called after the driver has run out of resources.
34 * It processes send requests or shedules them to be processed
37 PLIST_ENTRY CurrentADFEntry;
38 PLIST_ENTRY CurrentSREntry;
39 PADDRESS_FILE CurrentADF;
40 PDATAGRAM_SEND_REQUEST CurrentSR;
44 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
46 KeAcquireSpinLock(&DGPendingListLock, &OldIrql1);
48 CurrentADFEntry = DGPendingListHead.Flink;
49 while (CurrentADFEntry != &DGPendingListHead) {
50 RemoveEntryList(CurrentADFEntry);
51 CurrentADF = CONTAINING_RECORD(CurrentADFEntry,
55 KeAcquireSpinLock(&CurrentADF->Lock, &OldIrql2);
57 if (AF_IS_BUSY(CurrentADF)) {
58 /* The send worker function is already running so we just
59 set the pending send flag on the address file object */
61 AF_SET_PENDING(CurrentADF, AFF_SEND);
62 KeReleaseSpinLock(&CurrentADF->Lock, OldIrql2);
64 if (!IsListEmpty(&CurrentADF->TransmitQueue)) {
65 /* The transmit queue is not empty. Dequeue a send
66 request and process it */
68 CurrentSREntry = RemoveHeadList(&CurrentADF->TransmitQueue);
69 CurrentSR = CONTAINING_RECORD(CurrentADFEntry,
70 DATAGRAM_SEND_REQUEST,
73 KeReleaseSpinLock(&CurrentADF->Lock, OldIrql2);
75 DGSend(CurrentADF, CurrentSR);
77 KeReleaseSpinLock(&CurrentADF->Lock, OldIrql2);
79 CurrentADFEntry = CurrentADFEntry->Flink;
82 KeReleaseSpinLock(&DGPendingListLock, OldIrql1);
84 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
88 VOID SendDatagramComplete(
91 NDIS_STATUS NdisStatus)
93 * FUNCTION: Datagram transmit completion handler
95 * Context = Pointer to context infomation (DATAGRAM_SEND_REQUEST)
96 * Packet = Pointer to NDIS packet
97 * NdisStatus = Status of transmit operation
99 * This routine is called by IP when a datagram send completes.
100 * We shedule the out-of-resource worker function if there
101 * are pending address files in the queue
106 PVOID CompleteContext;
107 PNDIS_BUFFER NdisBuffer;
108 PDATAGRAM_SEND_REQUEST SendRequest;
109 DATAGRAM_COMPLETION_ROUTINE Complete;
110 BOOLEAN QueueWorkItem;
112 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
114 SendRequest = (PDATAGRAM_SEND_REQUEST)Context;
115 Complete = SendRequest->Complete;
116 CompleteContext = SendRequest->Context;
117 BytesSent = SendRequest->BufferSize;
119 /* Remove data buffer before releasing memory for packet buffers */
120 NdisQueryPacket(Packet, NULL, NULL, &NdisBuffer, NULL);
121 NdisUnchainBufferAtBack(Packet, &NdisBuffer);
122 FreeNdisPacket(Packet);
123 DereferenceObject(SendRequest->RemoteAddress);
124 ExFreePool(SendRequest);
126 /* If there are pending send requests, shedule worker function */
127 KeAcquireSpinLock(&DGPendingListLock, &OldIrql);
128 QueueWorkItem = (!IsListEmpty(&DGPendingListHead));
129 KeReleaseSpinLock(&DGPendingListLock, OldIrql);
131 ExQueueWorkItem(&DGWorkItem, CriticalWorkQueue);
133 TI_DbgPrint(MAX_TRACE, ("Calling 0x%X.\n", Complete));
135 /* Call completion routine for send request */
136 (*Complete)(CompleteContext, NdisStatus, BytesSent);
138 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
144 PDATAGRAM_SEND_REQUEST SendRequest)
146 * FUNCTION: Sends a datagram to IP layer
148 * Context = Pointer to context information (ADDRESS_FILE)
149 * SendRequest = Pointer to send request
156 PROUTE_CACHE_NODE RCN;
157 PLIST_ENTRY CurrentEntry;
158 PADDRESS_FILE AddrFile = Context;
161 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
163 /* Get the information we need from the address file
164 now so we minimize the time we hold the spin lock */
165 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
166 LocalPort = AddrFile->Port;
168 ReferenceObject(ADE);
169 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
171 /* Loop until there are no more send requests in the
172 transmit queue or until we run out of resources */
174 Status = (*SendRequest->Build)(SendRequest, ADE->Address, LocalPort, &IPPacket);
175 if (!NT_SUCCESS(Status)) {
176 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
177 /* An error occurred, enqueue the send request again and return */
178 InsertTailList(&AddrFile->TransmitQueue, &SendRequest->ListEntry);
179 DereferenceObject(ADE);
180 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
182 TI_DbgPrint(MIN_TRACE, ("Leaving (insufficient resources).\n"));
186 /* Get a route to the destination address */
187 if (RouteGetRouteToDestination(SendRequest->RemoteAddress, ADE->NTE, &RCN) == IP_SUCCESS) {
188 /* Set completion routine and send the packet */
189 PC(IPPacket->NdisPacket)->Complete = SendDatagramComplete;
190 PC(IPPacket->NdisPacket)->Context = SendRequest;
191 if (IPSendDatagram(IPPacket, RCN) != STATUS_SUCCESS)
192 SendDatagramComplete(
194 IPPacket->NdisPacket,
195 NDIS_STATUS_REQUEST_ABORTED);
196 /* We're done with the RCN */
197 DereferenceObject(RCN);
199 /* No route to destination */
200 /* FIXME: Which error code should we use here? */
201 TI_DbgPrint(MIN_TRACE, ("No route to destination address (0x%X).\n",
202 SendRequest->RemoteAddress->Address.IPv4Address));
203 SendDatagramComplete(
205 IPPacket->NdisPacket,
206 NDIS_STATUS_REQUEST_ABORTED);
209 (*IPPacket->Free)(IPPacket);
211 /* Check transmit queue for more to send */
213 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
215 if (!IsListEmpty(&AddrFile->TransmitQueue)) {
216 /* Transmit queue is not empty, process one more request */
217 CurrentEntry = RemoveHeadList(&AddrFile->TransmitQueue);
218 SendRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry);
220 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
222 /* Transmit queue is empty */
223 AF_CLR_PENDING(AddrFile, AFF_SEND);
224 DereferenceObject(ADE);
225 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
227 TI_DbgPrint(MAX_TRACE, ("Leaving (empty queue).\n"));
235 PADDRESS_FILE AddrFile,
240 * FUNCTION: Delivers datagram data to a user
242 * AddrFile = Address file to deliver data to
243 * Address = Remote address the packet came from
244 * IPPacket = Pointer to IP packet to deliver
245 * DataSize = Number of bytes in data area
246 * (incl. IP header for raw IP file objects)
248 * If there is a receive request, then we copy the data to the
249 * buffer supplied by the user and complete the receive request.
250 * If no suitable receive request exists, then we call the event
251 * handler if it exists, otherwise we drop the packet.
255 PTDI_IND_RECEIVE_DATAGRAM ReceiveHandler;
256 PVOID HandlerContext;
263 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
265 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
267 if (AddrFile->Protocol == IPPROTO_UDP) {
268 DataBuffer = IPPacket->Data;
270 /* Give client the IP header too if it is a raw IP file object */
271 DataBuffer = IPPacket->Header;
274 if (!IsListEmpty(&AddrFile->ReceiveQueue)) {
275 PLIST_ENTRY CurrentEntry;
276 PDATAGRAM_RECEIVE_REQUEST Current;
279 TI_DbgPrint(MAX_TRACE, ("There is a receive request.\n"));
281 /* Search receive request list to find a match */
283 CurrentEntry = AddrFile->ReceiveQueue.Flink;
284 while ((CurrentEntry != &AddrFile->ReceiveQueue) && (!Found)) {
285 Current = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
286 if (!Current->RemoteAddress)
288 else if (AddrIsEqual(Address, Current->RemoteAddress))
292 /* FIXME: Maybe we should check if the buffer of this
293 receive request is large enough and if not, search
296 /* Remove the request from the queue */
297 RemoveEntryList(&Current->ListEntry);
298 AddrFile->RefCount--;
301 CurrentEntry = CurrentEntry->Flink;
304 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
307 TI_DbgPrint(MAX_TRACE, ("Suitable receive request found.\n"));
309 /* Copy the data into buffer provided by the user */
310 CopyBufferToBufferChain(
316 /* Complete the receive request */
317 (*Current->Complete)(Current->Context, STATUS_SUCCESS, DataSize);
319 /* Finally free the receive request */
320 if (Current->RemoteAddress)
321 DereferenceObject(Current->RemoteAddress);
324 } else if (AddrFile->RegisteredReceiveDatagramHandler) {
325 TI_DbgPrint(MAX_TRACE, ("Calling receive event handler.\n"));
327 ReceiveHandler = AddrFile->ReceiveDatagramHandler;
328 HandlerContext = AddrFile->ReceiveDatagramHandlerContext;
330 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
332 if (Address->Type == IP_ADDRESS_V4) {
333 AddressLength = sizeof(IPv4_RAW_ADDRESS);
334 SourceAddress = &Address->Address.IPv4Address;
335 } else /* (Address->Type == IP_ADDRESS_V6) */ {
336 AddressLength = sizeof(IPv6_RAW_ADDRESS);
337 SourceAddress = Address->Address.IPv6Address;
340 Status = (*ReceiveHandler)(
346 TDI_RECEIVE_ENTIRE_MESSAGE,
353 TI_DbgPrint(MAX_TRACE, ("Discarding datagram.\n"));
356 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
360 VOID DGCancelSendRequest(
361 PADDRESS_FILE AddrFile,
364 * FUNCTION: Cancels a datagram send request
366 * AddrFile = Pointer to address file of the request
367 * Context = Pointer to context information for completion handler
371 PLIST_ENTRY CurrentEntry;
372 PDATAGRAM_SEND_REQUEST Current = NULL;
373 BOOLEAN Found = FALSE;
375 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
377 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
379 /* Search the request list for the specified request and remove it */
380 CurrentEntry = AddrFile->TransmitQueue.Flink;
381 while ((CurrentEntry != &AddrFile->TransmitQueue) && (!Found)) {
382 Current = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry);
383 if (Context == Current->Context) {
384 /* We've found the request, now remove it from the queue */
385 RemoveEntryList(CurrentEntry);
386 AddrFile->RefCount--;
390 CurrentEntry = CurrentEntry->Flink;
393 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
396 /* Complete the request and free its resources */
397 (*Current->Complete)(Current->Context, STATUS_CANCELLED, 0);
398 DereferenceObject(Current->RemoteAddress);
401 TI_DbgPrint(MID_TRACE, ("Cannot find send request.\n"));
406 VOID DGCancelReceiveRequest(
407 PADDRESS_FILE AddrFile,
410 * FUNCTION: Cancels a datagram receive request
412 * AddrFile = Pointer to address file of the request
413 * Context = Pointer to context information for completion handler
417 PLIST_ENTRY CurrentEntry;
418 PDATAGRAM_RECEIVE_REQUEST Current = NULL;
419 BOOLEAN Found = FALSE;
421 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
423 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
425 /* Search the request list for the specified request and remove it */
426 CurrentEntry = AddrFile->ReceiveQueue.Flink;
427 while ((CurrentEntry != &AddrFile->ReceiveQueue) && (!Found)) {
428 Current = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
429 if (Context == Current->Context) {
430 /* We've found the request, now remove it from the queue */
431 RemoveEntryList(CurrentEntry);
432 AddrFile->RefCount--;
436 CurrentEntry = CurrentEntry->Flink;
439 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
442 /* Complete the request and free its resources */
443 (*Current->Complete)(Current->Context, STATUS_CANCELLED, 0);
444 /* Remote address can be NULL if the caller wants to receive
445 packets sent from any address */
446 if (Current->RemoteAddress)
447 DereferenceObject(Current->RemoteAddress);
450 TI_DbgPrint(MID_TRACE, ("Cannot find receive request.\n"));
456 PADDRESS_FILE AddressFile,
457 PDATAGRAM_SEND_REQUEST SendRequest)
459 * FUNCTION: Transmits or queues a send request
461 * AddressFile = Pointer to address file
462 * SendRequest = Pointer to send request
464 * Status of operation
469 KeAcquireSpinLock(&AddressFile->Lock, &OldIrql);
471 if (AF_IS_BUSY(AddressFile)) {
473 /* Queue send request on the transmit queue */
474 InsertTailList(&AddressFile->TransmitQueue, &SendRequest->ListEntry);
476 /* Reference address file and set pending send request flag */
477 ReferenceObject(AddressFile);
479 AF_SET_PENDING(AddressFile, AFF_SEND);
481 KeReleaseSpinLock(&AddressFile->Lock, OldIrql);
483 TI_DbgPrint(MAX_TRACE, ("Leaving (queued).\n"));
486 KeReleaseSpinLock(&AddressFile->Lock, OldIrql);
488 /* Send the datagram */
489 DGSend(AddressFile, SendRequest);
491 TI_DbgPrint(MAX_TRACE, ("Leaving (pending).\n"));
494 return STATUS_PENDING;
497 NTSTATUS DGSendDatagram(
498 PTDI_REQUEST Request,
499 PTDI_CONNECTION_INFORMATION ConnInfo,
502 DATAGRAM_BUILD_ROUTINE Build)
504 * FUNCTION: Sends a datagram to a remote address
506 * Request = Pointer to TDI request
507 * ConnInfo = Pointer to connection information
508 * Buffer = Pointer to NDIS buffer with data
509 * DataSize = Size in bytes of data to be sent
510 * Build = Pointer to datagram build routine
512 * Status of operation
515 PADDRESS_FILE AddrFile;
518 PDATAGRAM_SEND_REQUEST SendRequest;
520 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
522 AddrFile = Request->Handle.AddressHandle;
524 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
526 if (AF_IS_VALID(AddrFile)) {
527 /* Initialize a send request */
528 Status = BuildDatagramSendRequest(
534 Request->RequestNotifyObject,
535 Request->RequestContext,
538 if (NT_SUCCESS(Status)) {
539 Status = AddrGetAddress(
540 ConnInfo->RemoteAddress,
541 &SendRequest->RemoteAddress,
542 &SendRequest->RemotePort,
543 &AddrFile->AddrCache);
544 if (NT_SUCCESS(Status)) {
545 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
546 return DGTransmit(AddrFile, SendRequest);
548 ExFreePool(SendRequest);
551 Status = STATUS_INSUFFICIENT_RESOURCES;
553 Status = STATUS_ADDRESS_CLOSED;
555 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
557 TI_DbgPrint(MAX_TRACE, ("Leaving. Status (0x%X)\n", Status));
563 NTSTATUS DGReceiveDatagram(
564 PTDI_REQUEST Request,
565 PTDI_CONNECTION_INFORMATION ConnInfo,
569 PTDI_CONNECTION_INFORMATION ReturnInfo,
570 PULONG BytesReceived)
572 * FUNCTION: Attempts to receive a datagram from a remote address
574 * Request = Pointer to TDI request
575 * ConnInfo = Pointer to connection information
576 * Buffer = Pointer to NDIS buffer chain to store received data
577 * ReceiveLength = Maximum size to use of buffer (0 if all can be used)
578 * ReceiveFlags = Receive flags (None, Normal, Peek)
579 * ReturnInfo = Pointer to structure for return information
580 * BytesReceive = Pointer to structure for number of bytes received
582 * Status of operation
584 * This is the high level interface for receiving datagrams
587 PADDRESS_FILE AddrFile;
590 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest;
592 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
594 AddrFile = Request->Handle.AddressHandle;
596 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
598 if (AF_IS_VALID(AddrFile)) {
599 ReceiveRequest = ExAllocatePool(NonPagedPool, sizeof(DATAGRAM_RECEIVE_REQUEST));
600 if (ReceiveRequest) {
601 /* Initialize a receive request */
603 /* Extract the remote address filter from the request (if any) */
604 if (((ConnInfo->RemoteAddressLength != 0)) && (ConnInfo->RemoteAddress)) {
605 Status = AddrGetAddress(ConnInfo->RemoteAddress,
606 &ReceiveRequest->RemoteAddress,
607 &ReceiveRequest->RemotePort,
608 &AddrFile->AddrCache);
609 if (!NT_SUCCESS(Status)) {
610 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
611 ExFreePool(ReceiveRequest);
615 ReceiveRequest->RemotePort = 0;
616 ReceiveRequest->RemoteAddress = NULL;
618 ReceiveRequest->ReturnInfo = ReturnInfo;
619 ReceiveRequest->Buffer = Buffer;
620 /* If ReceiveLength is 0, the whole buffer is available to us */
621 ReceiveRequest->BufferSize = (ReceiveLength == 0) ?
622 MmGetMdlByteCount(Buffer) : ReceiveLength;
623 ReceiveRequest->Complete = Request->RequestNotifyObject;
624 ReceiveRequest->Context = Request->RequestContext;
626 /* Queue receive request */
627 InsertTailList(&AddrFile->ReceiveQueue, &ReceiveRequest->ListEntry);
629 /* Reference address file and set pending receive request flag */
630 AddrFile->RefCount++;
631 AF_SET_PENDING(AddrFile, AFF_RECEIVE);
633 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
635 TI_DbgPrint(MAX_TRACE, ("Leaving (pending).\n"));
637 return STATUS_PENDING;
639 Status = STATUS_INSUFFICIENT_RESOURCES;
641 Status = STATUS_INVALID_ADDRESS;
643 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
645 TI_DbgPrint(MAX_TRACE, ("Leaving with errors (0x%X).\n", Status));
654 * FUNCTION: Initializes the datagram subsystem
656 * Status of operation
659 InitializeListHead(&DGPendingListHead);
661 KeInitializeSpinLock(&DGPendingListLock);
663 ExInitializeWorkItem(&DGWorkItem, DatagramWorker, NULL);
665 return STATUS_SUCCESS;
672 * FUNCTION: Shuts down the datagram subsystem
674 * Status of operation
677 return STATUS_SUCCESS;