a30fa0d555d5fac187f50c9d5566391217da171e
[reactos.git] / reactos / drivers / net / tcpip / transport / udp / udp.c
1 /*
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)
7 * REVISIONS:
8 * CSH 01/08-2000 Created
9 */
10 #include <tcpip.h>
11 #include <udp.h>
12 #include <routines.h>
13 #include <transmit.h>
14 #include <datagram.h>
15 #include <checksum.h>
16 #include <address.h>
17 #include <pool.h>
18
19
20 BOOLEAN UDPInitialized = FALSE;
21
22
23 NTSTATUS AddUDPHeaderIPv4(
24 PDATAGRAM_SEND_REQUEST SendRequest,
25 PIP_ADDRESS LocalAddress,
26 USHORT LocalPort,
27 PIP_PACKET IPPacket)
28 /*
29 * FUNCTION: Adds an IPv4 and UDP header to an IP packet
30 * ARGUMENTS:
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
35 * RETURNS:
36 * Status of operation
37 */
38 {
39 PIPv4_HEADER IPHeader;
40 PUDP_HEADER UDPHeader;
41 PVOID Header;
42 ULONG BufferSize;
43 NDIS_STATUS NdisStatus;
44 PNDIS_BUFFER HeaderBuffer;
45
46 BufferSize = MaxLLHeaderSize + sizeof(IPv4_HEADER) + sizeof(UDP_HEADER);
47 Header = PoolAllocateBuffer(BufferSize);
48
49 if (!Header) {
50 TI_DbgPrint(MIN_TRACE, ("Cannot allocate memory for packet headers.\n"));
51 return STATUS_INSUFFICIENT_RESOURCES;
52 }
53
54 /* Allocate NDIS buffer for maximum Link level, IP and UDP header */
55 NdisAllocateBuffer(&NdisStatus,
56 &HeaderBuffer,
57 GlobalBufferPool,
58 Header,
59 BufferSize);
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;
64 }
65
66 /* Chain header at front of NDIS packet */
67 NdisChainBufferAtFront(IPPacket->NdisPacket, HeaderBuffer);
68
69 IPPacket->Header = (PVOID)((ULONG_PTR)Header + MaxLLHeaderSize);
70 IPPacket->HeaderSize = 20;
71
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 */
77 IPHeader->Tos = 0;
78 /* Length of header and data */
79 IPHeader->TotalLength = WH2N((USHORT)IPPacket->TotalSize);
80 /* Identification */
81 IPHeader->Id = 0;
82 /* One fragment at offset 0 */
83 IPHeader->FlagsFragOfs = 0;
84 /* Time-to-Live is 128 */
85 IPHeader->Ttl = 128;
86 /* User Datagram Protocol */
87 IPHeader->Protocol = IPPROTO_UDP;
88 /* Checksum is 0 (for later calculation of this) */
89 IPHeader->Checksum = 0;
90 /* Source address */
91 IPHeader->SrcAddr = LocalAddress->Address.IPv4Address;
92 /* Destination address. FIXME: IPv4 only */
93 IPHeader->DstAddr = SendRequest->RemoteAddress->Address.IPv4Address;
94
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);
104
105 return STATUS_SUCCESS;
106 }
107
108
109 NTSTATUS BuildUDPPacket(
110 PVOID Context,
111 PIP_ADDRESS LocalAddress,
112 USHORT LocalPort,
113 PIP_PACKET *IPPacket)
114 /*
115 * FUNCTION: Builds an UDP packet
116 * ARGUMENTS:
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
121 * RETURNS:
122 * Status of operation
123 */
124 {
125 NTSTATUS Status;
126 PIP_PACKET Packet;
127 NDIS_STATUS NdisStatus;
128 PDATAGRAM_SEND_REQUEST SendRequest = (PDATAGRAM_SEND_REQUEST)Context;
129
130 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
131
132 /* Prepare packet */
133 Packet = PoolAllocateBuffer(sizeof(IP_PACKET));
134 if (!Packet) {
135 TI_DbgPrint(MIN_TRACE, ("Cannot allocate memory for packet.\n"));
136 return STATUS_INSUFFICIENT_RESOURCES;
137 }
138
139 RtlZeroMemory(Packet, sizeof(IP_PACKET));
140 Packet->RefCount = 1;
141 Packet->TotalSize = sizeof(IPv4_HEADER) +
142 sizeof(UDP_HEADER) +
143 SendRequest->BufferSize;
144
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;
151 }
152
153 switch (SendRequest->RemoteAddress->Type) {
154 case IP_ADDRESS_V4:
155 Status = AddUDPHeaderIPv4(SendRequest, LocalAddress, LocalPort, Packet);
156 break;
157 case IP_ADDRESS_V6:
158 /* FIXME: Support IPv6 */
159 TI_DbgPrint(MIN_TRACE, ("IPv6 UDP datagrams are not supported.\n"));
160 default:
161 Status = STATUS_UNSUCCESSFUL;
162 break;
163 }
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);
168 return Status;
169 }
170
171 /* Chain data after header */
172 NdisChainBufferAtBack(Packet->NdisPacket, SendRequest->Buffer);
173
174 *IPPacket = Packet;
175
176 return STATUS_SUCCESS;
177 }
178
179
180 VOID DeliverUDPData(
181 PADDRESS_FILE AddrFile,
182 PIP_ADDRESS Address,
183 PIP_PACKET IPPacket,
184 UINT DataSize)
185 /*
186 * FUNCTION: Delivers UDP data to a user
187 * ARGUMENTS:
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
192 * NOTES:
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.
197 */
198 {
199 KIRQL OldIrql;
200
201 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
202
203 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
204
205 if (!IsListEmpty(&AddrFile->ReceiveQueue)) {
206 PLIST_ENTRY CurrentEntry;
207 PDATAGRAM_RECEIVE_REQUEST Current;
208 BOOLEAN Found;
209
210 /* Search receive request list to find a match */
211 Found = FALSE;
212 CurrentEntry = AddrFile->ReceiveQueue.Flink;
213 while ((CurrentEntry != &AddrFile->ReceiveQueue) && (!Found)) {
214 Current = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
215 if (!Current->RemoteAddress)
216 Found = TRUE;
217 else if (AddrIsEqual(Address, Current->RemoteAddress))
218 Found = TRUE;
219
220 if (Found) {
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. */
224
225 /* Remove the request from the queue */
226 RemoveEntryList(&Current->ListEntry);
227 AddrFile->RefCount--;
228 break;
229 }
230 CurrentEntry = CurrentEntry->Flink;
231 }
232
233 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
234
235 if (Found) {
236 /* Copy the data into buffer provided by the user */
237 CopyBufferToBufferChain(Current->Buffer,
238 0,
239 IPPacket->Data,
240 DataSize);
241
242 /* Complete the receive request */
243 (*Current->Complete)(Current->Context, STATUS_SUCCESS, DataSize);
244
245 /* Finally free the receive request */
246 if (Current->RemoteAddress)
247 PoolFreeBuffer(Current->RemoteAddress);
248 PoolFreeBuffer(Current);
249 }
250 } else {
251 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
252
253 /* FIXME: Call event handler */
254 TI_DbgPrint(MAX_TRACE, ("Calling receive event handler.\n"));
255 }
256
257 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
258 }
259
260
261 NTSTATUS UDPSendDatagram(
262 PTDI_REQUEST Request,
263 PTDI_CONNECTION_INFORMATION ConnInfo,
264 PNDIS_BUFFER Buffer,
265 ULONG DataSize)
266 /*
267 * FUNCTION: Sends an UDP datagram to a remote address
268 * ARGUMENTS:
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
273 * RETURNS:
274 * Status of operation
275 */
276 {
277 return DGSendDatagram(Request,
278 ConnInfo,
279 Buffer,
280 DataSize,
281 BuildUDPPacket);
282 }
283
284
285 NTSTATUS UDPReceiveDatagram(
286 PTDI_REQUEST Request,
287 PTDI_CONNECTION_INFORMATION ConnInfo,
288 PNDIS_BUFFER Buffer,
289 ULONG ReceiveLength,
290 ULONG ReceiveFlags,
291 PTDI_CONNECTION_INFORMATION ReturnInfo,
292 PULONG BytesReceived)
293 /*
294 * FUNCTION: Attempts to receive an UDP datagram from a remote address
295 * ARGUMENTS:
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
303 * RETURNS:
304 * Status of operation
305 * NOTES:
306 * This is the high level interface for receiving UDP datagrams
307 */
308 {
309 return DGReceiveDatagram(Request,
310 ConnInfo,
311 Buffer,
312 ReceiveLength,
313 ReceiveFlags,
314 ReturnInfo,
315 BytesReceived);
316 }
317
318
319 VOID UDPReceive(
320 PNET_TABLE_ENTRY NTE,
321 PIP_PACKET IPPacket)
322 /*
323 * FUNCTION: Receives and queues a UDP datagram
324 * ARGUMENTS:
325 * NTE = Pointer to net table entry which the packet was received on
326 * IPPacket = Pointer to an IP packet that was received
327 * NOTES:
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
330 */
331 {
332 AF_SEARCH SearchContext;
333 PIPv4_HEADER IPv4Header;
334 PADDRESS_FILE AddrFile;
335 PUDP_HEADER UDPHeader;
336 PIP_ADDRESS DstAddress;
337 UINT DataSize, i;
338
339 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
340
341 switch (IPPacket->Type) {
342 /* IPv4 packet */
343 case IP_ADDRESS_V4:
344 IPv4Header = IPPacket->Header;
345 DstAddress = &IPPacket->DstAddr;
346 break;
347
348 /* IPv6 packet */
349 case IP_ADDRESS_V6:
350 TI_DbgPrint(MIN_TRACE, ("Discarded IPv6 UDP datagram (%i bytes).\n", IPPacket->TotalSize));
351
352 /* FIXME: IPv6 is not supported */
353 return;
354
355 default:
356 return;
357 }
358
359 UDPHeader = (PUDP_HEADER)IPPacket->Data;
360
361 /* FIXME: Calculate and validate UDP checksum */
362
363 /* Sanity checks */
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"));
368 return;
369 }
370
371 DataSize = i - sizeof(UDP_HEADER);
372
373 /* Go to UDP data area */
374 (ULONG_PTR)IPPacket->Data += sizeof(UDP_HEADER);
375
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 */
380
381 AddrFile = AddrSearchFirst(DstAddress,
382 UDPHeader->DestPort,
383 IPPROTO_UDP,
384 &SearchContext);
385 if (AddrFile) {
386 do {
387 DeliverUDPData(AddrFile,
388 DstAddress,
389 IPPacket,
390 DataSize);
391 } while ((AddrFile = AddrSearchNext(&SearchContext)) != NULL);
392 } else {
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)));
397
398 /* FIXME: Send ICMP reply */
399 }
400 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
401 }
402
403
404 NTSTATUS UDPStartup(
405 VOID)
406 /*
407 * FUNCTION: Initializes the UDP subsystem
408 * RETURNS:
409 * Status of operation
410 */
411 {
412 RtlZeroMemory(&UDPStats, sizeof(UDP_STATISTICS));
413
414 /* Register this protocol with IP layer */
415 IPRegisterProtocol(IPPROTO_UDP, UDPReceive);
416
417 UDPInitialized = TRUE;
418
419 return STATUS_SUCCESS;
420 }
421
422
423 NTSTATUS UDPShutdown(
424 VOID)
425 /*
426 * FUNCTION: Shuts down the UDP subsystem
427 * RETURNS:
428 * Status of operation
429 */
430 {
431 if (!UDPInitialized)
432 return STATUS_SUCCESS;
433
434 /* Deregister this protocol with IP layer */
435 IPRegisterProtocol(IPPROTO_UDP, NULL);
436
437 return STATUS_SUCCESS;
438 }
439
440 /* EOF */