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
243 * If there is a receive request, then we copy the data to the
244 * buffer supplied by the user and complete the receive request.
245 * If no suitable receive request exists, then we call the event
246 * handler if it exists, otherwise we drop the packet.
250 PTDI_IND_RECEIVE_DATAGRAM ReceiveHandler
;
251 PVOID HandlerContext
;
257 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
259 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
261 if (!IsListEmpty(&AddrFile
->ReceiveQueue
)) {
262 PLIST_ENTRY CurrentEntry
;
263 PDATAGRAM_RECEIVE_REQUEST Current
;
266 TI_DbgPrint(MAX_TRACE
, ("There is a receive request.\n"));
268 /* Search receive request list to find a match */
270 CurrentEntry
= AddrFile
->ReceiveQueue
.Flink
;
271 while ((CurrentEntry
!= &AddrFile
->ReceiveQueue
) && (!Found
)) {
272 Current
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_RECEIVE_REQUEST
, ListEntry
);
273 if (!Current
->RemoteAddress
)
275 else if (AddrIsEqual(Address
, Current
->RemoteAddress
))
279 /* FIXME: Maybe we should check if the buffer of this
280 receive request is large enough and if not, search
283 /* Remove the request from the queue */
284 RemoveEntryList(&Current
->ListEntry
);
285 AddrFile
->RefCount
--;
288 CurrentEntry
= CurrentEntry
->Flink
;
291 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
294 TI_DbgPrint(MAX_TRACE
, ("Suitable receive request found.\n"));
296 /* Copy the data into buffer provided by the user */
297 CopyBufferToBufferChain(Current
->Buffer
,
302 /* Complete the receive request */
303 (*Current
->Complete
)(Current
->Context
, STATUS_SUCCESS
, DataSize
);
305 /* Finally free the receive request */
306 if (Current
->RemoteAddress
)
307 PoolFreeBuffer(Current
->RemoteAddress
);
308 PoolFreeBuffer(Current
);
310 } else if (AddrFile
->RegisteredReceiveDatagramHandler
) {
311 TI_DbgPrint(MAX_TRACE
, ("Calling receive event handler.\n"));
313 ReceiveHandler
= AddrFile
->ReceiveDatagramHandler
;
314 HandlerContext
= AddrFile
->ReceiveDatagramHandlerContext
;
316 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
318 if (Address
->Type
== IP_ADDRESS_V4
) {
319 AddressLength
= sizeof(IPv4_RAW_ADDRESS
);
320 SourceAddress
= &Address
->Address
.IPv4Address
;
321 } else /* (Address->Type == IP_ADDRESS_V6) */ {
322 AddressLength
= sizeof(IPv6_RAW_ADDRESS
);
323 SourceAddress
= Address
->Address
.IPv6Address
;
326 Status
= (*ReceiveHandler
)(HandlerContext
,
331 TDI_RECEIVE_ENTIRE_MESSAGE
,
338 TI_DbgPrint(MAX_TRACE
, ("Discarding datagram.\n"));
341 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
345 VOID
DGCancelSendRequest(
346 PADDRESS_FILE AddrFile
,
349 * FUNCTION: Cancels a datagram send request
351 * AddrFile = Pointer to address file of the request
352 * Context = Pointer to context information for completion handler
356 PLIST_ENTRY CurrentEntry
;
357 PDATAGRAM_SEND_REQUEST Current
= NULL
;
358 BOOLEAN Found
= FALSE
;
360 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
362 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
364 /* Search the request list for the specified request and remove it */
365 CurrentEntry
= AddrFile
->TransmitQueue
.Flink
;
366 while ((CurrentEntry
!= &AddrFile
->TransmitQueue
) && (!Found
)) {
367 Current
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_SEND_REQUEST
, ListEntry
);
368 if (Context
== Current
->Context
) {
369 /* We've found the request, now remove it from the queue */
370 RemoveEntryList(CurrentEntry
);
371 AddrFile
->RefCount
--;
375 CurrentEntry
= CurrentEntry
->Flink
;
378 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
381 /* Complete the request and free its resources */
382 (*Current
->Complete
)(Current
->Context
, STATUS_CANCELLED
, 0);
383 PoolFreeBuffer(Current
->RemoteAddress
);
384 PoolFreeBuffer(Current
);
386 TI_DbgPrint(MID_TRACE
, ("Cannot find send request.\n"));
391 VOID
DGCancelReceiveRequest(
392 PADDRESS_FILE AddrFile
,
395 * FUNCTION: Cancels a datagram receive request
397 * AddrFile = Pointer to address file of the request
398 * Context = Pointer to context information for completion handler
402 PLIST_ENTRY CurrentEntry
;
403 PDATAGRAM_RECEIVE_REQUEST Current
= NULL
;
404 BOOLEAN Found
= FALSE
;
406 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
408 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
410 /* Search the request list for the specified request and remove it */
411 CurrentEntry
= AddrFile
->ReceiveQueue
.Flink
;
412 while ((CurrentEntry
!= &AddrFile
->ReceiveQueue
) && (!Found
)) {
413 Current
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_RECEIVE_REQUEST
, ListEntry
);
414 if (Context
== Current
->Context
) {
415 /* We've found the request, now remove it from the queue */
416 RemoveEntryList(CurrentEntry
);
417 AddrFile
->RefCount
--;
421 CurrentEntry
= CurrentEntry
->Flink
;
424 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
427 /* Complete the request and free its resources */
428 (*Current
->Complete
)(Current
->Context
, STATUS_CANCELLED
, 0);
429 /* Remote address can be NULL if the caller wants to receive
430 packets sent from any address */
431 if (Current
->RemoteAddress
)
432 PoolFreeBuffer(Current
->RemoteAddress
);
433 PoolFreeBuffer(Current
);
435 TI_DbgPrint(MID_TRACE
, ("Cannot find receive request.\n"));
440 NTSTATUS
DGSendDatagram(
441 PTDI_REQUEST Request
,
442 PTDI_CONNECTION_INFORMATION ConnInfo
,
445 DATAGRAM_BUILD_ROUTINE Build
)
447 * FUNCTION: Sends a datagram to a remote address
449 * Request = Pointer to TDI request
450 * ConnInfo = Pointer to connection information
451 * Buffer = Pointer to NDIS buffer with data
452 * DataSize = Size in bytes of data to be sent
453 * Build = Pointer to datagram build routine
455 * Status of operation
458 PADDRESS_FILE AddrFile
;
461 PDATAGRAM_SEND_REQUEST SendRequest
= NULL
;
463 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
465 AddrFile
= Request
->Handle
.AddressHandle
;
467 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
469 if (AF_IS_VALID(AddrFile
)) {
470 SendRequest
= PoolAllocateBuffer(sizeof(DATAGRAM_SEND_REQUEST
));
472 /* Initialize a send request */
473 Status
= AddrGetAddress(ConnInfo
->RemoteAddress
,
474 &SendRequest
->RemoteAddress
, &SendRequest
->RemotePort
,
475 &AddrFile
->AddrCache
);
476 if (NT_SUCCESS(Status
)) {
477 SendRequest
->Buffer
= Buffer
;
478 SendRequest
->BufferSize
= DataSize
;
479 SendRequest
->Complete
= Request
->RequestNotifyObject
;
480 SendRequest
->Context
= Request
->RequestContext
;
481 SendRequest
->Build
= Build
;
483 if (AF_IS_BUSY(AddrFile
)) {
484 /* Queue send request on the transmit queue */
485 InsertTailList(&AddrFile
->TransmitQueue
, &SendRequest
->ListEntry
);
487 /* Reference address file and set pending send request flag */
488 AddrFile
->RefCount
++;
489 AF_SET_PENDING(AddrFile
, AFF_SEND
);
491 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
493 TI_DbgPrint(MAX_TRACE
, ("Leaving (queued).\n"));
495 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
497 /* Send the datagram */
498 DGSend(AddrFile
, SendRequest
);
500 TI_DbgPrint(MAX_TRACE
, ("Leaving (pending).\n"));
502 return STATUS_PENDING
;
505 Status
= STATUS_INSUFFICIENT_RESOURCES
;
507 Status
= STATUS_ADDRESS_CLOSED
;
509 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
511 TI_DbgPrint(MAX_TRACE
, ("Leaving. Status (0x%X)\n", Status
));
517 NTSTATUS
DGReceiveDatagram(
518 PTDI_REQUEST Request
,
519 PTDI_CONNECTION_INFORMATION ConnInfo
,
523 PTDI_CONNECTION_INFORMATION ReturnInfo
,
524 PULONG BytesReceived
)
526 * FUNCTION: Attempts to receive a datagram from a remote address
528 * Request = Pointer to TDI request
529 * ConnInfo = Pointer to connection information
530 * Buffer = Pointer to NDIS buffer chain to store received data
531 * ReceiveLength = Maximum size to use of buffer (0 if all can be used)
532 * ReceiveFlags = Receive flags (None, Normal, Peek)
533 * ReturnInfo = Pointer to structure for return information
534 * BytesReceive = Pointer to structure for number of bytes received
536 * Status of operation
538 * This is the high level interface for receiving datagrams
541 PADDRESS_FILE AddrFile
;
544 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest
;
546 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
548 AddrFile
= Request
->Handle
.AddressHandle
;
550 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
552 if (AF_IS_VALID(AddrFile
)) {
553 ReceiveRequest
= PoolAllocateBuffer(sizeof(DATAGRAM_RECEIVE_REQUEST
));
554 if (ReceiveRequest
) {
555 /* Initialize a receive request */
557 /* Extract the remote address filter from the request (if any) */
558 if (((ConnInfo
->RemoteAddressLength
!= 0)) && (ConnInfo
->RemoteAddress
)) {
559 Status
= AddrGetAddress(ConnInfo
->RemoteAddress
,
560 &ReceiveRequest
->RemoteAddress
,
561 &ReceiveRequest
->RemotePort
,
562 &AddrFile
->AddrCache
);
563 if (!NT_SUCCESS(Status
)) {
564 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
565 PoolFreeBuffer(ReceiveRequest
);
569 ReceiveRequest
->RemotePort
= 0;
570 ReceiveRequest
->RemoteAddress
= NULL
;
572 ReceiveRequest
->ReturnInfo
= ReturnInfo
;
573 ReceiveRequest
->Buffer
= Buffer
;
574 /* If ReceiveLength is 0, the whole buffer is available to us */
575 ReceiveRequest
->BufferSize
= (ReceiveLength
== 0) ?
576 MmGetMdlByteCount(Buffer
) : ReceiveLength
;
577 ReceiveRequest
->Complete
= Request
->RequestNotifyObject
;
578 ReceiveRequest
->Context
= Request
->RequestContext
;
580 /* Queue receive request */
581 InsertTailList(&AddrFile
->ReceiveQueue
, &ReceiveRequest
->ListEntry
);
583 /* Reference address file and set pending receive request flag */
584 AddrFile
->RefCount
++;
585 AF_SET_PENDING(AddrFile
, AFF_RECEIVE
);
587 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
589 TI_DbgPrint(MAX_TRACE
, ("Leaving (pending).\n"));
591 return STATUS_PENDING
;
593 Status
= STATUS_INSUFFICIENT_RESOURCES
;
595 Status
= STATUS_INVALID_ADDRESS
;
597 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
599 TI_DbgPrint(MAX_TRACE
, ("Leaving with errors (0x%X).\n", Status
));
608 * FUNCTION: Initializes the datagram subsystem
610 * Status of operation
613 InitializeListHead(&DGPendingListHead
);
615 KeInitializeSpinLock(&DGPendingListLock
);
617 ExInitializeWorkItem(&DGWorkItem
, DatagramWorker
, NULL
);
619 return STATUS_SUCCESS
;
626 * FUNCTION: Shuts down the datagram subsystem
628 * Status of operation
631 return STATUS_SUCCESS
;