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