More work on winsock stack (ping is now working)
[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 <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 PoolFreeBuffer(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 IPPacket = PoolAllocateBuffer(sizeof(IP_PACKET));
72 if (!IPPacket)
73 return NULL;
74
75 TI_DbgPrint(DEBUG_ICMP, ("IPPacket at (0x%X).\n", IPPacket));
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 PoolFreeBuffer(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 PoolFreeBuffer(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 PoolFreeBuffer(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 PoolFreeBuffer(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 PoolFreeBuffer(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 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
300 return;
301 }
302
303 RtlCopyMemory((PVOID)((ULONG_PTR)NewPacket->Data + sizeof(ICMP_HEADER)),
304 IPPacket->Header, DataSize);
305 ((PICMP_HEADER)NewPacket->Data)->Type = Type;
306 ((PICMP_HEADER)NewPacket->Data)->Code = Code;
307 ((PICMP_HEADER)NewPacket->Data)->Checksum = 0;
308
309 ICMPTransmit(NTE, NewPacket);
310 }
311
312 /* EOF */