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)
8 * CSH 01/08-2000 Created
13 NEIGHBOR_CACHE_TABLE NeighborCache
[NB_HASHMASK
+ 1];
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"));
28 VOID
NBSendPackets( PNEIGHBOR_CACHE_ENTRY NCE
) {
29 PLIST_ENTRY PacketEntry
;
30 PNEIGHBOR_PACKET Packet
;
33 ASSERT(!(NCE
->State
& NUD_INCOMPLETE
));
35 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
36 HashValue
^= HashValue
>> 16;
37 HashValue
^= HashValue
>> 8;
38 HashValue
^= HashValue
>> 4;
39 HashValue
&= NB_HASHMASK
;
41 /* Send any waiting packets */
42 while ((PacketEntry
= ExInterlockedRemoveHeadList(&NCE
->PacketQueue
,
43 &NeighborCache
[HashValue
].Lock
)) != NULL
)
45 Packet
= CONTAINING_RECORD( PacketEntry
, NEIGHBOR_PACKET
, Next
);
49 ("PacketEntry: %x, NdisPacket %x\n",
50 PacketEntry
, Packet
->Packet
));
52 PC(Packet
->Packet
)->DLComplete
= NBCompleteSend
;
53 PC(Packet
->Packet
)->Context
= Packet
;
55 NCE
->Interface
->Transmit
56 ( NCE
->Interface
->Context
,
64 /* Must be called with table lock acquired */
65 VOID
NBFlushPacketQueue( PNEIGHBOR_CACHE_ENTRY NCE
,
66 NTSTATUS ErrorCode
) {
67 PLIST_ENTRY PacketEntry
;
68 PNEIGHBOR_PACKET Packet
;
70 while( !IsListEmpty(&NCE
->PacketQueue
) ) {
71 PacketEntry
= RemoveHeadList(&NCE
->PacketQueue
);
72 Packet
= CONTAINING_RECORD
73 ( PacketEntry
, NEIGHBOR_PACKET
, Next
);
75 ASSERT_KM_POINTER(Packet
);
79 ("PacketEntry: %x, NdisPacket %x\n",
80 PacketEntry
, Packet
->Packet
));
82 ASSERT_KM_POINTER(Packet
->Complete
);
83 Packet
->Complete( Packet
->Context
,
87 ExFreePoolWithTag( Packet
, NEIGHBOR_PACKET_TAG
);
93 * FUNCTION: Neighbor address cache timeout handler
95 * This routine is called by IPTimeout to remove outdated cache
100 PNEIGHBOR_CACHE_ENTRY
*PrevNCE
;
101 PNEIGHBOR_CACHE_ENTRY NCE
;
104 for (i
= 0; i
<= NB_HASHMASK
; i
++) {
105 TcpipAcquireSpinLockAtDpcLevel(&NeighborCache
[i
].Lock
);
107 for (PrevNCE
= &NeighborCache
[i
].Cache
;
108 (NCE
= *PrevNCE
) != NULL
;) {
109 if (NCE
->State
& NUD_INCOMPLETE
)
111 /* Solicit for an address */
113 if (NCE
->EventTimer
== 0)
116 if (NCE
->EventCount
== ARP_INCOMPLETE_TIMEOUT
)
118 NBFlushPacketQueue(NCE
, NDIS_STATUS_NETWORK_UNREACHABLE
);
124 /* Check if event timer is running */
125 if (NCE
->EventTimer
> 0) {
126 ASSERT(!(NCE
->State
& NUD_PERMANENT
));
129 if ((NCE
->EventCount
> ARP_RATE
&&
130 NCE
->EventCount
% ARP_TIMEOUT_RETRANSMISSION
== 0) ||
131 (NCE
->EventCount
== ARP_RATE
))
133 /* We haven't gotten a packet from them in
134 * EventCount seconds so we mark them as stale
136 NCE
->State
|= NUD_STALE
;
139 if (NCE
->EventTimer
- NCE
->EventCount
== 0) {
140 /* Unlink and destroy the NCE */
141 *PrevNCE
= NCE
->Next
;
143 /* Choose the proper failure status */
144 if (NCE
->State
& NUD_INCOMPLETE
)
146 /* We couldn't get an address to this IP at all */
147 Status
= NDIS_STATUS_HOST_UNREACHABLE
;
151 /* This guy was stale for way too long */
152 Status
= NDIS_STATUS_REQUEST_ABORTED
;
155 NBFlushPacketQueue(NCE
, Status
);
157 ExFreePoolWithTag(NCE
, NCE_TAG
);
162 PrevNCE
= &NCE
->Next
;
165 TcpipReleaseSpinLockFromDpcLevel(&NeighborCache
[i
].Lock
);
171 * FUNCTION: Starts the neighbor cache
176 TI_DbgPrint(DEBUG_NCACHE
, ("Called.\n"));
178 for (i
= 0; i
<= NB_HASHMASK
; i
++) {
179 NeighborCache
[i
].Cache
= NULL
;
180 TcpipInitializeSpinLock(&NeighborCache
[i
].Lock
);
184 VOID
NBShutdown(VOID
)
186 * FUNCTION: Shuts down the neighbor cache
189 PNEIGHBOR_CACHE_ENTRY NextNCE
;
190 PNEIGHBOR_CACHE_ENTRY CurNCE
;
194 TI_DbgPrint(DEBUG_NCACHE
, ("Called.\n"));
196 /* Remove possible entries from the cache */
197 for (i
= 0; i
<= NB_HASHMASK
; i
++)
199 TcpipAcquireSpinLock(&NeighborCache
[i
].Lock
, &OldIrql
);
201 CurNCE
= NeighborCache
[i
].Cache
;
203 NextNCE
= CurNCE
->Next
;
205 /* Flush wait queue */
206 NBFlushPacketQueue( CurNCE
, NDIS_STATUS_NOT_ACCEPTED
);
208 ExFreePoolWithTag(CurNCE
, NCE_TAG
);
213 NeighborCache
[i
].Cache
= NULL
;
215 TcpipReleaseSpinLock(&NeighborCache
[i
].Lock
, OldIrql
);
218 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
221 VOID
NBSendSolicit(PNEIGHBOR_CACHE_ENTRY NCE
)
223 * FUNCTION: Sends a neighbor solicitation message
225 * NCE = Pointer to NCE of neighbor to solicit
227 * May be called with lock held on NCE's table
230 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X).\n", NCE
));
232 ARPTransmit(&NCE
->Address
,
233 (NCE
->State
& NUD_INCOMPLETE
) ? NULL
: NCE
->LinkAddress
,
237 VOID
NBDestroyNeighborsForInterface(PIP_INTERFACE Interface
)
240 PNEIGHBOR_CACHE_ENTRY
*PrevNCE
;
241 PNEIGHBOR_CACHE_ENTRY NCE
;
244 KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
245 for (i
= 0; i
<= NB_HASHMASK
; i
++)
247 TcpipAcquireSpinLockAtDpcLevel(&NeighborCache
[i
].Lock
);
249 for (PrevNCE
= &NeighborCache
[i
].Cache
;
250 (NCE
= *PrevNCE
) != NULL
;)
252 if (NCE
->Interface
== Interface
)
254 /* Unlink and destroy the NCE */
255 *PrevNCE
= NCE
->Next
;
257 NBFlushPacketQueue(NCE
, NDIS_STATUS_REQUEST_ABORTED
);
258 ExFreePoolWithTag(NCE
, NCE_TAG
);
264 PrevNCE
= &NCE
->Next
;
268 TcpipReleaseSpinLockFromDpcLevel(&NeighborCache
[i
].Lock
);
270 KeLowerIrql(OldIrql
);
273 PNEIGHBOR_CACHE_ENTRY
NBAddNeighbor(
274 PIP_INTERFACE Interface
,
277 UINT LinkAddressLength
,
281 * FUNCTION: Adds a neighbor to the neighbor cache
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
289 * Pointer to NCE, NULL there is not enough free resources
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
296 PNEIGHBOR_CACHE_ENTRY NCE
;
302 ("Called. Interface (0x%X) Address (0x%X) "
303 "LinkAddress (0x%X) LinkAddressLength (%d) State (0x%X)\n",
304 Interface
, Address
, LinkAddress
, LinkAddressLength
, State
));
306 NCE
= ExAllocatePoolWithTag
307 (NonPagedPool
, sizeof(NEIGHBOR_CACHE_ENTRY
) + LinkAddressLength
,
311 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
315 NCE
->Interface
= Interface
;
316 NCE
->Address
= *Address
;
317 NCE
->LinkAddressLength
= LinkAddressLength
;
318 NCE
->LinkAddress
= (PVOID
)&NCE
[1];
320 RtlCopyMemory(NCE
->LinkAddress
, LinkAddress
, LinkAddressLength
);
322 memset(NCE
->LinkAddress
, 0xff, LinkAddressLength
);
324 NCE
->EventTimer
= EventTimer
;
326 InitializeListHead( &NCE
->PacketQueue
);
328 TI_DbgPrint(MID_TRACE
,("NCE: %x\n", NCE
));
330 HashValue
= *(PULONG
)&Address
->Address
;
331 HashValue
^= HashValue
>> 16;
332 HashValue
^= HashValue
>> 8;
333 HashValue
^= HashValue
>> 4;
334 HashValue
&= NB_HASHMASK
;
336 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
338 NCE
->Next
= NeighborCache
[HashValue
].Cache
;
339 NeighborCache
[HashValue
].Cache
= NCE
;
341 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
346 VOID
NBUpdateNeighbor(
347 PNEIGHBOR_CACHE_ENTRY NCE
,
351 * FUNCTION: Update link address information in NCE
353 * NCE = Pointer to NCE to update
354 * LinkAddress = Pointer to link address
355 * State = State of NCE
357 * The link address and state is updated. Any waiting packets are sent
363 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X) LinkAddress (0x%X) State (0x%X).\n", NCE
, LinkAddress
, State
));
365 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
366 HashValue
^= HashValue
>> 16;
367 HashValue
^= HashValue
>> 8;
368 HashValue
^= HashValue
>> 4;
369 HashValue
&= NB_HASHMASK
;
371 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
373 RtlCopyMemory(NCE
->LinkAddress
, LinkAddress
, NCE
->LinkAddressLength
);
377 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
379 if( !(NCE
->State
& NUD_INCOMPLETE
) )
381 if (NCE
->EventTimer
) NCE
->EventTimer
= ARP_COMPLETE_TIMEOUT
;
382 NBSendPackets( NCE
);
387 NBResetNeighborTimeout(PIP_ADDRESS Address
)
391 PNEIGHBOR_CACHE_ENTRY NCE
;
393 TI_DbgPrint(DEBUG_NCACHE
, ("Resetting NCE timout for 0x%s\n", A2S(Address
)));
395 HashValue
= *(PULONG
)(&Address
->Address
);
396 HashValue
^= HashValue
>> 16;
397 HashValue
^= HashValue
>> 8;
398 HashValue
^= HashValue
>> 4;
399 HashValue
&= NB_HASHMASK
;
401 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
403 for (NCE
= NeighborCache
[HashValue
].Cache
;
407 if (AddrIsEqual(Address
, &NCE
->Address
))
414 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
417 PNEIGHBOR_CACHE_ENTRY
NBLocateNeighbor(
419 PIP_INTERFACE Interface
)
421 * FUNCTION: Locates a neighbor in the neighbor cache
423 * Address = Pointer to IP address
424 * Interface = Pointer to IP interface
426 * Pointer to NCE, NULL if not found
428 * If the NCE is found, it is referenced. The caller is
429 * responsible for dereferencing it again after use
432 PNEIGHBOR_CACHE_ENTRY NCE
;
435 PIP_INTERFACE FirstInterface
;
437 TI_DbgPrint(DEBUG_NCACHE
, ("Called. Address (0x%X).\n", Address
));
439 HashValue
= *(PULONG
)&Address
->Address
;
440 HashValue
^= HashValue
>> 16;
441 HashValue
^= HashValue
>> 8;
442 HashValue
^= HashValue
>> 4;
443 HashValue
&= NB_HASHMASK
;
445 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
447 /* If there's no adapter specified, we'll look for a match on
449 if (Interface
== NULL
)
451 FirstInterface
= GetDefaultInterface();
452 Interface
= FirstInterface
;
456 FirstInterface
= NULL
;
461 NCE
= NeighborCache
[HashValue
].Cache
;
464 if (NCE
->Interface
== Interface
&&
465 AddrIsEqual(Address
, &NCE
->Address
))
476 while ((FirstInterface
!= NULL
) &&
477 ((Interface
= GetDefaultInterface()) != FirstInterface
));
479 if ((NCE
== NULL
) && (FirstInterface
!= NULL
))
481 /* This time we'll even match loopback NCEs */
482 NCE
= NeighborCache
[HashValue
].Cache
;
485 if (AddrIsEqual(Address
, &NCE
->Address
))
494 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
496 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
501 PNEIGHBOR_CACHE_ENTRY
NBFindOrCreateNeighbor(
502 PIP_INTERFACE Interface
,
506 * FUNCTION: Tries to find a neighbor and if unsuccesful, creates a new NCE
508 * Interface = Pointer to interface to use (in case NCE is not found)
509 * Address = Pointer to IP address
511 * Pointer to NCE, NULL if there is not enough free resources
513 * The NCE is referenced if found or created. The caller is
514 * responsible for dereferencing it again after use
517 PNEIGHBOR_CACHE_ENTRY NCE
;
519 TI_DbgPrint(DEBUG_NCACHE
, ("Called. Interface (0x%X) Address (0x%X).\n", Interface
, Address
));
521 NCE
= NBLocateNeighbor(Address
, Interface
);
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);
531 NCE
= NBAddNeighbor(Interface
, Address
, NULL
,
532 Interface
->AddressLength
, NUD_INCOMPLETE
, NoTimeout
? 0 : ARP_INCOMPLETE_TIMEOUT
);
533 if (!NCE
) return NULL
;
541 BOOLEAN
NBQueuePacket(
542 PNEIGHBOR_CACHE_ENTRY NCE
,
543 PNDIS_PACKET NdisPacket
,
544 PNEIGHBOR_PACKET_COMPLETE PacketComplete
,
547 * FUNCTION: Queues a packet on an NCE for later transmission
549 * NCE = Pointer to NCE to queue packet on
550 * NdisPacket = Pointer to NDIS packet to queue
552 * TRUE if the packet was successfully queued, FALSE if not
556 PNEIGHBOR_PACKET Packet
;
561 ("Called. NCE (0x%X) NdisPacket (0x%X).\n", NCE
, NdisPacket
));
563 Packet
= ExAllocatePoolWithTag( NonPagedPool
, sizeof(NEIGHBOR_PACKET
),
564 NEIGHBOR_PACKET_TAG
);
565 if( !Packet
) return FALSE
;
567 /* FIXME: Should we limit the number of queued packets? */
569 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
570 HashValue
^= HashValue
>> 16;
571 HashValue
^= HashValue
>> 8;
572 HashValue
^= HashValue
>> 4;
573 HashValue
&= NB_HASHMASK
;
575 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
577 Packet
->Complete
= PacketComplete
;
578 Packet
->Context
= PacketContext
;
579 Packet
->Packet
= NdisPacket
;
580 InsertTailList( &NCE
->PacketQueue
, &Packet
->Next
);
582 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
584 if( !(NCE
->State
& NUD_INCOMPLETE
) )
585 NBSendPackets( NCE
);
590 VOID
NBRemoveNeighbor(
591 PNEIGHBOR_CACHE_ENTRY NCE
)
593 * FUNCTION: Removes a neighbor from the neighbor cache
595 * NCE = Pointer to NCE to remove from cache
597 * The NCE must be in a safe state
600 PNEIGHBOR_CACHE_ENTRY
*PrevNCE
;
601 PNEIGHBOR_CACHE_ENTRY CurNCE
;
605 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X).\n", NCE
));
607 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
608 HashValue
^= HashValue
>> 16;
609 HashValue
^= HashValue
>> 8;
610 HashValue
^= HashValue
>> 4;
611 HashValue
&= NB_HASHMASK
;
613 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
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
)
622 /* Found it, now unlink it from the list */
623 *PrevNCE
= CurNCE
->Next
;
625 NBFlushPacketQueue( CurNCE
, NDIS_STATUS_REQUEST_ABORTED
);
626 ExFreePoolWithTag(CurNCE
, NCE_TAG
);
632 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
635 ULONG NBCopyNeighbors
636 (PIP_INTERFACE Interface
,
637 PIPARP_ENTRY ArpTable
)
639 PNEIGHBOR_CACHE_ENTRY CurNCE
;
643 for (i
= 0; i
<= NB_HASHMASK
; i
++) {
644 TcpipAcquireSpinLock(&NeighborCache
[i
].Lock
, &OldIrql
);
645 for( CurNCE
= NeighborCache
[i
].Cache
;
647 CurNCE
= CurNCE
->Next
) {
648 if( CurNCE
->Interface
== Interface
&&
649 !AddrIsEqual( &CurNCE
->Address
, &CurNCE
->Interface
->Unicast
) ) {
651 ArpTable
[Size
].Index
= Interface
->Index
;
652 ArpTable
[Size
].AddrSize
= CurNCE
->LinkAddressLength
;
654 (ArpTable
[Size
].PhysAddr
,
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
;
663 ArpTable
[Size
].Type
= ARP_ENTRY_DYNAMIC
;
668 TcpipReleaseSpinLock(&NeighborCache
[i
].Lock
, OldIrql
);