5af8086678465336c9ba67e2a3b073bbbc3ba845
[reactos.git] / reactos / 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 PADDRESS_FILE AddrFile,
52 PIP_INTERFACE Interface,
53 PIP_PACKET IPPacket,
54 PIP_ADDRESS Destination,
55 PCHAR Data,
56 UINT DataSize)
57 /*
58 * FUNCTION: Prepares an ICMP packet
59 * ARGUMENTS:
60 * NTE = Pointer to net table entry to use
61 * Destination = Pointer to destination address
62 * DataSize = Size of dataarea
63 * RETURNS:
64 * Pointer to IP packet, NULL if there is not enough free resources
65 */
66 {
67 PNDIS_PACKET NdisPacket;
68 NDIS_STATUS NdisStatus;
69 PIPv4_HEADER IPHeader;
70 ULONG Size;
71
72 TI_DbgPrint(DEBUG_ICMP, ("Called. DataSize (%d).\n", DataSize));
73
74 IPInitializePacket(IPPacket, IP_ADDRESS_V4);
75
76 /* No special flags */
77 IPPacket->Flags = 0;
78
79 Size = sizeof(IPv4_HEADER) + DataSize;
80
81 /* Allocate NDIS packet */
82 NdisStatus = AllocatePacketWithBuffer( &NdisPacket, NULL, Size );
83
84 if( !NT_SUCCESS(NdisStatus) ) return FALSE;
85
86 IPPacket->NdisPacket = NdisPacket;
87 IPPacket->MappedHeader = TRUE;
88
89 GetDataPtr( IPPacket->NdisPacket, 0,
90 (PCHAR *)&IPPacket->Header, &IPPacket->TotalSize );
91 ASSERT(IPPacket->TotalSize == Size);
92
93 TI_DbgPrint(DEBUG_ICMP, ("Size (%d). Data at (0x%X).\n", Size, Data));
94 TI_DbgPrint(DEBUG_ICMP, ("NdisPacket at (0x%X).\n", NdisPacket));
95
96 IPPacket->HeaderSize = sizeof(IPv4_HEADER);
97 IPPacket->Data = ((PCHAR)IPPacket->Header) + IPPacket->HeaderSize;
98
99 TI_DbgPrint(DEBUG_ICMP, ("Copying Address: %x -> %x\n",
100 &IPPacket->DstAddr, Destination));
101
102 RtlCopyMemory(&IPPacket->DstAddr, Destination, sizeof(IP_ADDRESS));
103 RtlCopyMemory(IPPacket->Data, Data, DataSize);
104
105 /* Build IPv4 header. FIXME: IPv4 only */
106
107 IPHeader = (PIPv4_HEADER)IPPacket->Header;
108
109 /* Version = 4, Length = 5 DWORDs */
110 IPHeader->VerIHL = 0x45;
111 /* Normal Type-of-Service */
112 IPHeader->Tos = 0;
113 /* Length of data and header */
114 IPHeader->TotalLength = WH2N((USHORT)DataSize + sizeof(IPv4_HEADER));
115 /* Identification */
116 IPHeader->Id = (USHORT)Random();
117 /* One fragment at offset 0 */
118 IPHeader->FlagsFragOfs = 0;
119 /* Set TTL */
120 if (AddrFile)
121 IPHeader->Ttl = AddrFile->TTL;
122 else
123 IPHeader->Ttl = 128;
124 /* Internet Control Message Protocol */
125 IPHeader->Protocol = IPPROTO_ICMP;
126 /* Checksum is 0 (for later calculation of this) */
127 IPHeader->Checksum = 0;
128 /* Source address */
129 IPHeader->SrcAddr = Interface->Unicast.Address.IPv4Address;
130 /* Destination address */
131 IPHeader->DstAddr = Destination->Address.IPv4Address;
132
133
134 TI_DbgPrint(MID_TRACE,("Leaving\n"));
135
136 return TRUE;
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 NTSTATUS Status;
160 PNEIGHBOR_CACHE_ENTRY NCE;
161 KIRQL OldIrql;
162
163 TI_DbgPrint(MID_TRACE,("Sending Datagram(%x %x %x %d)\n",
164 AddrFile, ConnInfo, BufferData, DataSize));
165 TI_DbgPrint(MID_TRACE,("RemoteAddressTa: %x\n", RemoteAddressTa));
166
167 switch( RemoteAddressTa->Address[0].AddressType ) {
168 case TDI_ADDRESS_TYPE_IP:
169 RemoteAddress.Type = IP_ADDRESS_V4;
170 RemoteAddress.Address.IPv4Address =
171 RemoteAddressTa->Address[0].Address[0].in_addr;
172 break;
173
174 default:
175 return STATUS_UNSUCCESSFUL;
176 }
177
178 TI_DbgPrint(MID_TRACE,("About to get route to destination\n"));
179
180 LockObject(AddrFile, &OldIrql);
181
182 LocalAddress = AddrFile->Address;
183 if (AddrIsUnspecified(&LocalAddress))
184 {
185 /* If the local address is unspecified (0),
186 * then use the unicast address of the
187 * interface we're sending over
188 */
189 if(!(NCE = RouteGetRouteToDestination( &RemoteAddress )))
190 {
191 UnlockObject(AddrFile, OldIrql);
192 return STATUS_NETWORK_UNREACHABLE;
193 }
194
195 LocalAddress = NCE->Interface->Unicast;
196 }
197 else
198 {
199 if(!(NCE = NBLocateNeighbor( &LocalAddress )))
200 {
201 UnlockObject(AddrFile, OldIrql);
202 return STATUS_INVALID_PARAMETER;
203 }
204 }
205
206 Status = PrepareICMPPacket( AddrFile,
207 NCE->Interface,
208 &Packet,
209 &RemoteAddress,
210 BufferData,
211 DataSize );
212
213 UnlockObject(AddrFile, OldIrql);
214
215 if( !NT_SUCCESS(Status) )
216 return Status;
217
218 TI_DbgPrint(MID_TRACE,("About to send datagram\n"));
219
220 Status = IPSendDatagram(&Packet, NCE);
221 if (!NT_SUCCESS(Status))
222 {
223 Packet.Free(&Packet);
224 return Status;
225 }
226
227 *DataUsed = DataSize;
228
229 TI_DbgPrint(MID_TRACE,("Leaving\n"));
230
231 return STATUS_SUCCESS;
232 }
233
234
235 VOID ICMPReceive(
236 PIP_INTERFACE Interface,
237 PIP_PACKET IPPacket)
238 /*
239 * FUNCTION: Receives an ICMP packet
240 * ARGUMENTS:
241 * NTE = Pointer to net table entry which the packet was received on
242 * IPPacket = Pointer to an IP packet that was received
243 */
244 {
245 PICMP_HEADER ICMPHeader;
246
247 TI_DbgPrint(DEBUG_ICMP, ("Called.\n"));
248
249 ICMPHeader = (PICMP_HEADER)IPPacket->Data;
250
251 TI_DbgPrint(DEBUG_ICMP, ("Size (%d).\n", IPPacket->TotalSize));
252
253 TI_DbgPrint(DEBUG_ICMP, ("HeaderSize (%d).\n", IPPacket->HeaderSize));
254
255 TI_DbgPrint(DEBUG_ICMP, ("Type (%d).\n", ICMPHeader->Type));
256
257 TI_DbgPrint(DEBUG_ICMP, ("Code (%d).\n", ICMPHeader->Code));
258
259 TI_DbgPrint(DEBUG_ICMP, ("Checksum (0x%X).\n", ICMPHeader->Checksum));
260
261 /* Checksum ICMP header and data */
262 if (!IPv4CorrectChecksum(IPPacket->Data, IPPacket->TotalSize - IPPacket->HeaderSize)) {
263 TI_DbgPrint(DEBUG_ICMP, ("Bad ICMP checksum.\n"));
264 /* Discard packet */
265 return;
266 }
267
268 RawIpReceive(Interface, IPPacket);
269
270 switch (ICMPHeader->Type) {
271 case ICMP_TYPE_ECHO_REQUEST:
272 ICMPReply( Interface, IPPacket, ICMP_TYPE_ECHO_REPLY, 0 );
273 break;
274
275 case ICMP_TYPE_ECHO_REPLY:
276 break;
277
278 default:
279 TI_DbgPrint(DEBUG_ICMP,
280 ("Discarded ICMP datagram of unknown type %d.\n",
281 ICMPHeader->Type));
282 /* Discard packet */
283 break;
284 }
285 }
286
287
288 VOID ICMPTransmit(
289 PIP_PACKET IPPacket,
290 PIP_TRANSMIT_COMPLETE Complete,
291 PVOID Context)
292 /*
293 * FUNCTION: Transmits an ICMP packet
294 * ARGUMENTS:
295 * NTE = Pointer to net table entry to use (NULL if don't care)
296 * IPPacket = Pointer to IP packet to transmit
297 */
298 {
299 PNEIGHBOR_CACHE_ENTRY NCE;
300
301 TI_DbgPrint(DEBUG_ICMP, ("Called.\n"));
302
303 /* Calculate checksum of ICMP header and data */
304 ((PICMP_HEADER)IPPacket->Data)->Checksum = (USHORT)
305 IPv4Checksum(IPPacket->Data, IPPacket->TotalSize - IPPacket->HeaderSize, 0);
306
307 /* Get a route to the destination address */
308 if ((NCE = RouteGetRouteToDestination(&IPPacket->DstAddr))) {
309 /* Send the packet */
310 IPSendDatagram(IPPacket, NCE);
311 } else {
312 /* No route to destination (or no free resources) */
313 TI_DbgPrint(DEBUG_ICMP, ("No route to destination address 0x%X.\n",
314 IPPacket->DstAddr.Address.IPv4Address));
315 IPPacket->Free(IPPacket);
316 }
317 }
318
319
320 VOID ICMPReply(
321 PIP_INTERFACE Interface,
322 PIP_PACKET IPPacket,
323 UCHAR Type,
324 UCHAR Code)
325 /*
326 * FUNCTION: Transmits an ICMP packet in response to an incoming packet
327 * ARGUMENTS:
328 * NTE = Pointer to net table entry to use
329 * IPPacket = Pointer to IP packet that was received
330 * Type = ICMP message type
331 * Code = ICMP message code
332 * NOTES:
333 * We have received a packet from someone and is unable to
334 * process it due to error(s) in the packet or we have run out
335 * of resources. We transmit an ICMP message to the host to
336 * notify him of the problem
337 */
338 {
339 UINT DataSize;
340 IP_PACKET NewPacket;
341
342 TI_DbgPrint(DEBUG_ICMP, ("Called. Type (%d) Code (%d).\n", Type, Code));
343
344 DataSize = IPPacket->TotalSize - IPPacket->HeaderSize;
345
346 if( !PrepareICMPPacket(NULL, Interface, &NewPacket, &IPPacket->SrcAddr,
347 IPPacket->Data, DataSize) ) return;
348
349 ((PICMP_HEADER)NewPacket.Data)->Type = Type;
350 ((PICMP_HEADER)NewPacket.Data)->Code = Code;
351 ((PICMP_HEADER)NewPacket.Data)->Checksum = 0;
352
353 ICMPTransmit(&NewPacket, NULL, NULL);
354 }
355
356 /* EOF */