1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS TCP/IP protocol driver
4  * FILE:        network/neighbor.c
5  * PURPOSE:     Neighbor address cache
6  * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7  * REVISIONS:
8  *   CSH 01/08-2000 Created
9  */
10 
11 #include "precomp.h"
12 
13 NEIGHBOR_CACHE_TABLE NeighborCache[NB_HASHMASK + 1];
14 
NBCompleteSend(PVOID Context,PNDIS_PACKET NdisPacket,NDIS_STATUS Status)15 VOID NBCompleteSend( PVOID Context,
16 		     PNDIS_PACKET NdisPacket,
17 		     NDIS_STATUS Status ) {
18     PNEIGHBOR_PACKET Packet = (PNEIGHBOR_PACKET)Context;
19     TI_DbgPrint(MID_TRACE, ("Called\n"));
20     ASSERT_KM_POINTER(Packet);
21     ASSERT_KM_POINTER(Packet->Complete);
22     Packet->Complete( Packet->Context, Packet->Packet, Status );
23     TI_DbgPrint(MID_TRACE, ("Completed\n"));
24     ExFreePoolWithTag( Packet, NEIGHBOR_PACKET_TAG );
25     TI_DbgPrint(MID_TRACE, ("Freed\n"));
26 }
27 
NBSendPackets(PNEIGHBOR_CACHE_ENTRY NCE)28 VOID NBSendPackets( PNEIGHBOR_CACHE_ENTRY NCE ) {
29     PLIST_ENTRY PacketEntry;
30     PNEIGHBOR_PACKET Packet;
31     UINT HashValue;
32 
33     ASSERT(!(NCE->State & NUD_INCOMPLETE));
34 
35     HashValue  = *(PULONG)(&NCE->Address.Address);
36     HashValue ^= HashValue >> 16;
37     HashValue ^= HashValue >> 8;
38     HashValue ^= HashValue >> 4;
39     HashValue &= NB_HASHMASK;
40 
41     /* Send any waiting packets */
42     while ((PacketEntry = ExInterlockedRemoveHeadList(&NCE->PacketQueue,
43                                               &NeighborCache[HashValue].Lock)) != NULL)
44     {
45 	Packet = CONTAINING_RECORD( PacketEntry, NEIGHBOR_PACKET, Next );
46 
47 	TI_DbgPrint
48 	    (MID_TRACE,
49 	     ("PacketEntry: %x, NdisPacket %x\n",
50 	      PacketEntry, Packet->Packet));
51 
52 	PC(Packet->Packet)->DLComplete = NBCompleteSend;
53 	PC(Packet->Packet)->Context  = Packet;
54 
55 	NCE->Interface->Transmit
56 	    ( NCE->Interface->Context,
57 	      Packet->Packet,
58 	      0,
59 	      NCE->LinkAddress,
60 	      LAN_PROTO_IPv4 );
61     }
62 }
63 
64 /* Must be called with table lock acquired */
NBFlushPacketQueue(PNEIGHBOR_CACHE_ENTRY NCE,NTSTATUS ErrorCode)65 VOID NBFlushPacketQueue( PNEIGHBOR_CACHE_ENTRY NCE,
66 			 NTSTATUS ErrorCode ) {
67     PLIST_ENTRY PacketEntry;
68     PNEIGHBOR_PACKET Packet;
69 
70     while( !IsListEmpty(&NCE->PacketQueue) ) {
71         PacketEntry = RemoveHeadList(&NCE->PacketQueue);
72 	Packet = CONTAINING_RECORD
73 	    ( PacketEntry, NEIGHBOR_PACKET, Next );
74 
75         ASSERT_KM_POINTER(Packet);
76 
77 	TI_DbgPrint
78 	    (MID_TRACE,
79 	     ("PacketEntry: %x, NdisPacket %x\n",
80 	      PacketEntry, Packet->Packet));
81 
82         ASSERT_KM_POINTER(Packet->Complete);
83 	Packet->Complete( Packet->Context,
84                           Packet->Packet,
85                           ErrorCode );
86 
87 	ExFreePoolWithTag( Packet, NEIGHBOR_PACKET_TAG );
88     }
89 }
90 
NBTimeout(VOID)91 VOID NBTimeout(VOID)
92 /*
93  * FUNCTION: Neighbor address cache timeout handler
94  * NOTES:
95  *     This routine is called by IPTimeout to remove outdated cache
96  *     entries.
97  */
98 {
99     UINT i;
100     PNEIGHBOR_CACHE_ENTRY *PrevNCE;
101     PNEIGHBOR_CACHE_ENTRY NCE;
102     NDIS_STATUS Status;
103 
104     for (i = 0; i <= NB_HASHMASK; i++) {
105         TcpipAcquireSpinLockAtDpcLevel(&NeighborCache[i].Lock);
106 
107         for (PrevNCE = &NeighborCache[i].Cache;
108              (NCE = *PrevNCE) != NULL;) {
109             if (NCE->State & NUD_INCOMPLETE)
110             {
111                 /* Solicit for an address */
112                 NBSendSolicit(NCE);
113                 if (NCE->EventTimer == 0)
114                 {
115                     NCE->EventCount++;
116                     if (NCE->EventCount == ARP_INCOMPLETE_TIMEOUT)
117                     {
118                         NBFlushPacketQueue(NCE, NDIS_STATUS_NETWORK_UNREACHABLE);
119                         NCE->EventCount = 0;
120                     }
121                 }
122             }
123 
124             /* Check if event timer is running */
125             if (NCE->EventTimer > 0)  {
126                 ASSERT(!(NCE->State & NUD_PERMANENT));
127                 NCE->EventCount++;
128 
129                 if ((NCE->EventCount > ARP_RATE &&
130                      NCE->EventCount % ARP_TIMEOUT_RETRANSMISSION == 0) ||
131                     (NCE->EventCount == ARP_RATE))
132                 {
133                     /* We haven't gotten a packet from them in
134                      * EventCount seconds so we mark them as stale
135                      * and solicit now */
136                     NCE->State |= NUD_STALE;
137                     NBSendSolicit(NCE);
138                 }
139                 if (NCE->EventTimer - NCE->EventCount == 0) {
140                     /* Unlink and destroy the NCE */
141                     *PrevNCE = NCE->Next;
142 
143                     /* Choose the proper failure status */
144                     if (NCE->State & NUD_INCOMPLETE)
145                     {
146                         /* We couldn't get an address to this IP at all */
147                         Status = NDIS_STATUS_HOST_UNREACHABLE;
148                     }
149                     else
150                     {
151                         /* This guy was stale for way too long */
152                         Status = NDIS_STATUS_REQUEST_ABORTED;
153                     }
154 
155                     NBFlushPacketQueue(NCE, Status);
156 
157                     ExFreePoolWithTag(NCE, NCE_TAG);
158 
159                     continue;
160                 }
161             }
162             PrevNCE = &NCE->Next;
163         }
164 
165         TcpipReleaseSpinLockFromDpcLevel(&NeighborCache[i].Lock);
166     }
167 }
168 
NBStartup(VOID)169 VOID NBStartup(VOID)
170 /*
171  * FUNCTION: Starts the neighbor cache
172  */
173 {
174     UINT i;
175 
176     TI_DbgPrint(DEBUG_NCACHE, ("Called.\n"));
177 
178     for (i = 0; i <= NB_HASHMASK; i++) {
179 	NeighborCache[i].Cache = NULL;
180 	TcpipInitializeSpinLock(&NeighborCache[i].Lock);
181     }
182 }
183 
NBShutdown(VOID)184 VOID NBShutdown(VOID)
185 /*
186  * FUNCTION: Shuts down the neighbor cache
187  */
188 {
189   PNEIGHBOR_CACHE_ENTRY NextNCE;
190   PNEIGHBOR_CACHE_ENTRY CurNCE;
191   KIRQL OldIrql;
192   UINT i;
193 
194   TI_DbgPrint(DEBUG_NCACHE, ("Called.\n"));
195 
196   /* Remove possible entries from the cache */
197   for (i = 0; i <= NB_HASHMASK; i++)
198     {
199       TcpipAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql);
200 
201       CurNCE = NeighborCache[i].Cache;
202       while (CurNCE) {
203           NextNCE = CurNCE->Next;
204 
205           /* Flush wait queue */
206 	  NBFlushPacketQueue( CurNCE, NDIS_STATUS_NOT_ACCEPTED );
207 
208           ExFreePoolWithTag(CurNCE, NCE_TAG);
209 
210 	  CurNCE = NextNCE;
211       }
212 
213     NeighborCache[i].Cache = NULL;
214 
215     TcpipReleaseSpinLock(&NeighborCache[i].Lock, OldIrql);
216   }
217 
218   TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
219 }
220 
NBSendSolicit(PNEIGHBOR_CACHE_ENTRY NCE)221 VOID NBSendSolicit(PNEIGHBOR_CACHE_ENTRY NCE)
222 /*
223  * FUNCTION: Sends a neighbor solicitation message
224  * ARGUMENTS:
225  *   NCE = Pointer to NCE of neighbor to solicit
226  * NOTES:
227  *   May be called with lock held on NCE's table
228  */
229 {
230     TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
231 
232     ARPTransmit(&NCE->Address,
233                 (NCE->State & NUD_INCOMPLETE) ? NULL : NCE->LinkAddress,
234                 NCE->Interface);
235 }
236 
NBDestroyNeighborsForInterface(PIP_INTERFACE Interface)237 VOID NBDestroyNeighborsForInterface(PIP_INTERFACE Interface)
238 {
239     KIRQL OldIrql;
240     PNEIGHBOR_CACHE_ENTRY *PrevNCE;
241     PNEIGHBOR_CACHE_ENTRY NCE;
242     ULONG i;
243 
244     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
245     for (i = 0; i <= NB_HASHMASK; i++)
246     {
247         TcpipAcquireSpinLockAtDpcLevel(&NeighborCache[i].Lock);
248 
249         for (PrevNCE = &NeighborCache[i].Cache;
250              (NCE = *PrevNCE) != NULL;)
251         {
252             if (NCE->Interface == Interface)
253             {
254                 /* Unlink and destroy the NCE */
255                 *PrevNCE = NCE->Next;
256 
257                 NBFlushPacketQueue(NCE, NDIS_STATUS_REQUEST_ABORTED);
258                 ExFreePoolWithTag(NCE, NCE_TAG);
259 
260                 continue;
261             }
262             else
263             {
264                 PrevNCE = &NCE->Next;
265             }
266         }
267 
268         TcpipReleaseSpinLockFromDpcLevel(&NeighborCache[i].Lock);
269     }
270     KeLowerIrql(OldIrql);
271 }
272 
NBAddNeighbor(PIP_INTERFACE Interface,PIP_ADDRESS Address,PVOID LinkAddress,UINT LinkAddressLength,UCHAR State,UINT EventTimer)273 PNEIGHBOR_CACHE_ENTRY NBAddNeighbor(
274   PIP_INTERFACE Interface,
275   PIP_ADDRESS Address,
276   PVOID LinkAddress,
277   UINT LinkAddressLength,
278   UCHAR State,
279   UINT EventTimer)
280 /*
281  * FUNCTION: Adds a neighbor to the neighbor cache
282  * ARGUMENTS:
283  *   Interface         = Pointer to interface
284  *   Address           = Pointer to IP address
285  *   LinkAddress       = Pointer to link address (may be NULL)
286  *   LinkAddressLength = Length of link address
287  *   State             = State of NCE
288  * RETURNS:
289  *   Pointer to NCE, NULL there is not enough free resources
290  * NOTES:
291  *   The NCE if referenced for the caller if created. The NCE retains
292  *   a reference to the IP address if it is created, the caller is
293  *   responsible for providing this reference
294  */
295 {
296   PNEIGHBOR_CACHE_ENTRY NCE;
297   ULONG HashValue;
298   KIRQL OldIrql;
299 
300   TI_DbgPrint
301       (DEBUG_NCACHE,
302        ("Called. Interface (0x%X)  Address (0x%X)  "
303 	"LinkAddress (0x%X)  LinkAddressLength (%d)  State (0x%X)\n",
304 	Interface, Address, LinkAddress, LinkAddressLength, State));
305 
306   NCE = ExAllocatePoolWithTag
307       (NonPagedPool, sizeof(NEIGHBOR_CACHE_ENTRY) + LinkAddressLength,
308        NCE_TAG);
309   if (NCE == NULL)
310     {
311       TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
312       return NULL;
313     }
314 
315   NCE->Interface = Interface;
316   NCE->Address = *Address;
317   NCE->LinkAddressLength = LinkAddressLength;
318   NCE->LinkAddress = (PVOID)&NCE[1];
319   if( LinkAddress )
320       RtlCopyMemory(NCE->LinkAddress, LinkAddress, LinkAddressLength);
321   else
322       memset(NCE->LinkAddress, 0xff, LinkAddressLength);
323   NCE->State = State;
324   NCE->EventTimer = EventTimer;
325   NCE->EventCount = 0;
326   InitializeListHead( &NCE->PacketQueue );
327 
328   TI_DbgPrint(MID_TRACE,("NCE: %x\n", NCE));
329 
330   HashValue = *(PULONG)&Address->Address;
331   HashValue ^= HashValue >> 16;
332   HashValue ^= HashValue >> 8;
333   HashValue ^= HashValue >> 4;
334   HashValue &= NB_HASHMASK;
335 
336   TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
337 
338   NCE->Next = NeighborCache[HashValue].Cache;
339   NeighborCache[HashValue].Cache = NCE;
340 
341   TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
342 
343   return NCE;
344 }
345 
NBUpdateNeighbor(PNEIGHBOR_CACHE_ENTRY NCE,PVOID LinkAddress,UCHAR State)346 VOID NBUpdateNeighbor(
347   PNEIGHBOR_CACHE_ENTRY NCE,
348   PVOID LinkAddress,
349   UCHAR State)
350 /*
351  * FUNCTION: Update link address information in NCE
352  * ARGUMENTS:
353  *   NCE         = Pointer to NCE to update
354  *   LinkAddress = Pointer to link address
355  *   State       = State of NCE
356  * NOTES:
357  *   The link address and state is updated. Any waiting packets are sent
358  */
359 {
360     KIRQL OldIrql;
361     UINT HashValue;
362 
363     TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X)  LinkAddress (0x%X)  State (0x%X).\n", NCE, LinkAddress, State));
364 
365     HashValue  = *(PULONG)(&NCE->Address.Address);
366     HashValue ^= HashValue >> 16;
367     HashValue ^= HashValue >> 8;
368     HashValue ^= HashValue >> 4;
369     HashValue &= NB_HASHMASK;
370 
371     TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
372 
373     RtlCopyMemory(NCE->LinkAddress, LinkAddress, NCE->LinkAddressLength);
374     NCE->State = State;
375     NCE->EventCount = 0;
376 
377     TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
378 
379     if( !(NCE->State & NUD_INCOMPLETE) )
380     {
381         if (NCE->EventTimer) NCE->EventTimer = ARP_COMPLETE_TIMEOUT;
382         NBSendPackets( NCE );
383     }
384 }
385 
386 VOID
NBResetNeighborTimeout(PIP_ADDRESS Address)387 NBResetNeighborTimeout(PIP_ADDRESS Address)
388 {
389     KIRQL OldIrql;
390     UINT HashValue;
391     PNEIGHBOR_CACHE_ENTRY NCE;
392 
393     TI_DbgPrint(DEBUG_NCACHE, ("Resetting NCE timout for 0x%s\n", A2S(Address)));
394 
395     HashValue  = *(PULONG)(&Address->Address);
396     HashValue ^= HashValue >> 16;
397     HashValue ^= HashValue >> 8;
398     HashValue ^= HashValue >> 4;
399     HashValue &= NB_HASHMASK;
400 
401     TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
402 
403     for (NCE = NeighborCache[HashValue].Cache;
404          NCE != NULL;
405          NCE = NCE->Next)
406     {
407          if (AddrIsEqual(Address, &NCE->Address))
408          {
409              NCE->EventCount = 0;
410              break;
411          }
412     }
413 
414     TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
415 }
416 
NBLocateNeighbor(PIP_ADDRESS Address,PIP_INTERFACE Interface)417 PNEIGHBOR_CACHE_ENTRY NBLocateNeighbor(
418   PIP_ADDRESS Address,
419   PIP_INTERFACE Interface)
420 /*
421  * FUNCTION: Locates a neighbor in the neighbor cache
422  * ARGUMENTS:
423  *   Address = Pointer to IP address
424  *   Interface = Pointer to IP interface
425  * RETURNS:
426  *   Pointer to NCE, NULL if not found
427  * NOTES:
428  *   If the NCE is found, it is referenced. The caller is
429  *   responsible for dereferencing it again after use
430  */
431 {
432   PNEIGHBOR_CACHE_ENTRY NCE;
433   UINT HashValue;
434   KIRQL OldIrql;
435   PIP_INTERFACE FirstInterface;
436 
437   TI_DbgPrint(DEBUG_NCACHE, ("Called. Address (0x%X).\n", Address));
438 
439   HashValue = *(PULONG)&Address->Address;
440   HashValue ^= HashValue >> 16;
441   HashValue ^= HashValue >> 8;
442   HashValue ^= HashValue >> 4;
443   HashValue &= NB_HASHMASK;
444 
445   TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
446 
447   /* If there's no adapter specified, we'll look for a match on
448    * each one. */
449   if (Interface == NULL)
450   {
451       FirstInterface = GetDefaultInterface();
452       Interface = FirstInterface;
453   }
454   else
455   {
456       FirstInterface = NULL;
457   }
458 
459   do
460   {
461       NCE = NeighborCache[HashValue].Cache;
462       while (NCE != NULL)
463       {
464          if (NCE->Interface == Interface &&
465              AddrIsEqual(Address, &NCE->Address))
466          {
467              break;
468          }
469 
470          NCE = NCE->Next;
471       }
472 
473       if (NCE != NULL)
474           break;
475   }
476   while ((FirstInterface != NULL) &&
477          ((Interface = GetDefaultInterface()) != FirstInterface));
478 
479   if ((NCE == NULL) && (FirstInterface != NULL))
480   {
481       /* This time we'll even match loopback NCEs */
482       NCE = NeighborCache[HashValue].Cache;
483       while (NCE != NULL)
484       {
485          if (AddrIsEqual(Address, &NCE->Address))
486          {
487              break;
488          }
489 
490          NCE = NCE->Next;
491       }
492   }
493 
494   TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
495 
496   TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
497 
498   return NCE;
499 }
500 
NBFindOrCreateNeighbor(PIP_INTERFACE Interface,PIP_ADDRESS Address,BOOLEAN NoTimeout)501 PNEIGHBOR_CACHE_ENTRY NBFindOrCreateNeighbor(
502   PIP_INTERFACE Interface,
503   PIP_ADDRESS Address,
504   BOOLEAN NoTimeout)
505 /*
506  * FUNCTION: Tries to find a neighbor and if unsuccesful, creates a new NCE
507  * ARGUMENTS:
508  *   Interface = Pointer to interface to use (in case NCE is not found)
509  *   Address   = Pointer to IP address
510  * RETURNS:
511  *   Pointer to NCE, NULL if there is not enough free resources
512  * NOTES:
513  *   The NCE is referenced if found or created. The caller is
514  *   responsible for dereferencing it again after use
515  */
516 {
517   PNEIGHBOR_CACHE_ENTRY NCE;
518 
519   TI_DbgPrint(DEBUG_NCACHE, ("Called. Interface (0x%X)  Address (0x%X).\n", Interface, Address));
520 
521   NCE = NBLocateNeighbor(Address, Interface);
522   if (NCE == NULL)
523     {
524         TI_DbgPrint(MID_TRACE,("BCAST: %s\n", A2S(&Interface->Broadcast)));
525         if( AddrIsEqual(Address, &Interface->Broadcast) ||
526             AddrIsUnspecified(Address) ) {
527             TI_DbgPrint(MID_TRACE,("Packet targeted at broadcast addr\n"));
528             NCE = NBAddNeighbor(Interface, Address, NULL,
529                                 Interface->AddressLength, NUD_PERMANENT, 0);
530         } else {
531             NCE = NBAddNeighbor(Interface, Address, NULL,
532                                 Interface->AddressLength, NUD_INCOMPLETE, NoTimeout ? 0 : ARP_INCOMPLETE_TIMEOUT);
533             if (!NCE) return NULL;
534             NBSendSolicit(NCE);
535         }
536     }
537 
538   return NCE;
539 }
540 
NBQueuePacket(PNEIGHBOR_CACHE_ENTRY NCE,PNDIS_PACKET NdisPacket,PNEIGHBOR_PACKET_COMPLETE PacketComplete,PVOID PacketContext)541 BOOLEAN NBQueuePacket(
542   PNEIGHBOR_CACHE_ENTRY NCE,
543   PNDIS_PACKET NdisPacket,
544   PNEIGHBOR_PACKET_COMPLETE PacketComplete,
545   PVOID PacketContext)
546 /*
547  * FUNCTION: Queues a packet on an NCE for later transmission
548  * ARGUMENTS:
549  *   NCE        = Pointer to NCE to queue packet on
550  *   NdisPacket = Pointer to NDIS packet to queue
551  * RETURNS:
552  *   TRUE if the packet was successfully queued, FALSE if not
553  */
554 {
555   KIRQL OldIrql;
556   PNEIGHBOR_PACKET Packet;
557   UINT HashValue;
558 
559   TI_DbgPrint
560       (DEBUG_NCACHE,
561        ("Called. NCE (0x%X)  NdisPacket (0x%X).\n", NCE, NdisPacket));
562 
563   Packet = ExAllocatePoolWithTag( NonPagedPool, sizeof(NEIGHBOR_PACKET),
564                                   NEIGHBOR_PACKET_TAG );
565   if( !Packet ) return FALSE;
566 
567   /* FIXME: Should we limit the number of queued packets? */
568 
569   HashValue  = *(PULONG)(&NCE->Address.Address);
570   HashValue ^= HashValue >> 16;
571   HashValue ^= HashValue >> 8;
572   HashValue ^= HashValue >> 4;
573   HashValue &= NB_HASHMASK;
574 
575   TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
576 
577   Packet->Complete = PacketComplete;
578   Packet->Context = PacketContext;
579   Packet->Packet = NdisPacket;
580   InsertTailList( &NCE->PacketQueue, &Packet->Next );
581 
582   TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
583 
584   if( !(NCE->State & NUD_INCOMPLETE) )
585       NBSendPackets( NCE );
586 
587   return TRUE;
588 }
589 
NBRemoveNeighbor(PNEIGHBOR_CACHE_ENTRY NCE)590 VOID NBRemoveNeighbor(
591   PNEIGHBOR_CACHE_ENTRY NCE)
592 /*
593  * FUNCTION: Removes a neighbor from the neighbor cache
594  * ARGUMENTS:
595  *   NCE = Pointer to NCE to remove from cache
596  * NOTES:
597  *   The NCE must be in a safe state
598  */
599 {
600   PNEIGHBOR_CACHE_ENTRY *PrevNCE;
601   PNEIGHBOR_CACHE_ENTRY CurNCE;
602   ULONG HashValue;
603   KIRQL OldIrql;
604 
605   TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
606 
607   HashValue  = *(PULONG)(&NCE->Address.Address);
608   HashValue ^= HashValue >> 16;
609   HashValue ^= HashValue >> 8;
610   HashValue ^= HashValue >> 4;
611   HashValue &= NB_HASHMASK;
612 
613   TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
614 
615   /* Search the list and remove the NCE from the list if found */
616   for (PrevNCE = &NeighborCache[HashValue].Cache;
617     (CurNCE = *PrevNCE) != NULL;
618     PrevNCE = &CurNCE->Next)
619     {
620       if (CurNCE == NCE)
621         {
622           /* Found it, now unlink it from the list */
623           *PrevNCE = CurNCE->Next;
624 
625 	  NBFlushPacketQueue( CurNCE, NDIS_STATUS_REQUEST_ABORTED );
626           ExFreePoolWithTag(CurNCE, NCE_TAG);
627 
628 	  break;
629         }
630     }
631 
632   TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
633 }
634 
NBCopyNeighbors(PIP_INTERFACE Interface,PIPARP_ENTRY ArpTable)635 ULONG NBCopyNeighbors
636 (PIP_INTERFACE Interface,
637  PIPARP_ENTRY ArpTable)
638 {
639   PNEIGHBOR_CACHE_ENTRY CurNCE;
640   KIRQL OldIrql;
641   UINT Size = 0, i;
642 
643   for (i = 0; i <= NB_HASHMASK; i++) {
644       TcpipAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql);
645       for( CurNCE = NeighborCache[i].Cache;
646 	   CurNCE;
647 	   CurNCE = CurNCE->Next ) {
648 	  if( CurNCE->Interface == Interface &&
649               !AddrIsEqual( &CurNCE->Address, &CurNCE->Interface->Unicast ) ) {
650 	      if( ArpTable ) {
651 		  ArpTable[Size].Index = Interface->Index;
652 		  ArpTable[Size].AddrSize = CurNCE->LinkAddressLength;
653 		  RtlCopyMemory
654 		      (ArpTable[Size].PhysAddr,
655 		       CurNCE->LinkAddress,
656 		       CurNCE->LinkAddressLength);
657 		  ArpTable[Size].LogAddr = CurNCE->Address.Address.IPv4Address;
658 		  if( CurNCE->State & NUD_PERMANENT )
659 		      ArpTable[Size].Type = ARP_ENTRY_STATIC;
660 		  else if( CurNCE->State & NUD_INCOMPLETE )
661 		      ArpTable[Size].Type = ARP_ENTRY_INVALID;
662 		  else
663 		      ArpTable[Size].Type = ARP_ENTRY_DYNAMIC;
664 	      }
665 	      Size++;
666 	  }
667       }
668       TcpipReleaseSpinLock(&NeighborCache[i].Lock, OldIrql);
669   }
670 
671   return Size;
672 }
673