:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / drivers / net / tcpip / 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 #include <tcpip.h>
11 #include <icmp.h>
12 #include <rawip.h>
13 #include <checksum.h>
14 #include <routines.h>
15 #include <transmit.h>
16 #include <pool.h>
17
18
19 VOID SendICMPComplete(
20     PVOID Context,
21     PNDIS_PACKET Packet,
22     NDIS_STATUS NdisStatus)
23 /*
24  * FUNCTION: ICMP datagram transmit completion handler
25  * ARGUMENTS:
26  *     Context    = Pointer to context infomation (IP_PACKET)
27  *     Packet     = Pointer to NDIS packet
28  *     NdisStatus = Status of transmit operation
29  * NOTES:
30  *     This routine is called by IP when a ICMP send completes
31  */
32 {
33     PIP_PACKET IPPacket = (PIP_PACKET)Context;
34
35     TI_DbgPrint(DEBUG_ICMP, ("Freeing NDIS packet (%X).\n", Packet));
36
37     /* Free packet */
38     FreeNdisPacket(Packet);
39
40     TI_DbgPrint(DEBUG_ICMP, ("Freeing IP packet at %X.\n", IPPacket));
41
42     (*IPPacket->Free)(IPPacket);
43 }
44
45
46 PIP_PACKET PrepareICMPPacket(
47     PNET_TABLE_ENTRY NTE,
48     PIP_ADDRESS Destination,
49     UINT DataSize)
50 /*
51  * FUNCTION: Prepares an ICMP packet
52  * ARGUMENTS:
53  *     NTE         = Pointer to net table entry to use
54  *     Destination = Pointer to destination address
55  *     DataSize    = Size of dataarea
56  * RETURNS:
57  *     Pointer to IP packet, NULL if there is not enough free resources
58  */
59 {
60     PIP_PACKET IPPacket;
61     PNDIS_PACKET NdisPacket;
62     PNDIS_BUFFER NdisBuffer;
63     NDIS_STATUS NdisStatus;
64     PIPv4_HEADER IPHeader;
65     PVOID DataBuffer;
66     ULONG Size;
67
68     TI_DbgPrint(DEBUG_ICMP, ("Called. DataSize (%d).\n", DataSize));
69
70     /* Prepare ICMP packet */
71
72     /* FIXME: Assumes IPv4*/
73     IPPacket = IPCreatePacket(IP_ADDRESS_V4);
74     if (!IPPacket)
75         return NULL;
76
77     /* No special flags */
78     IPPacket->Flags = 0;
79
80     Size = MaxLLHeaderSize + sizeof(IPv4_HEADER) +
81         sizeof(ICMP_HEADER) + DataSize;
82     DataBuffer = ExAllocatePool(NonPagedPool, Size);
83     if (!DataBuffer) {
84         (*IPPacket->Free)(IPPacket);
85         return NULL;
86     }
87
88     TI_DbgPrint(DEBUG_ICMP, ("Size (%d). Data at (0x%X).\n", Size, DataBuffer));
89
90     /* Allocate NDIS packet */
91     NdisAllocatePacket(&NdisStatus, &NdisPacket, GlobalPacketPool);
92     if (NdisStatus != NDIS_STATUS_SUCCESS) {
93         (*IPPacket->Free)(IPPacket);
94         ExFreePool(DataBuffer);
95         return NULL;
96     }
97
98     TI_DbgPrint(MAX_TRACE, ("NdisPacket at (0x%X).\n", NdisPacket));
99
100     /* Allocate NDIS buffer for maximum link level header and ICMP packet */
101     NdisAllocateBuffer(&NdisStatus, &NdisBuffer, GlobalBufferPool,
102         DataBuffer, Size);
103     if (NdisStatus != NDIS_STATUS_SUCCESS) {
104         (*IPPacket->Free)(IPPacket);
105         NdisFreePacket(NdisPacket);
106         ExFreePool(DataBuffer);
107         return NULL;
108     }
109
110     TI_DbgPrint(MAX_TRACE, ("NdisBuffer at (0x%X).\n", NdisBuffer));
111
112     /* Link NDIS buffer into packet */
113     NdisChainBufferAtFront(NdisPacket, NdisBuffer);
114     IPPacket->NdisPacket = NdisPacket;
115     IPPacket->Header     = (PVOID)((ULONG_PTR)DataBuffer + MaxLLHeaderSize);
116     IPPacket->Data       = (PVOID)((ULONG_PTR)DataBuffer + MaxLLHeaderSize + sizeof(IPv4_HEADER));
117
118     IPPacket->HeaderSize = sizeof(IPv4_HEADER);
119     IPPacket->TotalSize  = Size - MaxLLHeaderSize;
120     RtlCopyMemory(&IPPacket->DstAddr, Destination, sizeof(IP_ADDRESS));
121
122     /* Build IPv4 header. FIXME: IPv4 only */
123
124     IPHeader = (PIPv4_HEADER)IPPacket->Header;
125
126     /* Version = 4, Length = 5 DWORDs */
127     IPHeader->VerIHL = 0x45;
128     /* Normal Type-of-Service */
129     IPHeader->Tos = 0;
130     /* Length of data and header */
131     IPHeader->TotalLength = WH2N((USHORT)DataSize +
132         sizeof(IPv4_HEADER) + sizeof(ICMP_HEADER));
133     /* Identification */
134     IPHeader->Id = (USHORT)Random();
135     /* One fragment at offset 0 */
136     IPHeader->FlagsFragOfs = 0;
137     /* Time-to-Live is 128 */
138     IPHeader->Ttl = 128;
139     /* Internet Control Message Protocol */
140     IPHeader->Protocol = IPPROTO_ICMP;
141     /* Checksum is 0 (for later calculation of this) */
142     IPHeader->Checksum = 0;
143     /* Source address */
144     IPHeader->SrcAddr = NTE->Address->Address.IPv4Address;
145     /* Destination address */
146     IPHeader->DstAddr = Destination->Address.IPv4Address;
147
148     /* Completion handler */
149     PC(NdisPacket)->Complete = SendICMPComplete;
150     PC(NdisPacket)->Context  = IPPacket;
151
152     return IPPacket;
153 }
154
155
156 VOID ICMPReceive(
157     PNET_TABLE_ENTRY NTE,
158     PIP_PACKET IPPacket)
159 /*
160  * FUNCTION: Receives an ICMP packet
161  * ARGUMENTS:
162  *     NTE      = Pointer to net table entry which the packet was received on
163  *     IPPacket = Pointer to an IP packet that was received
164  */
165 {
166     PICMP_HEADER ICMPHeader;
167     PIP_PACKET NewPacket;
168     UINT DataSize;
169
170     TI_DbgPrint(DEBUG_ICMP, ("Called.\n"));
171
172     ICMPHeader = (PICMP_HEADER)IPPacket->Data;
173
174     TI_DbgPrint(DEBUG_ICMP, ("Size (%d).\n", IPPacket->TotalSize));
175
176     TI_DbgPrint(DEBUG_ICMP, ("HeaderSize (%d).\n", IPPacket->HeaderSize));
177
178     TI_DbgPrint(DEBUG_ICMP, ("Type (%d).\n", ICMPHeader->Type));
179
180     TI_DbgPrint(DEBUG_ICMP, ("Code (%d).\n", ICMPHeader->Code));
181
182     TI_DbgPrint(DEBUG_ICMP, ("Checksum (0x%X).\n", ICMPHeader->Checksum));
183
184     /* Checksum ICMP header and data */
185     if (!CorrectChecksum(IPPacket->Data, IPPacket->TotalSize - IPPacket->HeaderSize)) {
186         TI_DbgPrint(DEBUG_ICMP, ("Bad ICMP checksum.\n"));
187         /* Discard packet */
188         return;
189     }
190
191     switch (ICMPHeader->Type) {
192     case ICMP_TYPE_ECHO_REQUEST:
193         /* Reply with an ICMP echo reply message */
194         DataSize  = IPPacket->TotalSize - IPPacket->HeaderSize - sizeof(ICMP_HEADER);
195         NewPacket = PrepareICMPPacket(NTE, &IPPacket->SrcAddr, DataSize);
196         if (!NewPacket)
197             return;
198
199         /* Copy ICMP header and data into new packet */
200         RtlCopyMemory(NewPacket->Data, IPPacket->Data, DataSize + sizeof(ICMP_HEADER));
201         ((PICMP_HEADER)NewPacket->Data)->Type     = ICMP_TYPE_ECHO_REPLY;
202         ((PICMP_HEADER)NewPacket->Data)->Code     = 0;
203         ((PICMP_HEADER)NewPacket->Data)->Checksum = 0;
204
205         DisplayIPPacket(IPPacket);
206
207         DisplayIPPacket(NewPacket);
208
209         ICMPTransmit(NTE, NewPacket);
210
211         TI_DbgPrint(DEBUG_ICMP, ("Echo reply sent.\n"));
212         return;
213
214     case ICMP_TYPE_ECHO_REPLY:
215         break;
216
217     default:
218         TI_DbgPrint(DEBUG_ICMP, ("Discarded ICMP datagram of unknown type %d.\n",
219             ICMPHeader->Type));
220         /* Discard packet */
221         break;
222     }
223
224     /* Send datagram up the protocol stack */
225     RawIPReceive(NTE, IPPacket);
226 }
227
228
229 VOID ICMPTransmit(
230     PNET_TABLE_ENTRY NTE,
231     PIP_PACKET IPPacket)
232 /*
233  * FUNCTION: Transmits an ICMP packet
234  * ARGUMENTS:
235  *     NTE      = Pointer to net table entry to use (NULL if don't care)
236  *     IPPacket = Pointer to IP packet to transmit
237  */
238 {
239     PROUTE_CACHE_NODE RCN;
240
241     TI_DbgPrint(DEBUG_ICMP, ("Called.\n"));
242
243     /* Calculate checksum of ICMP header and data */
244     ((PICMP_HEADER)IPPacket->Data)->Checksum = (USHORT)
245         IPv4Checksum(IPPacket->Data, IPPacket->TotalSize - IPPacket->HeaderSize, 0);
246
247     /* Get a route to the destination address */
248     if (RouteGetRouteToDestination(&IPPacket->DstAddr, NTE, &RCN) == IP_SUCCESS) {
249         /* Send the packet */
250         if (IPSendDatagram(IPPacket, RCN) != STATUS_SUCCESS) {
251             FreeNdisPacket(IPPacket->NdisPacket);
252             (*IPPacket->Free)(IPPacket);
253         }
254         /* We're done with the RCN */
255         DereferenceObject(RCN);
256     } else {
257         TI_DbgPrint(MIN_TRACE, ("RCN at (0x%X).\n", RCN));
258
259         /* No route to destination (or no free resources) */
260         TI_DbgPrint(DEBUG_ICMP, ("No route to destination address 0x%X.\n",
261             IPPacket->DstAddr.Address.IPv4Address));
262         /* Discard packet */
263         FreeNdisPacket(IPPacket->NdisPacket);
264         (*IPPacket->Free)(IPPacket);
265     }
266 }
267
268
269 VOID ICMPReply(
270     PNET_TABLE_ENTRY NTE,
271     PIP_PACKET IPPacket,
272           UCHAR Type,
273           UCHAR Code)
274 /*
275  * FUNCTION: Transmits an ICMP packet in response to an incoming packet
276  * ARGUMENTS:
277  *     NTE      = Pointer to net table entry to use
278  *     IPPacket = Pointer to IP packet that was received
279  *     Type     = ICMP message type
280  *     Code     = ICMP message code
281  * NOTES:
282  *     We have received a packet from someone and is unable to
283  *     process it due to error(s) in the packet or we have run out
284  *     of resources. We transmit an ICMP message to the host to
285  *     notify him of the problem
286  */
287 {
288     UINT DataSize;
289     PIP_PACKET NewPacket;
290
291     TI_DbgPrint(DEBUG_ICMP, ("Called. Type (%d)  Code (%d).\n", Type, Code));
292
293     DataSize = IPPacket->TotalSize;
294     if ((DataSize) > (576 - sizeof(IPv4_HEADER) - sizeof(ICMP_HEADER)))
295         DataSize = 576;
296
297     NewPacket = PrepareICMPPacket(NTE, &IPPacket->SrcAddr, DataSize);
298     if (!NewPacket)
299         return;
300
301     RtlCopyMemory((PVOID)((ULONG_PTR)NewPacket->Data + sizeof(ICMP_HEADER)),
302         IPPacket->Header, DataSize);
303     ((PICMP_HEADER)NewPacket->Data)->Type     = Type;
304     ((PICMP_HEADER)NewPacket->Data)->Code     = Code;
305     ((PICMP_HEADER)NewPacket->Data)->Checksum = 0;
306
307     ICMPTransmit(NTE, NewPacket);
308 }
309
310 /* EOF */