merge ROS Shell without integrated explorer part into trunk
[reactos.git] / reactos / drivers / lib / ip / 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
11 #include "precomp.h"
12
13
14 BOOLEAN UDPInitialized = FALSE;
15 PORT_SET UDPPorts;
16
17 NTSTATUS AddUDPHeaderIPv4(
18 PIP_ADDRESS RemoteAddress,
19 USHORT RemotePort,
20 PIP_ADDRESS LocalAddress,
21 USHORT LocalPort,
22 PIP_PACKET IPPacket,
23 UINT DataLength)
24 /*
25 * FUNCTION: Adds an IPv4 and UDP header to an IP packet
26 * ARGUMENTS:
27 * SendRequest = Pointer to send request
28 * LocalAddress = Pointer to our local address
29 * LocalPort = The port we send this datagram from
30 * IPPacket = Pointer to IP packet
31 * RETURNS:
32 * Status of operation
33 */
34 {
35 PUDP_HEADER UDPHeader;
36
37 TI_DbgPrint(MID_TRACE, ("Packet: %x NdisPacket %x\n",
38 IPPacket, IPPacket->NdisPacket));
39
40 AddGenericHeaderIPv4
41 ( RemoteAddress, RemotePort,
42 LocalAddress, LocalPort,
43 IPPacket, DataLength, IPPROTO_UDP,
44 sizeof(UDP_HEADER), (PVOID *)&UDPHeader );
45
46 /* Build UDP header */
47 UDPHeader = (PUDP_HEADER)(IPPacket->Data - sizeof(UDP_HEADER));
48 /* Port values are already big-endian values */
49 UDPHeader->SourcePort = LocalPort;
50 UDPHeader->DestPort = RemotePort;
51 /* FIXME: Calculate UDP checksum and put it in UDP header */
52 UDPHeader->Checksum = 0;
53 /* Length of UDP header and data */
54 UDPHeader->Length = WH2N(DataLength + sizeof(UDP_HEADER));
55
56 IPPacket->Data = ((PCHAR)UDPHeader) + sizeof(UDP_HEADER);
57
58 TI_DbgPrint(MID_TRACE, ("Packet: %d ip %d udp %d payload\n",
59 (PCHAR)UDPHeader - (PCHAR)IPPacket->Header,
60 (PCHAR)IPPacket->Data - (PCHAR)UDPHeader,
61 DataLength));
62
63 return STATUS_SUCCESS;
64 }
65
66
67 NTSTATUS BuildUDPPacket(
68 PIP_PACKET Packet,
69 PIP_ADDRESS RemoteAddress,
70 USHORT RemotePort,
71 PIP_ADDRESS LocalAddress,
72 USHORT LocalPort,
73 PCHAR DataBuffer,
74 UINT DataLen )
75 /*
76 * FUNCTION: Builds an UDP packet
77 * ARGUMENTS:
78 * Context = Pointer to context information (DATAGRAM_SEND_REQUEST)
79 * LocalAddress = Pointer to our local address
80 * LocalPort = The port we send this datagram from
81 * IPPacket = Address of pointer to IP packet
82 * RETURNS:
83 * Status of operation
84 */
85 {
86 NTSTATUS Status;
87
88 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
89
90 /* FIXME: Assumes IPv4 */
91 IPInitializePacket(Packet, IP_ADDRESS_V4);
92 if (!Packet)
93 return STATUS_INSUFFICIENT_RESOURCES;
94
95 Packet->TotalSize = sizeof(IPv4_HEADER) + sizeof(UDP_HEADER) + DataLen;
96
97 /* Prepare packet */
98 Status = AllocatePacketWithBuffer( &Packet->NdisPacket,
99 NULL,
100 Packet->TotalSize + MaxLLHeaderSize );
101
102 if( !NT_SUCCESS(Status) ) return Status;
103
104 TI_DbgPrint(MID_TRACE, ("Allocated packet: %x\n", Packet->NdisPacket));
105 TI_DbgPrint(MID_TRACE, ("Local Addr : %s\n", A2S(LocalAddress)));
106 TI_DbgPrint(MID_TRACE, ("Remote Addr: %s\n", A2S(RemoteAddress)));
107
108 switch (RemoteAddress->Type) {
109 case IP_ADDRESS_V4:
110 Status = AddUDPHeaderIPv4(RemoteAddress, RemotePort,
111 LocalAddress, LocalPort, Packet, DataLen);
112 break;
113 case IP_ADDRESS_V6:
114 /* FIXME: Support IPv6 */
115 TI_DbgPrint(MIN_TRACE, ("IPv6 UDP datagrams are not supported.\n"));
116 default:
117 Status = STATUS_UNSUCCESSFUL;
118 break;
119 }
120 if (!NT_SUCCESS(Status)) {
121 TI_DbgPrint(MIN_TRACE, ("Cannot add UDP header. Status = (0x%X)\n",
122 Status));
123 FreeNdisPacket(Packet->NdisPacket);
124 return Status;
125 }
126
127 TI_DbgPrint(MID_TRACE, ("Copying data (hdr %x data %x (%d))\n",
128 Packet->Header, Packet->Data,
129 (PCHAR)Packet->Data - (PCHAR)Packet->Header));
130
131 RtlCopyMemory( Packet->Data, DataBuffer, DataLen );
132
133 TI_DbgPrint(MID_TRACE, ("Displaying packet\n"));
134
135 DISPLAY_IP_PACKET(Packet);
136
137 TI_DbgPrint(MID_TRACE, ("Leaving\n"));
138
139 return STATUS_SUCCESS;
140 }
141
142 VOID UDPSendPacketComplete
143 ( PVOID Context, PNDIS_PACKET Packet, NDIS_STATUS Status ) {
144 FreeNdisPacket( Packet );
145 }
146
147 NTSTATUS UDPSendDatagram(
148 PADDRESS_FILE AddrFile,
149 PTDI_CONNECTION_INFORMATION ConnInfo,
150 PCHAR BufferData,
151 ULONG DataSize,
152 PULONG DataUsed )
153 /*
154 * FUNCTION: Sends an UDP datagram to a remote address
155 * ARGUMENTS:
156 * Request = Pointer to TDI request
157 * ConnInfo = Pointer to connection information
158 * Buffer = Pointer to NDIS buffer with data
159 * DataSize = Size in bytes of data to be sent
160 * RETURNS:
161 * Status of operation
162 */
163 {
164 IP_PACKET Packet;
165 PTA_IP_ADDRESS RemoteAddressTa = (PTA_IP_ADDRESS)ConnInfo->RemoteAddress;
166 IP_ADDRESS RemoteAddress;
167 USHORT RemotePort;
168 NTSTATUS Status;
169 PNEIGHBOR_CACHE_ENTRY NCE;
170
171 TI_DbgPrint(MID_TRACE,("Sending Datagram(%x %x %x %d)\n",
172 AddrFile, ConnInfo, BufferData, DataSize));
173 TI_DbgPrint(MID_TRACE,("RemoteAddressTa: %x\n", RemoteAddressTa));
174
175 switch( RemoteAddressTa->Address[0].AddressType ) {
176 case TDI_ADDRESS_TYPE_IP:
177 RemoteAddress.Type = IP_ADDRESS_V4;
178 RemoteAddress.Address.IPv4Address =
179 RemoteAddressTa->Address[0].Address[0].in_addr;
180 RemotePort = RemoteAddressTa->Address[0].Address[0].sin_port;
181 break;
182
183 default:
184 return STATUS_UNSUCCESSFUL;
185 }
186
187 Status = BuildUDPPacket( &Packet,
188 &RemoteAddress,
189 RemotePort,
190 &AddrFile->Address,
191 AddrFile->Port,
192 BufferData,
193 DataSize );
194
195 if( !NT_SUCCESS(Status) )
196 return Status;
197
198 if(!(NCE = RouteGetRouteToDestination( &RemoteAddress )))
199 return STATUS_UNSUCCESSFUL;
200
201 IPSendDatagram( &Packet, NCE, UDPSendPacketComplete, NULL );
202
203 return STATUS_SUCCESS;
204 }
205
206 VOID UDPReceiveComplete(PVOID Context, NTSTATUS Status, ULONG Count) {
207 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest =
208 (PDATAGRAM_RECEIVE_REQUEST)Context;
209 TI_DbgPrint(MAX_TRACE,("Called\n"));
210 ReceiveRequest->UserComplete( ReceiveRequest->UserContext, Status, Count );
211 exFreePool( ReceiveRequest );
212 TI_DbgPrint(MAX_TRACE,("Done\n"));
213 }
214
215 NTSTATUS UDPReceiveDatagram(
216 PADDRESS_FILE AddrFile,
217 PTDI_CONNECTION_INFORMATION ConnInfo,
218 PCHAR BufferData,
219 ULONG ReceiveLength,
220 ULONG ReceiveFlags,
221 PTDI_CONNECTION_INFORMATION ReturnInfo,
222 PULONG BytesReceived,
223 PDATAGRAM_COMPLETION_ROUTINE Complete,
224 PVOID Context)
225 /*
226 * FUNCTION: Attempts to receive an UDP datagram from a remote address
227 * ARGUMENTS:
228 * Request = Pointer to TDI request
229 * ConnInfo = Pointer to connection information
230 * Buffer = Pointer to NDIS buffer chain to store received data
231 * ReceiveLength = Maximum size to use of buffer, 0 if all can be used
232 * ReceiveFlags = Receive flags (None, Normal, Peek)
233 * ReturnInfo = Pointer to structure for return information
234 * BytesReceive = Pointer to structure for number of bytes received
235 * RETURNS:
236 * Status of operation
237 * NOTES:
238 * This is the high level interface for receiving UDP datagrams
239 */
240 {
241 KIRQL OldIrql;
242 NTSTATUS Status;
243 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest;
244
245 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
246
247 TcpipAcquireSpinLock(&AddrFile->Lock, &OldIrql);
248
249 if (AF_IS_VALID(AddrFile))
250 {
251 ReceiveRequest = exAllocatePool(NonPagedPool, sizeof(DATAGRAM_RECEIVE_REQUEST));
252 if (ReceiveRequest)
253 {
254 /* Initialize a receive request */
255
256 /* Extract the remote address filter from the request (if any) */
257 if ((ConnInfo->RemoteAddressLength != 0) &&
258 (ConnInfo->RemoteAddress))
259 {
260 Status = AddrGetAddress(ConnInfo->RemoteAddress,
261 &ReceiveRequest->RemoteAddress,
262 &ReceiveRequest->RemotePort);
263 if (!NT_SUCCESS(Status))
264 {
265 TcpipReleaseSpinLock(&AddrFile->Lock, OldIrql);
266 exFreePool(ReceiveRequest);
267 return Status;
268 }
269 }
270 else
271 {
272 ReceiveRequest->RemotePort = 0;
273 }
274 ReceiveRequest->ReturnInfo = ReturnInfo;
275 ReceiveRequest->Buffer = BufferData;
276 ReceiveRequest->BufferSize = ReceiveLength;
277 ReceiveRequest->UserComplete = Complete;
278 ReceiveRequest->UserContext = Context;
279 ReceiveRequest->Complete =
280 (PDATAGRAM_COMPLETION_ROUTINE)UDPReceiveComplete;
281 ReceiveRequest->Context = ReceiveRequest;
282
283 /* Queue receive request */
284 InsertTailList(&AddrFile->ReceiveQueue, &ReceiveRequest->ListEntry);
285 AF_SET_PENDING(AddrFile, AFF_RECEIVE);
286
287 TcpipReleaseSpinLock(&AddrFile->Lock, OldIrql);
288
289 TI_DbgPrint(MAX_TRACE, ("Leaving (pending).\n"));
290
291 return STATUS_PENDING;
292 }
293 else
294 {
295 Status = STATUS_INSUFFICIENT_RESOURCES;
296 }
297 }
298 else
299 {
300 Status = STATUS_INVALID_ADDRESS;
301 }
302
303 TcpipReleaseSpinLock(&AddrFile->Lock, OldIrql);
304
305 TI_DbgPrint(MAX_TRACE, ("Leaving with errors (0x%X).\n", Status));
306
307 return Status;
308 }
309
310
311 VOID UDPReceive(PIP_INTERFACE Interface, PIP_PACKET IPPacket)
312 /*
313 * FUNCTION: Receives and queues a UDP datagram
314 * ARGUMENTS:
315 * NTE = Pointer to net table entry which the packet was received on
316 * IPPacket = Pointer to an IP packet that was received
317 * NOTES:
318 * This is the low level interface for receiving UDP datagrams. It strips
319 * the UDP header from a packet and delivers the data to anyone that wants it
320 */
321 {
322 AF_SEARCH SearchContext;
323 PIPv4_HEADER IPv4Header;
324 PADDRESS_FILE AddrFile;
325 PUDP_HEADER UDPHeader;
326 PIP_ADDRESS DstAddress, SrcAddress;
327 UINT DataSize, i;
328
329 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
330
331 switch (IPPacket->Type) {
332 /* IPv4 packet */
333 case IP_ADDRESS_V4:
334 IPv4Header = IPPacket->Header;
335 DstAddress = &IPPacket->DstAddr;
336 SrcAddress = &IPPacket->SrcAddr;
337 break;
338
339 /* IPv6 packet */
340 case IP_ADDRESS_V6:
341 TI_DbgPrint(MIN_TRACE, ("Discarded IPv6 UDP datagram (%i bytes).\n", IPPacket->TotalSize));
342
343 /* FIXME: IPv6 is not supported */
344 return;
345
346 default:
347 return;
348 }
349
350 UDPHeader = (PUDP_HEADER)IPPacket->Data;
351
352 /* FIXME: Calculate and validate UDP checksum */
353
354 /* Sanity checks */
355 i = WH2N(UDPHeader->Length);
356 if ((i < sizeof(UDP_HEADER)) || (i > IPPacket->TotalSize - IPPacket->Position)) {
357 /* Incorrect or damaged packet received, discard it */
358 TI_DbgPrint(MIN_TRACE, ("Incorrect or damaged UDP packet received.\n"));
359 return;
360 }
361
362 DataSize = i - sizeof(UDP_HEADER);
363
364 /* Go to UDP data area */
365 IPPacket->Data = (PVOID)((ULONG_PTR)IPPacket->Data + sizeof(UDP_HEADER));
366
367 /* Locate a receive request on destination address file object
368 and deliver the packet if one is found. If there is no receive
369 request on the address file object, call the associated receive
370 handler. If no receive handler is registered, drop the packet */
371
372 AddrFile = AddrSearchFirst(DstAddress,
373 UDPHeader->DestPort,
374 IPPROTO_UDP,
375 &SearchContext);
376 if (AddrFile) {
377 do {
378 DGDeliverData(AddrFile,
379 SrcAddress,
380 DstAddress,
381 UDPHeader->SourcePort,
382 UDPHeader->DestPort,
383 IPPacket,
384 DataSize);
385 } while ((AddrFile = AddrSearchNext(&SearchContext)) != NULL);
386 } else {
387 /* There are no open address files that will take this datagram */
388 /* FIXME: IPv4 only */
389 TI_DbgPrint(MID_TRACE, ("Cannot deliver IPv4 UDP datagram to address (0x%X).\n",
390 DN2H(DstAddress->Address.IPv4Address)));
391
392 /* FIXME: Send ICMP reply */
393 }
394 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
395 }
396
397
398 NTSTATUS UDPStartup(
399 VOID)
400 /*
401 * FUNCTION: Initializes the UDP subsystem
402 * RETURNS:
403 * Status of operation
404 */
405 {
406 #ifdef __NTDRIVER__
407 RtlZeroMemory(&UDPStats, sizeof(UDP_STATISTICS));
408 #endif
409
410 PortsStartup( &UDPPorts, 1, 0xfffe );
411
412 /* Register this protocol with IP layer */
413 IPRegisterProtocol(IPPROTO_UDP, UDPReceive);
414
415 UDPInitialized = TRUE;
416
417 return STATUS_SUCCESS;
418 }
419
420
421 NTSTATUS UDPShutdown(
422 VOID)
423 /*
424 * FUNCTION: Shuts down the UDP subsystem
425 * RETURNS:
426 * Status of operation
427 */
428 {
429 if (!UDPInitialized)
430 return STATUS_SUCCESS;
431
432 PortsShutdown( &UDPPorts );
433
434 /* Deregister this protocol with IP layer */
435 IPRegisterProtocol(IPPROTO_UDP, NULL);
436
437 return STATUS_SUCCESS;
438 }
439
440 UINT UDPAllocatePort( UINT HintPort ) {
441 if( HintPort ) {
442 if( AllocatePort( &UDPPorts, HintPort ) ) return HintPort;
443 else return (UINT)-1;
444 } else return AllocatePortFromRange
445 ( &UDPPorts, UDP_STARTING_PORT,
446 UDP_STARTING_PORT + UDP_DYNAMIC_PORTS );
447 }
448
449 VOID UDPFreePort( UINT Port ) {
450 DeallocatePort( &UDPPorts, Port );
451 }
452
453 /* EOF */