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