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 /* Allocate NDIS buffer for maximum Link level, IP and UDP header */
55 NdisAllocateBuffer(&NdisStatus
,
60 if (NdisStatus
!= NDIS_STATUS_SUCCESS
) {
61 TI_DbgPrint(MIN_TRACE
, ("Cannot allocate NDIS buffer for packet headers. NdisStatus = (0x%X)\n", NdisStatus
));
62 PoolFreeBuffer(Header
);
63 return STATUS_INSUFFICIENT_RESOURCES
;
66 /* Chain header at front of NDIS packet */
67 NdisChainBufferAtFront(IPPacket
->NdisPacket
, HeaderBuffer
);
69 IPPacket
->Header
= (PVOID
)((ULONG_PTR
)Header
+ MaxLLHeaderSize
);
70 IPPacket
->HeaderSize
= 20;
72 /* Build IPv4 header */
73 IPHeader
= (PIPv4_HEADER
)IPPacket
->Header
;
74 /* Version = 4, Length = 5 DWORDs */
75 IPHeader
->VerIHL
= 0x45;
76 /* Normal Type-of-Service */
78 /* Length of header and data */
79 IPHeader
->TotalLength
= WH2N((USHORT
)IPPacket
->TotalSize
);
82 /* One fragment at offset 0 */
83 IPHeader
->FlagsFragOfs
= 0;
84 /* Time-to-Live is 128 */
86 /* User Datagram Protocol */
87 IPHeader
->Protocol
= IPPROTO_UDP
;
88 /* Checksum is 0 (for later calculation of this) */
89 IPHeader
->Checksum
= 0;
91 IPHeader
->SrcAddr
= LocalAddress
->Address
.IPv4Address
;
92 /* Destination address. FIXME: IPv4 only */
93 IPHeader
->DstAddr
= SendRequest
->RemoteAddress
->Address
.IPv4Address
;
95 /* Build UDP header */
96 UDPHeader
= (PUDP_HEADER
)((ULONG_PTR
)IPHeader
+ sizeof(IPv4_HEADER
));
97 /* Port values are already big-endian values */
98 UDPHeader
->SourcePort
= LocalPort
;
99 UDPHeader
->DestPort
= SendRequest
->RemotePort
;
100 /* FIXME: Calculate UDP checksum and put it in UDP header */
101 UDPHeader
->Checksum
= 0;
102 /* Length of UDP header and data */
103 UDPHeader
->Length
= WH2N((USHORT
)IPPacket
->TotalSize
- IPPacket
->HeaderSize
);
105 return STATUS_SUCCESS
;
109 NTSTATUS
BuildUDPPacket(
111 PIP_ADDRESS LocalAddress
,
113 PIP_PACKET
*IPPacket
)
115 * FUNCTION: Builds an UDP packet
117 * Context = Pointer to context information (DATAGRAM_SEND_REQUEST)
118 * LocalAddress = Pointer to our local address
119 * LocalPort = The port we send this datagram from
120 * IPPacket = Address of pointer to IP packet
122 * Status of operation
127 NDIS_STATUS NdisStatus
;
128 PDATAGRAM_SEND_REQUEST SendRequest
= (PDATAGRAM_SEND_REQUEST
)Context
;
130 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
133 Packet
= PoolAllocateBuffer(sizeof(IP_PACKET
));
135 TI_DbgPrint(MIN_TRACE
, ("Cannot allocate memory for packet.\n"));
136 return STATUS_INSUFFICIENT_RESOURCES
;
139 RtlZeroMemory(Packet
, sizeof(IP_PACKET
));
140 Packet
->RefCount
= 1;
141 Packet
->TotalSize
= sizeof(IPv4_HEADER
) +
143 SendRequest
->BufferSize
;
145 /* Allocate NDIS packet */
146 NdisAllocatePacket(&NdisStatus
, &Packet
->NdisPacket
, GlobalPacketPool
);
147 if (NdisStatus
!= NDIS_STATUS_SUCCESS
) {
148 TI_DbgPrint(MIN_TRACE
, ("Cannot allocate NDIS packet. NdisStatus = (0x%X)\n", NdisStatus
));
149 PoolFreeBuffer(Packet
);
150 return STATUS_INSUFFICIENT_RESOURCES
;
153 switch (SendRequest
->RemoteAddress
->Type
) {
155 Status
= AddUDPHeaderIPv4(SendRequest
, LocalAddress
, LocalPort
, Packet
);
158 /* FIXME: Support IPv6 */
159 TI_DbgPrint(MIN_TRACE
, ("IPv6 UDP datagrams are not supported.\n"));
161 Status
= STATUS_UNSUCCESSFUL
;
164 if (!NT_SUCCESS(Status
)) {
165 TI_DbgPrint(MIN_TRACE
, ("Cannot add UDP header. Status = (0x%X)\n", Status
));
166 NdisFreePacket(Packet
->NdisPacket
);
167 PoolFreeBuffer(Packet
);
171 /* Chain data after header */
172 NdisChainBufferAtBack(Packet
->NdisPacket
, SendRequest
->Buffer
);
176 return STATUS_SUCCESS
;
181 PADDRESS_FILE AddrFile
,
186 * FUNCTION: Delivers UDP data to a user
188 * AddrFile = Address file to deliver data to
189 * Address = Remote address the packet came from
190 * IPPacket = Pointer to IP packet to deliver
191 * DataSize = Number of bytes in data area
193 * If there is a receive request, then we copy the data to the
194 * buffer supplied by the user and complete the receive request.
195 * If no suitable receive request exists, then we call the event
196 * handler if it exists, otherwise we drop the packet.
201 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
203 KeAcquireSpinLock(&AddrFile
->Lock
, &OldIrql
);
205 if (!IsListEmpty(&AddrFile
->ReceiveQueue
)) {
206 PLIST_ENTRY CurrentEntry
;
207 PDATAGRAM_RECEIVE_REQUEST Current
;
210 /* Search receive request list to find a match */
212 CurrentEntry
= AddrFile
->ReceiveQueue
.Flink
;
213 while ((CurrentEntry
!= &AddrFile
->ReceiveQueue
) && (!Found
)) {
214 Current
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_RECEIVE_REQUEST
, ListEntry
);
215 if (!Current
->RemoteAddress
)
217 else if (AddrIsEqual(Address
, Current
->RemoteAddress
))
221 /* FIXME: Maybe we should check if the buffer of this
222 receive request is large enough and if not, search
223 for another. Also a 'best fit' strategy could be used. */
225 /* Remove the request from the queue */
226 RemoveEntryList(&Current
->ListEntry
);
227 AddrFile
->RefCount
--;
230 CurrentEntry
= CurrentEntry
->Flink
;
233 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
236 /* Copy the data into buffer provided by the user */
237 CopyBufferToBufferChain(Current
->Buffer
,
242 /* Complete the receive request */
243 (*Current
->Complete
)(Current
->Context
, STATUS_SUCCESS
, DataSize
);
245 /* Finally free the receive request */
246 if (Current
->RemoteAddress
)
247 PoolFreeBuffer(Current
->RemoteAddress
);
248 PoolFreeBuffer(Current
);
251 KeReleaseSpinLock(&AddrFile
->Lock
, OldIrql
);
253 /* FIXME: Call event handler */
254 TI_DbgPrint(MAX_TRACE
, ("Calling receive event handler.\n"));
257 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
261 NTSTATUS
UDPSendDatagram(
262 PTDI_REQUEST Request
,
263 PTDI_CONNECTION_INFORMATION ConnInfo
,
267 * FUNCTION: Sends an UDP datagram to a remote address
269 * Request = Pointer to TDI request
270 * ConnInfo = Pointer to connection information
271 * Buffer = Pointer to NDIS buffer with data
272 * DataSize = Size in bytes of data to be sent
274 * Status of operation
277 return DGSendDatagram(Request
,
285 NTSTATUS
UDPReceiveDatagram(
286 PTDI_REQUEST Request
,
287 PTDI_CONNECTION_INFORMATION ConnInfo
,
291 PTDI_CONNECTION_INFORMATION ReturnInfo
,
292 PULONG BytesReceived
)
294 * FUNCTION: Attempts to receive an UDP datagram from a remote address
296 * Request = Pointer to TDI request
297 * ConnInfo = Pointer to connection information
298 * Buffer = Pointer to NDIS buffer chain to store received data
299 * ReceiveLength = Maximum size to use of buffer, 0 if all can be used
300 * ReceiveFlags = Receive flags (None, Normal, Peek)
301 * ReturnInfo = Pointer to structure for return information
302 * BytesReceive = Pointer to structure for number of bytes received
304 * Status of operation
306 * This is the high level interface for receiving UDP datagrams
309 return DGReceiveDatagram(Request
,
320 PNET_TABLE_ENTRY NTE
,
323 * FUNCTION: Receives and queues a UDP datagram
325 * NTE = Pointer to net table entry which the packet was received on
326 * IPPacket = Pointer to an IP packet that was received
328 * This is the low level interface for receiving UDP datagrams. It strips
329 * the UDP header from a packet and delivers the data to anyone that wants it
332 AF_SEARCH SearchContext
;
333 PIPv4_HEADER IPv4Header
;
334 PADDRESS_FILE AddrFile
;
335 PUDP_HEADER UDPHeader
;
336 PIP_ADDRESS DstAddress
;
339 TI_DbgPrint(MAX_TRACE
, ("Called.\n"));
341 switch (IPPacket
->Type
) {
344 IPv4Header
= IPPacket
->Header
;
345 DstAddress
= &IPPacket
->DstAddr
;
350 TI_DbgPrint(MIN_TRACE
, ("Discarded IPv6 UDP datagram (%i bytes).\n", IPPacket
->TotalSize
));
352 /* FIXME: IPv6 is not supported */
359 UDPHeader
= (PUDP_HEADER
)IPPacket
->Data
;
361 /* FIXME: Calculate and validate UDP checksum */
364 i
= WH2N(UDPHeader
->Length
);
365 if ((i
< sizeof(UDP_HEADER
)) || (i
> IPPacket
->TotalSize
- IPPacket
->Position
)) {
366 /* Incorrect or damaged packet received, discard it */
367 TI_DbgPrint(MIN_TRACE
, ("Incorrect or damaged UDP packet received.\n"));
371 DataSize
= i
- sizeof(UDP_HEADER
);
373 /* Go to UDP data area */
374 (ULONG_PTR
)IPPacket
->Data
+= sizeof(UDP_HEADER
);
376 /* Locate a receive request on destination address file object
377 and deliver the packet if one is found. If there is no receive
378 request on the address file object, call the associated receive
379 handler. If no receive handler is registered, drop the packet */
381 AddrFile
= AddrSearchFirst(DstAddress
,
387 DeliverUDPData(AddrFile
,
391 } while ((AddrFile
= AddrSearchNext(&SearchContext
)) != NULL
);
393 /* There are no open address files that will take this datagram */
394 /* FIXME: IPv4 only */
395 TI_DbgPrint(MID_TRACE
, ("Cannot deliver IPv4 UDP datagram to address (0x%X).\n",
396 DN2H(DstAddress
->Address
.IPv4Address
)));
398 /* FIXME: Send ICMP reply */
400 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
407 * FUNCTION: Initializes the UDP subsystem
409 * Status of operation
412 RtlZeroMemory(&UDPStats
, sizeof(UDP_STATISTICS
));
414 /* Register this protocol with IP layer */
415 IPRegisterProtocol(IPPROTO_UDP
, UDPReceive
);
417 UDPInitialized
= TRUE
;
419 return STATUS_SUCCESS
;
423 NTSTATUS
UDPShutdown(
426 * FUNCTION: Shuts down the UDP subsystem
428 * Status of operation
432 return STATUS_SUCCESS
;
434 /* Deregister this protocol with IP layer */
435 IPRegisterProtocol(IPPROTO_UDP
, NULL
);
437 return STATUS_SUCCESS
;