no message
[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 TI_DbgPrint(MAX_TRACE, ("Allocated %d bytes for headers at 0x%X.\n", BufferSize, Header));
55
56 /* Allocate NDIS buffer for maximum Link level, IP and UDP header */
57 NdisAllocateBuffer(&NdisStatus,
58 &HeaderBuffer,
59 GlobalBufferPool,
60 Header,
61 BufferSize);
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;
66 }
67
68 /* Chain header at front of NDIS packet */
69 NdisChainBufferAtFront(IPPacket->NdisPacket, HeaderBuffer);
70
71 IPPacket->Header = (PVOID)((ULONG_PTR)Header + MaxLLHeaderSize);
72 IPPacket->HeaderSize = 20;
73
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 */
79 IPHeader->Tos = 0;
80 /* Length of header and data */
81 IPHeader->TotalLength = WH2N((USHORT)IPPacket->TotalSize);
82 /* Identification */
83 IPHeader->Id = 0;
84 /* One fragment at offset 0 */
85 IPHeader->FlagsFragOfs = 0;
86 /* Time-to-Live is 128 */
87 IPHeader->Ttl = 128;
88 /* User Datagram Protocol */
89 IPHeader->Protocol = IPPROTO_UDP;
90 /* Checksum is 0 (for later calculation of this) */
91 IPHeader->Checksum = 0;
92 /* Source address */
93 IPHeader->SrcAddr = LocalAddress->Address.IPv4Address;
94 /* Destination address. FIXME: IPv4 only */
95 IPHeader->DstAddr = SendRequest->RemoteAddress->Address.IPv4Address;
96
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);
106
107 return STATUS_SUCCESS;
108 }
109
110
111 NTSTATUS BuildUDPPacket(
112 PVOID Context,
113 PIP_ADDRESS LocalAddress,
114 USHORT LocalPort,
115 PIP_PACKET *IPPacket)
116 /*
117 * FUNCTION: Builds an UDP packet
118 * ARGUMENTS:
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
123 * RETURNS:
124 * Status of operation
125 */
126 {
127 NTSTATUS Status;
128 PIP_PACKET Packet;
129 NDIS_STATUS NdisStatus;
130 PDATAGRAM_SEND_REQUEST SendRequest = (PDATAGRAM_SEND_REQUEST)Context;
131
132 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
133
134 /* Prepare packet */
135 Packet = PoolAllocateBuffer(sizeof(IP_PACKET));
136 if (!Packet) {
137 TI_DbgPrint(MIN_TRACE, ("Cannot allocate memory for packet.\n"));
138 return STATUS_INSUFFICIENT_RESOURCES;
139 }
140
141 RtlZeroMemory(Packet, sizeof(IP_PACKET));
142 Packet->RefCount = 1;
143 Packet->TotalSize = sizeof(IPv4_HEADER) +
144 sizeof(UDP_HEADER) +
145 SendRequest->BufferSize;
146
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;
153 }
154
155 switch (SendRequest->RemoteAddress->Type) {
156 case IP_ADDRESS_V4:
157 Status = AddUDPHeaderIPv4(SendRequest, LocalAddress, LocalPort, Packet);
158 break;
159 case IP_ADDRESS_V6:
160 /* FIXME: Support IPv6 */
161 TI_DbgPrint(MIN_TRACE, ("IPv6 UDP datagrams are not supported.\n"));
162 default:
163 Status = STATUS_UNSUCCESSFUL;
164 break;
165 }
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);
170 return Status;
171 }
172
173 /* Chain data after header */
174 NdisChainBufferAtBack(Packet->NdisPacket, SendRequest->Buffer);
175
176 DISPLAY_IP_PACKET(Packet);
177
178 *IPPacket = Packet;
179
180 return STATUS_SUCCESS;
181 }
182
183
184 VOID DeliverUDPData(
185 PADDRESS_FILE AddrFile,
186 PIP_ADDRESS Address,
187 PIP_PACKET IPPacket,
188 UINT DataSize)
189 /*
190 * FUNCTION: Delivers UDP data to a user
191 * ARGUMENTS:
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
196 * NOTES:
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.
201 */
202 {
203 KIRQL OldIrql;
204 PTDI_IND_RECEIVE_DATAGRAM ReceiveHandler;
205 PVOID HandlerContext;
206 LONG AddressLength;
207 PVOID SourceAddress;
208 ULONG BytesTaken;
209 NTSTATUS Status;
210
211 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
212
213 KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
214
215 if (!IsListEmpty(&AddrFile->ReceiveQueue)) {
216 PLIST_ENTRY CurrentEntry;
217 PDATAGRAM_RECEIVE_REQUEST Current;
218 BOOLEAN Found;
219
220 /* Search receive request list to find a match */
221 Found = FALSE;
222 CurrentEntry = AddrFile->ReceiveQueue.Flink;
223 while ((CurrentEntry != &AddrFile->ReceiveQueue) && (!Found)) {
224 Current = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
225 if (!Current->RemoteAddress)
226 Found = TRUE;
227 else if (AddrIsEqual(Address, Current->RemoteAddress))
228 Found = TRUE;
229
230 if (Found) {
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. */
234
235 /* Remove the request from the queue */
236 RemoveEntryList(&Current->ListEntry);
237 AddrFile->RefCount--;
238 break;
239 }
240 CurrentEntry = CurrentEntry->Flink;
241 }
242
243 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
244
245 if (Found) {
246 /* Copy the data into buffer provided by the user */
247 CopyBufferToBufferChain(Current->Buffer,
248 0,
249 IPPacket->Data,
250 DataSize);
251
252 /* Complete the receive request */
253 (*Current->Complete)(Current->Context, STATUS_SUCCESS, DataSize);
254
255 /* Finally free the receive request */
256 if (Current->RemoteAddress)
257 PoolFreeBuffer(Current->RemoteAddress);
258 PoolFreeBuffer(Current);
259 }
260 } else if (AddrFile->RegisteredReceiveDatagramHandler) {
261 TI_DbgPrint(MAX_TRACE, ("Calling receive event handler.\n"));
262
263 ReceiveHandler = AddrFile->ReceiveDatagramHandler;
264 HandlerContext = AddrFile->ReceiveDatagramHandlerContext;
265
266 KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
267
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;
274 }
275
276 Status = (*ReceiveHandler)(HandlerContext,
277 AddressLength,
278 SourceAddress,
279 0,
280 NULL,
281 TDI_RECEIVE_ENTIRE_MESSAGE,
282 DataSize,
283 DataSize,
284 &BytesTaken,
285 IPPacket->Data,
286 NULL);
287 }
288
289 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
290 }
291
292
293 NTSTATUS UDPSendDatagram(
294 PTDI_REQUEST Request,
295 PTDI_CONNECTION_INFORMATION ConnInfo,
296 PNDIS_BUFFER Buffer,
297 ULONG DataSize)
298 /*
299 * FUNCTION: Sends an UDP datagram to a remote address
300 * ARGUMENTS:
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
305 * RETURNS:
306 * Status of operation
307 */
308 {
309 return DGSendDatagram(Request,
310 ConnInfo,
311 Buffer,
312 DataSize,
313 BuildUDPPacket);
314 }
315
316
317 NTSTATUS UDPReceiveDatagram(
318 PTDI_REQUEST Request,
319 PTDI_CONNECTION_INFORMATION ConnInfo,
320 PNDIS_BUFFER Buffer,
321 ULONG ReceiveLength,
322 ULONG ReceiveFlags,
323 PTDI_CONNECTION_INFORMATION ReturnInfo,
324 PULONG BytesReceived)
325 /*
326 * FUNCTION: Attempts to receive an UDP datagram from a remote address
327 * ARGUMENTS:
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
335 * RETURNS:
336 * Status of operation
337 * NOTES:
338 * This is the high level interface for receiving UDP datagrams
339 */
340 {
341 return DGReceiveDatagram(Request,
342 ConnInfo,
343 Buffer,
344 ReceiveLength,
345 ReceiveFlags,
346 ReturnInfo,
347 BytesReceived);
348 }
349
350
351 VOID UDPReceive(
352 PNET_TABLE_ENTRY NTE,
353 PIP_PACKET IPPacket)
354 /*
355 * FUNCTION: Receives and queues a UDP datagram
356 * ARGUMENTS:
357 * NTE = Pointer to net table entry which the packet was received on
358 * IPPacket = Pointer to an IP packet that was received
359 * NOTES:
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
362 */
363 {
364 AF_SEARCH SearchContext;
365 PIPv4_HEADER IPv4Header;
366 PADDRESS_FILE AddrFile;
367 PUDP_HEADER UDPHeader;
368 PIP_ADDRESS DstAddress;
369 UINT DataSize, i;
370
371 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
372
373 switch (IPPacket->Type) {
374 /* IPv4 packet */
375 case IP_ADDRESS_V4:
376 IPv4Header = IPPacket->Header;
377 DstAddress = &IPPacket->DstAddr;
378 break;
379
380 /* IPv6 packet */
381 case IP_ADDRESS_V6:
382 TI_DbgPrint(MIN_TRACE, ("Discarded IPv6 UDP datagram (%i bytes).\n", IPPacket->TotalSize));
383
384 /* FIXME: IPv6 is not supported */
385 return;
386
387 default:
388 return;
389 }
390
391 UDPHeader = (PUDP_HEADER)IPPacket->Data;
392
393 /* FIXME: Calculate and validate UDP checksum */
394
395 /* Sanity checks */
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"));
400 return;
401 }
402
403 DataSize = i - sizeof(UDP_HEADER);
404
405 /* Go to UDP data area */
406 (ULONG_PTR)IPPacket->Data += sizeof(UDP_HEADER);
407
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 */
412
413 AddrFile = AddrSearchFirst(DstAddress,
414 UDPHeader->DestPort,
415 IPPROTO_UDP,
416 &SearchContext);
417 if (AddrFile) {
418 do {
419 DeliverUDPData(AddrFile,
420 DstAddress,
421 IPPacket,
422 DataSize);
423 } while ((AddrFile = AddrSearchNext(&SearchContext)) != NULL);
424 } else {
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)));
429
430 /* FIXME: Send ICMP reply */
431 }
432 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
433 }
434
435
436 NTSTATUS UDPStartup(
437 VOID)
438 /*
439 * FUNCTION: Initializes the UDP subsystem
440 * RETURNS:
441 * Status of operation
442 */
443 {
444 RtlZeroMemory(&UDPStats, sizeof(UDP_STATISTICS));
445
446 /* Register this protocol with IP layer */
447 IPRegisterProtocol(IPPROTO_UDP, UDPReceive);
448
449 UDPInitialized = TRUE;
450
451 return STATUS_SUCCESS;
452 }
453
454
455 NTSTATUS UDPShutdown(
456 VOID)
457 /*
458 * FUNCTION: Shuts down the UDP subsystem
459 * RETURNS:
460 * Status of operation
461 */
462 {
463 if (!UDPInitialized)
464 return STATUS_SUCCESS;
465
466 /* Deregister this protocol with IP layer */
467 IPRegisterProtocol(IPPROTO_UDP, NULL);
468
469 return STATUS_SUCCESS;
470 }
471
472 /* EOF */