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