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
22 NEIGHBOR_CACHE_TABLE NeighborCache[NB_HASHMASK + 1];
33 PNEIGHBOR_CACHE_ENTRY NCE)
35 * FUNCTION: Neighbor cache entry timeout handler
37 * The neighbor cache lock must be held
40 PNDIS_PACKET NdisPacket, Next;
42 TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
44 TI_DbgPrint(DEBUG_NCACHE, ("NCE->State is (0x%X).\n", NCE->State));
48 /* Retransmission timer expired */
49 if (NCE->EventCount++ > MAX_MULTICAST_SOLICIT) {
50 /* We have retransmitted too many times */
52 /* Calling IPSendComplete with cache lock held is not
53 a great thing to do. We don't get here very often
54 so maybe it's not that big a problem */
56 /* Flush packet queue */
57 NdisPacket = NCE->WaitQueue;
59 Next = (PNDIS_PACKET)PC(NdisPacket)->DLComplete;
60 IPSendComplete((PVOID)NCE->Interface, NdisPacket,
61 NDIS_STATUS_REQUEST_ABORTED);
64 NCE->WaitQueue = NULL;
68 /* Remove route cache entries with references to this NCE.
69 Remember that neighbor cache lock is taken before
71 RouteInvalidateNCE(NCE);
73 /* Retransmit request */
78 /* FIXME: Delayed state */
79 TI_DbgPrint(DEBUG_NCACHE, ("NCE delay state.\n"));
83 /* FIXME: Probe state */
84 TI_DbgPrint(DEBUG_NCACHE, ("NCE probe state.\n"));
88 /* Should not happen since other states don't use the event timer */
89 TI_DbgPrint(MIN_TRACE, ("Invalid NCE state (%d).\n", NCE->State));
98 * FUNCTION: Neighbor address cache timeout handler
100 * This routine is called by IPTimeout to remove outdated cache
106 PNEIGHBOR_CACHE_ENTRY NCE;
108 for (i = 0; i <= NB_HASHMASK; i++) {
109 KeAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql);
111 for (NCE = NeighborCache[i].Cache;
112 NCE != NULL; NCE = NCE->Next) {
113 /* Check if event timer is running */
114 if (NCE->EventTimer != 0) {
115 if (--NCE->EventTimer == 0) {
116 /* Call timeout handler for NCE */
122 KeReleaseSpinLock(&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 KeInitializeSpinLock(&NeighborCache[i].Lock);
147 * FUNCTION: Shuts down the neighbor cache
152 PNDIS_PACKET NdisPacket, Next;
153 PNEIGHBOR_CACHE_ENTRY CurNCE, NextNCE;
155 TI_DbgPrint(DEBUG_NCACHE, ("Called.\n"));
157 /* Remove possible entries from the cache */
158 for (i = 0; i <= NB_HASHMASK; i++) {
159 KeAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql);
161 CurNCE = NeighborCache[i].Cache;
163 NextNCE = CurNCE->Next;
165 /* Remove all references from route cache */
166 RouteInvalidateNCE(CurNCE);
168 /* Flush wait queue */
169 NdisPacket = CurNCE->WaitQueue;
171 Next = (PNDIS_PACKET)PC(NdisPacket)->DLComplete;
172 FreeNdisPacket(NdisPacket);
177 if (CurNCE->RefCount != 1) {
178 TI_DbgPrint(DEBUG_REFCOUNT, ("NCE at (0x%X) has (%d) references (should be 1).\n", CurNCE, CurNCE->RefCount));
182 /* Remove reference for being alive */
183 DereferenceObject(CurNCE);
187 NeighborCache[i].Cache = NULL;
189 KeReleaseSpinLock(&NeighborCache[i].Lock, OldIrql);
192 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
197 PNEIGHBOR_CACHE_ENTRY NCE)
199 * FUNCTION: Sends a neighbor solicitation message
201 * NCE = Pointer to NCE of neighbor to solicit
203 * May be called with lock held on NCE's table
206 PLIST_ENTRY CurrentEntry;
207 PNET_TABLE_ENTRY NTE;
209 TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
211 if (NCE->State == NUD_INCOMPLETE) {
212 /* This is the first solicitation of this neighbor. Broadcast
213 a request for the neighbor */
215 /* FIXME: Choose first NTE. We might want to give an NTE as argument */
216 CurrentEntry = NCE->Interface->NTEListHead.Flink;
217 if (!IsListEmpty(CurrentEntry)) {
218 NTE = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, IFListEntry);
219 ARPTransmit(NCE->Address, NTE);
221 TI_DbgPrint(MIN_TRACE, ("Interface at 0x%X has zero NTE.\n", NCE->Interface));
224 /* FIXME: Unicast solicitation since we have a cached address */
225 TI_DbgPrint(MIN_TRACE, ("Uninplemented unicast solicitation.\n"));
230 PNEIGHBOR_CACHE_ENTRY NBAddNeighbor(
231 PIP_INTERFACE Interface,
234 UINT LinkAddressLength,
237 * FUNCTION: Adds a neighbor to the neighbor cache
239 * Interface = Pointer to interface
240 * Address = Pointer to IP address
241 * LinkAddress = Pointer to link address (may be NULL)
242 * LinkAddressLength = Length of link address
243 * State = State of NCE
245 * Pointer to NCE, NULL there is not enough free resources
247 * The NCE if referenced for the caller if created. The NCE retains
248 * a reference to the IP address if it is created, the caller is
249 * responsible for providing this reference
254 PNEIGHBOR_CACHE_ENTRY NCE;
256 TI_DbgPrint(DEBUG_NCACHE, ("Called. Interface (0x%X) Address (0x%X) "
257 "LinkAddress (0x%X) LinkAddressLength (%d) State (0x%X)\n",
258 Interface, Address, LinkAddress, LinkAddressLength, State));
260 NCE = ExAllocatePool(NonPagedPool, sizeof(NEIGHBOR_CACHE_ENTRY) + LinkAddressLength);
262 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
266 INIT_TAG(NCE, TAG('N','C','E',' '));
268 /* Initialize NCE free routine */
271 /* Reference once for beeing alive and once for the caller */
273 NCE->Interface = Interface;
274 NCE->Address = Address;
275 NCE->LinkAddressLength = LinkAddressLength;
276 NCE->LinkAddress = (PVOID)((ULONG_PTR)NCE + sizeof(NEIGHBOR_CACHE_ENTRY));
278 RtlCopyMemory(NCE->LinkAddress, LinkAddress, LinkAddressLength);
280 NCE->EventTimer = 0; /* Not in use */
281 NCE->WaitQueue = NULL;
283 HashValue = *(PULONG)&Address->Address;
284 HashValue ^= HashValue >> 16;
285 HashValue ^= HashValue >> 8;
286 HashValue ^= HashValue >> 4;
287 HashValue &= NB_HASHMASK;
289 NCE->Table = &NeighborCache[HashValue];
291 KeAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
293 NCE->Next = NeighborCache[HashValue].Cache;
294 NeighborCache[HashValue].Cache = NCE;
296 KeReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
302 VOID NBUpdateNeighbor(
303 PNEIGHBOR_CACHE_ENTRY NCE,
307 * FUNCTION: Update link address information in NCE
309 * NCE = Pointer to NCE to update
310 * LinkAddress = Pointer to link address
311 * State = State of NCE
313 * The link address and state is updated. Any waiting packets are sent
317 PNDIS_PACKET Current;
320 TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X) LinkAddress (0x%X) State (0x%X).\n", NCE, LinkAddress, State));
322 KeAcquireSpinLock(&NCE->Table->Lock, &OldIrql);
324 RtlCopyMemory(NCE->LinkAddress, LinkAddress, NCE->LinkAddressLength);
326 Current = NCE->WaitQueue;
327 NCE->WaitQueue = NULL;
329 KeReleaseSpinLock(&NCE->Table->Lock, OldIrql);
331 /* Send any waiting packets */
333 /* Our link to the next packet is broken by the
334 datalink layer code so we must save it here */
335 Next = (PNDIS_PACKET)PC(Current)->DLComplete;
336 IPSendFragment(Current, NCE);
343 PNEIGHBOR_CACHE_ENTRY NBLocateNeighbor(
346 * FUNCTION: Locates a neighbor in the neighbor cache
348 * Address = Pointer to IP address
350 * Pointer to NCE, NULL if not found
352 * If the NCE is found, it is referenced. The caller is
353 * responsible for dereferencing it again after use
358 PNEIGHBOR_CACHE_ENTRY NCE;
360 TI_DbgPrint(DEBUG_NCACHE, ("Called. Address (0x%X).\n", Address));
362 HashValue = *(PULONG)&Address->Address;
363 HashValue ^= HashValue >> 16;
364 HashValue ^= HashValue >> 8;
365 HashValue ^= HashValue >> 4;
366 HashValue &= NB_HASHMASK;
368 KeAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
370 NCE = NeighborCache[HashValue].Cache;
372 while ((NCE) && (!AddrIsEqual(Address, NCE->Address)))
376 ReferenceObject(NCE);
378 KeReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
380 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
386 PNEIGHBOR_CACHE_ENTRY NBFindOrCreateNeighbor(
387 PIP_INTERFACE Interface,
390 * FUNCTION: Tries to find a neighbor and if unsuccesful, creates a new NCE
392 * Interface = Pointer to interface to use (if NCE is not found)
393 * Address = Pointer to IP address
395 * Pointer to NCE, NULL if there is not enough free resources
397 * The NCE is referenced if found or created. The caller is
398 * responsible for dereferencing it again after use
401 PNEIGHBOR_CACHE_ENTRY NCE;
403 TI_DbgPrint(DEBUG_NCACHE, ("Called. Interface (0x%X) Address (0x%X).\n", Interface, Address));
405 NCE = NBLocateNeighbor(Address);
407 ReferenceObject(Address);
408 NCE = NBAddNeighbor(Interface, Address, NULL,
409 Interface->AddressLength, NUD_INCOMPLETE);
418 BOOLEAN NBQueuePacket(
419 PNEIGHBOR_CACHE_ENTRY NCE,
420 PNDIS_PACKET NdisPacket)
422 * FUNCTION: Queues a packet on an NCE for later transmission
424 * NCE = Pointer to NCE to queue packet on
425 * NdisPacket = Pointer to NDIS packet to queue
427 * TRUE if the packet was successfully queued, FALSE if not
433 TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X) NdisPacket (0x%X).\n", NCE, NdisPacket));
435 /* FIXME: Should we limit the number of queued packets? */
437 Lock = &NCE->Table->Lock;
439 KeAcquireSpinLock(Lock, &OldIrql);
441 /* Use data link level completion handler pointer to link
442 queued packets together */
443 PC(NdisPacket)->DLComplete = (PACKET_COMPLETION_ROUTINE)NCE->WaitQueue;
444 NCE->WaitQueue = NdisPacket;
446 KeReleaseSpinLock(Lock, OldIrql);
452 VOID NBRemoveNeighbor(
453 PNEIGHBOR_CACHE_ENTRY NCE)
455 * FUNCTION: Removes a neighbor from the neighbor cache
457 * NCE = Pointer to NCE to remove from cache
459 * The NCE must be in a safe state
464 PNEIGHBOR_CACHE_ENTRY *PrevNCE;
465 PNEIGHBOR_CACHE_ENTRY CurNCE;
466 PNDIS_PACKET NdisPacket, Next;
468 TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
470 HashValue = *(PULONG)(&NCE->Address->Address);
471 HashValue ^= HashValue >> 16;
472 HashValue ^= HashValue >> 8;
473 HashValue ^= HashValue >> 4;
474 HashValue &= NB_HASHMASK;
476 KeAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
478 /* Search the list and remove the NCE from the list if found */
479 for (PrevNCE = &NeighborCache[HashValue].Cache;
480 (CurNCE = *PrevNCE) != NULL;
481 PrevNCE = &CurNCE->Next) {
483 /* Found it, now unlink it from the list */
484 *PrevNCE = CurNCE->Next;
486 /* Purge wait queue */
487 NdisPacket = CurNCE->WaitQueue;
489 Next = (PNDIS_PACKET)PC(NdisPacket)->DLComplete;
490 FreeNdisPacket(NdisPacket);
494 /* Remove all references from route cache */
495 RouteInvalidateNCE(CurNCE);
497 /* Remove reference to the address */
498 DereferenceObject(CurNCE->Address);
503 if (CurNCE->RefCount != 0) {
504 TI_DbgPrint(DEBUG_REFCOUNT, ("NCE at (0x%X) has (%d) references (should be 0).\n", CurNCE, CurNCE->RefCount));
509 KeReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
515 KeReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);