2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: network/receive.c
5 * PURPOSE: Internet Protocol receive routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * NOTES: The IP datagram reassembly algorithm is taken from
10 * CSH 01/08-2000 Created
22 LIST_ENTRY ReassemblyListHead;
23 KSPIN_LOCK ReassemblyListLock;
24 NPAGED_LOOKASIDE_LIST IPDRList;
25 NPAGED_LOOKASIDE_LIST IPFragmentList;
26 NPAGED_LOOKASIDE_LIST IPHoleList;
29 PIPDATAGRAM_HOLE CreateHoleDescriptor(
33 * FUNCTION: Returns a pointer to a IP datagram hole descriptor
35 * First = Offset of first octet of the hole
36 * Last = Offset of last octet of the hole
38 * Pointer to descriptor, NULL if there was not enough free
42 PIPDATAGRAM_HOLE Hole;
44 TI_DbgPrint(DEBUG_IP, ("Called. First (%d) Last (%d).\n", First, Last));
46 Hole = ExAllocateFromNPagedLookasideList(&IPHoleList);
48 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
55 TI_DbgPrint(DEBUG_IP, ("Returning hole descriptor at (0x%X).\n", Hole));
62 PIPDATAGRAM_REASSEMBLY IPDR)
64 * FUNCTION: Frees an IP datagram reassembly structure
66 * IPDR = Pointer to IP datagram reassembly structure
69 PLIST_ENTRY CurrentEntry;
70 PLIST_ENTRY NextEntry;
71 PIPDATAGRAM_HOLE CurrentH;
72 PIP_FRAGMENT CurrentF;
74 TI_DbgPrint(DEBUG_IP, ("Freeing IP datagram reassembly descriptor (0x%X).\n", IPDR));
76 /* Free all descriptors */
77 CurrentEntry = IPDR->HoleListHead.Flink;
78 while (CurrentEntry != &IPDR->HoleListHead) {
79 NextEntry = CurrentEntry->Flink;
80 CurrentH = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_HOLE, ListEntry);
81 /* Unlink it from the list */
82 RemoveEntryList(CurrentEntry);
84 TI_DbgPrint(DEBUG_IP, ("Freeing hole descriptor at (0x%X).\n", CurrentH));
86 /* And free the hole descriptor */
87 ExFreeToNPagedLookasideList(&IPHoleList, CurrentH);
89 CurrentEntry = NextEntry;
92 /* Free all fragments */
93 CurrentEntry = IPDR->FragmentListHead.Flink;
94 while (CurrentEntry != &IPDR->FragmentListHead) {
95 NextEntry = CurrentEntry->Flink;
96 CurrentF = CONTAINING_RECORD(CurrentEntry, IP_FRAGMENT, ListEntry);
97 /* Unlink it from the list */
98 RemoveEntryList(CurrentEntry);
100 TI_DbgPrint(DEBUG_IP, ("Freeing fragment data at (0x%X).\n", CurrentF->Data));
102 /* Free the fragment data buffer */
103 ExFreePool(CurrentF->Data);
105 TI_DbgPrint(DEBUG_IP, ("Freeing fragment at (0x%X).\n", CurrentF));
107 /* And free the fragment descriptor */
108 ExFreeToNPagedLookasideList(&IPFragmentList, CurrentF);
109 CurrentEntry = NextEntry;
112 /* Free resources for the header, if it exists */
113 if (IPDR->IPv4Header) {
114 TI_DbgPrint(DEBUG_IP, ("Freeing IPv4 header data at (0x%X).\n", IPDR->IPv4Header));
115 ExFreePool(IPDR->IPv4Header);
118 TI_DbgPrint(DEBUG_IP, ("Freeing IPDR data at (0x%X).\n", IPDR));
120 ExFreeToNPagedLookasideList(&IPDRList, IPDR);
125 PIPDATAGRAM_REASSEMBLY IPDR)
127 * FUNCTION: Removes an IP datagram reassembly structure from the global list
129 * IPDR = Pointer to IP datagram reassembly structure
134 TI_DbgPrint(DEBUG_IP, ("Removing IPDR at (0x%X).\n", IPDR));
136 KeAcquireSpinLock(&ReassemblyListLock, &OldIrql);
137 RemoveEntryList(&IPDR->ListEntry);
138 KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
142 PIPDATAGRAM_REASSEMBLY GetReassemblyInfo(
145 * FUNCTION: Returns a pointer to an IP datagram reassembly structure
147 * IPPacket = Pointer to IP packet
149 * A datagram is identified by four paramters, which are
150 * Source and destination address, protocol number and
151 * identification number
155 PLIST_ENTRY CurrentEntry;
156 PIPDATAGRAM_REASSEMBLY Current;
157 PIPv4_HEADER Header = (PIPv4_HEADER)IPPacket->Header;
159 TI_DbgPrint(DEBUG_IP, ("Searching for IPDR for IP packet at (0x%X).\n", IPPacket));
161 KeAcquireSpinLock(&ReassemblyListLock, &OldIrql);
163 /* FIXME: Assume IPv4 */
165 CurrentEntry = ReassemblyListHead.Flink;
166 while (CurrentEntry != &ReassemblyListHead) {
167 Current = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_REASSEMBLY, ListEntry);
168 if (AddrIsEqual(&IPPacket->SrcAddr, &Current->SrcAddr) &&
169 (Header->Id == Current->Id) &&
170 (Header->Protocol == Current->Protocol) &&
171 (AddrIsEqual(&IPPacket->DstAddr, &Current->DstAddr))) {
172 KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
176 CurrentEntry = CurrentEntry->Flink;
179 KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
185 PIP_PACKET ReassembleDatagram(
186 PIPDATAGRAM_REASSEMBLY IPDR)
188 * FUNCTION: Reassembles an IP datagram
190 * IPDR = Pointer to IP datagram reassembly structure
192 * This routine concatenates fragments into a complete IP datagram.
193 * The lock is held when this routine is called
195 * Pointer to IP packet, NULL if there was not enough free resources
199 PLIST_ENTRY CurrentEntry;
200 PIP_FRAGMENT Current;
203 TI_DbgPrint(DEBUG_IP, ("Reassembling datagram from IPDR at (0x%X).\n", IPDR));
205 /* FIXME: Assume IPv4 */
206 IPPacket = IPCreatePacket(IP_ADDRESS_V4);
210 IPPacket->TotalSize = IPDR->HeaderSize + IPDR->DataSize;
211 IPPacket->ContigSize = IPPacket->TotalSize;
212 IPPacket->HeaderSize = IPDR->HeaderSize;
213 IPPacket->Position = IPDR->HeaderSize;
215 RtlCopyMemory(&IPPacket->SrcAddr, &IPDR->SrcAddr, sizeof(IP_ADDRESS));
216 RtlCopyMemory(&IPPacket->DstAddr, &IPDR->DstAddr, sizeof(IP_ADDRESS));
218 /* Allocate space for full IP datagram */
219 IPPacket->Header = ExAllocatePool(NonPagedPool, IPPacket->TotalSize);
220 if (!IPPacket->Header) {
221 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
222 (*IPPacket->Free)(IPPacket);
226 /* Copy the header into the buffer */
227 RtlCopyMemory(IPPacket->Header, IPDR->IPv4Header, IPDR->HeaderSize);
229 Data = (PVOID)((ULONG_PTR)IPPacket->Header + IPDR->HeaderSize);
230 IPPacket->Data = Data;
232 /* Copy data from all fragments into buffer */
233 CurrentEntry = IPDR->FragmentListHead.Flink;
234 while (CurrentEntry != &IPDR->FragmentListHead) {
235 Current = CONTAINING_RECORD(CurrentEntry, IP_FRAGMENT, ListEntry);
237 TI_DbgPrint(DEBUG_IP, ("Copying (%d) bytes of fragment data from (0x%X) to offset (%d).\n",
238 Current->Size, Data, Current->Offset));
239 /* Copy fragment data to the destination buffer at the correct offset */
241 (PVOID)((ULONG_PTR)Data + Current->Offset),
245 CurrentEntry = CurrentEntry->Flink;
252 __inline VOID Cleanup(
255 PIPDATAGRAM_REASSEMBLY IPDR,
256 PVOID Buffer OPTIONAL)
258 * FUNCTION: Performs cleaning operations on errors
260 * Lock = Pointer to spin lock to be released
261 * OldIrql = Value of IRQL when spin lock was acquired
262 * IPDR = Pointer to IP datagram reassembly structure to free
263 * Buffer = Optional pointer to a buffer to free
266 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
268 KeReleaseSpinLock(Lock, OldIrql);
276 VOID ProcessFragment(
279 PNET_TABLE_ENTRY NTE)
281 * FUNCTION: Processes an IP datagram or fragment
283 * IF = Pointer to IP interface packet was receive on
284 * IPPacket = Pointer to IP packet
285 * NTE = Pointer to NTE packet was received on
287 * This routine reassembles fragments and, if a whole datagram can
288 * be assembled, passes the datagram on to the IP protocol dispatcher
292 PIPDATAGRAM_REASSEMBLY IPDR;
293 PLIST_ENTRY CurrentEntry;
294 PIPDATAGRAM_HOLE Hole, NewHole;
297 BOOLEAN MoreFragments;
298 PIPv4_HEADER IPv4Header;
300 PIP_FRAGMENT Fragment;
302 /* FIXME: Assume IPv4 */
304 IPv4Header = (PIPv4_HEADER)IPPacket->Header;
306 /* Check if we already have an reassembly structure for this datagram */
307 IPDR = GetReassemblyInfo(IPPacket);
309 TI_DbgPrint(DEBUG_IP, ("Continueing assembly.\n"));
310 /* We have a reassembly structure */
311 KeAcquireSpinLock(&IPDR->Lock, &OldIrql);
312 CurrentEntry = IPDR->HoleListHead.Flink;
313 Hole = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_HOLE, ListEntry);
315 TI_DbgPrint(DEBUG_IP, ("Starting new assembly.\n"));
317 /* We don't have a reassembly structure, create one */
318 IPDR = ExAllocateFromNPagedLookasideList(&IPDRList);
320 /* We don't have the resources to process this packet, discard it */
323 /* Create a descriptor spanning from zero to infinity.
324 Actually, we use a value slightly greater than the
325 maximum number of octets an IP datagram can contain */
326 Hole = CreateHoleDescriptor(0, 65536);
328 /* We don't have the resources to process this packet, discard it */
329 ExFreeToNPagedLookasideList(&IPDRList, IPDR);
332 AddrInitIPv4(&IPDR->SrcAddr, IPv4Header->SrcAddr);
333 AddrInitIPv4(&IPDR->DstAddr, IPv4Header->DstAddr);
334 IPDR->Id = IPv4Header->Id;
335 IPDR->Protocol = IPv4Header->Protocol;
336 IPDR->IPv4Header = NULL;
337 InitializeListHead(&IPDR->FragmentListHead);
338 InitializeListHead(&IPDR->HoleListHead);
339 InsertTailList(&IPDR->HoleListHead, &Hole->ListEntry);
340 CurrentEntry = IPDR->HoleListHead.Flink;
342 KeInitializeSpinLock(&IPDR->Lock);
344 KeAcquireSpinLock(&IPDR->Lock, &OldIrql);
346 /* Update the reassembly list */
347 ExInterlockedInsertTailList(
350 &ReassemblyListLock);
353 FragFirst = (WN2H(IPv4Header->FlagsFragOfs) & IPv4_FRAGOFS_MASK) << 3;
354 FragLast = FragFirst + WN2H(IPv4Header->TotalLength);
355 MoreFragments = (WN2H(IPv4Header->FlagsFragOfs) & IPv4_MF_MASK) > 0;
358 if (CurrentEntry == &IPDR->HoleListHead)
359 /* No more entries */
362 TI_DbgPrint(DEBUG_IP, ("Comparing Fragment (%d,%d) to Hole (%d,%d).\n",
363 FragFirst, FragLast, Hole->First, Hole->Last));
365 if ((FragFirst > Hole->Last) || (FragLast < Hole->First)) {
366 TI_DbgPrint(MID_TRACE, ("No overlap.\n"));
367 /* The fragment does not overlap with the hole, try next
368 descriptor in the list */
370 CurrentEntry = CurrentEntry->Flink;
371 if (CurrentEntry != &IPDR->HoleListHead)
372 Hole = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_HOLE, ListEntry);
376 /* The fragment overlap with the hole, unlink the descriptor */
377 RemoveEntryList(CurrentEntry);
379 if (FragFirst > Hole->First) {
380 NewHole = CreateHoleDescriptor(Hole->First, FragLast - 1);
382 /* We don't have the resources to process this packet, discard it */
383 Cleanup(&IPDR->Lock, OldIrql, IPDR, Hole);
387 /* Put the new descriptor in the list */
388 InsertTailList(&IPDR->HoleListHead, &NewHole->ListEntry);
391 if ((FragLast < Hole->Last) && (MoreFragments)) {
392 /* We can reuse the descriptor for the new hole */
393 Hole->First = FragLast + 1;
395 /* Put the new hole descriptor in the list */
396 InsertTailList(&IPDR->HoleListHead, &Hole->ListEntry);
398 ExFreeToNPagedLookasideList(&IPHoleList, Hole);
400 /* If this is the first fragment, save the IP header */
401 if (FragFirst == 0) {
402 IPDR->IPv4Header = ExAllocatePool(NonPagedPool, IPPacket->HeaderSize);
403 if (!IPDR->IPv4Header) {
404 /* We don't have the resources to process this packet, discard it */
405 Cleanup(&IPDR->Lock, OldIrql, IPDR, NULL);
409 TI_DbgPrint(DEBUG_IP, ("First fragment found. Header buffer is at (0x%X). "
410 "Header size is (%d).\n", IPDR->IPv4Header, IPPacket->HeaderSize));
412 RtlCopyMemory(IPDR->IPv4Header, IPPacket->Header, IPPacket->HeaderSize);
413 IPDR->HeaderSize = IPPacket->HeaderSize;
416 /* Create a buffer, copy the data into it and put it
417 in the fragment list */
419 Fragment = ExAllocateFromNPagedLookasideList(&IPFragmentList);
421 /* We don't have the resources to process this packet, discard it */
422 Cleanup(&IPDR->Lock, OldIrql, IPDR, NULL);
426 TI_DbgPrint(DEBUG_IP, ("Fragment descriptor allocated at (0x%X).\n", Fragment));
428 Fragment->Size = IPPacket->TotalSize - IPPacket->HeaderSize;
429 Fragment->Data = ExAllocatePool(NonPagedPool, Fragment->Size);
430 if (!Fragment->Data) {
431 /* We don't have the resources to process this packet, discard it */
432 Cleanup(&IPDR->Lock, OldIrql, IPDR, Fragment);
436 TI_DbgPrint(DEBUG_IP, ("Fragment data buffer allocated at (0x%X) Size (%d).\n",
437 Fragment->Data, Fragment->Size));
439 /* Copy datagram data into fragment buffer */
442 IPPacket->NdisPacket,
445 Fragment->Offset = FragFirst;
447 /* If this is the last fragment, compute and save the datagram data size */
449 IPDR->DataSize = FragFirst + Fragment->Size;
451 /* Put the fragment in the list */
452 InsertTailList(&IPDR->FragmentListHead, &Fragment->ListEntry);
456 TI_DbgPrint(DEBUG_IP, ("Done searching for hole descriptor.\n"));
458 if (IsListEmpty(&IPDR->HoleListHead)) {
459 /* Hole list is empty which means a complete datagram can be assembled.
460 Assemble the datagram and pass it to an upper layer protocol */
462 TI_DbgPrint(DEBUG_IP, ("Complete datagram received.\n"));
464 Datagram = ReassembleDatagram(IPDR);
466 KeReleaseSpinLock(&IPDR->Lock, OldIrql);
472 /* Not enough free resources, discard the packet */
475 DISPLAY_IP_PACKET(Datagram);
477 /* Give the packet to the protocol dispatcher */
478 IPDispatchProtocol(NTE, Datagram);
480 /* We're done with this datagram */
481 ExFreePool(Datagram->Header);
482 TI_DbgPrint(MAX_TRACE, ("Freeing datagram at (0x%X).\n", Datagram));
483 (*Datagram->Free)(Datagram);
485 KeReleaseSpinLock(&IPDR->Lock, OldIrql);
489 VOID IPFreeReassemblyList(
492 * FUNCTION: Frees all IP datagram reassembly structures in the list
496 PLIST_ENTRY CurrentEntry;
497 PIPDATAGRAM_REASSEMBLY Current;
499 KeAcquireSpinLock(&ReassemblyListLock, &OldIrql);
501 CurrentEntry = ReassemblyListHead.Flink;
502 while (CurrentEntry != &ReassemblyListHead) {
503 Current = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_REASSEMBLY, ListEntry);
504 /* Unlink it from the list */
505 RemoveEntryList(CurrentEntry);
507 /* And free the descriptor */
510 CurrentEntry = CurrentEntry->Flink;
513 KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
517 VOID IPDatagramReassemblyTimeout(
520 * FUNCTION: IP datagram reassembly timeout handler
522 * This routine is called by IPTimeout to free any resources used
523 * to hold IP fragments that have taken too long to reassemble
533 * FUNCTION: Receives an IPv4 datagram (or fragment)
535 * Context = Pointer to context information (IP_INTERFACE)
536 * IPPacket = Pointer to IP packet
539 PNEIGHBOR_CACHE_ENTRY NCE;
540 PNET_TABLE_ENTRY NTE;
543 TI_DbgPrint(DEBUG_IP, ("Received IPv4 datagram.\n"));
545 IPPacket->HeaderSize = (((PIPv4_HEADER)IPPacket->Header)->VerIHL & 0x0F) << 2;
547 if (IPPacket->HeaderSize > IPv4_MAX_HEADER_SIZE) {
548 TI_DbgPrint(MIN_TRACE, ("Datagram received with incorrect header size (%d).\n",
549 IPPacket->HeaderSize));
554 /* Checksum IPv4 header */
555 if (!CorrectChecksum(IPPacket->Header, IPPacket->HeaderSize)) {
556 TI_DbgPrint(MIN_TRACE, ("Datagram received with bad checksum. Checksum field (0x%X)\n",
557 WN2H(((PIPv4_HEADER)IPPacket->Header)->Checksum)));
562 // TI_DbgPrint(DEBUG_IP, ("TotalSize (datalink) is (%d).\n", IPPacket->TotalSize));
564 IPPacket->TotalSize = WN2H(((PIPv4_HEADER)IPPacket->Header)->TotalLength);
566 // TI_DbgPrint(DEBUG_IP, ("TotalSize (IPv4) is (%d).\n", IPPacket->TotalSize));
568 AddrInitIPv4(&IPPacket->SrcAddr, ((PIPv4_HEADER)IPPacket->Header)->SrcAddr);
569 AddrInitIPv4(&IPPacket->DstAddr, ((PIPv4_HEADER)IPPacket->Header)->DstAddr);
571 IPPacket->Position = IPPacket->HeaderSize;
572 IPPacket->Data = (PVOID)((ULONG_PTR)IPPacket->Header + IPPacket->HeaderSize);
574 /* FIXME: Possibly forward packets with multicast addresses */
576 /* FIXME: Should we allow packets to be received on the wrong interface? */
578 NTE = IPLocateNTE(&IPPacket->DstAddr, &AddressType);
580 NTE = IPLocateNTEOnInterface((PIP_INTERFACE)Context, &IPPacket->DstAddr, &AddressType);
583 /* This packet is destined for us */
584 ProcessFragment((PIP_INTERFACE)Context, IPPacket, NTE);
586 /* Done with this NTE */
587 DereferenceObject(NTE);
589 /* This packet is not destined for us. If we are a router,
590 try to find a route and forward the packet */
592 /* FIXME: Check if acting as a router */
594 //NCE = RouteFindRouter(&IPPacket->DstAddr, NULL);
597 /* FIXME: Possibly fragment datagram */
598 /* Forward the packet */
599 IPSendFragment(IPPacket, NCE);
601 TI_DbgPrint(MIN_TRACE, ("No route to destination (0x%X).\n",
602 IPPacket->DstAddr.Address.IPv4Address));
604 /* FIXME: Send ICMP error code */
615 * FUNCTION: Receives an IP datagram (or fragment)
617 * Context = Pointer to context information (IP_INTERFACE)
618 * IPPacket = Pointer to IP packet
623 /* Check that IP header has a supported version */
624 Version = (((PIPv4_HEADER)IPPacket->Header)->VerIHL >> 4);
628 IPPacket->Type = IP_ADDRESS_V4;
629 IPv4Receive(Context, IPPacket);
632 IPPacket->Type = IP_ADDRESS_V6;
633 TI_DbgPrint(MAX_TRACE, ("Datagram of type IPv6 discarded.\n"));
636 TI_DbgPrint(MIN_TRACE, ("Datagram has an unsupported IP version %d.\n", Version));