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) )
113     {
114         Packet->Free(Packet);
115         return Status;
116     }
117 
118     TI_DbgPrint(MID_TRACE, ("Allocated packet: %x\n", Packet->NdisPacket));
119     TI_DbgPrint(MID_TRACE, ("Local Addr : %s\n", A2S(LocalAddress)));
120     TI_DbgPrint(MID_TRACE, ("Remote Addr: %s\n", A2S(RemoteAddress)));
121 
122     switch (RemoteAddress->Type) {
123         case IP_ADDRESS_V4:
124             Status = AddUDPHeaderIPv4(AddrFile, RemoteAddress, RemotePort,
125                                       LocalAddress, LocalPort, Packet, DataBuffer, DataLen);
126             break;
127         case IP_ADDRESS_V6:
128             /* FIXME: Support IPv6 */
129             TI_DbgPrint(MIN_TRACE, ("IPv6 UDP datagrams are not supported.\n"));
130         default:
131             Status = STATUS_UNSUCCESSFUL;
132             break;
133     }
134     if (!NT_SUCCESS(Status)) {
135         TI_DbgPrint(MIN_TRACE, ("Cannot add UDP header. Status = (0x%X)\n",
136                                 Status));
137         Packet->Free(Packet);
138         return Status;
139     }
140 
141     TI_DbgPrint(MID_TRACE, ("Displaying packet\n"));
142 
143     DISPLAY_IP_PACKET(Packet);
144 
145     TI_DbgPrint(MID_TRACE, ("Leaving\n"));
146 
147     return STATUS_SUCCESS;
148 }
149 
150 NTSTATUS UDPSendDatagram(
151     PADDRESS_FILE AddrFile,
152     PTDI_CONNECTION_INFORMATION ConnInfo,
153     PCHAR BufferData,
154     ULONG DataSize,
155     PULONG DataUsed )
156 /*
157  * FUNCTION: Sends an UDP datagram to a remote address
158  * ARGUMENTS:
159  *     Request   = Pointer to TDI request
160  *     ConnInfo  = Pointer to connection information
161  *     Buffer    = Pointer to NDIS buffer with data
162  *     DataSize  = Size in bytes of data to be sent
163  * RETURNS:
164  *     Status of operation
165  */
166 {
167     IP_PACKET Packet;
168     PTA_IP_ADDRESS RemoteAddressTa = (PTA_IP_ADDRESS)ConnInfo->RemoteAddress;
169     IP_ADDRESS RemoteAddress;
170     IP_ADDRESS LocalAddress;
171     USHORT RemotePort;
172     NTSTATUS Status;
173     PNEIGHBOR_CACHE_ENTRY NCE;
174 
175     LockObject(AddrFile);
176 
177     TI_DbgPrint(MID_TRACE,("Sending Datagram(%x %x %x %d)\n",
178 						   AddrFile, ConnInfo, BufferData, DataSize));
179     TI_DbgPrint(MID_TRACE,("RemoteAddressTa: %x\n", RemoteAddressTa));
180 
181     switch( RemoteAddressTa->Address[0].AddressType ) {
182     case TDI_ADDRESS_TYPE_IP:
183 		RemoteAddress.Type = IP_ADDRESS_V4;
184 		RemoteAddress.Address.IPv4Address =
185 			RemoteAddressTa->Address[0].Address[0].in_addr;
186 		RemotePort = RemoteAddressTa->Address[0].Address[0].sin_port;
187 		break;
188 
189     default:
190 		UnlockObject(AddrFile);
191 		return STATUS_UNSUCCESSFUL;
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         if(!(NCE = RouteGetRouteToDestination( &RemoteAddress ))) {
202             UnlockObject(AddrFile);
203             return STATUS_NETWORK_UNREACHABLE;
204         }
205 
206         LocalAddress = NCE->Interface->Unicast;
207     }
208     else
209     {
210         if(!(NCE = NBLocateNeighbor( &LocalAddress, NULL ))) {
211             UnlockObject(AddrFile);
212             return STATUS_INVALID_PARAMETER;
213         }
214     }
215 
216     Status = BuildUDPPacket( AddrFile,
217 							 &Packet,
218 							 &RemoteAddress,
219 							 RemotePort,
220 							 &LocalAddress,
221 							 AddrFile->Port,
222 							 BufferData,
223 							 DataSize );
224 
225     UnlockObject(AddrFile);
226 
227     if( !NT_SUCCESS(Status) )
228 		return Status;
229 
230     Status = IPSendDatagram(&Packet, NCE);
231     if (!NT_SUCCESS(Status))
232         return Status;
233 
234     *DataUsed = DataSize;
235 
236     return STATUS_SUCCESS;
237 }
238 
239 
240 VOID UDPReceive(PIP_INTERFACE Interface, PIP_PACKET IPPacket)
241 /*
242  * FUNCTION: Receives and queues a UDP datagram
243  * ARGUMENTS:
244  *     NTE      = Pointer to net table entry which the packet was received on
245 *     IPPacket = Pointer to an IP packet that was received
246 * NOTES:
247 *     This is the low level interface for receiving UDP datagrams. It strips
248 *     the UDP header from a packet and delivers the data to anyone that wants it
249 */
250 {
251   AF_SEARCH SearchContext;
252   PIPv4_HEADER IPv4Header;
253   PADDRESS_FILE AddrFile;
254   PUDP_HEADER UDPHeader;
255   PIP_ADDRESS DstAddress, SrcAddress;
256   UINT DataSize, i;
257 
258   TI_DbgPrint(MAX_TRACE, ("Called.\n"));
259 
260   switch (IPPacket->Type) {
261   /* IPv4 packet */
262   case IP_ADDRESS_V4:
263     IPv4Header = IPPacket->Header;
264     DstAddress = &IPPacket->DstAddr;
265     SrcAddress = &IPPacket->SrcAddr;
266     break;
267 
268   /* IPv6 packet */
269   case IP_ADDRESS_V6:
270     TI_DbgPrint(MIN_TRACE, ("Discarded IPv6 UDP datagram (%i bytes).\n", IPPacket->TotalSize));
271 
272     /* FIXME: IPv6 is not supported */
273     return;
274 
275   default:
276     return;
277   }
278 
279   UDPHeader = (PUDP_HEADER)IPPacket->Data;
280 
281   /* Calculate and validate UDP checksum */
282   i = UDPv4ChecksumCalculate(IPv4Header,
283                              (PUCHAR)UDPHeader,
284                              WH2N(UDPHeader->Length));
285   if (i != DH2N(0x0000FFFF) && UDPHeader->Checksum != 0)
286   {
287       TI_DbgPrint(MIN_TRACE, ("Bad checksum on packet received.\n"));
288       return;
289   }
290 
291   /* Sanity checks */
292   i = WH2N(UDPHeader->Length);
293   if ((i < sizeof(UDP_HEADER)) || (i > IPPacket->TotalSize - IPPacket->Position)) {
294     /* Incorrect or damaged packet received, discard it */
295     TI_DbgPrint(MIN_TRACE, ("Incorrect or damaged UDP packet received.\n"));
296     return;
297   }
298 
299   DataSize = i - sizeof(UDP_HEADER);
300 
301   /* Go to UDP data area */
302   IPPacket->Data = (PVOID)((ULONG_PTR)IPPacket->Data + sizeof(UDP_HEADER));
303 
304   /* Locate a receive request on destination address file object
305      and deliver the packet if one is found. If there is no receive
306      request on the address file object, call the associated receive
307      handler. If no receive handler is registered, drop the packet */
308 
309   AddrFile = AddrSearchFirst(DstAddress,
310                              UDPHeader->DestPort,
311                              IPPROTO_UDP,
312                              &SearchContext);
313   if (AddrFile) {
314     do {
315       DGDeliverData(AddrFile,
316 		    SrcAddress,
317                     DstAddress,
318 		    UDPHeader->SourcePort,
319 		    UDPHeader->DestPort,
320                     IPPacket,
321                     DataSize);
322       DereferenceObject(AddrFile);
323     } while ((AddrFile = AddrSearchNext(&SearchContext)) != NULL);
324   } else {
325     /* There are no open address files that will take this datagram */
326     /* FIXME: IPv4 only */
327     TI_DbgPrint(MID_TRACE, ("Cannot deliver IPv4 UDP datagram to address (0x%X).\n",
328       DN2H(DstAddress->Address.IPv4Address)));
329 
330     /* FIXME: Send ICMP reply */
331   }
332   TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
333 }
334 
335 
336 NTSTATUS UDPStartup(
337   VOID)
338 /*
339  * FUNCTION: Initializes the UDP subsystem
340  * RETURNS:
341  *     Status of operation
342  */
343 {
344   NTSTATUS Status;
345 
346   RtlZeroMemory(&UDPStats, sizeof(UDP_STATISTICS));
347 
348   Status = PortsStartup( &UDPPorts, 1, UDP_STARTING_PORT + UDP_DYNAMIC_PORTS );
349 
350   if( !NT_SUCCESS(Status) ) return Status;
351 
352   /* Register this protocol with IP layer */
353   IPRegisterProtocol(IPPROTO_UDP, UDPReceive);
354 
355   UDPInitialized = TRUE;
356 
357   return STATUS_SUCCESS;
358 }
359 
360 
361 NTSTATUS UDPShutdown(
362   VOID)
363 /*
364  * FUNCTION: Shuts down the UDP subsystem
365  * RETURNS:
366  *     Status of operation
367  */
368 {
369   if (!UDPInitialized)
370       return STATUS_SUCCESS;
371 
372   PortsShutdown( &UDPPorts );
373 
374   /* Deregister this protocol with IP layer */
375   IPRegisterProtocol(IPPROTO_UDP, NULL);
376 
377   UDPInitialized = FALSE;
378 
379   return STATUS_SUCCESS;
380 }
381 
382 UINT UDPAllocatePort( UINT HintPort ) {
383     if( HintPort ) {
384         if( AllocatePort( &UDPPorts, HintPort ) ) return HintPort;
385         else return (UINT)-1;
386     } else return AllocatePortFromRange
387                ( &UDPPorts, UDP_STARTING_PORT,
388                  UDP_STARTING_PORT + UDP_DYNAMIC_PORTS );
389 }
390 
391 VOID UDPFreePort( UINT Port ) {
392     DeallocatePort( &UDPPorts, Port );
393 }
394 
395 /* EOF */
396