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