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