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 if(!(NCE
->State
& NUD_CONNECTED
))
36 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
37 HashValue
^= HashValue
>> 16;
38 HashValue
^= HashValue
>> 8;
39 HashValue
^= HashValue
>> 4;
40 HashValue
&= NB_HASHMASK
;
42 /* Send any waiting packets */
43 while ((PacketEntry
= ExInterlockedRemoveHeadList(&NCE
->PacketQueue
,
44 &NeighborCache
[HashValue
].Lock
)) != NULL
)
46 Packet
= CONTAINING_RECORD( PacketEntry
, NEIGHBOR_PACKET
, Next
);
50 ("PacketEntry: %x, NdisPacket %x\n",
51 PacketEntry
, Packet
->Packet
));
53 PC(Packet
->Packet
)->DLComplete
= NBCompleteSend
;
54 PC(Packet
->Packet
)->Context
= Packet
;
56 NCE
->Interface
->Transmit
57 ( NCE
->Interface
->Context
,
65 /* Must be called with table lock acquired */
66 VOID
NBFlushPacketQueue( PNEIGHBOR_CACHE_ENTRY NCE
,
67 NTSTATUS ErrorCode
) {
68 PLIST_ENTRY PacketEntry
;
69 PNEIGHBOR_PACKET Packet
;
71 while( !IsListEmpty(&NCE
->PacketQueue
) ) {
72 PacketEntry
= RemoveHeadList(&NCE
->PacketQueue
);
73 Packet
= CONTAINING_RECORD
74 ( PacketEntry
, NEIGHBOR_PACKET
, Next
);
76 ASSERT_KM_POINTER(Packet
);
80 ("PacketEntry: %x, NdisPacket %x\n",
81 PacketEntry
, Packet
->Packet
));
83 ASSERT_KM_POINTER(Packet
->Complete
);
84 Packet
->Complete( Packet
->Context
,
93 PNEIGHBOR_CACHE_ENTRY NCE
)
95 * FUNCTION: Neighbor cache entry timeout handler
97 * The neighbor cache lock must be held
100 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X).\n", NCE
));
101 TI_DbgPrint(DEBUG_NCACHE
, ("NCE->State is (0x%X).\n", NCE
->State
));
106 /* Retransmission timer expired */
107 if (NCE
->EventCount
++ > MAX_MULTICAST_SOLICIT
)
109 /* We have retransmitted too many times */
111 /* Calling IPSendComplete with cache lock held is not
112 a great thing to do. We don't get here very often
113 so maybe it's not that big a problem */
115 /* Flush packet queue */
116 NBFlushPacketQueue( NCE
, NDIS_STATUS_REQUEST_ABORTED
);
121 /* Retransmit request */
127 /* FIXME: Delayed state */
128 TI_DbgPrint(DEBUG_NCACHE
, ("NCE delay state.\n"));
132 /* FIXME: Probe state */
133 TI_DbgPrint(DEBUG_NCACHE
, ("NCE probe state.\n"));
137 /* Should not happen since the event timer is not used in the other states */
138 TI_DbgPrint(MIN_TRACE
, ("Invalid NCE state (%d).\n", NCE
->State
));
146 * FUNCTION: Neighbor address cache timeout handler
148 * This routine is called by IPTimeout to remove outdated cache
154 PNEIGHBOR_CACHE_ENTRY NCE
;
156 for (i
= 0; i
<= NB_HASHMASK
; i
++) {
157 TcpipAcquireSpinLock(&NeighborCache
[i
].Lock
, &OldIrql
);
159 for (NCE
= NeighborCache
[i
].Cache
;
160 NCE
!= NULL
; NCE
= NCE
->Next
) {
161 /* Check if event timer is running */
162 if (NCE
->EventTimer
> 0) {
164 if (NCE
->EventTimer
== 0) {
165 /* Call timeout handler for NCE */
171 TcpipReleaseSpinLock(&NeighborCache
[i
].Lock
, OldIrql
);
177 * FUNCTION: Starts the neighbor cache
182 TI_DbgPrint(DEBUG_NCACHE
, ("Called.\n"));
184 for (i
= 0; i
<= NB_HASHMASK
; i
++) {
185 NeighborCache
[i
].Cache
= NULL
;
186 TcpipInitializeSpinLock(&NeighborCache
[i
].Lock
);
190 VOID
NBShutdown(VOID
)
192 * FUNCTION: Shuts down the neighbor cache
195 PNEIGHBOR_CACHE_ENTRY NextNCE
;
196 PNEIGHBOR_CACHE_ENTRY CurNCE
;
200 TI_DbgPrint(DEBUG_NCACHE
, ("Called.\n"));
202 /* Remove possible entries from the cache */
203 for (i
= 0; i
<= NB_HASHMASK
; i
++)
205 TcpipAcquireSpinLock(&NeighborCache
[i
].Lock
, &OldIrql
);
207 CurNCE
= NeighborCache
[i
].Cache
;
209 NextNCE
= CurNCE
->Next
;
211 /* Flush wait queue */
212 NBFlushPacketQueue( CurNCE
, NDIS_STATUS_NOT_ACCEPTED
);
217 NeighborCache
[i
].Cache
= NULL
;
219 TcpipReleaseSpinLock(&NeighborCache
[i
].Lock
, OldIrql
);
222 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
225 VOID
NBSendSolicit(PNEIGHBOR_CACHE_ENTRY NCE
)
227 * FUNCTION: Sends a neighbor solicitation message
229 * NCE = Pointer to NCE of neighbor to solicit
231 * May be called with lock held on NCE's table
234 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X).\n", NCE
));
236 if (NCE
->State
& NUD_INCOMPLETE
)
238 /* This is the first solicitation of this neighbor. Broadcast
239 a request for the neighbor */
241 TI_DbgPrint(MID_TRACE
,("NCE: %x\n", NCE
));
243 ARPTransmit(&NCE
->Address
, NCE
->Interface
);
245 /* FIXME: Unicast solicitation since we have a cached address */
246 TI_DbgPrint(MIN_TRACE
, ("Uninplemented unicast solicitation.\n"));
250 PNEIGHBOR_CACHE_ENTRY
NBAddNeighbor(
251 PIP_INTERFACE Interface
,
254 UINT LinkAddressLength
,
257 * FUNCTION: Adds a neighbor to the neighbor cache
259 * Interface = Pointer to interface
260 * Address = Pointer to IP address
261 * LinkAddress = Pointer to link address (may be NULL)
262 * LinkAddressLength = Length of link address
263 * State = State of NCE
265 * Pointer to NCE, NULL there is not enough free resources
267 * The NCE if referenced for the caller if created. The NCE retains
268 * a reference to the IP address if it is created, the caller is
269 * responsible for providing this reference
272 PNEIGHBOR_CACHE_ENTRY NCE
;
278 ("Called. Interface (0x%X) Address (0x%X) "
279 "LinkAddress (0x%X) LinkAddressLength (%d) State (0x%X)\n",
280 Interface
, Address
, LinkAddress
, LinkAddressLength
, State
));
283 (NonPagedPool
, sizeof(NEIGHBOR_CACHE_ENTRY
) + LinkAddressLength
);
286 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
290 INIT_TAG(NCE
, TAG('N','C','E',' '));
292 NCE
->Interface
= Interface
;
293 NCE
->Address
= *Address
;
294 NCE
->LinkAddressLength
= LinkAddressLength
;
295 NCE
->LinkAddress
= (PVOID
)&NCE
[1];
297 RtlCopyMemory(NCE
->LinkAddress
, LinkAddress
, LinkAddressLength
);
299 memset(NCE
->LinkAddress
, 0xff, LinkAddressLength
);
301 NCE
->EventTimer
= 0; /* Not in use */
302 InitializeListHead( &NCE
->PacketQueue
);
304 TI_DbgPrint(MID_TRACE
,("NCE: %x\n", NCE
));
306 HashValue
= *(PULONG
)&Address
->Address
;
307 HashValue
^= HashValue
>> 16;
308 HashValue
^= HashValue
>> 8;
309 HashValue
^= HashValue
>> 4;
310 HashValue
&= NB_HASHMASK
;
312 NCE
->Table
= &NeighborCache
[HashValue
];
314 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
316 NCE
->Next
= NeighborCache
[HashValue
].Cache
;
317 NeighborCache
[HashValue
].Cache
= NCE
;
319 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
324 VOID
NBUpdateNeighbor(
325 PNEIGHBOR_CACHE_ENTRY NCE
,
329 * FUNCTION: Update link address information in NCE
331 * NCE = Pointer to NCE to update
332 * LinkAddress = Pointer to link address
333 * State = State of NCE
335 * The link address and state is updated. Any waiting packets are sent
341 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X) LinkAddress (0x%X) State (0x%X).\n", NCE
, LinkAddress
, State
));
343 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
344 HashValue
^= HashValue
>> 16;
345 HashValue
^= HashValue
>> 8;
346 HashValue
^= HashValue
>> 4;
347 HashValue
&= NB_HASHMASK
;
349 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
351 RtlCopyMemory(NCE
->LinkAddress
, LinkAddress
, NCE
->LinkAddressLength
);
354 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
356 if( NCE
->State
& NUD_CONNECTED
)
357 NBSendPackets( NCE
);
360 PNEIGHBOR_CACHE_ENTRY
NBLocateNeighbor(
363 * FUNCTION: Locates a neighbor in the neighbor cache
365 * Address = Pointer to IP address
367 * Pointer to NCE, NULL if not found
369 * If the NCE is found, it is referenced. The caller is
370 * responsible for dereferencing it again after use
373 PNEIGHBOR_CACHE_ENTRY NCE
;
377 TI_DbgPrint(DEBUG_NCACHE
, ("Called. Address (0x%X).\n", Address
));
379 HashValue
= *(PULONG
)&Address
->Address
;
380 HashValue
^= HashValue
>> 16;
381 HashValue
^= HashValue
>> 8;
382 HashValue
^= HashValue
>> 4;
383 HashValue
&= NB_HASHMASK
;
385 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
387 NCE
= NeighborCache
[HashValue
].Cache
;
389 while ((NCE
) && (!AddrIsEqual(Address
, &NCE
->Address
)))
394 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
396 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
401 PNEIGHBOR_CACHE_ENTRY
NBFindOrCreateNeighbor(
402 PIP_INTERFACE Interface
,
405 * FUNCTION: Tries to find a neighbor and if unsuccesful, creates a new NCE
407 * Interface = Pointer to interface to use (in case NCE is not found)
408 * Address = Pointer to IP address
410 * Pointer to NCE, NULL if there is not enough free resources
412 * The NCE is referenced if found or created. The caller is
413 * responsible for dereferencing it again after use
416 PNEIGHBOR_CACHE_ENTRY NCE
;
418 TI_DbgPrint(DEBUG_NCACHE
, ("Called. Interface (0x%X) Address (0x%X).\n", Interface
, Address
));
420 NCE
= NBLocateNeighbor(Address
);
423 TI_DbgPrint(MID_TRACE
,("BCAST: %s\n", A2S(&Interface
->Broadcast
)));
424 if( AddrIsEqual(Address
, &Interface
->Broadcast
) ||
425 AddrIsUnspecified(Address
) ) {
426 TI_DbgPrint(MID_TRACE
,("Packet targeted at broadcast addr\n"));
427 NCE
= NBAddNeighbor(Interface
, Address
, NULL
,
428 Interface
->AddressLength
, NUD_CONNECTED
);
429 if (!NCE
) return NULL
;
433 NCE
= NBAddNeighbor(Interface
, Address
, NULL
,
434 Interface
->AddressLength
, NUD_INCOMPLETE
);
435 if (!NCE
) return NULL
;
444 BOOLEAN
NBQueuePacket(
445 PNEIGHBOR_CACHE_ENTRY NCE
,
446 PNDIS_PACKET NdisPacket
,
447 PNEIGHBOR_PACKET_COMPLETE PacketComplete
,
450 * FUNCTION: Queues a packet on an NCE for later transmission
452 * NCE = Pointer to NCE to queue packet on
453 * NdisPacket = Pointer to NDIS packet to queue
455 * TRUE if the packet was successfully queued, FALSE if not
459 PNEIGHBOR_PACKET Packet
;
464 ("Called. NCE (0x%X) NdisPacket (0x%X).\n", NCE
, NdisPacket
));
466 Packet
= exAllocatePool( NonPagedPool
, sizeof(NEIGHBOR_PACKET
) );
467 if( !Packet
) return FALSE
;
469 /* FIXME: Should we limit the number of queued packets? */
471 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
472 HashValue
^= HashValue
>> 16;
473 HashValue
^= HashValue
>> 8;
474 HashValue
^= HashValue
>> 4;
475 HashValue
&= NB_HASHMASK
;
477 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
479 Packet
->Complete
= PacketComplete
;
480 Packet
->Context
= PacketContext
;
481 Packet
->Packet
= NdisPacket
;
482 InsertTailList( &NCE
->PacketQueue
, &Packet
->Next
);
484 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
486 if( NCE
->State
& NUD_CONNECTED
)
487 NBSendPackets( NCE
);
493 VOID
NBRemoveNeighbor(
494 PNEIGHBOR_CACHE_ENTRY NCE
)
496 * FUNCTION: Removes a neighbor from the neighbor cache
498 * NCE = Pointer to NCE to remove from cache
500 * The NCE must be in a safe state
503 PNEIGHBOR_CACHE_ENTRY
*PrevNCE
;
504 PNEIGHBOR_CACHE_ENTRY CurNCE
;
508 TI_DbgPrint(DEBUG_NCACHE
, ("Called. NCE (0x%X).\n", NCE
));
510 HashValue
= *(PULONG
)(&NCE
->Address
.Address
);
511 HashValue
^= HashValue
>> 16;
512 HashValue
^= HashValue
>> 8;
513 HashValue
^= HashValue
>> 4;
514 HashValue
&= NB_HASHMASK
;
516 TcpipAcquireSpinLock(&NeighborCache
[HashValue
].Lock
, &OldIrql
);
518 /* Search the list and remove the NCE from the list if found */
519 for (PrevNCE
= &NeighborCache
[HashValue
].Cache
;
520 (CurNCE
= *PrevNCE
) != NULL
;
521 PrevNCE
= &CurNCE
->Next
)
525 /* Found it, now unlink it from the list */
526 *PrevNCE
= CurNCE
->Next
;
528 NBFlushPacketQueue( CurNCE
, NDIS_STATUS_REQUEST_ABORTED
);
535 TcpipReleaseSpinLock(&NeighborCache
[HashValue
].Lock
, OldIrql
);
538 ULONG NBCopyNeighbors
539 (PIP_INTERFACE Interface
,
540 PIPARP_ENTRY ArpTable
)
542 PNEIGHBOR_CACHE_ENTRY CurNCE
;
546 for (i
= 0; i
<= NB_HASHMASK
; i
++) {
547 TcpipAcquireSpinLock(&NeighborCache
[i
].Lock
, &OldIrql
);
548 for( CurNCE
= NeighborCache
[i
].Cache
;
550 CurNCE
= CurNCE
->Next
) {
551 if( CurNCE
->Interface
== Interface
) {
553 ArpTable
[Size
].Index
= Interface
->Index
;
554 ArpTable
[Size
].AddrSize
= CurNCE
->LinkAddressLength
;
556 (ArpTable
[Size
].PhysAddr
,
558 CurNCE
->LinkAddressLength
);
559 ArpTable
[Size
].LogAddr
= CurNCE
->Address
.Address
.IPv4Address
;
560 if( CurNCE
->State
& NUD_PERMANENT
)
561 ArpTable
[Size
].Type
= ARP_ENTRY_STATIC
;
562 else if( CurNCE
->State
& NUD_CONNECTED
)
563 ArpTable
[Size
].Type
= ARP_ENTRY_DYNAMIC
;
564 else if( !(CurNCE
->State
& NUD_VALID
) )
565 ArpTable
[Size
].Type
= ARP_ENTRY_INVALID
;
567 ArpTable
[Size
].Type
= ARP_ENTRY_OTHER
;
572 TcpipReleaseSpinLock(&NeighborCache
[i
].Lock
, OldIrql
);