6802230ee0cfa4550c5a1b7b3930246e8b66f26d
[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
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 RemotePort = RemoteAddressTa->Address[0].Address[0].sin_port;
173 break;
174
175 default:
176 return STATUS_UNSUCCESSFUL;
177 }
178
179 TI_DbgPrint(MID_TRACE,("About to get route to destination\n"));
180
181 if(!(NCE = RouteGetRouteToDestination( &RemoteAddress )))
182 return STATUS_NETWORK_UNREACHABLE;
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 LocalAddress = NCE->Interface->Unicast;
192 }
193
194 Status = PrepareICMPPacket( NCE->Interface,
195 &Packet,
196 &RemoteAddress,
197 BufferData,
198 DataSize );
199
200 if( !NT_SUCCESS(Status) )
201 return Status;
202
203 TI_DbgPrint(MID_TRACE,("About to send datagram\n"));
204
205 if (!NT_SUCCESS(Status = IPSendDatagram( &Packet, NCE, ICMPSendPacketComplete, NULL )))
206 {
207 FreeNdisPacket(Packet.NdisPacket);
208 return Status;
209 }
210
211 TI_DbgPrint(MID_TRACE,("Leaving\n"));
212
213 return STATUS_SUCCESS;
214 }
215
216
217
218 VOID ICMPReceive(
219 PIP_INTERFACE Interface,
220 PIP_PACKET IPPacket)
221 /*
222 * FUNCTION: Receives an ICMP packet
223 * ARGUMENTS:
224 * NTE = Pointer to net table entry which the packet was received on
225 * IPPacket = Pointer to an IP packet that was received
226 */
227 {
228 PICMP_HEADER ICMPHeader;
229
230 TI_DbgPrint(DEBUG_ICMP, ("Called.\n"));
231
232 ICMPHeader = (PICMP_HEADER)IPPacket->Data;
233
234 TI_DbgPrint(DEBUG_ICMP, ("Size (%d).\n", IPPacket->TotalSize));
235
236 TI_DbgPrint(DEBUG_ICMP, ("HeaderSize (%d).\n", IPPacket->HeaderSize));
237
238 TI_DbgPrint(DEBUG_ICMP, ("Type (%d).\n", ICMPHeader->Type));
239
240 TI_DbgPrint(DEBUG_ICMP, ("Code (%d).\n", ICMPHeader->Code));
241
242 TI_DbgPrint(DEBUG_ICMP, ("Checksum (0x%X).\n", ICMPHeader->Checksum));
243
244 RawIpReceive(Interface, IPPacket);
245
246 /* Checksum ICMP header and data */
247 if (!IPv4CorrectChecksum(IPPacket->Data, IPPacket->TotalSize - IPPacket->HeaderSize)) {
248 TI_DbgPrint(DEBUG_ICMP, ("Bad ICMP checksum.\n"));
249 /* Discard packet */
250 return;
251 }
252
253 switch (ICMPHeader->Type) {
254 case ICMP_TYPE_ECHO_REQUEST:
255 ICMPReply( Interface, IPPacket, ICMP_TYPE_ECHO_REPLY, 0 );
256 return;
257
258 case ICMP_TYPE_ECHO_REPLY:
259 break;
260
261 default:
262 TI_DbgPrint(DEBUG_ICMP,
263 ("Discarded ICMP datagram of unknown type %d.\n",
264 ICMPHeader->Type));
265 /* Discard packet */
266 break;
267 }
268 }
269
270
271 VOID ICMPTransmit(
272 PIP_PACKET IPPacket,
273 PIP_TRANSMIT_COMPLETE Complete,
274 PVOID Context)
275 /*
276 * FUNCTION: Transmits an ICMP packet
277 * ARGUMENTS:
278 * NTE = Pointer to net table entry to use (NULL if don't care)
279 * IPPacket = Pointer to IP packet to transmit
280 */
281 {
282 PNEIGHBOR_CACHE_ENTRY NCE;
283 NTSTATUS Status;
284
285 TI_DbgPrint(DEBUG_ICMP, ("Called.\n"));
286
287 /* Calculate checksum of ICMP header and data */
288 ((PICMP_HEADER)IPPacket->Data)->Checksum = (USHORT)
289 IPv4Checksum(IPPacket->Data, IPPacket->TotalSize - IPPacket->HeaderSize, 0);
290
291 /* Get a route to the destination address */
292 if ((NCE = RouteGetRouteToDestination(&IPPacket->DstAddr))) {
293 /* Send the packet */
294 Status = IPSendDatagram(IPPacket, NCE, Complete, Context);
295 if (!NT_SUCCESS(Status))
296 {
297 Complete(Context, IPPacket->NdisPacket, Status);
298 }
299 } else {
300 /* No route to destination (or no free resources) */
301 TI_DbgPrint(DEBUG_ICMP, ("No route to destination address 0x%X.\n",
302 IPPacket->DstAddr.Address.IPv4Address));
303 /* Discard packet */
304 Complete( Context, IPPacket->NdisPacket, NDIS_STATUS_NOT_ACCEPTED );
305 }
306 }
307
308
309 VOID ICMPReply(
310 PIP_INTERFACE Interface,
311 PIP_PACKET IPPacket,
312 UCHAR Type,
313 UCHAR Code)
314 /*
315 * FUNCTION: Transmits an ICMP packet in response to an incoming packet
316 * ARGUMENTS:
317 * NTE = Pointer to net table entry to use
318 * IPPacket = Pointer to IP packet that was received
319 * Type = ICMP message type
320 * Code = ICMP message code
321 * NOTES:
322 * We have received a packet from someone and is unable to
323 * process it due to error(s) in the packet or we have run out
324 * of resources. We transmit an ICMP message to the host to
325 * notify him of the problem
326 */
327 {
328 UINT DataSize;
329 IP_PACKET NewPacket = *IPPacket;
330
331 TI_DbgPrint(DEBUG_ICMP, ("Called. Type (%d) Code (%d).\n", Type, Code));
332
333 DataSize = IPPacket->TotalSize - IPPacket->HeaderSize;
334
335 if( !PrepareICMPPacket(Interface, &NewPacket, &IPPacket->SrcAddr,
336 IPPacket->Data, DataSize) ) return;
337
338 ((PICMP_HEADER)NewPacket.Data)->Type = Type;
339 ((PICMP_HEADER)NewPacket.Data)->Code = Code;
340 ((PICMP_HEADER)NewPacket.Data)->Checksum = 0;
341
342 ICMPTransmit(&NewPacket, SendICMPComplete, NULL);
343 }
344
345 /* EOF */