- Rework our aging neighbor cache system that never quite worked correctly

- Fixes several memory leaks
 - Fix the event timer and NCE timeouts
 - We now keep our neighbor cache updated and remove stale entries

svn path=/trunk/; revision=43048
This commit is contained in:
Cameron Gutman 2009-09-14 04:20:05 +00:00
parent 9422642223
commit 89975fc479
4 changed files with 45 additions and 110 deletions

View file

@ -29,7 +29,6 @@ typedef struct NEIGHBOR_CACHE_TABLE {
typedef struct NEIGHBOR_CACHE_ENTRY {
DEFINE_TAG
struct NEIGHBOR_CACHE_ENTRY *Next; /* Pointer to next entry */
struct NEIGHBOR_CACHE_TABLE *Table; /* Pointer to table */
UCHAR State; /* State of NCE */
UINT EventTimer; /* Ticks since last event */
UINT EventCount; /* Number of events */
@ -41,28 +40,18 @@ typedef struct NEIGHBOR_CACHE_ENTRY {
} NEIGHBOR_CACHE_ENTRY, *PNEIGHBOR_CACHE_ENTRY;
/* NCE states */
#define NUD_NONE 0x00
#define NUD_INCOMPLETE 0x01
#define NUD_REACHABLE 0x02
#define NUD_STALE 0x04
#define NUD_DELAY 0x08
#define NUD_PROBE 0x10
#define NUD_FAILED 0x20
#define NUD_NOARP 0x40
#define NUD_PERMANENT 0x80
#define NUD_PERMANENT 0x04
#define NUD_IN_TIMER (NUD_INCOMPLETE | NUD_DELAY | NUD_PROBE)
#define NUD_VALID (NUD_REACHABLE | NUD_NOARP | NUD_STALE | NUD_DELAY | \
NUD_PROBE | NUD_PERMANENT)
#define NUD_CONNECTED (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE)
#define NUD_BROADCAST (NUD_PERMANENT | NUD_REACHABLE)
#define NUD_LOCAL (NUD_PERMANENT | NUD_REACHABLE)
/* Number of seconds before the NCE times out */
#define ARP_TIMEOUT 30
/* Maximum number of retransmissions of multicast solicits */
#define MAX_MULTICAST_SOLICIT 3 /* 3 transmissions */
/* Number of ticks between address resolution messages */
#define RETRANS_TIMER IP_TICKS_SECOND /* One second */
/* Number of seconds between ARP transmissions */
#define ARP_RATE 10
extern NEIGHBOR_CACHE_TABLE NeighborCache[NB_HASHMASK + 1];
@ -84,7 +73,8 @@ PNEIGHBOR_CACHE_ENTRY NBAddNeighbor(
PIP_ADDRESS Address,
PVOID LinkAddress,
UINT LinkAddressLength,
UCHAR Type);
UCHAR Type,
UINT EventTimer);
VOID NBUpdateNeighbor(
PNEIGHBOR_CACHE_ENTRY NCE,

View file

@ -210,6 +210,7 @@ VOID ARPReceive(
/* Check if we know the sender */
AddrInitIPv4(&Address, *((PULONG)SenderProtoAddress));
NCE = NBLocateNeighbor(&Address);
if (NCE) {
/* We know the sender. Update the hardware address
@ -220,7 +221,7 @@ VOID ARPReceive(
may want to communicate with us soon, so add his address
to our address cache */
NCE = NBAddNeighbor(Interface, &Address, SenderHWAddress,
Header->HWAddrLen, NUD_REACHABLE);
Header->HWAddrLen, NUD_REACHABLE, ARP_TIMEOUT);
}
if (Header->Opcode != ARP_OPCODE_REQUEST)

View file

@ -212,7 +212,7 @@ VOID IPAddInterfaceRoute( PIP_INTERFACE IF ) {
/* Add a permanent neighbor for this NTE */
NCE = NBAddNeighbor(IF, &IF->Unicast,
IF->Address, IF->AddressLength,
NUD_PERMANENT);
NUD_LOCAL, 0);
if (!NCE) {
TI_DbgPrint(MIN_TRACE, ("Could not create NCE.\n"));
return;

View file

@ -30,8 +30,7 @@ VOID NBSendPackets( PNEIGHBOR_CACHE_ENTRY NCE ) {
PNEIGHBOR_PACKET Packet;
UINT HashValue;
if(!(NCE->State & NUD_CONNECTED))
return;
ASSERT(NCE->State & NUD_REACHABLE);
HashValue = *(PULONG)(&NCE->Address.Address);
HashValue ^= HashValue >> 16;
@ -89,58 +88,6 @@ VOID NBFlushPacketQueue( PNEIGHBOR_CACHE_ENTRY NCE,
}
}
VOID NCETimeout(
PNEIGHBOR_CACHE_ENTRY NCE)
/*
* FUNCTION: Neighbor cache entry timeout handler
* NOTES:
* The neighbor cache lock must be held
*/
{
TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
TI_DbgPrint(DEBUG_NCACHE, ("NCE->State is (0x%X).\n", NCE->State));
switch (NCE->State)
{
case NUD_INCOMPLETE:
/* Retransmission timer expired */
if (NCE->EventCount++ > MAX_MULTICAST_SOLICIT)
{
/* We have retransmitted too many times */
/* Calling IPSendComplete with cache lock held is not
a great thing to do. We don't get here very often
so maybe it's not that big a problem */
/* Flush packet queue */
NBFlushPacketQueue( NCE, NDIS_STATUS_REQUEST_ABORTED );
NCE->EventCount = 0;
}
else
{
/* Retransmit request */
NBSendSolicit(NCE);
}
break;
case NUD_DELAY:
/* FIXME: Delayed state */
TI_DbgPrint(DEBUG_NCACHE, ("NCE delay state.\n"));
break;
case NUD_PROBE:
/* FIXME: Probe state */
TI_DbgPrint(DEBUG_NCACHE, ("NCE probe state.\n"));
break;
default:
/* Should not happen since the event timer is not used in the other states */
TI_DbgPrint(MIN_TRACE, ("Invalid NCE state (%d).\n", NCE->State));
break;
}
}
VOID NBTimeout(VOID)
/*
* FUNCTION: Neighbor address cache timeout handler
@ -151,21 +98,33 @@ VOID NBTimeout(VOID)
{
UINT i;
KIRQL OldIrql;
PNEIGHBOR_CACHE_ENTRY *PrevNCE;
PNEIGHBOR_CACHE_ENTRY NCE;
for (i = 0; i <= NB_HASHMASK; i++) {
TcpipAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql);
for (NCE = NeighborCache[i].Cache;
NCE != NULL; NCE = NCE->Next) {
for (PrevNCE = &NeighborCache[i].Cache;
(NCE = *PrevNCE) != NULL;) {
/* Check if event timer is running */
if (NCE->EventTimer > 0) {
NCE->EventTimer--;
if (NCE->EventTimer == 0) {
/* Call timeout handler for NCE */
NCETimeout(NCE);
NCE->EventCount++;
if (NCE->EventCount % ARP_RATE == 0)
NBSendSolicit(NCE);
if (NCE->EventTimer - NCE->EventCount == 0) {
ASSERT(!(NCE->State & NUD_PERMANENT));
/* Flush packet queue */
NBFlushPacketQueue( NCE, NDIS_STATUS_REQUEST_ABORTED );
*PrevNCE = NCE->Next;
exFreePool(NCE);
continue;
}
}
PrevNCE = &NCE->Next;
}
TcpipReleaseSpinLock(&NeighborCache[i].Lock, OldIrql);
@ -211,6 +170,8 @@ VOID NBShutdown(VOID)
/* Flush wait queue */
NBFlushPacketQueue( CurNCE, NDIS_STATUS_NOT_ACCEPTED );
exFreePool(CurNCE);
CurNCE = NextNCE;
}
@ -233,18 +194,7 @@ VOID NBSendSolicit(PNEIGHBOR_CACHE_ENTRY NCE)
{
TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
if (NCE->State & NUD_INCOMPLETE)
{
/* This is the first solicitation of this neighbor. Broadcast
a request for the neighbor */
TI_DbgPrint(MID_TRACE,("NCE: %x\n", NCE));
ARPTransmit(&NCE->Address, NCE->Interface);
} else {
/* FIXME: Unicast solicitation since we have a cached address */
TI_DbgPrint(MIN_TRACE, ("Uninplemented unicast solicitation.\n"));
}
ARPTransmit(&NCE->Address, NCE->Interface);
}
PNEIGHBOR_CACHE_ENTRY NBAddNeighbor(
@ -252,7 +202,8 @@ PNEIGHBOR_CACHE_ENTRY NBAddNeighbor(
PIP_ADDRESS Address,
PVOID LinkAddress,
UINT LinkAddressLength,
UCHAR State)
UCHAR State,
UINT EventTimer)
/*
* FUNCTION: Adds a neighbor to the neighbor cache
* ARGUMENTS:
@ -298,7 +249,8 @@ PNEIGHBOR_CACHE_ENTRY NBAddNeighbor(
else
memset(NCE->LinkAddress, 0xff, LinkAddressLength);
NCE->State = State;
NCE->EventTimer = 0; /* Not in use */
NCE->EventTimer = EventTimer;
NCE->EventCount = 0;
InitializeListHead( &NCE->PacketQueue );
TI_DbgPrint(MID_TRACE,("NCE: %x\n", NCE));
@ -309,8 +261,6 @@ PNEIGHBOR_CACHE_ENTRY NBAddNeighbor(
HashValue ^= HashValue >> 4;
HashValue &= NB_HASHMASK;
NCE->Table = &NeighborCache[HashValue];
TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
NCE->Next = NeighborCache[HashValue].Cache;
@ -350,10 +300,11 @@ VOID NBUpdateNeighbor(
RtlCopyMemory(NCE->LinkAddress, LinkAddress, NCE->LinkAddressLength);
NCE->State = State;
NCE->EventCount = 0;
TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
if( NCE->State & NUD_CONNECTED )
if( NCE->State & NUD_REACHABLE )
NBSendPackets( NCE );
}
@ -425,16 +376,12 @@ PNEIGHBOR_CACHE_ENTRY NBFindOrCreateNeighbor(
AddrIsUnspecified(Address) ) {
TI_DbgPrint(MID_TRACE,("Packet targeted at broadcast addr\n"));
NCE = NBAddNeighbor(Interface, Address, NULL,
Interface->AddressLength, NUD_CONNECTED);
if (!NCE) return NULL;
NCE->EventTimer = 0;
NCE->EventCount = 0;
Interface->AddressLength, NUD_BROADCAST, 0);
} else {
NCE = NBAddNeighbor(Interface, Address, NULL,
Interface->AddressLength, NUD_INCOMPLETE);
Interface->AddressLength, NUD_INCOMPLETE, ARP_TIMEOUT);
if (!NCE) return NULL;
NCE->EventTimer = 1;
NCE->EventCount = 0;
NBSendSolicit(NCE);
}
}
@ -483,13 +430,12 @@ BOOLEAN NBQueuePacket(
TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
if( NCE->State & NUD_CONNECTED )
if( NCE->State & NUD_REACHABLE )
NBSendPackets( NCE );
return TRUE;
}
VOID NBRemoveNeighbor(
PNEIGHBOR_CACHE_ENTRY NCE)
/*
@ -559,10 +505,8 @@ ULONG NBCopyNeighbors
ArpTable[Size].LogAddr = CurNCE->Address.Address.IPv4Address;
if( CurNCE->State & NUD_PERMANENT )
ArpTable[Size].Type = ARP_ENTRY_STATIC;
else if( CurNCE->State & NUD_CONNECTED )
else if( CurNCE->State & NUD_REACHABLE )
ArpTable[Size].Type = ARP_ENTRY_DYNAMIC;
else if( !(CurNCE->State & NUD_VALID) )
ArpTable[Size].Type = ARP_ENTRY_INVALID;
else
ArpTable[Size].Type = ARP_ENTRY_OTHER;
}