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