Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / sdk / lib / drivers / ip / network / neighbor.c
diff --git a/sdk/lib/drivers/ip/network/neighbor.c b/sdk/lib/drivers/ip/network/neighbor.c
new file mode 100644 (file)
index 0000000..a7c1d35
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        network/neighbor.c
+ * PURPOSE:     Neighbor address cache
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+NEIGHBOR_CACHE_TABLE NeighborCache[NB_HASHMASK + 1];
+
+VOID NBCompleteSend( PVOID Context,
+                    PNDIS_PACKET NdisPacket,
+                    NDIS_STATUS Status ) {
+    PNEIGHBOR_PACKET Packet = (PNEIGHBOR_PACKET)Context;
+    TI_DbgPrint(MID_TRACE, ("Called\n"));
+    ASSERT_KM_POINTER(Packet);
+    ASSERT_KM_POINTER(Packet->Complete);
+    Packet->Complete( Packet->Context, Packet->Packet, Status );
+    TI_DbgPrint(MID_TRACE, ("Completed\n"));
+    ExFreePoolWithTag( Packet, NEIGHBOR_PACKET_TAG );
+    TI_DbgPrint(MID_TRACE, ("Freed\n"));
+}
+
+VOID NBSendPackets( PNEIGHBOR_CACHE_ENTRY NCE ) {
+    PLIST_ENTRY PacketEntry;
+    PNEIGHBOR_PACKET Packet;
+    UINT HashValue;
+
+    ASSERT(!(NCE->State & NUD_INCOMPLETE));
+
+    HashValue  = *(PULONG)(&NCE->Address.Address);
+    HashValue ^= HashValue >> 16;
+    HashValue ^= HashValue >> 8;
+    HashValue ^= HashValue >> 4;
+    HashValue &= NB_HASHMASK;
+
+    /* Send any waiting packets */
+    while ((PacketEntry = ExInterlockedRemoveHeadList(&NCE->PacketQueue,
+                                              &NeighborCache[HashValue].Lock)) != NULL)
+    {
+       Packet = CONTAINING_RECORD( PacketEntry, NEIGHBOR_PACKET, Next );
+
+       TI_DbgPrint
+           (MID_TRACE,
+            ("PacketEntry: %x, NdisPacket %x\n",
+             PacketEntry, Packet->Packet));
+
+       PC(Packet->Packet)->DLComplete = NBCompleteSend;
+       PC(Packet->Packet)->Context  = Packet;
+
+       NCE->Interface->Transmit
+           ( NCE->Interface->Context,
+             Packet->Packet,
+             0,
+             NCE->LinkAddress,
+             LAN_PROTO_IPv4 );
+    }
+}
+
+/* Must be called with table lock acquired */
+VOID NBFlushPacketQueue( PNEIGHBOR_CACHE_ENTRY NCE,
+                        NTSTATUS ErrorCode ) {
+    PLIST_ENTRY PacketEntry;
+    PNEIGHBOR_PACKET Packet;
+
+    while( !IsListEmpty(&NCE->PacketQueue) ) {
+        PacketEntry = RemoveHeadList(&NCE->PacketQueue);
+       Packet = CONTAINING_RECORD
+           ( PacketEntry, NEIGHBOR_PACKET, Next );
+
+        ASSERT_KM_POINTER(Packet);
+
+       TI_DbgPrint
+           (MID_TRACE,
+            ("PacketEntry: %x, NdisPacket %x\n",
+             PacketEntry, Packet->Packet));
+
+        ASSERT_KM_POINTER(Packet->Complete);
+       Packet->Complete( Packet->Context,
+                          Packet->Packet,
+                          ErrorCode );
+
+       ExFreePoolWithTag( Packet, NEIGHBOR_PACKET_TAG );
+    }
+}
+
+VOID NBTimeout(VOID)
+/*
+ * FUNCTION: Neighbor address cache timeout handler
+ * NOTES:
+ *     This routine is called by IPTimeout to remove outdated cache
+ *     entries.
+ */
+{
+    UINT i;
+    PNEIGHBOR_CACHE_ENTRY *PrevNCE;
+    PNEIGHBOR_CACHE_ENTRY NCE;
+    NDIS_STATUS Status;
+
+    for (i = 0; i <= NB_HASHMASK; i++) {
+        TcpipAcquireSpinLockAtDpcLevel(&NeighborCache[i].Lock);
+
+        for (PrevNCE = &NeighborCache[i].Cache;
+             (NCE = *PrevNCE) != NULL;) {
+            if (NCE->State & NUD_INCOMPLETE)
+            {
+                /* Solicit for an address */
+                NBSendSolicit(NCE);
+                if (NCE->EventTimer == 0)
+                {
+                    NCE->EventCount++;
+                    if (NCE->EventCount == ARP_INCOMPLETE_TIMEOUT)
+                    {
+                        NBFlushPacketQueue(NCE, NDIS_STATUS_NETWORK_UNREACHABLE);
+                        NCE->EventCount = 0;
+                    }
+                }
+            }
+
+            /* Check if event timer is running */
+            if (NCE->EventTimer > 0)  {
+                ASSERT(!(NCE->State & NUD_PERMANENT));
+                NCE->EventCount++;
+
+                if ((NCE->EventCount > ARP_RATE &&
+                     NCE->EventCount % ARP_TIMEOUT_RETRANSMISSION == 0) ||
+                    (NCE->EventCount == ARP_RATE))
+                {
+                    /* We haven't gotten a packet from them in
+                     * EventCount seconds so we mark them as stale
+                     * and solicit now */
+                    NCE->State |= NUD_STALE;
+                    NBSendSolicit(NCE);
+                }
+                if (NCE->EventTimer - NCE->EventCount == 0) {
+                    /* Unlink and destroy the NCE */
+                    *PrevNCE = NCE->Next;
+                    
+                    /* Choose the proper failure status */
+                    if (NCE->State & NUD_INCOMPLETE)
+                    {
+                        /* We couldn't get an address to this IP at all */
+                        Status = NDIS_STATUS_NETWORK_UNREACHABLE;
+                    }
+                    else
+                    {
+                        /* This guy was stale for way too long */
+                        Status = NDIS_STATUS_REQUEST_ABORTED;
+                    }
+                    
+                    NBFlushPacketQueue(NCE, Status);
+
+                    ExFreePoolWithTag(NCE, NCE_TAG);
+
+                    continue;
+                }
+            }
+            PrevNCE = &NCE->Next;
+        }
+
+        TcpipReleaseSpinLockFromDpcLevel(&NeighborCache[i].Lock);
+    }
+}
+
+VOID NBStartup(VOID)
+/*
+ * FUNCTION: Starts the neighbor cache
+ */
+{
+    UINT i;
+
+    TI_DbgPrint(DEBUG_NCACHE, ("Called.\n"));
+
+    for (i = 0; i <= NB_HASHMASK; i++) {
+       NeighborCache[i].Cache = NULL;
+       TcpipInitializeSpinLock(&NeighborCache[i].Lock);
+    }
+}
+
+VOID NBShutdown(VOID)
+/*
+ * FUNCTION: Shuts down the neighbor cache
+ */
+{
+  PNEIGHBOR_CACHE_ENTRY NextNCE;
+  PNEIGHBOR_CACHE_ENTRY CurNCE;
+  KIRQL OldIrql;
+  UINT i;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called.\n"));
+
+  /* Remove possible entries from the cache */
+  for (i = 0; i <= NB_HASHMASK; i++)
+    {
+      TcpipAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql);
+
+      CurNCE = NeighborCache[i].Cache;
+      while (CurNCE) {
+          NextNCE = CurNCE->Next;
+
+          /* Flush wait queue */
+         NBFlushPacketQueue( CurNCE, NDIS_STATUS_NOT_ACCEPTED );
+
+          ExFreePoolWithTag(CurNCE, NCE_TAG);
+
+         CurNCE = NextNCE;
+      }
+
+    NeighborCache[i].Cache = NULL;
+
+    TcpipReleaseSpinLock(&NeighborCache[i].Lock, OldIrql);
+  }
+
+  TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+}
+
+VOID NBSendSolicit(PNEIGHBOR_CACHE_ENTRY NCE)
+/*
+ * FUNCTION: Sends a neighbor solicitation message
+ * ARGUMENTS:
+ *   NCE = Pointer to NCE of neighbor to solicit
+ * NOTES:
+ *   May be called with lock held on NCE's table
+ */
+{
+    TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
+
+    ARPTransmit(&NCE->Address,
+                (NCE->State & NUD_INCOMPLETE) ? NULL : NCE->LinkAddress,
+                NCE->Interface);
+}
+
+VOID NBDestroyNeighborsForInterface(PIP_INTERFACE Interface)
+{
+    KIRQL OldIrql;
+    PNEIGHBOR_CACHE_ENTRY *PrevNCE;
+    PNEIGHBOR_CACHE_ENTRY NCE;
+    ULONG i;
+
+    KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+    for (i = 0; i <= NB_HASHMASK; i++)
+    {
+        TcpipAcquireSpinLockAtDpcLevel(&NeighborCache[i].Lock);
+        
+        for (PrevNCE = &NeighborCache[i].Cache;
+             (NCE = *PrevNCE) != NULL;)
+        {
+            if (NCE->Interface == Interface)
+            {
+                /* Unlink and destroy the NCE */
+                *PrevNCE = NCE->Next;
+
+                NBFlushPacketQueue(NCE, NDIS_STATUS_REQUEST_ABORTED);
+                ExFreePoolWithTag(NCE, NCE_TAG);
+
+                continue;
+            }
+            else
+            {
+                PrevNCE = &NCE->Next;
+            }
+        }
+        
+        TcpipReleaseSpinLockFromDpcLevel(&NeighborCache[i].Lock);
+    }
+    KeLowerIrql(OldIrql);
+}
+
+PNEIGHBOR_CACHE_ENTRY NBAddNeighbor(
+  PIP_INTERFACE Interface,
+  PIP_ADDRESS Address,
+  PVOID LinkAddress,
+  UINT LinkAddressLength,
+  UCHAR State,
+  UINT EventTimer)
+/*
+ * FUNCTION: Adds a neighbor to the neighbor cache
+ * ARGUMENTS:
+ *   Interface         = Pointer to interface
+ *   Address           = Pointer to IP address
+ *   LinkAddress       = Pointer to link address (may be NULL)
+ *   LinkAddressLength = Length of link address
+ *   State             = State of NCE
+ * RETURNS:
+ *   Pointer to NCE, NULL there is not enough free resources
+ * NOTES:
+ *   The NCE if referenced for the caller if created. The NCE retains
+ *   a reference to the IP address if it is created, the caller is
+ *   responsible for providing this reference
+ */
+{
+  PNEIGHBOR_CACHE_ENTRY NCE;
+  ULONG HashValue;
+  KIRQL OldIrql;
+
+  TI_DbgPrint
+      (DEBUG_NCACHE,
+       ("Called. Interface (0x%X)  Address (0x%X)  "
+       "LinkAddress (0x%X)  LinkAddressLength (%d)  State (0x%X)\n",
+       Interface, Address, LinkAddress, LinkAddressLength, State));
+
+  NCE = ExAllocatePoolWithTag
+      (NonPagedPool, sizeof(NEIGHBOR_CACHE_ENTRY) + LinkAddressLength,
+       NCE_TAG);
+  if (NCE == NULL)
+    {
+      TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+      return NULL;
+    }
+
+  NCE->Interface = Interface;
+  NCE->Address = *Address;
+  NCE->LinkAddressLength = LinkAddressLength;
+  NCE->LinkAddress = (PVOID)&NCE[1];
+  if( LinkAddress )
+      RtlCopyMemory(NCE->LinkAddress, LinkAddress, LinkAddressLength);
+  else
+      memset(NCE->LinkAddress, 0xff, LinkAddressLength);
+  NCE->State = State;
+  NCE->EventTimer = EventTimer;
+  NCE->EventCount = 0;
+  InitializeListHead( &NCE->PacketQueue );
+
+  TI_DbgPrint(MID_TRACE,("NCE: %x\n", NCE));
+
+  HashValue = *(PULONG)&Address->Address;
+  HashValue ^= HashValue >> 16;
+  HashValue ^= HashValue >> 8;
+  HashValue ^= HashValue >> 4;
+  HashValue &= NB_HASHMASK;
+
+  TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
+
+  NCE->Next = NeighborCache[HashValue].Cache;
+  NeighborCache[HashValue].Cache = NCE;
+
+  TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
+
+  return NCE;
+}
+
+VOID NBUpdateNeighbor(
+  PNEIGHBOR_CACHE_ENTRY NCE,
+  PVOID LinkAddress,
+  UCHAR State)
+/*
+ * FUNCTION: Update link address information in NCE
+ * ARGUMENTS:
+ *   NCE         = Pointer to NCE to update
+ *   LinkAddress = Pointer to link address
+ *   State       = State of NCE
+ * NOTES:
+ *   The link address and state is updated. Any waiting packets are sent
+ */
+{
+    KIRQL OldIrql;
+    UINT HashValue;
+
+    TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X)  LinkAddress (0x%X)  State (0x%X).\n", NCE, LinkAddress, State));
+
+    HashValue  = *(PULONG)(&NCE->Address.Address);
+    HashValue ^= HashValue >> 16;
+    HashValue ^= HashValue >> 8;
+    HashValue ^= HashValue >> 4;
+    HashValue &= NB_HASHMASK;
+
+    TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
+
+    RtlCopyMemory(NCE->LinkAddress, LinkAddress, NCE->LinkAddressLength);
+    NCE->State = State;
+    NCE->EventCount = 0;
+
+    TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
+
+    if( !(NCE->State & NUD_INCOMPLETE) )
+    {
+        if (NCE->EventTimer) NCE->EventTimer = ARP_COMPLETE_TIMEOUT;
+        NBSendPackets( NCE );
+    }
+}
+
+VOID
+NBResetNeighborTimeout(PIP_ADDRESS Address)
+{
+    KIRQL OldIrql;
+    UINT HashValue;
+    PNEIGHBOR_CACHE_ENTRY NCE;
+
+    TI_DbgPrint(DEBUG_NCACHE, ("Resetting NCE timout for 0x%s\n", A2S(Address)));
+
+    HashValue  = *(PULONG)(&Address->Address);
+    HashValue ^= HashValue >> 16;
+    HashValue ^= HashValue >> 8;
+    HashValue ^= HashValue >> 4;
+    HashValue &= NB_HASHMASK;
+
+    TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
+
+    for (NCE = NeighborCache[HashValue].Cache;
+         NCE != NULL;
+         NCE = NCE->Next)
+    {
+         if (AddrIsEqual(Address, &NCE->Address))
+         {
+             NCE->EventCount = 0;
+             break;
+         }
+    }
+
+    TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
+}
+
+PNEIGHBOR_CACHE_ENTRY NBLocateNeighbor(
+  PIP_ADDRESS Address,
+  PIP_INTERFACE Interface)
+/*
+ * FUNCTION: Locates a neighbor in the neighbor cache
+ * ARGUMENTS:
+ *   Address = Pointer to IP address
+ *   Interface = Pointer to IP interface
+ * RETURNS:
+ *   Pointer to NCE, NULL if not found
+ * NOTES:
+ *   If the NCE is found, it is referenced. The caller is
+ *   responsible for dereferencing it again after use
+ */
+{
+  PNEIGHBOR_CACHE_ENTRY NCE;
+  UINT HashValue;
+  KIRQL OldIrql;
+  PIP_INTERFACE FirstInterface;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called. Address (0x%X).\n", Address));
+
+  HashValue = *(PULONG)&Address->Address;
+  HashValue ^= HashValue >> 16;
+  HashValue ^= HashValue >> 8;
+  HashValue ^= HashValue >> 4;
+  HashValue &= NB_HASHMASK;
+
+  TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
+
+  /* If there's no adapter specified, we'll look for a match on
+   * each one. */
+  if (Interface == NULL)
+  {
+      FirstInterface = GetDefaultInterface();
+      Interface = FirstInterface;
+  }
+  else
+  {
+      FirstInterface = NULL;
+  }
+
+  do
+  {
+      NCE = NeighborCache[HashValue].Cache;
+      while (NCE != NULL)
+      {
+         if (NCE->Interface == Interface &&
+             AddrIsEqual(Address, &NCE->Address))
+         {
+             break;
+         }
+         NCE = NCE->Next;
+      }
+      
+      if (NCE != NULL)
+          break;
+  }
+  while ((FirstInterface != NULL) &&
+         ((Interface = GetDefaultInterface()) != FirstInterface));
+
+  if ((NCE == NULL) && (FirstInterface != NULL))
+  {
+      /* This time we'll even match loopback NCEs */
+      NCE = NeighborCache[HashValue].Cache;
+      while (NCE != NULL)
+      {
+         if (AddrIsEqual(Address, &NCE->Address))
+         {
+             break;
+         }
+
+         NCE = NCE->Next;
+      }
+  }
+
+  TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
+
+  TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+
+  return NCE;
+}
+
+PNEIGHBOR_CACHE_ENTRY NBFindOrCreateNeighbor(
+  PIP_INTERFACE Interface,
+  PIP_ADDRESS Address,
+  BOOLEAN NoTimeout)
+/*
+ * FUNCTION: Tries to find a neighbor and if unsuccesful, creates a new NCE
+ * ARGUMENTS:
+ *   Interface = Pointer to interface to use (in case NCE is not found)
+ *   Address   = Pointer to IP address
+ * RETURNS:
+ *   Pointer to NCE, NULL if there is not enough free resources
+ * NOTES:
+ *   The NCE is referenced if found or created. The caller is
+ *   responsible for dereferencing it again after use
+ */
+{
+  PNEIGHBOR_CACHE_ENTRY NCE;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called. Interface (0x%X)  Address (0x%X).\n", Interface, Address));
+
+  NCE = NBLocateNeighbor(Address, Interface);
+  if (NCE == NULL)
+    {
+        TI_DbgPrint(MID_TRACE,("BCAST: %s\n", A2S(&Interface->Broadcast)));
+        if( AddrIsEqual(Address, &Interface->Broadcast) ||
+            AddrIsUnspecified(Address) ) {
+            TI_DbgPrint(MID_TRACE,("Packet targeted at broadcast addr\n"));
+            NCE = NBAddNeighbor(Interface, Address, NULL,
+                                Interface->AddressLength, NUD_PERMANENT, 0);
+        } else {
+            NCE = NBAddNeighbor(Interface, Address, NULL,
+                                Interface->AddressLength, NUD_INCOMPLETE, NoTimeout ? 0 : ARP_INCOMPLETE_TIMEOUT);
+            if (!NCE) return NULL;
+            NBSendSolicit(NCE);
+        }
+    }
+
+  return NCE;
+}
+
+BOOLEAN NBQueuePacket(
+  PNEIGHBOR_CACHE_ENTRY NCE,
+  PNDIS_PACKET NdisPacket,
+  PNEIGHBOR_PACKET_COMPLETE PacketComplete,
+  PVOID PacketContext)
+/*
+ * FUNCTION: Queues a packet on an NCE for later transmission
+ * ARGUMENTS:
+ *   NCE        = Pointer to NCE to queue packet on
+ *   NdisPacket = Pointer to NDIS packet to queue
+ * RETURNS:
+ *   TRUE if the packet was successfully queued, FALSE if not
+ */
+{
+  KIRQL OldIrql;
+  PNEIGHBOR_PACKET Packet;
+  UINT HashValue;
+
+  TI_DbgPrint
+      (DEBUG_NCACHE,
+       ("Called. NCE (0x%X)  NdisPacket (0x%X).\n", NCE, NdisPacket));
+
+  Packet = ExAllocatePoolWithTag( NonPagedPool, sizeof(NEIGHBOR_PACKET),
+                                  NEIGHBOR_PACKET_TAG );
+  if( !Packet ) return FALSE;
+
+  /* FIXME: Should we limit the number of queued packets? */
+
+  HashValue  = *(PULONG)(&NCE->Address.Address);
+  HashValue ^= HashValue >> 16;
+  HashValue ^= HashValue >> 8;
+  HashValue ^= HashValue >> 4;
+  HashValue &= NB_HASHMASK;
+
+  TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
+
+  Packet->Complete = PacketComplete;
+  Packet->Context = PacketContext;
+  Packet->Packet = NdisPacket;
+  InsertTailList( &NCE->PacketQueue, &Packet->Next );
+
+  TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
+
+  if( !(NCE->State & NUD_INCOMPLETE) )
+      NBSendPackets( NCE );
+
+  return TRUE;
+}
+
+VOID NBRemoveNeighbor(
+  PNEIGHBOR_CACHE_ENTRY NCE)
+/*
+ * FUNCTION: Removes a neighbor from the neighbor cache
+ * ARGUMENTS:
+ *   NCE = Pointer to NCE to remove from cache
+ * NOTES:
+ *   The NCE must be in a safe state
+ */
+{
+  PNEIGHBOR_CACHE_ENTRY *PrevNCE;
+  PNEIGHBOR_CACHE_ENTRY CurNCE;
+  ULONG HashValue;
+  KIRQL OldIrql;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
+
+  HashValue  = *(PULONG)(&NCE->Address.Address);
+  HashValue ^= HashValue >> 16;
+  HashValue ^= HashValue >> 8;
+  HashValue ^= HashValue >> 4;
+  HashValue &= NB_HASHMASK;
+
+  TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
+
+  /* Search the list and remove the NCE from the list if found */
+  for (PrevNCE = &NeighborCache[HashValue].Cache;
+    (CurNCE = *PrevNCE) != NULL;
+    PrevNCE = &CurNCE->Next)
+    {
+      if (CurNCE == NCE)
+        {
+          /* Found it, now unlink it from the list */
+          *PrevNCE = CurNCE->Next;
+
+         NBFlushPacketQueue( CurNCE, NDIS_STATUS_REQUEST_ABORTED );
+          ExFreePoolWithTag(CurNCE, NCE_TAG);
+
+         break;
+        }
+    }
+
+  TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
+}
+
+ULONG NBCopyNeighbors
+(PIP_INTERFACE Interface,
+ PIPARP_ENTRY ArpTable)
+{
+  PNEIGHBOR_CACHE_ENTRY CurNCE;
+  KIRQL OldIrql;
+  UINT Size = 0, i;
+
+  for (i = 0; i <= NB_HASHMASK; i++) {
+      TcpipAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql);
+      for( CurNCE = NeighborCache[i].Cache;
+          CurNCE;
+          CurNCE = CurNCE->Next ) {
+         if( CurNCE->Interface == Interface &&
+              !AddrIsEqual( &CurNCE->Address, &CurNCE->Interface->Unicast ) ) {
+             if( ArpTable ) {
+                 ArpTable[Size].Index = Interface->Index;
+                 ArpTable[Size].AddrSize = CurNCE->LinkAddressLength;
+                 RtlCopyMemory
+                     (ArpTable[Size].PhysAddr, 
+                      CurNCE->LinkAddress,
+                      CurNCE->LinkAddressLength);
+                 ArpTable[Size].LogAddr = CurNCE->Address.Address.IPv4Address;
+                 if( CurNCE->State & NUD_PERMANENT )
+                     ArpTable[Size].Type = ARP_ENTRY_STATIC;
+                 else if( CurNCE->State & NUD_INCOMPLETE )
+                     ArpTable[Size].Type = ARP_ENTRY_INVALID;
+                 else
+                     ArpTable[Size].Type = ARP_ENTRY_DYNAMIC;
+             }
+             Size++;
+         }
+      }
+      TcpipReleaseSpinLock(&NeighborCache[i].Lock, OldIrql);
+  }
+  
+  return Size;
+}