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 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 PoolFreeBuffer(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 /* Call completion routine for send request */
134 (*Complete
)(CompleteContext
, NdisStatus
, BytesSent
);
136 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
142 PDATAGRAM_SEND_REQUEST SendRequest
)
144 * FUNCTION: Sends a datagram to IP layer
146 * Context = Pointer to context information (ADDRESS_FILE)
147 * SendRequest = Pointer to send request
154 PROUTE_CACHE_NODE RCN
;
155 PLIST_ENTRY CurrentEntry
;
156 PADDRESS_FILE AddrFile
= Context
;
159 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
161 /* Get the information we need from the address file
162 now so we minimize the time we hold the spin lock */
163 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
164 LocalPort
= AddrFile
->Port
;
166 ReferenceObject(ADE
);
167 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
169 /* Loop until there are no more send requests in the
170 transmit queue or until we run out of resources */
172 Status
= SendRequest
->Build(SendRequest
, ADE
->Address
, LocalPort
, &IPPacket
);
173 if (!NT_SUCCESS(Status
)) {
174 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
175 /* An error occurred, enqueue the send request again and return */
176 InsertTailList(&AddrFile
->TransmitQueue
, &SendRequest
->ListEntry
);
177 DereferenceObject(ADE
);
178 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
180 TI_DbgPrint(MIN_TRACE
, ("Leaving (insufficient resources).\n"));
184 /* Get a route to the destination address */
185 if (RouteGetRouteToDestination(SendRequest
->RemoteAddress
, ADE
->NTE
, &RCN
) == IP_SUCCESS
) {
186 /* Set completion routine and send the packet */
187 PC(IPPacket
->NdisPacket
)->Complete
= SendDatagramComplete
;
188 PC(IPPacket
->NdisPacket
)->Context
= SendRequest
;
189 if (IPSendDatagram(IPPacket
, RCN
) != STATUS_SUCCESS
)
190 SendDatagramComplete(SendRequest
,
191 IPPacket
->NdisPacket
,
192 NDIS_STATUS_REQUEST_ABORTED
);
193 /* We're done with the RCN */
194 DereferenceObject(RCN
);
196 /* No route to destination */
197 /* FIXME: Which error code should we use here? */
198 TI_DbgPrint(MIN_TRACE
, ("No route to destination address (0x%X).\n",
199 SendRequest
->RemoteAddress
->Address
.IPv4Address
));
200 SendDatagramComplete(SendRequest
,
201 IPPacket
->NdisPacket
,
202 NDIS_STATUS_REQUEST_ABORTED
);
205 PoolFreeBuffer(IPPacket
);
207 /* Check transmit queue for more to send */
209 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
211 if (!IsListEmpty(&AddrFile
->TransmitQueue
)) {
212 /* Transmit queue is not empty, process one more request */
213 CurrentEntry
= RemoveHeadList(&AddrFile
->TransmitQueue
);
214 SendRequest
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_SEND_REQUEST
, ListEntry
);
216 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
218 /* Transmit queue is empty */
219 AF_CLR_PENDING(AddrFile
, AFF_SEND
);
220 DereferenceObject(ADE
);
221 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
223 TI_DbgPrint(MAX_TRACE
, ("Leaving (empty queue).\n"));
231 PADDRESS_FILE AddrFile
,
236 * FUNCTION: Delivers datagram data to a user
238 * AddrFile = Address file to deliver data to
239 * Address = Remote address the packet came from
240 * IPPacket = Pointer to IP packet to deliver
241 * DataSize = Number of bytes in data area
242 * (incl. IP header for raw IP file objects)
244 * If there is a receive request, then we copy the data to the
245 * buffer supplied by the user and complete the receive request.
246 * If no suitable receive request exists, then we call the event
247 * handler if it exists, otherwise we drop the packet.
251 PTDI_IND_RECEIVE_DATAGRAM ReceiveHandler
;
252 PVOID HandlerContext
;
259 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
261 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
263 if (AddrFile
->Protocol
== IPPROTO_UDP
) {
264 DataBuffer
= IPPacket
->Data
;
266 /* Give client the IP header too if it is a raw IP file object */
267 DataBuffer
= IPPacket
->Header
;
270 if (!IsListEmpty(&AddrFile
->ReceiveQueue
)) {
271 PLIST_ENTRY CurrentEntry
;
272 PDATAGRAM_RECEIVE_REQUEST Current
;
275 TI_DbgPrint(MAX_TRACE
, ("There is a receive request.\n"));
277 /* Search receive request list to find a match */
279 CurrentEntry
= AddrFile
->ReceiveQueue
.Flink
;
280 while ((CurrentEntry
!= &AddrFile
->ReceiveQueue
) && (!Found
)) {
281 Current
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_RECEIVE_REQUEST
, ListEntry
);
282 if (!Current
->RemoteAddress
)
284 else if (AddrIsEqual(Address
, Current
->RemoteAddress
))
288 /* FIXME: Maybe we should check if the buffer of this
289 receive request is large enough and if not, search
292 /* Remove the request from the queue */
293 RemoveEntryList(&Current
->ListEntry
);
294 AddrFile
->RefCount
--;
297 CurrentEntry
= CurrentEntry
->Flink
;
300 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
303 TI_DbgPrint(MAX_TRACE
, ("Suitable receive request found.\n"));
305 /* Copy the data into buffer provided by the user */
306 CopyBufferToBufferChain(Current
->Buffer
,
311 /* Complete the receive request */
312 (*Current
->Complete
)(Current
->Context
, STATUS_SUCCESS
, DataSize
);
314 /* Finally free the receive request */
315 if (Current
->RemoteAddress
)
316 PoolFreeBuffer(Current
->RemoteAddress
);
317 PoolFreeBuffer(Current
);
319 } else if (AddrFile
->RegisteredReceiveDatagramHandler
) {
320 TI_DbgPrint(MAX_TRACE
, ("Calling receive event handler.\n"));
322 ReceiveHandler
= AddrFile
->ReceiveDatagramHandler
;
323 HandlerContext
= AddrFile
->ReceiveDatagramHandlerContext
;
325 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
327 if (Address
->Type
== IP_ADDRESS_V4
) {
328 AddressLength
= sizeof(IPv4_RAW_ADDRESS
);
329 SourceAddress
= &Address
->Address
.IPv4Address
;
330 } else /* (Address->Type == IP_ADDRESS_V6) */ {
331 AddressLength
= sizeof(IPv6_RAW_ADDRESS
);
332 SourceAddress
= Address
->Address
.IPv6Address
;
335 Status
= (*ReceiveHandler
)(HandlerContext
,
340 TDI_RECEIVE_ENTIRE_MESSAGE
,
347 TI_DbgPrint(MAX_TRACE
, ("Discarding datagram.\n"));
350 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
354 VOID
DGCancelSendRequest(
355 PADDRESS_FILE AddrFile
,
358 * FUNCTION: Cancels a datagram send request
360 * AddrFile = Pointer to address file of the request
361 * Context = Pointer to context information for completion handler
365 PLIST_ENTRY CurrentEntry
;
366 PDATAGRAM_SEND_REQUEST Current
= NULL
;
367 BOOLEAN Found
= FALSE
;
369 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
371 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
373 /* Search the request list for the specified request and remove it */
374 CurrentEntry
= AddrFile
->TransmitQueue
.Flink
;
375 while ((CurrentEntry
!= &AddrFile
->TransmitQueue
) && (!Found
)) {
376 Current
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_SEND_REQUEST
, ListEntry
);
377 if (Context
== Current
->Context
) {
378 /* We've found the request, now remove it from the queue */
379 RemoveEntryList(CurrentEntry
);
380 AddrFile
->RefCount
--;
384 CurrentEntry
= CurrentEntry
->Flink
;
387 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
390 /* Complete the request and free its resources */
391 (*Current
->Complete
)(Current
->Context
, STATUS_CANCELLED
, 0);
392 PoolFreeBuffer(Current
->RemoteAddress
);
393 PoolFreeBuffer(Current
);
395 TI_DbgPrint(MID_TRACE
, ("Cannot find send request.\n"));
400 VOID
DGCancelReceiveRequest(
401 PADDRESS_FILE AddrFile
,
404 * FUNCTION: Cancels a datagram receive request
406 * AddrFile = Pointer to address file of the request
407 * Context = Pointer to context information for completion handler
411 PLIST_ENTRY CurrentEntry
;
412 PDATAGRAM_RECEIVE_REQUEST Current
= NULL
;
413 BOOLEAN Found
= FALSE
;
415 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
417 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
419 /* Search the request list for the specified request and remove it */
420 CurrentEntry
= AddrFile
->ReceiveQueue
.Flink
;
421 while ((CurrentEntry
!= &AddrFile
->ReceiveQueue
) && (!Found
)) {
422 Current
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_RECEIVE_REQUEST
, ListEntry
);
423 if (Context
== Current
->Context
) {
424 /* We've found the request, now remove it from the queue */
425 RemoveEntryList(CurrentEntry
);
426 AddrFile
->RefCount
--;
430 CurrentEntry
= CurrentEntry
->Flink
;
433 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
436 /* Complete the request and free its resources */
437 (*Current
->Complete
)(Current
->Context
, STATUS_CANCELLED
, 0);
438 /* Remote address can be NULL if the caller wants to receive
439 packets sent from any address */
440 if (Current
->RemoteAddress
)
441 PoolFreeBuffer(Current
->RemoteAddress
);
442 PoolFreeBuffer(Current
);
444 TI_DbgPrint(MID_TRACE
, ("Cannot find receive request.\n"));
449 NTSTATUS
DGSendDatagram(
450 PTDI_REQUEST Request
,
451 PTDI_CONNECTION_INFORMATION ConnInfo
,
454 DATAGRAM_BUILD_ROUTINE Build
)
456 * FUNCTION: Sends a datagram to a remote address
458 * Request = Pointer to TDI request
459 * ConnInfo = Pointer to connection information
460 * Buffer = Pointer to NDIS buffer with data
461 * DataSize = Size in bytes of data to be sent
462 * Build = Pointer to datagram build routine
464 * Status of operation
467 PADDRESS_FILE AddrFile
;
470 PDATAGRAM_SEND_REQUEST SendRequest
= NULL
;
472 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
474 AddrFile
= Request
->Handle
.AddressHandle
;
476 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
478 if (AF_IS_VALID(AddrFile
)) {
479 SendRequest
= PoolAllocateBuffer(sizeof(DATAGRAM_SEND_REQUEST
));
481 /* Initialize a send request */
482 Status
= AddrGetAddress(ConnInfo
->RemoteAddress
,
483 &SendRequest
->RemoteAddress
, &SendRequest
->RemotePort
,
484 &AddrFile
->AddrCache
);
485 if (NT_SUCCESS(Status
)) {
486 SendRequest
->Buffer
= Buffer
;
487 SendRequest
->BufferSize
= DataSize
;
488 SendRequest
->Complete
= Request
->RequestNotifyObject
;
489 SendRequest
->Context
= Request
->RequestContext
;
490 SendRequest
->Build
= Build
;
492 if (AF_IS_BUSY(AddrFile
)) {
493 /* Queue send request on the transmit queue */
494 InsertTailList(&AddrFile
->TransmitQueue
, &SendRequest
->ListEntry
);
496 /* Reference address file and set pending send request flag */
497 AddrFile
->RefCount
++;
498 AF_SET_PENDING(AddrFile
, AFF_SEND
);
500 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
502 TI_DbgPrint(MAX_TRACE
, ("Leaving (queued).\n"));
504 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
506 /* Send the datagram */
507 DGSend(AddrFile
, SendRequest
);
509 TI_DbgPrint(MAX_TRACE
, ("Leaving (pending).\n"));
511 return STATUS_PENDING
;
514 Status
= STATUS_INSUFFICIENT_RESOURCES
;
516 Status
= STATUS_ADDRESS_CLOSED
;
518 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
520 TI_DbgPrint(MAX_TRACE
, ("Leaving. Status (0x%X)\n", Status
));
526 NTSTATUS
DGReceiveDatagram(
527 PTDI_REQUEST Request
,
528 PTDI_CONNECTION_INFORMATION ConnInfo
,
532 PTDI_CONNECTION_INFORMATION ReturnInfo
,
533 PULONG BytesReceived
)
535 * FUNCTION: Attempts to receive a datagram from a remote address
537 * Request = Pointer to TDI request
538 * ConnInfo = Pointer to connection information
539 * Buffer = Pointer to NDIS buffer chain to store received data
540 * ReceiveLength = Maximum size to use of buffer (0 if all can be used)
541 * ReceiveFlags = Receive flags (None, Normal, Peek)
542 * ReturnInfo = Pointer to structure for return information
543 * BytesReceive = Pointer to structure for number of bytes received
545 * Status of operation
547 * This is the high level interface for receiving datagrams
550 PADDRESS_FILE AddrFile
;
553 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest
;
555 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
557 AddrFile
= Request
->Handle
.AddressHandle
;
559 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
561 if (AF_IS_VALID(AddrFile
)) {
562 ReceiveRequest
= PoolAllocateBuffer(sizeof(DATAGRAM_RECEIVE_REQUEST
));
563 if (ReceiveRequest
) {
564 /* Initialize a receive request */
566 /* Extract the remote address filter from the request (if any) */
567 if (((ConnInfo
->RemoteAddressLength
!= 0)) && (ConnInfo
->RemoteAddress
)) {
568 Status
= AddrGetAddress(ConnInfo
->RemoteAddress
,
569 &ReceiveRequest
->RemoteAddress
,
570 &ReceiveRequest
->RemotePort
,
571 &AddrFile
->AddrCache
);
572 if (!NT_SUCCESS(Status
)) {
573 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
574 PoolFreeBuffer(ReceiveRequest
);
578 ReceiveRequest
->RemotePort
= 0;
579 ReceiveRequest
->RemoteAddress
= NULL
;
581 ReceiveRequest
->ReturnInfo
= ReturnInfo
;
582 ReceiveRequest
->Buffer
= Buffer
;
583 /* If ReceiveLength is 0, the whole buffer is available to us */
584 ReceiveRequest
->BufferSize
= (ReceiveLength
== 0) ?
585 MmGetMdlByteCount(Buffer
) : ReceiveLength
;
586 ReceiveRequest
->Complete
= Request
->RequestNotifyObject
;
587 ReceiveRequest
->Context
= Request
->RequestContext
;
589 /* Queue receive request */
590 InsertTailList(&AddrFile
->ReceiveQueue
, &ReceiveRequest
->ListEntry
);
592 /* Reference address file and set pending receive request flag */
593 AddrFile
->RefCount
++;
594 AF_SET_PENDING(AddrFile
, AFF_RECEIVE
);
596 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
598 TI_DbgPrint(MAX_TRACE
, ("Leaving (pending).\n"));
600 return STATUS_PENDING
;
602 Status
= STATUS_INSUFFICIENT_RESOURCES
;
604 Status
= STATUS_INVALID_ADDRESS
;
606 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
608 TI_DbgPrint(MAX_TRACE
, ("Leaving with errors (0x%X).\n", Status
));
617 * FUNCTION: Initializes the datagram subsystem
619 * Status of operation
622 InitializeListHead(&DGPendingListHead
);
624 KeInitializeSpinLock(&DGPendingListLock
);
626 ExInitializeWorkItem(&DGWorkItem
, DatagramWorker
, NULL
);
628 return STATUS_SUCCESS
;
635 * FUNCTION: Shuts down the datagram subsystem
637 * Status of operation
640 return STATUS_SUCCESS
;