IP library. These files were moved from tcpip.sys and can be linked with
[reactos.git] / reactos / drivers / lib / ip / network / receive.c
diff --git a/reactos/drivers/lib/ip/network/receive.c b/reactos/drivers/lib/ip/network/receive.c
new file mode 100644 (file)
index 0000000..4c8c9f3
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        network/receive.c
+ * PURPOSE:     Internet Protocol receive routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * NOTES:       The IP datagram reassembly algorithm is taken from
+ *              from RFC 815
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+LIST_ENTRY ReassemblyListHead;
+KSPIN_LOCK ReassemblyListLock;
+NPAGED_LOOKASIDE_LIST IPDRList;
+NPAGED_LOOKASIDE_LIST IPFragmentList;
+NPAGED_LOOKASIDE_LIST IPHoleList;
+
+
+PIPDATAGRAM_HOLE CreateHoleDescriptor(
+  ULONG First,
+  ULONG Last)
+/*
+ * FUNCTION: Returns a pointer to a IP datagram hole descriptor
+ * ARGUMENTS:
+ *     First = Offset of first octet of the hole
+ *     Last  = Offset of last octet of the hole
+ * RETURNS:
+ *     Pointer to descriptor, NULL if there was not enough free
+ *     resources
+ */
+{
+       PIPDATAGRAM_HOLE Hole;
+
+       TI_DbgPrint(DEBUG_IP, ("Called. First (%d)  Last (%d).\n", First, Last));
+
+       Hole = ExAllocateFromNPagedLookasideList(&IPHoleList);
+       if (!Hole) {
+           TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+           return NULL;
+       }
+
+       Hole->First = First;
+       Hole->Last  = Last;
+
+       TI_DbgPrint(DEBUG_IP, ("Returning hole descriptor at (0x%X).\n", Hole));
+
+       return Hole;
+}
+
+
+VOID FreeIPDR(
+  PIPDATAGRAM_REASSEMBLY IPDR)
+/*
+ * FUNCTION: Frees an IP datagram reassembly structure
+ * ARGUMENTS:
+ *     IPDR = Pointer to IP datagram reassembly structure
+ */
+{
+  PLIST_ENTRY CurrentEntry;
+  PLIST_ENTRY NextEntry;
+  PIPDATAGRAM_HOLE CurrentH;
+  PIP_FRAGMENT CurrentF;
+
+  TI_DbgPrint(DEBUG_IP, ("Freeing IP datagram reassembly descriptor (0x%X).\n", IPDR));
+
+  /* Free all descriptors */
+  CurrentEntry = IPDR->HoleListHead.Flink;
+  while (CurrentEntry != &IPDR->HoleListHead) {
+    NextEntry = CurrentEntry->Flink;
+         CurrentH = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_HOLE, ListEntry);
+    /* Unlink it from the list */
+    RemoveEntryList(CurrentEntry);
+
+    TI_DbgPrint(DEBUG_IP, ("Freeing hole descriptor at (0x%X).\n", CurrentH));
+
+    /* And free the hole descriptor */
+    ExFreeToNPagedLookasideList(&IPHoleList, CurrentH);
+
+    CurrentEntry = NextEntry;
+  }
+
+  /* Free all fragments */
+  CurrentEntry = IPDR->FragmentListHead.Flink;
+  while (CurrentEntry != &IPDR->FragmentListHead) {
+    NextEntry = CurrentEntry->Flink;
+         CurrentF = CONTAINING_RECORD(CurrentEntry, IP_FRAGMENT, ListEntry);
+    /* Unlink it from the list */
+    RemoveEntryList(CurrentEntry);
+
+    TI_DbgPrint(DEBUG_IP, ("Freeing fragment data at (0x%X).\n", CurrentF->Data));
+
+    /* Free the fragment data buffer */
+    exFreePool(CurrentF->Data);
+
+    TI_DbgPrint(DEBUG_IP, ("Freeing fragment at (0x%X).\n", CurrentF));
+
+    /* And free the fragment descriptor */
+    ExFreeToNPagedLookasideList(&IPFragmentList, CurrentF);
+    CurrentEntry = NextEntry;
+  }
+
+  /* Free resources for the header, if it exists */
+  if (IPDR->IPv4Header) {
+    TI_DbgPrint(DEBUG_IP, ("Freeing IPv4 header data at (0x%X).\n", IPDR->IPv4Header));
+    exFreePool(IPDR->IPv4Header);
+  }
+
+  TI_DbgPrint(DEBUG_IP, ("Freeing IPDR data at (0x%X).\n", IPDR));
+
+  ExFreeToNPagedLookasideList(&IPDRList, IPDR);
+}
+
+
+VOID RemoveIPDR(
+  PIPDATAGRAM_REASSEMBLY IPDR)
+/*
+ * FUNCTION: Removes an IP datagram reassembly structure from the global list
+ * ARGUMENTS:
+ *     IPDR = Pointer to IP datagram reassembly structure
+ */
+{
+  KIRQL OldIrql;
+
+  TI_DbgPrint(DEBUG_IP, ("Removing IPDR at (0x%X).\n", IPDR));
+
+  KeAcquireSpinLock(&ReassemblyListLock, &OldIrql);
+  RemoveEntryList(&IPDR->ListEntry);
+  KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
+}
+
+
+PIPDATAGRAM_REASSEMBLY GetReassemblyInfo(
+  PIP_PACKET IPPacket)
+/*
+ * FUNCTION: Returns a pointer to an IP datagram reassembly structure
+ * ARGUMENTS:
+ *     IPPacket = Pointer to IP packet
+ * NOTES:
+ *     A datagram is identified by four paramters, which are
+ *     Source and destination address, protocol number and
+ *     identification number
+ */
+{
+  KIRQL OldIrql;
+  PLIST_ENTRY CurrentEntry;
+  PIPDATAGRAM_REASSEMBLY Current;
+  PIPv4_HEADER Header = (PIPv4_HEADER)IPPacket->Header;
+
+  TI_DbgPrint(DEBUG_IP, ("Searching for IPDR for IP packet at (0x%X).\n", IPPacket));
+
+  KeAcquireSpinLock(&ReassemblyListLock, &OldIrql);
+
+  /* FIXME: Assume IPv4 */
+
+  CurrentEntry = ReassemblyListHead.Flink;
+  while (CurrentEntry != &ReassemblyListHead) {
+         Current = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_REASSEMBLY, ListEntry);
+    if (AddrIsEqual(&IPPacket->SrcAddr, &Current->SrcAddr) &&
+      (Header->Id == Current->Id) &&
+      (Header->Protocol == Current->Protocol) &&
+      (AddrIsEqual(&IPPacket->DstAddr, &Current->DstAddr))) {
+      KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
+
+      return Current;
+    }
+    CurrentEntry = CurrentEntry->Flink;
+  }
+
+  KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
+
+  return NULL;
+}
+
+
+PIP_PACKET ReassembleDatagram(
+  PIPDATAGRAM_REASSEMBLY IPDR)
+/*
+ * FUNCTION: Reassembles an IP datagram
+ * ARGUMENTS:
+ *     IPDR = Pointer to IP datagram reassembly structure
+ * NOTES:
+ *     This routine concatenates fragments into a complete IP datagram.
+ *     The lock is held when this routine is called
+ * RETURNS:
+ *     Pointer to IP packet, NULL if there was not enough free resources
+ * NOTES:
+ *     At this point, header is expected to point to the IP header
+ */
+{
+  PIP_PACKET IPPacket;
+  PLIST_ENTRY CurrentEntry;
+  PIP_FRAGMENT Current;
+  PVOID Data;
+
+  TI_DbgPrint(DEBUG_IP, ("Reassembling datagram from IPDR at (0x%X).\n", IPDR));
+  TI_DbgPrint(DEBUG_IP, ("IPDR->HeaderSize = %d\n", IPDR->HeaderSize));
+  TI_DbgPrint(DEBUG_IP, ("IPDR->DataSize = %d\n", IPDR->DataSize));
+
+  TI_DbgPrint(DEBUG_IP, ("Fragment header:\n"));
+  //OskitDumpBuffer((PCHAR)IPDR->IPv4Header, IPDR->HeaderSize);
+
+  /* FIXME: Assume IPv4 */
+  IPPacket = IPCreatePacket(IP_ADDRESS_V4);
+  if (!IPPacket)
+    return NULL;
+
+  IPPacket->TotalSize  = IPDR->HeaderSize + IPDR->DataSize;
+  IPPacket->ContigSize = IPPacket->TotalSize;
+  IPPacket->HeaderSize = IPDR->HeaderSize;
+  /*IPPacket->Position   = IPDR->HeaderSize;*/
+
+  RtlCopyMemory(&IPPacket->SrcAddr, &IPDR->SrcAddr, sizeof(IP_ADDRESS));
+  RtlCopyMemory(&IPPacket->DstAddr, &IPDR->DstAddr, sizeof(IP_ADDRESS));
+
+  /* Allocate space for full IP datagram */
+  IPPacket->Header = exAllocatePool(NonPagedPool, IPPacket->TotalSize);
+  if (!IPPacket->Header) {
+    TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+    (*IPPacket->Free)(IPPacket);
+    return NULL;
+  }
+
+  /* Copy the header into the buffer */
+  RtlCopyMemory(IPPacket->Header, IPDR->IPv4Header, IPDR->HeaderSize);  
+  
+  Data = IPPacket->Header + IPDR->HeaderSize;
+  IPPacket->Data = Data;
+
+  /* Copy data from all fragments into buffer */
+  CurrentEntry = IPDR->FragmentListHead.Flink;
+  while (CurrentEntry != &IPDR->FragmentListHead) {
+    Current = CONTAINING_RECORD(CurrentEntry, IP_FRAGMENT, ListEntry);
+
+    TI_DbgPrint(DEBUG_IP, ("Copying (%d) bytes of fragment data from (0x%X) to offset (%d).\n",
+      Current->Size, Data, Current->Offset));
+    /* Copy fragment data to the destination buffer at the correct offset */
+    RtlCopyMemory((PVOID)((ULONG_PTR)Data + Current->Offset),
+                 Current->Data,
+                 Current->Size);
+    //OskitDumpBuffer( Data, Current->Offset + Current->Size );
+    CurrentEntry = CurrentEntry->Flink;
+  }
+
+  return IPPacket;
+}
+
+
+__inline VOID Cleanup(
+  PKSPIN_LOCK Lock,
+  KIRQL OldIrql,
+  PIPDATAGRAM_REASSEMBLY IPDR,
+  PVOID Buffer OPTIONAL)
+/*
+ * FUNCTION: Performs cleaning operations on errors
+ * ARGUMENTS:
+ *     Lock     = Pointer to spin lock to be released
+ *     OldIrql  = Value of IRQL when spin lock was acquired
+ *     IPDR     = Pointer to IP datagram reassembly structure to free
+ *     Buffer   = Optional pointer to a buffer to free
+ */
+{
+  TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+
+  KeReleaseSpinLock(Lock, OldIrql);
+  RemoveIPDR(IPDR);
+  FreeIPDR(IPDR);
+  if (Buffer)
+    exFreePool(Buffer);
+}
+
+
+VOID ProcessFragment(
+  PIP_INTERFACE IF,
+  PIP_PACKET IPPacket,
+  PNET_TABLE_ENTRY NTE)
+/*
+ * FUNCTION: Processes an IP datagram or fragment
+ * ARGUMENTS:
+ *     IF       = Pointer to IP interface packet was receive on
+ *     IPPacket = Pointer to IP packet
+ *     NTE      = Pointer to NTE packet was received on
+ * NOTES:
+ *     This routine reassembles fragments and, if a whole datagram can
+ *     be assembled, passes the datagram on to the IP protocol dispatcher
+ */
+{
+  KIRQL OldIrql;
+  PIPDATAGRAM_REASSEMBLY IPDR;
+  PLIST_ENTRY CurrentEntry;
+  PIPDATAGRAM_HOLE Hole, NewHole;
+  USHORT FragFirst;
+  USHORT FragLast;
+  BOOLEAN MoreFragments;
+  PIPv4_HEADER IPv4Header;
+  PIP_PACKET Datagram;
+  PIP_FRAGMENT Fragment;
+
+  /* FIXME: Assume IPv4 */
+
+  IPv4Header = (PIPv4_HEADER)IPPacket->Header;
+
+  /* Check if we already have an reassembly structure for this datagram */
+  IPDR = GetReassemblyInfo(IPPacket);
+  if (IPDR) {
+    TI_DbgPrint(DEBUG_IP, ("Continueing assembly.\n"));
+    /* We have a reassembly structure */
+    KeAcquireSpinLock(&IPDR->Lock, &OldIrql);
+    CurrentEntry = IPDR->HoleListHead.Flink;
+    Hole = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_HOLE, ListEntry);
+  } else {
+    TI_DbgPrint(DEBUG_IP, ("Starting new assembly.\n"));
+
+    /* We don't have a reassembly structure, create one */
+    IPDR = ExAllocateFromNPagedLookasideList(&IPDRList);
+    if (!IPDR)
+      /* We don't have the resources to process this packet, discard it */
+      return;
+
+    /* Create a descriptor spanning from zero to infinity.
+       Actually, we use a value slightly greater than the
+       maximum number of octets an IP datagram can contain */
+    Hole = CreateHoleDescriptor(0, 65536);
+    if (!Hole) {
+      /* We don't have the resources to process this packet, discard it */
+      ExFreeToNPagedLookasideList(&IPDRList, IPDR);
+      return;
+    }
+    AddrInitIPv4(&IPDR->SrcAddr, IPv4Header->SrcAddr);
+    AddrInitIPv4(&IPDR->DstAddr, IPv4Header->DstAddr);
+    IPDR->Id         = IPv4Header->Id;
+    IPDR->Protocol   = IPv4Header->Protocol;
+    IPDR->IPv4Header = NULL;
+    InitializeListHead(&IPDR->FragmentListHead);
+    InitializeListHead(&IPDR->HoleListHead);
+    InsertTailList(&IPDR->HoleListHead, &Hole->ListEntry);
+    CurrentEntry = IPDR->HoleListHead.Flink;
+
+    KeInitializeSpinLock(&IPDR->Lock);
+
+    KeAcquireSpinLock(&IPDR->Lock, &OldIrql);
+
+    /* Update the reassembly list */
+    ExInterlockedInsertTailList(
+                 &ReassemblyListHead,
+      &IPDR->ListEntry,
+      &ReassemblyListLock);
+  }
+
+  FragFirst     = (WN2H(IPv4Header->FlagsFragOfs) & IPv4_FRAGOFS_MASK) << 3;
+  FragLast      = FragFirst + WN2H(IPv4Header->TotalLength);
+  MoreFragments = (WN2H(IPv4Header->FlagsFragOfs) & IPv4_MF_MASK) > 0;
+
+  for (;;) {
+    if (CurrentEntry == &IPDR->HoleListHead)
+      /* No more entries */
+      break;
+
+    TI_DbgPrint(DEBUG_IP, ("Comparing Fragment (%d,%d) to Hole (%d,%d).\n",
+      FragFirst, FragLast, Hole->First, Hole->Last));
+
+    if ((FragFirst > Hole->Last) || (FragLast < Hole->First)) {
+      TI_DbgPrint(MID_TRACE, ("No overlap.\n"));
+      /* The fragment does not overlap with the hole, try next
+         descriptor in the list */
+
+      CurrentEntry = CurrentEntry->Flink;
+      if (CurrentEntry != &IPDR->HoleListHead)
+          Hole = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_HOLE, ListEntry);
+      continue;
+    }
+
+    /* The fragment overlap with the hole, unlink the descriptor */
+    RemoveEntryList(CurrentEntry);
+
+    if (FragFirst > Hole->First) {
+      NewHole = CreateHoleDescriptor(Hole->First, FragLast - 1);
+      if (!NewHole) {
+        /* We don't have the resources to process this packet, discard it */
+        Cleanup(&IPDR->Lock, OldIrql, IPDR, Hole);
+        return;
+      }
+
+      /* Put the new descriptor in the list */
+      InsertTailList(&IPDR->HoleListHead, &NewHole->ListEntry);
+    }
+
+    if ((FragLast < Hole->Last) && (MoreFragments)) {
+      /* We can reuse the descriptor for the new hole */
+                 Hole->First = FragLast + 1;
+
+                 /* Put the new hole descriptor in the list */
+      InsertTailList(&IPDR->HoleListHead, &Hole->ListEntry);
+    } else
+      ExFreeToNPagedLookasideList(&IPHoleList, Hole);
+
+    /* If this is the first fragment, save the IP header */
+    if (FragFirst == 0) {
+       IPDR->IPv4Header = exAllocatePool(NonPagedPool, IPPacket->HeaderSize);
+      if (!IPDR->IPv4Header) {
+        /* We don't have the resources to process this packet, discard it */
+        Cleanup(&IPDR->Lock, OldIrql, IPDR, NULL);
+        return;
+      }
+
+      TI_DbgPrint(DEBUG_IP, ("First fragment found. Header buffer is at (0x%X). "
+        "Header size is (%d).\n", IPDR->IPv4Header, IPPacket->HeaderSize));
+
+      RtlCopyMemory(IPDR->IPv4Header, IPPacket->Header, IPPacket->HeaderSize);
+      IPDR->HeaderSize = IPPacket->HeaderSize;
+    }
+
+    /* Create a buffer, copy the data into it and put it
+       in the fragment list */
+
+    Fragment = ExAllocateFromNPagedLookasideList(&IPFragmentList);
+    if (!Fragment) {
+      /* We don't have the resources to process this packet, discard it */
+      Cleanup(&IPDR->Lock, OldIrql, IPDR, NULL);
+      return;
+    }
+
+    TI_DbgPrint(DEBUG_IP, ("Fragment descriptor allocated at (0x%X).\n", Fragment));
+
+    Fragment->Size = IPPacket->TotalSize - IPPacket->HeaderSize;
+    Fragment->Data = exAllocatePool(NonPagedPool, Fragment->Size);
+    if (!Fragment->Data) {
+      /* We don't have the resources to process this packet, discard it */
+      Cleanup(&IPDR->Lock, OldIrql, IPDR, Fragment);
+      return;
+    }
+
+    /* Position here is an offset from the NdisPacket start, not the header */
+    TI_DbgPrint(DEBUG_IP, ("Fragment data buffer allocated at (0x%X)  Size (%d) Pos (%d).\n",
+                          Fragment->Data, Fragment->Size, IPPacket->Position));
+    
+    /* Copy datagram data into fragment buffer */
+    CopyPacketToBuffer(Fragment->Data,
+                      IPPacket->NdisPacket,
+                      IPPacket->Position,
+                      Fragment->Size);
+    Fragment->Offset = FragFirst;
+    
+    /* If this is the last fragment, compute and save the datagram data size */
+    if (!MoreFragments)
+      IPDR->DataSize = FragFirst + Fragment->Size;
+
+    /* Put the fragment in the list */
+    InsertTailList(&IPDR->FragmentListHead, &Fragment->ListEntry);
+         break;
+  }
+
+  TI_DbgPrint(DEBUG_IP, ("Done searching for hole descriptor.\n"));
+
+  if (IsListEmpty(&IPDR->HoleListHead)) {
+    /* Hole list is empty which means a complete datagram can be assembled.
+       Assemble the datagram and pass it to an upper layer protocol */
+
+    TI_DbgPrint(DEBUG_IP, ("Complete datagram received.\n"));
+
+    Datagram = ReassembleDatagram(IPDR);
+
+    KeReleaseSpinLock(&IPDR->Lock, OldIrql);
+
+    RemoveIPDR(IPDR);
+    FreeIPDR(IPDR);
+
+    if (!Datagram)
+      /* Not enough free resources, discard the packet */
+      return;
+
+    DISPLAY_IP_PACKET(Datagram);
+
+    /* Give the packet to the protocol dispatcher */
+    IPDispatchProtocol(NTE, Datagram);
+
+    /* We're done with this datagram */
+    exFreePool(Datagram->Header);
+    TI_DbgPrint(MAX_TRACE, ("Freeing datagram at (0x%X).\n", Datagram));
+    (*Datagram->Free)(Datagram);
+  } else
+    KeReleaseSpinLock(&IPDR->Lock, OldIrql);
+}
+
+
+VOID IPFreeReassemblyList(
+  VOID)
+/*
+ * FUNCTION: Frees all IP datagram reassembly structures in the list
+ */
+{
+  KIRQL OldIrql;
+  PLIST_ENTRY CurrentEntry;
+  PIPDATAGRAM_REASSEMBLY Current;
+
+  KeAcquireSpinLock(&ReassemblyListLock, &OldIrql);
+
+  CurrentEntry = ReassemblyListHead.Flink;
+  while (CurrentEntry != &ReassemblyListHead) {
+         Current = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_REASSEMBLY, ListEntry);
+    /* Unlink it from the list */
+    RemoveEntryList(CurrentEntry);
+
+    /* And free the descriptor */
+    FreeIPDR(Current);
+
+    CurrentEntry = CurrentEntry->Flink;
+  }
+
+  KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
+}
+
+
+VOID IPDatagramReassemblyTimeout(
+  VOID)
+/*
+ * FUNCTION: IP datagram reassembly timeout handler
+ * NOTES:
+ *     This routine is called by IPTimeout to free any resources used
+ *     to hold IP fragments that have taken too long to reassemble
+ */
+{
+}
+
+VOID IPv4Receive(PIP_INTERFACE IF, PIP_PACKET IPPacket)
+/*
+ * FUNCTION: Receives an IPv4 datagram (or fragment)
+ * ARGUMENTS:
+ *     Context  = Pointer to context information (IP_INTERFACE)
+ *     IPPacket = Pointer to IP packet
+ */
+{
+    PNEIGHBOR_CACHE_ENTRY NCE;
+    PNET_TABLE_ENTRY NTE;
+    UINT AddressType;
+    
+    TI_DbgPrint(DEBUG_IP, ("Received IPv4 datagram.\n"));
+    
+    IPPacket->HeaderSize = (((PIPv4_HEADER)IPPacket->Header)->VerIHL & 0x0F) << 2;
+    TI_DbgPrint(DEBUG_IP, ("IPPacket->HeaderSize = %d\n", IPPacket->HeaderSize));
+
+    if (IPPacket->HeaderSize > IPv4_MAX_HEADER_SIZE) {
+       TI_DbgPrint
+           (MIN_TRACE, 
+            ("Datagram received with incorrect header size (%d).\n",
+             IPPacket->HeaderSize));
+       /* Discard packet */
+       return;
+    }
+    
+    /* Checksum IPv4 header */
+    if (!IPv4CorrectChecksum(IPPacket->Header, IPPacket->HeaderSize)) {
+       TI_DbgPrint
+           (MIN_TRACE, 
+            ("Datagram received with bad checksum. Checksum field (0x%X)\n",
+             WN2H(((PIPv4_HEADER)IPPacket->Header)->Checksum)));
+       /* Discard packet */
+       return;
+    }
+    
+    IPPacket->TotalSize = WN2H(((PIPv4_HEADER)IPPacket->Header)->TotalLength);
+    
+    AddrInitIPv4(&IPPacket->SrcAddr, ((PIPv4_HEADER)IPPacket->Header)->SrcAddr);
+    AddrInitIPv4(&IPPacket->DstAddr, ((PIPv4_HEADER)IPPacket->Header)->DstAddr);
+    
+    IPPacket->Position += IPPacket->HeaderSize;
+    IPPacket->Data     = (PVOID)((ULONG_PTR)IPPacket->Header + IPPacket->HeaderSize);
+    
+    TI_DbgPrint(MID_TRACE,("IPPacket->Position = %d\n",
+                          IPPacket->Position));
+
+    //OskitDumpBuffer(IPPacket->Header, IPPacket->TotalSize);
+
+    /* FIXME: Possibly forward packets with multicast addresses */
+    
+    /* FIXME: Should we allow packets to be received on the wrong interface? */
+    NTE = IPLocateNTEOnInterface(IF, &IPPacket->DstAddr, &AddressType);
+    
+    if (NTE) {
+       /* This packet is destined for us */
+       ProcessFragment(IF, IPPacket, NTE);
+       
+       /* Done with this NTE */
+       DereferenceObject(NTE);
+    } else {
+       /* This packet is not destined for us. If we are a router,
+          try to find a route and forward the packet */
+       
+       /* FIXME: Check if acting as a router */
+       NCE = NULL;
+       if (NCE) {
+           /* FIXME: Possibly fragment datagram */
+           /* Forward the packet */
+           IPSendFragment(IPPacket->NdisPacket, NCE);
+       } else {
+           TI_DbgPrint(MIN_TRACE, ("No route to destination (0x%X).\n",
+                                   IPPacket->DstAddr.Address.IPv4Address));
+           
+           /* FIXME: Send ICMP error code */
+       }
+    }
+}
+
+
+VOID IPReceive( PIP_INTERFACE IF, PIP_PACKET IPPacket )
+/*
+ * FUNCTION: Receives an IP datagram (or fragment)
+ * ARGUMENTS:
+ *     IF       = Interface
+ *     IPPacket = Pointer to IP packet
+ */
+{
+  UINT Version;
+
+  /* Check that IP header has a supported version */
+  Version = (((PIPv4_HEADER)IPPacket->Header)->VerIHL >> 4);
+
+  switch (Version) {
+  case 4:
+    IPPacket->Type = IP_ADDRESS_V4;
+    IPv4Receive(IF, IPPacket);
+    break;
+  case 6:
+    IPPacket->Type = IP_ADDRESS_V6;
+    TI_DbgPrint(MAX_TRACE, ("Datagram of type IPv6 discarded.\n"));
+    return;
+  default:
+         TI_DbgPrint(MIN_TRACE, ("Datagram has an unsupported IP version %d.\n", Version));
+    return;
+  }
+}
+
+/* EOF */