2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: transport/udp/udp.c
5 * PURPOSE: User Datagram Protocol routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
8 * CSH 01/08-2000 Created
20 BOOLEAN UDPInitialized
= FALSE
;
23 NTSTATUS
AddUDPHeaderIPv4(
24 PDATAGRAM_SEND_REQUEST SendRequest
,
25 PIP_ADDRESS LocalAddress
,
29 * FUNCTION: Adds an IPv4 and UDP header to an IP packet
31 * SendRequest = Pointer to send request
32 * LocalAddress = Pointer to our local address
33 * LocalPort = The port we send this datagram from
34 * IPPacket = Pointer to IP packet
39 PIPv4_HEADER IPHeader
;
40 PUDP_HEADER UDPHeader
;
43 NDIS_STATUS NdisStatus
;
44 PNDIS_BUFFER HeaderBuffer
;
46 BufferSize
= MaxLLHeaderSize
+ sizeof(IPv4_HEADER
) + sizeof(UDP_HEADER
);
47 Header
= PoolAllocateBuffer(BufferSize
);
50 TI_DbgPrint(MIN_TRACE
, ("Cannot allocate memory for packet headers.\n"));
51 return STATUS_INSUFFICIENT_RESOURCES
;
54 TI_DbgPrint(MAX_TRACE
, ("Allocated %d bytes for headers at 0x%X.\n", BufferSize
, Header
));
56 /* Allocate NDIS buffer for maximum Link level, IP and UDP header */
57 NdisAllocateBuffer(&NdisStatus
,
62 if (NdisStatus
!= NDIS_STATUS_SUCCESS
) {
63 TI_DbgPrint(MIN_TRACE
, ("Cannot allocate NDIS buffer for packet headers. NdisStatus = (0x%X)\n", NdisStatus
));
64 PoolFreeBuffer(Header
);
65 return STATUS_INSUFFICIENT_RESOURCES
;
68 /* Chain header at front of NDIS packet */
69 NdisChainBufferAtFront(IPPacket
->NdisPacket
, HeaderBuffer
);
71 IPPacket
->Header
= (PVOID
)((ULONG_PTR
)Header
+ MaxLLHeaderSize
);
72 IPPacket
->HeaderSize
= 20;
74 /* Build IPv4 header */
75 IPHeader
= (PIPv4_HEADER
)IPPacket
->Header
;
76 /* Version = 4, Length = 5 DWORDs */
77 IPHeader
->VerIHL
= 0x45;
78 /* Normal Type-of-Service */
80 /* Length of header and data */
81 IPHeader
->TotalLength
= WH2N((USHORT
)IPPacket
->TotalSize
);
84 /* One fragment at offset 0 */
85 IPHeader
->FlagsFragOfs
= 0;
86 /* Time-to-Live is 128 */
88 /* User Datagram Protocol */
89 IPHeader
->Protocol
= IPPROTO_UDP
;
90 /* Checksum is 0 (for later calculation of this) */
91 IPHeader
->Checksum
= 0;
93 IPHeader
->SrcAddr
= LocalAddress
->Address
.IPv4Address
;
94 /* Destination address. FIXME: IPv4 only */
95 IPHeader
->DstAddr
= SendRequest
->RemoteAddress
->Address
.IPv4Address
;
97 /* Build UDP header */
98 UDPHeader
= (PUDP_HEADER
)((ULONG_PTR
)IPHeader
+ sizeof(IPv4_HEADER
));
99 /* Port values are already big-endian values */
100 UDPHeader
->SourcePort
= LocalPort
;
101 UDPHeader
->DestPort
= SendRequest
->RemotePort
;
102 /* FIXME: Calculate UDP checksum and put it in UDP header */
103 UDPHeader
->Checksum
= 0;
104 /* Length of UDP header and data */
105 UDPHeader
->Length
= WH2N((USHORT
)IPPacket
->TotalSize
- IPPacket
->HeaderSize
);
107 return STATUS_SUCCESS
;
111 NTSTATUS
BuildUDPPacket(
113 PIP_ADDRESS LocalAddress
,
115 PIP_PACKET
*IPPacket
)
117 * FUNCTION: Builds an UDP packet
119 * Context = Pointer to context information (DATAGRAM_SEND_REQUEST)
120 * LocalAddress = Pointer to our local address
121 * LocalPort = The port we send this datagram from
122 * IPPacket = Address of pointer to IP packet
124 * Status of operation
129 NDIS_STATUS NdisStatus
;
130 PDATAGRAM_SEND_REQUEST SendRequest
= (PDATAGRAM_SEND_REQUEST
)Context
;
132 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
135 Packet
= PoolAllocateBuffer(sizeof(IP_PACKET
));
137 TI_DbgPrint(MIN_TRACE
, ("Cannot allocate memory for packet.\n"));
138 return STATUS_INSUFFICIENT_RESOURCES
;
141 RtlZeroMemory(Packet
, sizeof(IP_PACKET
));
142 Packet
->RefCount
= 1;
143 Packet
->TotalSize
= sizeof(IPv4_HEADER
) +
145 SendRequest
->BufferSize
;
147 /* Allocate NDIS packet */
148 NdisAllocatePacket(&NdisStatus
, &Packet
->NdisPacket
, GlobalPacketPool
);
149 if (NdisStatus
!= NDIS_STATUS_SUCCESS
) {
150 TI_DbgPrint(MIN_TRACE
, ("Cannot allocate NDIS packet. NdisStatus = (0x%X)\n", NdisStatus
));
151 PoolFreeBuffer(Packet
);
152 return STATUS_INSUFFICIENT_RESOURCES
;
155 switch (SendRequest
->RemoteAddress
->Type
) {
157 Status
= AddUDPHeaderIPv4(SendRequest
, LocalAddress
, LocalPort
, Packet
);
160 /* FIXME: Support IPv6 */
161 TI_DbgPrint(MIN_TRACE
, ("IPv6 UDP datagrams are not supported.\n"));
163 Status
= STATUS_UNSUCCESSFUL
;
166 if (!NT_SUCCESS(Status
)) {
167 TI_DbgPrint(MIN_TRACE
, ("Cannot add UDP header. Status = (0x%X)\n", Status
));
168 NdisFreePacket(Packet
->NdisPacket
);
169 PoolFreeBuffer(Packet
);
173 /* Chain data after header */
174 NdisChainBufferAtBack(Packet
->NdisPacket
, SendRequest
->Buffer
);
176 DISPLAY_IP_PACKET(Packet
);
180 return STATUS_SUCCESS
;
185 PADDRESS_FILE AddrFile
,
190 * FUNCTION: Delivers UDP data to a user
192 * AddrFile = Address file to deliver data to
193 * Address = Remote address the packet came from
194 * IPPacket = Pointer to IP packet to deliver
195 * DataSize = Number of bytes in data area
197 * If there is a receive request, then we copy the data to the
198 * buffer supplied by the user and complete the receive request.
199 * If no suitable receive request exists, then we call the event
200 * handler if it exists, otherwise we drop the packet.
204 PTDI_IND_RECEIVE_DATAGRAM ReceiveHandler
;
205 PVOID HandlerContext
;
211 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
213 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
215 if (!IsListEmpty(&AddrFile
->ReceiveQueue
)) {
216 PLIST_ENTRY CurrentEntry
;
217 PDATAGRAM_RECEIVE_REQUEST Current
;
220 /* Search receive request list to find a match */
222 CurrentEntry
= AddrFile
->ReceiveQueue
.Flink
;
223 while ((CurrentEntry
!= &AddrFile
->ReceiveQueue
) && (!Found
)) {
224 Current
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_RECEIVE_REQUEST
, ListEntry
);
225 if (!Current
->RemoteAddress
)
227 else if (AddrIsEqual(Address
, Current
->RemoteAddress
))
231 /* FIXME: Maybe we should check if the buffer of this
232 receive request is large enough and if not, search
233 for another. Also a 'best fit' strategy could be used. */
235 /* Remove the request from the queue */
236 RemoveEntryList(&Current
->ListEntry
);
237 AddrFile
->RefCount
--;
240 CurrentEntry
= CurrentEntry
->Flink
;
243 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
246 /* Copy the data into buffer provided by the user */
247 CopyBufferToBufferChain(Current
->Buffer
,
252 /* Complete the receive request */
253 (*Current
->Complete
)(Current
->Context
, STATUS_SUCCESS
, DataSize
);
255 /* Finally free the receive request */
256 if (Current
->RemoteAddress
)
257 PoolFreeBuffer(Current
->RemoteAddress
);
258 PoolFreeBuffer(Current
);
260 } else if (AddrFile
->RegisteredReceiveDatagramHandler
) {
261 TI_DbgPrint(MAX_TRACE
, ("Calling receive event handler.\n"));
263 ReceiveHandler
= AddrFile
->ReceiveDatagramHandler
;
264 HandlerContext
= AddrFile
->ReceiveDatagramHandlerContext
;
266 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
268 if (Address
->Type
== IP_ADDRESS_V4
) {
269 AddressLength
= sizeof(IPv4_RAW_ADDRESS
);
270 SourceAddress
= &Address
->Address
.IPv4Address
;
271 } else /* (Address->Type == IP_ADDRESS_V6) */ {
272 AddressLength
= sizeof(IPv6_RAW_ADDRESS
);
273 SourceAddress
= Address
->Address
.IPv6Address
;
276 Status
= (*ReceiveHandler
)(HandlerContext
,
281 TDI_RECEIVE_ENTIRE_MESSAGE
,
289 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
293 NTSTATUS
UDPSendDatagram(
294 PTDI_REQUEST Request
,
295 PTDI_CONNECTION_INFORMATION ConnInfo
,
299 * FUNCTION: Sends an UDP datagram to a remote address
301 * Request = Pointer to TDI request
302 * ConnInfo = Pointer to connection information
303 * Buffer = Pointer to NDIS buffer with data
304 * DataSize = Size in bytes of data to be sent
306 * Status of operation
309 return DGSendDatagram(Request
,
317 NTSTATUS
UDPReceiveDatagram(
318 PTDI_REQUEST Request
,
319 PTDI_CONNECTION_INFORMATION ConnInfo
,
323 PTDI_CONNECTION_INFORMATION ReturnInfo
,
324 PULONG BytesReceived
)
326 * FUNCTION: Attempts to receive an UDP datagram from a remote address
328 * Request = Pointer to TDI request
329 * ConnInfo = Pointer to connection information
330 * Buffer = Pointer to NDIS buffer chain to store received data
331 * ReceiveLength = Maximum size to use of buffer, 0 if all can be used
332 * ReceiveFlags = Receive flags (None, Normal, Peek)
333 * ReturnInfo = Pointer to structure for return information
334 * BytesReceive = Pointer to structure for number of bytes received
336 * Status of operation
338 * This is the high level interface for receiving UDP datagrams
341 return DGReceiveDatagram(Request
,
352 PNET_TABLE_ENTRY NTE
,
355 * FUNCTION: Receives and queues a UDP datagram
357 * NTE = Pointer to net table entry which the packet was received on
358 * IPPacket = Pointer to an IP packet that was received
360 * This is the low level interface for receiving UDP datagrams. It strips
361 * the UDP header from a packet and delivers the data to anyone that wants it
364 AF_SEARCH SearchContext
;
365 PIPv4_HEADER IPv4Header
;
366 PADDRESS_FILE AddrFile
;
367 PUDP_HEADER UDPHeader
;
368 PIP_ADDRESS DstAddress
;
371 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
373 switch (IPPacket
->Type
) {
376 IPv4Header
= IPPacket
->Header
;
377 DstAddress
= &IPPacket
->DstAddr
;
382 TI_DbgPrint(MIN_TRACE
, ("Discarded IPv6 UDP datagram (%i bytes).\n", IPPacket
->TotalSize
));
384 /* FIXME: IPv6 is not supported */
391 UDPHeader
= (PUDP_HEADER
)IPPacket
->Data
;
393 /* FIXME: Calculate and validate UDP checksum */
396 i
= WH2N(UDPHeader
->Length
);
397 if ((i
< sizeof(UDP_HEADER
)) || (i
> IPPacket
->TotalSize
- IPPacket
->Position
)) {
398 /* Incorrect or damaged packet received, discard it */
399 TI_DbgPrint(MIN_TRACE
, ("Incorrect or damaged UDP packet received.\n"));
403 DataSize
= i
- sizeof(UDP_HEADER
);
405 /* Go to UDP data area */
406 (ULONG_PTR
)IPPacket
->Data
+= sizeof(UDP_HEADER
);
408 /* Locate a receive request on destination address file object
409 and deliver the packet if one is found. If there is no receive
410 request on the address file object, call the associated receive
411 handler. If no receive handler is registered, drop the packet */
413 AddrFile
= AddrSearchFirst(DstAddress
,
419 DeliverUDPData(AddrFile
,
423 } while ((AddrFile
= AddrSearchNext(&SearchContext
)) != NULL
);
425 /* There are no open address files that will take this datagram */
426 /* FIXME: IPv4 only */
427 TI_DbgPrint(MID_TRACE
, ("Cannot deliver IPv4 UDP datagram to address (0x%X).\n",
428 DN2H(DstAddress
->Address
.IPv4Address
)));
430 /* FIXME: Send ICMP reply */
432 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
439 * FUNCTION: Initializes the UDP subsystem
441 * Status of operation
444 RtlZeroMemory(&UDPStats
, sizeof(UDP_STATISTICS
));
446 /* Register this protocol with IP layer */
447 IPRegisterProtocol(IPPROTO_UDP
, UDPReceive
);
449 UDPInitialized
= TRUE
;
451 return STATUS_SUCCESS
;
455 NTSTATUS
UDPShutdown(
458 * FUNCTION: Shuts down the UDP subsystem
460 * Status of operation
464 return STATUS_SUCCESS
;
466 /* Deregister this protocol with IP layer */
467 IPRegisterProtocol(IPPROTO_UDP
, NULL
);
469 return STATUS_SUCCESS
;