- Add locking to ICMPSendDatagram
[reactos.git] / lib / drivers / ip / network / icmp.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: network/icmp.c
5 * PURPOSE: Internet Control Message 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 NTSTATUS ICMPStartup()
14 {
15 IPRegisterProtocol(IPPROTO_ICMP, ICMPReceive);
16
17 return STATUS_SUCCESS;
18 }
19
20 NTSTATUS ICMPShutdown()
21 {
22 IPRegisterProtocol(IPPROTO_ICMP, NULL);
23
24 return STATUS_SUCCESS;
25 }
26
27 VOID SendICMPComplete(
28 PVOID Context,
29 PNDIS_PACKET Packet,
30 NDIS_STATUS NdisStatus)
31 /*
32 * FUNCTION: ICMP datagram transmit completion handler
33 * ARGUMENTS:
34 * Context = Pointer to context infomation (IP_PACKET)
35 * Packet = Pointer to NDIS packet
36 * NdisStatus = Status of transmit operation
37 * NOTES:
38 * This routine is called by IP when a ICMP send completes
39 */
40 {
41 TI_DbgPrint(DEBUG_ICMP, ("Freeing NDIS packet (%X).\n", Packet));
42
43 /* Free packet */
44 FreeNdisPacket(Packet);
45
46 TI_DbgPrint(DEBUG_ICMP, ("Done\n"));
47 }
48
49
50 BOOLEAN PrepareICMPPacket(
51 PIP_INTERFACE Interface,
52 PIP_PACKET IPPacket,
53 PIP_ADDRESS Destination,
54 PCHAR Data,
55 UINT DataSize)
56 /*
57 * FUNCTION: Prepares an ICMP packet
58 * ARGUMENTS:
59 * NTE = Pointer to net table entry to use
60 * Destination = Pointer to destination address
61 * DataSize = Size of dataarea
62 * RETURNS:
63 * Pointer to IP packet, NULL if there is not enough free resources
64 */
65 {
66 PNDIS_PACKET NdisPacket;
67 NDIS_STATUS NdisStatus;
68 PIPv4_HEADER IPHeader;
69 ULONG Size;
70
71 TI_DbgPrint(DEBUG_ICMP, ("Called. DataSize (%d).\n", DataSize));
72
73 IPInitializePacket(IPPacket, IP_ADDRESS_V4);
74
75 /* No special flags */
76 IPPacket->Flags = 0;
77
78 Size = sizeof(IPv4_HEADER) + DataSize;
79
80 /* Allocate NDIS packet */
81 NdisStatus = AllocatePacketWithBuffer( &NdisPacket, NULL, Size );
82
83 if( !NT_SUCCESS(NdisStatus) ) return FALSE;
84
85 IPPacket->NdisPacket = NdisPacket;
86
87 GetDataPtr( IPPacket->NdisPacket, 0,
88 (PCHAR *)&IPPacket->Header, &IPPacket->ContigSize );
89
90 TI_DbgPrint(DEBUG_ICMP, ("Size (%d). Data at (0x%X).\n", Size, Data));
91 TI_DbgPrint(DEBUG_ICMP, ("NdisPacket at (0x%X).\n", NdisPacket));
92
93 IPPacket->HeaderSize = sizeof(IPv4_HEADER);
94 IPPacket->TotalSize = Size;
95 IPPacket->Data = ((PCHAR)IPPacket->Header) + IPPacket->HeaderSize;
96
97 TI_DbgPrint(DEBUG_ICMP, ("Copying Address: %x -> %x\n",
98 &IPPacket->DstAddr, Destination));
99
100 RtlCopyMemory(&IPPacket->DstAddr, Destination, sizeof(IP_ADDRESS));
101 RtlCopyMemory(IPPacket->Data, Data, DataSize);
102
103 /* Build IPv4 header. FIXME: IPv4 only */
104
105 IPHeader = (PIPv4_HEADER)IPPacket->Header;
106
107 /* Version = 4, Length = 5 DWORDs */
108 IPHeader->VerIHL = 0x45;
109 /* Normal Type-of-Service */
110 IPHeader->Tos = 0;
111 /* Length of data and header */
112 IPHeader->TotalLength = WH2N((USHORT)DataSize + sizeof(IPv4_HEADER));
113 /* Identification */
114 IPHeader->Id = (USHORT)Random();
115 /* One fragment at offset 0 */
116 IPHeader->FlagsFragOfs = 0;
117 /* Time-to-Live is 128 */
118 IPHeader->Ttl = 128;
119 /* Internet Control Message Protocol */
120 IPHeader->Protocol = IPPROTO_ICMP;
121 /* Checksum is 0 (for later calculation of this) */
122 IPHeader->Checksum = 0;
123 /* Source address */
124 IPHeader->SrcAddr = Interface->Unicast.Address.IPv4Address;
125 /* Destination address */
126 IPHeader->DstAddr = Destination->Address.IPv4Address;
127
128
129 TI_DbgPrint(MID_TRACE,("Leaving\n"));
130
131 return TRUE;
132 }
133
134 VOID ICMPSendPacketComplete
135 ( PVOID Context, PNDIS_PACKET Packet, NDIS_STATUS Status ) {
136 FreeNdisPacket( Packet );
137 }
138
139 NTSTATUS ICMPSendDatagram(
140 PADDRESS_FILE AddrFile,
141 PTDI_CONNECTION_INFORMATION ConnInfo,
142 PCHAR BufferData,
143 ULONG DataSize,
144 PULONG DataUsed )
145 /*
146 * FUNCTION: Sends an ICMP datagram to a remote address
147 * ARGUMENTS:
148 * Request = Pointer to TDI request
149 * ConnInfo = Pointer to connection information
150 * Buffer = Pointer to NDIS buffer with data
151 * DataSize = Size in bytes of data to be sent
152 * RETURNS:
153 * Status of operation
154 */
155 {
156 IP_PACKET Packet;
157 PTA_IP_ADDRESS RemoteAddressTa = (PTA_IP_ADDRESS)ConnInfo->RemoteAddress;
158 IP_ADDRESS RemoteAddress, LocalAddress;
159 USHORT RemotePort;
160 NTSTATUS Status;
161 PNEIGHBOR_CACHE_ENTRY NCE;
162 KIRQL OldIrql;
163
164 TI_DbgPrint(MID_TRACE,("Sending Datagram(%x %x %x %d)\n",
165 AddrFile, ConnInfo, BufferData, DataSize));
166 TI_DbgPrint(MID_TRACE,("RemoteAddressTa: %x\n", RemoteAddressTa));
167
168 switch( RemoteAddressTa->Address[0].AddressType ) {
169 case TDI_ADDRESS_TYPE_IP:
170 RemoteAddress.Type = IP_ADDRESS_V4;
171 RemoteAddress.Address.IPv4Address =
172 RemoteAddressTa->Address[0].Address[0].in_addr;
173 RemotePort = RemoteAddressTa->Address[0].Address[0].sin_port;
174 break;
175
176 default:
177 return STATUS_UNSUCCESSFUL;
178 }
179
180 TI_DbgPrint(MID_TRACE,("About to get route to destination\n"));
181
182 LockObject(AddrFile, &OldIrql);
183
184 LocalAddress = AddrFile->Address;
185 if (AddrIsUnspecified(&LocalAddress))
186 {
187 /* If the local address is unspecified (0),
188 * then use the unicast address of the
189 * interface we're sending over
190 */
191 if(!(NCE = RouteGetRouteToDestination( &RemoteAddress )))
192 {
193 UnlockObject(AddrFile, OldIrql);
194 return STATUS_NETWORK_UNREACHABLE;
195 }
196
197 LocalAddress = NCE->Interface->Unicast;
198 }
199 else
200 {
201 if(!(NCE = NBLocateNeighbor( &LocalAddress )))
202 {
203 UnlockObject(AddrFile, OldIrql);
204 return STATUS_INVALID_PARAMETER;
205 }
206 }
207
208 Status = PrepareICMPPacket( NCE->Interface,
209 &Packet,
210 &RemoteAddress,
211 BufferData,
212 DataSize );
213
214 if( !NT_SUCCESS(Status) )
215 {
216 UnlockObject(AddrFile, OldIrql);
217 return Status;
218 }
219
220 TI_DbgPrint(MID_TRACE,("About to send datagram\n"));
221
222 if (!NT_SUCCESS(Status = IPSendDatagram( &Packet, NCE, ICMPSendPacketComplete, NULL )))
223 {
224 UnlockObject(AddrFile, OldIrql);
225 FreeNdisPacket(Packet.NdisPacket);
226 return Status;
227 }
228
229 TI_DbgPrint(MID_TRACE,("Leaving\n"));
230
231 UnlockObject(AddrFile, OldIrql);
232
233 return STATUS_SUCCESS;
234 }
235
236
237
238 VOID ICMPReceive(
239 PIP_INTERFACE Interface,
240 PIP_PACKET IPPacket)
241 /*
242 * FUNCTION: Receives an ICMP packet
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 */
247 {
248 PICMP_HEADER ICMPHeader;
249
250 TI_DbgPrint(DEBUG_ICMP, ("Called.\n"));
251
252 ICMPHeader = (PICMP_HEADER)IPPacket->Data;
253
254 TI_DbgPrint(DEBUG_ICMP, ("Size (%d).\n", IPPacket->TotalSize));
255
256 TI_DbgPrint(DEBUG_ICMP, ("HeaderSize (%d).\n", IPPacket->HeaderSize));
257
258 TI_DbgPrint(DEBUG_ICMP, ("Type (%d).\n", ICMPHeader->Type));
259
260 TI_DbgPrint(DEBUG_ICMP, ("Code (%d).\n", ICMPHeader->Code));
261
262 TI_DbgPrint(DEBUG_ICMP, ("Checksum (0x%X).\n", ICMPHeader->Checksum));
263
264 RawIpReceive(Interface, IPPacket);
265
266 /* Checksum ICMP header and data */
267 if (!IPv4CorrectChecksum(IPPacket->Data, IPPacket->TotalSize - IPPacket->HeaderSize)) {
268 TI_DbgPrint(DEBUG_ICMP, ("Bad ICMP checksum.\n"));
269 /* Discard packet */
270 return;
271 }
272
273 switch (ICMPHeader->Type) {
274 case ICMP_TYPE_ECHO_REQUEST:
275 ICMPReply( Interface, IPPacket, ICMP_TYPE_ECHO_REPLY, 0 );
276 return;
277
278 case ICMP_TYPE_ECHO_REPLY:
279 break;
280
281 default:
282 TI_DbgPrint(DEBUG_ICMP,
283 ("Discarded ICMP datagram of unknown type %d.\n",
284 ICMPHeader->Type));
285 /* Discard packet */
286 break;
287 }
288 }
289
290
291 VOID ICMPTransmit(
292 PIP_PACKET IPPacket,
293 PIP_TRANSMIT_COMPLETE Complete,
294 PVOID Context)
295 /*
296 * FUNCTION: Transmits an ICMP packet
297 * ARGUMENTS:
298 * NTE = Pointer to net table entry to use (NULL if don't care)
299 * IPPacket = Pointer to IP packet to transmit
300 */
301 {
302 PNEIGHBOR_CACHE_ENTRY NCE;
303 NTSTATUS Status;
304
305 TI_DbgPrint(DEBUG_ICMP, ("Called.\n"));
306
307 /* Calculate checksum of ICMP header and data */
308 ((PICMP_HEADER)IPPacket->Data)->Checksum = (USHORT)
309 IPv4Checksum(IPPacket->Data, IPPacket->TotalSize - IPPacket->HeaderSize, 0);
310
311 /* Get a route to the destination address */
312 if ((NCE = RouteGetRouteToDestination(&IPPacket->DstAddr))) {
313 /* Send the packet */
314 Status = IPSendDatagram(IPPacket, NCE, Complete, Context);
315 if (!NT_SUCCESS(Status))
316 {
317 Complete(Context, IPPacket->NdisPacket, Status);
318 }
319 } else {
320 /* No route to destination (or no free resources) */
321 TI_DbgPrint(DEBUG_ICMP, ("No route to destination address 0x%X.\n",
322 IPPacket->DstAddr.Address.IPv4Address));
323 /* Discard packet */
324 Complete( Context, IPPacket->NdisPacket, NDIS_STATUS_NOT_ACCEPTED );
325 }
326 }
327
328
329 VOID ICMPReply(
330 PIP_INTERFACE Interface,
331 PIP_PACKET IPPacket,
332 UCHAR Type,
333 UCHAR Code)
334 /*
335 * FUNCTION: Transmits an ICMP packet in response to an incoming packet
336 * ARGUMENTS:
337 * NTE = Pointer to net table entry to use
338 * IPPacket = Pointer to IP packet that was received
339 * Type = ICMP message type
340 * Code = ICMP message code
341 * NOTES:
342 * We have received a packet from someone and is unable to
343 * process it due to error(s) in the packet or we have run out
344 * of resources. We transmit an ICMP message to the host to
345 * notify him of the problem
346 */
347 {
348 UINT DataSize;
349 IP_PACKET NewPacket = *IPPacket;
350
351 TI_DbgPrint(DEBUG_ICMP, ("Called. Type (%d) Code (%d).\n", Type, Code));
352
353 DataSize = IPPacket->TotalSize - IPPacket->HeaderSize;
354
355 if( !PrepareICMPPacket(Interface, &NewPacket, &IPPacket->SrcAddr,
356 IPPacket->Data, DataSize) ) return;
357
358 ((PICMP_HEADER)NewPacket.Data)->Type = Type;
359 ((PICMP_HEADER)NewPacket.Data)->Code = Code;
360 ((PICMP_HEADER)NewPacket.Data)->Checksum = 0;
361
362 ICMPTransmit(&NewPacket, SendICMPComplete, NULL);
363 }
364
365 /* EOF */