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 Packet
->Complete( Packet
->Context
, Packet
->Packet
, STATUS_SUCCESS
);
21 TI_DbgPrint(MID_TRACE
, ("Completed\n"));
22 PoolFreeBuffer( Packet
);
23 TI_DbgPrint(MID_TRACE
, ("Freed\n"));
26 VOID
NBSendPackets( PNEIGHBOR_CACHE_ENTRY NCE
) {
27 PLIST_ENTRY PacketEntry
;
28 PNEIGHBOR_PACKET Packet
;
30 /* Send any waiting packets */
31 if( !IsListEmpty( &NCE
->PacketQueue
) ) {
32 PacketEntry
= RemoveHeadList( &NCE
->PacketQueue
);
33 Packet
= CONTAINING_RECORD( PacketEntry
, NEIGHBOR_PACKET
, Next
);
37 ("PacketEntry: %x, NdisPacket %x\n",
38 PacketEntry
, Packet
->Packet
));
40 PC(Packet
->Packet
)->DLComplete
= NBCompleteSend
;
41 PC(Packet
->Packet
)->Context
= Packet
;
43 NCE
->Interface
->Transmit
44 ( NCE
->Interface
->Context
,
52 VOID
NBFlushPacketQueue( PNEIGHBOR_CACHE_ENTRY NCE
,
54 NTSTATUS ErrorCode
) {
55 PLIST_ENTRY PacketEntry
;
56 PNEIGHBOR_PACKET Packet
;
58 while( !IsListEmpty( &NCE
->PacketQueue
) ) {
59 PacketEntry
= RemoveHeadList( &NCE
->PacketQueue
);
60 Packet
= CONTAINING_RECORD
61 ( PacketEntry
, NEIGHBOR_PACKET
, Next
);
65 ("PacketEntry: %x, NdisPacket %x\n",
66 PacketEntry
, Packet
->Packet
));
69 Packet
->Complete( Packet
->Context
,
71 NDIS_STATUS_REQUEST_ABORTED
);
73 PoolFreeBuffer( Packet
);
78 PNEIGHBOR_CACHE_ENTRY NCE
)
80 * FUNCTION: Neighbor cache entry timeout handler
82 * The neighbor cache lock must be held
85 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X).\n", NCE
));
86 TI_DbgPrint(DEBUG_NCACHE
, ("NCE->State is (0x%X).\n", NCE
->State
));
91 /* Retransmission timer expired */
92 if (NCE
->EventCount
++ > MAX_MULTICAST_SOLICIT
)
94 /* We have retransmitted too many times */
96 /* Calling IPSendComplete with cache lock held is not
97 a great thing to do. We don't get here very often
98 so maybe it's not that big a problem */
100 /* Flush packet queue */
101 NBFlushPacketQueue( NCE
, TRUE
, NDIS_STATUS_REQUEST_ABORTED
);
104 /* Remove route cache entries with references to this NCE.
105 Remember that neighbor cache lock is acquired before the
107 RouteInvalidateNCE(NCE
);
111 /* Retransmit request */
117 /* FIXME: Delayed state */
118 TI_DbgPrint(DEBUG_NCACHE
, ("NCE delay state.\n"));
122 /* FIXME: Probe state */
123 TI_DbgPrint(DEBUG_NCACHE
, ("NCE probe state.\n"));
127 /* Should not happen since the event timer is not used in the other states */
128 TI_DbgPrint(MIN_TRACE
, ("Invalid NCE state (%d).\n", NCE
->State
));
136 * FUNCTION: Neighbor address cache timeout handler
138 * This routine is called by IPTimeout to remove outdated cache
144 PNEIGHBOR_CACHE_ENTRY NCE
;
146 for (i
= 0; i
<= NB_HASHMASK
; i
++) {
147 TcpipAcquireSpinLock(&NeighborCache
[i
].Lock
, &OldIrql
);
149 for (NCE
= NeighborCache
[i
].Cache
;
150 NCE
!= NULL
; NCE
= NCE
->Next
) {
151 /* Check if event timer is running */
152 if (NCE
->EventTimer
> 0) {
154 if (NCE
->EventTimer
== 0) {
155 /* Call timeout handler for NCE */
161 TcpipReleaseSpinLock(&NeighborCache
[i
].Lock
, OldIrql
);
167 * FUNCTION: Starts the neighbor cache
172 TI_DbgPrint(DEBUG_NCACHE
, ("Called.\n"));
174 for (i
= 0; i
<= NB_HASHMASK
; i
++) {
175 NeighborCache
[i
].Cache
= NULL
;
176 TcpipInitializeSpinLock(&NeighborCache
[i
].Lock
);
180 VOID
NBShutdown(VOID
)
182 * FUNCTION: Shuts down the neighbor cache
185 PNEIGHBOR_CACHE_ENTRY NextNCE
;
186 PNEIGHBOR_CACHE_ENTRY CurNCE
;
190 TI_DbgPrint(DEBUG_NCACHE
, ("Called.\n"));
192 /* Remove possible entries from the cache */
193 for (i
= 0; i
<= NB_HASHMASK
; i
++)
195 TcpipAcquireSpinLock(&NeighborCache
[i
].Lock
, &OldIrql
);
197 CurNCE
= NeighborCache
[i
].Cache
;
199 NextNCE
= CurNCE
->Next
;
201 /* Remove all references from route cache */
202 RouteInvalidateNCE(CurNCE
);
204 /* Flush wait queue */
205 NBFlushPacketQueue( CurNCE
, FALSE
, STATUS_SUCCESS
);
210 NeighborCache
[i
].Cache
= NULL
;
212 TcpipReleaseSpinLock(&NeighborCache
[i
].Lock
, OldIrql
);
215 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
218 VOID
NBSendSolicit(PNEIGHBOR_CACHE_ENTRY NCE
)
220 * FUNCTION: Sends a neighbor solicitation message
222 * NCE = Pointer to NCE of neighbor to solicit
224 * May be called with lock held on NCE's table
227 PLIST_ENTRY CurrentEntry
;
228 PNET_TABLE_ENTRY NTE
;
230 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X).\n", NCE
));
232 if (NCE
->State
== NUD_INCOMPLETE
)
234 /* This is the first solicitation of this neighbor. Broadcast
235 a request for the neighbor */
237 /* FIXME: Choose first NTE. We might want to give an NTE as argument */
238 if (!NCE
->Interface
|| !NCE
->Interface
->NTEListHead
.Flink
) {
239 TI_DbgPrint(MID_TRACE
,
240 ("NCE->Interface: %x, "
241 "NCE->Interface->NTEListHead.Flink %x\n",
243 NCE
->Interface
? NCE
->Interface
->NTEListHead
.Flink
: 0));
246 TI_DbgPrint(MID_TRACE
,("MARK\n"));
247 TI_DbgPrint(MID_TRACE
,("NCE: %x\n", NCE
));
248 TI_DbgPrint(MID_TRACE
,("NCE->Interface: %x\n", NCE
->Interface
));
250 if (!IsListEmpty(&NCE
->Interface
->NTEListHead
)) {
251 CurrentEntry
= NCE
->Interface
->NTEListHead
.Flink
;
252 NTE
= CONTAINING_RECORD(CurrentEntry
, NET_TABLE_ENTRY
,
254 ARPTransmit(&NCE
->Address
, NTE
);
256 TI_DbgPrint(MIN_TRACE
, ("Interface at 0x%X has zero NTE.\n",
260 /* FIXME: Unicast solicitation since we have a cached address */
261 TI_DbgPrint(MIN_TRACE
, ("Uninplemented unicast solicitation.\n"));
265 PNEIGHBOR_CACHE_ENTRY
NBAddNeighbor(
266 PIP_INTERFACE Interface
,
269 UINT LinkAddressLength
,
272 * FUNCTION: Adds a neighbor to the neighbor cache
274 * Interface = Pointer to interface
275 * Address = Pointer to IP address
276 * LinkAddress = Pointer to link address (may be NULL)
277 * LinkAddressLength = Length of link address
278 * State = State of NCE
280 * Pointer to NCE, NULL there is not enough free resources
282 * The NCE if referenced for the caller if created. The NCE retains
283 * a reference to the IP address if it is created, the caller is
284 * responsible for providing this reference
287 PNEIGHBOR_CACHE_ENTRY NCE
;
293 ("Called. Interface (0x%X) Address (0x%X) "
294 "LinkAddress (0x%X) LinkAddressLength (%d) State (0x%X)\n",
295 Interface
, Address
, LinkAddress
, LinkAddressLength
, State
));
297 ASSERT(Address
->Type
== IP_ADDRESS_V4
);
300 (NonPagedPool
, sizeof(NEIGHBOR_CACHE_ENTRY
) + LinkAddressLength
);
303 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
307 INIT_TAG(NCE
, TAG('N','C','E',' '));
309 NCE
->Interface
= Interface
;
310 NCE
->Address
= *Address
;
311 NCE
->LinkAddressLength
= LinkAddressLength
;
312 NCE
->LinkAddress
= (PVOID
)&NCE
[1];
314 RtlCopyMemory(NCE
->LinkAddress
, LinkAddress
, LinkAddressLength
);
316 NCE
->EventTimer
= 0; /* Not in use */
317 InitializeListHead( &NCE
->PacketQueue
);
319 HashValue
= *(PULONG
)&Address
->Address
;
320 HashValue
^= HashValue
>> 16;
321 HashValue
^= HashValue
>> 8;
322 HashValue
^= HashValue
>> 4;
323 HashValue
&= NB_HASHMASK
;
325 NCE
->Table
= &NeighborCache
[HashValue
];
327 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
329 NCE
->Next
= NeighborCache
[HashValue
].Cache
;
330 NeighborCache
[HashValue
].Cache
= NCE
;
332 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
337 VOID
NBUpdateNeighbor(
338 PNEIGHBOR_CACHE_ENTRY NCE
,
342 * FUNCTION: Update link address information in NCE
344 * NCE = Pointer to NCE to update
345 * LinkAddress = Pointer to link address
346 * State = State of NCE
348 * The link address and state is updated. Any waiting packets are sent
353 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X) LinkAddress (0x%X) State (0x%X).\n", NCE
, LinkAddress
, State
));
355 TcpipAcquireSpinLock(&NCE
->Table
->Lock
, &OldIrql
);
357 RtlCopyMemory(NCE
->LinkAddress
, LinkAddress
, NCE
->LinkAddressLength
);
360 TcpipReleaseSpinLock(&NCE
->Table
->Lock
, OldIrql
);
362 if( NCE
->State
& NUD_CONNECTED
)
363 NBSendPackets( NCE
);
366 PNEIGHBOR_CACHE_ENTRY
NBLocateNeighbor(
369 * FUNCTION: Locates a neighbor in the neighbor cache
371 * Address = Pointer to IP address
373 * Pointer to NCE, NULL if not found
375 * If the NCE is found, it is referenced. The caller is
376 * responsible for dereferencing it again after use
379 PNEIGHBOR_CACHE_ENTRY NCE
;
383 TI_DbgPrint(DEBUG_NCACHE
, ("Called. Address (0x%X).\n", Address
));
385 HashValue
= *(PULONG
)&Address
->Address
;
386 HashValue
^= HashValue
>> 16;
387 HashValue
^= HashValue
>> 8;
388 HashValue
^= HashValue
>> 4;
389 HashValue
&= NB_HASHMASK
;
391 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
393 NCE
= NeighborCache
[HashValue
].Cache
;
395 while ((NCE
) && (!AddrIsEqual(Address
, &NCE
->Address
)))
400 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
402 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
407 PNEIGHBOR_CACHE_ENTRY
NBFindOrCreateNeighbor(
408 PIP_INTERFACE Interface
,
411 * FUNCTION: Tries to find a neighbor and if unsuccesful, creates a new NCE
413 * Interface = Pointer to interface to use (in case NCE is not found)
414 * Address = Pointer to IP address
416 * Pointer to NCE, NULL if there is not enough free resources
418 * The NCE is referenced if found or created. The caller is
419 * responsible for dereferencing it again after use
422 PNEIGHBOR_CACHE_ENTRY NCE
;
424 TI_DbgPrint(DEBUG_NCACHE
, ("Called. Interface (0x%X) Address (0x%X).\n", Interface
, Address
));
426 NCE
= NBLocateNeighbor(Address
);
429 NCE
= NBAddNeighbor(Interface
, Address
, NULL
,
430 Interface
->AddressLength
, NUD_INCOMPLETE
);
438 BOOLEAN
NBQueuePacket(
439 PNEIGHBOR_CACHE_ENTRY NCE
,
440 PNDIS_PACKET NdisPacket
,
441 PNEIGHBOR_PACKET_COMPLETE PacketComplete
,
444 * FUNCTION: Queues a packet on an NCE for later transmission
446 * NCE = Pointer to NCE to queue packet on
447 * NdisPacket = Pointer to NDIS packet to queue
449 * TRUE if the packet was successfully queued, FALSE if not
454 PNEIGHBOR_PACKET Packet
;
458 ("Called. NCE (0x%X) NdisPacket (0x%X).\n", NCE
, NdisPacket
));
460 Packet
= PoolAllocateBuffer( sizeof(NEIGHBOR_PACKET
) );
461 if( !Packet
) return FALSE
;
463 /* FIXME: Should we limit the number of queued packets? */
465 Lock
= &NCE
->Table
->Lock
;
467 TcpipAcquireSpinLock(Lock
, &OldIrql
);
469 Packet
->Complete
= PacketComplete
;
470 Packet
->Context
= PacketContext
;
471 Packet
->Packet
= NdisPacket
;
472 InsertTailList( &NCE
->PacketQueue
, &Packet
->Next
);
474 if( NCE
->State
& NUD_CONNECTED
)
475 NBSendPackets( NCE
);
477 TcpipReleaseSpinLock(Lock
, OldIrql
);
483 VOID
NBRemoveNeighbor(
484 PNEIGHBOR_CACHE_ENTRY NCE
)
486 * FUNCTION: Removes a neighbor from the neighbor cache
488 * NCE = Pointer to NCE to remove from cache
490 * The NCE must be in a safe state
493 PNEIGHBOR_CACHE_ENTRY
*PrevNCE
;
494 PNEIGHBOR_CACHE_ENTRY CurNCE
;
498 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X).\n", NCE
));
500 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
501 HashValue
^= HashValue
>> 16;
502 HashValue
^= HashValue
>> 8;
503 HashValue
^= HashValue
>> 4;
504 HashValue
&= NB_HASHMASK
;
506 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
508 /* Search the list and remove the NCE from the list if found */
509 for (PrevNCE
= &NeighborCache
[HashValue
].Cache
;
510 (CurNCE
= *PrevNCE
) != NULL
;
511 PrevNCE
= &CurNCE
->Next
)
515 /* Found it, now unlink it from the list */
516 *PrevNCE
= CurNCE
->Next
;
518 NBFlushPacketQueue( CurNCE
, TRUE
, NDIS_STATUS_REQUEST_ABORTED
);
520 /* Remove all references from route cache */
521 RouteInvalidateNCE(CurNCE
);
528 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);