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"));
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
,
93 * FUNCTION: Neighbor address cache timeout handler
95 * This routine is called by IPTimeout to remove outdated cache
101 PNEIGHBOR_CACHE_ENTRY
*PrevNCE
;
102 PNEIGHBOR_CACHE_ENTRY NCE
;
104 for (i
= 0; i
<= NB_HASHMASK
; i
++) {
105 TcpipAcquireSpinLock(&NeighborCache
[i
].Lock
, &OldIrql
);
107 for (PrevNCE
= &NeighborCache
[i
].Cache
;
108 (NCE
= *PrevNCE
) != NULL
;
109 PrevNCE
= &NCE
->Next
) {
110 /* Check if event timer is running */
111 if (NCE
->EventTimer
> 0) {
112 ASSERT(!(NCE
->State
& NUD_PERMANENT
));
114 if (NCE
->EventCount
% ARP_RATE
== 0)
116 if (NCE
->EventTimer
- NCE
->EventCount
== 0) {
117 NCE
->State
|= NUD_STALE
;
124 TcpipReleaseSpinLock(&NeighborCache
[i
].Lock
, OldIrql
);
130 * FUNCTION: Starts the neighbor cache
135 TI_DbgPrint(DEBUG_NCACHE
, ("Called.\n"));
137 for (i
= 0; i
<= NB_HASHMASK
; i
++) {
138 NeighborCache
[i
].Cache
= NULL
;
139 TcpipInitializeSpinLock(&NeighborCache
[i
].Lock
);
143 VOID
NBShutdown(VOID
)
145 * FUNCTION: Shuts down the neighbor cache
148 PNEIGHBOR_CACHE_ENTRY NextNCE
;
149 PNEIGHBOR_CACHE_ENTRY CurNCE
;
153 TI_DbgPrint(DEBUG_NCACHE
, ("Called.\n"));
155 /* Remove possible entries from the cache */
156 for (i
= 0; i
<= NB_HASHMASK
; i
++)
158 TcpipAcquireSpinLock(&NeighborCache
[i
].Lock
, &OldIrql
);
160 CurNCE
= NeighborCache
[i
].Cache
;
162 NextNCE
= CurNCE
->Next
;
164 /* Flush wait queue */
165 NBFlushPacketQueue( CurNCE
, NDIS_STATUS_NOT_ACCEPTED
);
172 NeighborCache
[i
].Cache
= NULL
;
174 TcpipReleaseSpinLock(&NeighborCache
[i
].Lock
, OldIrql
);
177 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
180 VOID
NBSendSolicit(PNEIGHBOR_CACHE_ENTRY NCE
)
182 * FUNCTION: Sends a neighbor solicitation message
184 * NCE = Pointer to NCE of neighbor to solicit
186 * May be called with lock held on NCE's table
189 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X).\n", NCE
));
191 ARPTransmit(&NCE
->Address
, NCE
->Interface
);
194 PNEIGHBOR_CACHE_ENTRY
NBAddNeighbor(
195 PIP_INTERFACE Interface
,
198 UINT LinkAddressLength
,
202 * FUNCTION: Adds a neighbor to the neighbor cache
204 * Interface = Pointer to interface
205 * Address = Pointer to IP address
206 * LinkAddress = Pointer to link address (may be NULL)
207 * LinkAddressLength = Length of link address
208 * State = State of NCE
210 * Pointer to NCE, NULL there is not enough free resources
212 * The NCE if referenced for the caller if created. The NCE retains
213 * a reference to the IP address if it is created, the caller is
214 * responsible for providing this reference
217 PNEIGHBOR_CACHE_ENTRY NCE
;
223 ("Called. Interface (0x%X) Address (0x%X) "
224 "LinkAddress (0x%X) LinkAddressLength (%d) State (0x%X)\n",
225 Interface
, Address
, LinkAddress
, LinkAddressLength
, State
));
228 (NonPagedPool
, sizeof(NEIGHBOR_CACHE_ENTRY
) + LinkAddressLength
);
231 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
235 INIT_TAG(NCE
, ' ECN');
237 NCE
->Interface
= Interface
;
238 NCE
->Address
= *Address
;
239 NCE
->LinkAddressLength
= LinkAddressLength
;
240 NCE
->LinkAddress
= (PVOID
)&NCE
[1];
242 RtlCopyMemory(NCE
->LinkAddress
, LinkAddress
, LinkAddressLength
);
244 memset(NCE
->LinkAddress
, 0xff, LinkAddressLength
);
246 NCE
->EventTimer
= EventTimer
;
248 InitializeListHead( &NCE
->PacketQueue
);
250 TI_DbgPrint(MID_TRACE
,("NCE: %x\n", NCE
));
252 HashValue
= *(PULONG
)&Address
->Address
;
253 HashValue
^= HashValue
>> 16;
254 HashValue
^= HashValue
>> 8;
255 HashValue
^= HashValue
>> 4;
256 HashValue
&= NB_HASHMASK
;
258 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
260 NCE
->Next
= NeighborCache
[HashValue
].Cache
;
261 NeighborCache
[HashValue
].Cache
= NCE
;
263 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
268 VOID
NBUpdateNeighbor(
269 PNEIGHBOR_CACHE_ENTRY NCE
,
273 * FUNCTION: Update link address information in NCE
275 * NCE = Pointer to NCE to update
276 * LinkAddress = Pointer to link address
277 * State = State of NCE
279 * The link address and state is updated. Any waiting packets are sent
285 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X) LinkAddress (0x%X) State (0x%X).\n", NCE
, LinkAddress
, State
));
287 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
288 HashValue
^= HashValue
>> 16;
289 HashValue
^= HashValue
>> 8;
290 HashValue
^= HashValue
>> 4;
291 HashValue
&= NB_HASHMASK
;
293 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
295 RtlCopyMemory(NCE
->LinkAddress
, LinkAddress
, NCE
->LinkAddressLength
);
299 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
301 if( !(NCE
->State
& NUD_INCOMPLETE
) )
302 NBSendPackets( NCE
);
305 PNEIGHBOR_CACHE_ENTRY
NBLocateNeighbor(
308 * FUNCTION: Locates a neighbor in the neighbor cache
310 * Address = Pointer to IP address
312 * Pointer to NCE, NULL if not found
314 * If the NCE is found, it is referenced. The caller is
315 * responsible for dereferencing it again after use
318 PNEIGHBOR_CACHE_ENTRY NCE
;
322 TI_DbgPrint(DEBUG_NCACHE
, ("Called. Address (0x%X).\n", Address
));
324 HashValue
= *(PULONG
)&Address
->Address
;
325 HashValue
^= HashValue
>> 16;
326 HashValue
^= HashValue
>> 8;
327 HashValue
^= HashValue
>> 4;
328 HashValue
&= NB_HASHMASK
;
330 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
332 NCE
= NeighborCache
[HashValue
].Cache
;
334 while ((NCE
) && (!AddrIsEqual(Address
, &NCE
->Address
)))
339 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
341 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
346 PNEIGHBOR_CACHE_ENTRY
NBFindOrCreateNeighbor(
347 PIP_INTERFACE Interface
,
350 * FUNCTION: Tries to find a neighbor and if unsuccesful, creates a new NCE
352 * Interface = Pointer to interface to use (in case NCE is not found)
353 * Address = Pointer to IP address
355 * Pointer to NCE, NULL if there is not enough free resources
357 * The NCE is referenced if found or created. The caller is
358 * responsible for dereferencing it again after use
361 PNEIGHBOR_CACHE_ENTRY NCE
;
363 TI_DbgPrint(DEBUG_NCACHE
, ("Called. Interface (0x%X) Address (0x%X).\n", Interface
, Address
));
365 NCE
= NBLocateNeighbor(Address
);
368 TI_DbgPrint(MID_TRACE
,("BCAST: %s\n", A2S(&Interface
->Broadcast
)));
369 if( AddrIsEqual(Address
, &Interface
->Broadcast
) ||
370 AddrIsUnspecified(Address
) ) {
371 TI_DbgPrint(MID_TRACE
,("Packet targeted at broadcast addr\n"));
372 NCE
= NBAddNeighbor(Interface
, Address
, NULL
,
373 Interface
->AddressLength
, NUD_PERMANENT
, 0);
375 NCE
= NBAddNeighbor(Interface
, Address
, NULL
,
376 Interface
->AddressLength
, NUD_INCOMPLETE
, ARP_TIMEOUT
);
377 if (!NCE
) return NULL
;
385 BOOLEAN
NBQueuePacket(
386 PNEIGHBOR_CACHE_ENTRY NCE
,
387 PNDIS_PACKET NdisPacket
,
388 PNEIGHBOR_PACKET_COMPLETE PacketComplete
,
391 * FUNCTION: Queues a packet on an NCE for later transmission
393 * NCE = Pointer to NCE to queue packet on
394 * NdisPacket = Pointer to NDIS packet to queue
396 * TRUE if the packet was successfully queued, FALSE if not
400 PNEIGHBOR_PACKET Packet
;
405 ("Called. NCE (0x%X) NdisPacket (0x%X).\n", NCE
, NdisPacket
));
407 Packet
= exAllocatePool( NonPagedPool
, sizeof(NEIGHBOR_PACKET
) );
408 if( !Packet
) return FALSE
;
410 /* FIXME: Should we limit the number of queued packets? */
412 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
413 HashValue
^= HashValue
>> 16;
414 HashValue
^= HashValue
>> 8;
415 HashValue
^= HashValue
>> 4;
416 HashValue
&= NB_HASHMASK
;
418 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
420 Packet
->Complete
= PacketComplete
;
421 Packet
->Context
= PacketContext
;
422 Packet
->Packet
= NdisPacket
;
423 InsertTailList( &NCE
->PacketQueue
, &Packet
->Next
);
425 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
427 if( !(NCE
->State
& NUD_INCOMPLETE
) )
428 NBSendPackets( NCE
);
433 VOID
NBRemoveNeighbor(
434 PNEIGHBOR_CACHE_ENTRY NCE
)
436 * FUNCTION: Removes a neighbor from the neighbor cache
438 * NCE = Pointer to NCE to remove from cache
440 * The NCE must be in a safe state
443 PNEIGHBOR_CACHE_ENTRY
*PrevNCE
;
444 PNEIGHBOR_CACHE_ENTRY CurNCE
;
448 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X).\n", NCE
));
450 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
451 HashValue
^= HashValue
>> 16;
452 HashValue
^= HashValue
>> 8;
453 HashValue
^= HashValue
>> 4;
454 HashValue
&= NB_HASHMASK
;
456 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
458 /* Search the list and remove the NCE from the list if found */
459 for (PrevNCE
= &NeighborCache
[HashValue
].Cache
;
460 (CurNCE
= *PrevNCE
) != NULL
;
461 PrevNCE
= &CurNCE
->Next
)
465 /* Found it, now unlink it from the list */
466 *PrevNCE
= CurNCE
->Next
;
468 NBFlushPacketQueue( CurNCE
, NDIS_STATUS_REQUEST_ABORTED
);
475 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
478 ULONG NBCopyNeighbors
479 (PIP_INTERFACE Interface
,
480 PIPARP_ENTRY ArpTable
)
482 PNEIGHBOR_CACHE_ENTRY CurNCE
;
486 for (i
= 0; i
<= NB_HASHMASK
; i
++) {
487 TcpipAcquireSpinLock(&NeighborCache
[i
].Lock
, &OldIrql
);
488 for( CurNCE
= NeighborCache
[i
].Cache
;
490 CurNCE
= CurNCE
->Next
) {
491 if( CurNCE
->Interface
== Interface
&&
492 !AddrIsEqual( &CurNCE
->Address
, &CurNCE
->Interface
->Unicast
) ) {
494 ArpTable
[Size
].Index
= Interface
->Index
;
495 ArpTable
[Size
].AddrSize
= CurNCE
->LinkAddressLength
;
497 (ArpTable
[Size
].PhysAddr
,
499 CurNCE
->LinkAddressLength
);
500 ArpTable
[Size
].LogAddr
= CurNCE
->Address
.Address
.IPv4Address
;
501 if( CurNCE
->State
& NUD_PERMANENT
)
502 ArpTable
[Size
].Type
= ARP_ENTRY_STATIC
;
503 else if( CurNCE
->State
& NUD_INCOMPLETE
)
504 ArpTable
[Size
].Type
= ARP_ENTRY_INVALID
;
506 ArpTable
[Size
].Type
= ARP_ENTRY_DYNAMIC
;
511 TcpipReleaseSpinLock(&NeighborCache
[i
].Lock
, OldIrql
);