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