mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1436 lines
45 KiB
1436 lines
45 KiB
//****************************************************************************
|
|
//
|
|
// Microsoft Windows NT RIP
|
|
//
|
|
// Copyright 1995-96
|
|
//
|
|
//
|
|
// Revision History
|
|
//
|
|
//
|
|
// 2/26/95 Gurdeep Singh Pall Picked up from JBallard's team
|
|
//
|
|
//
|
|
// Description: RIP Tables Manipulation Functions
|
|
//
|
|
//****************************************************************************
|
|
|
|
#include "pchrip.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function: InitializeRouteTable
|
|
//
|
|
// Initializes hash table
|
|
//-----------------------------------------------------------------------------
|
|
DWORD InitializeRouteTable() {
|
|
|
|
LPHASH_TABLE_ENTRY *lplpentry, *lplpend;
|
|
|
|
lplpend = g_ripcfg.lpRouteTable + HASH_TABLE_SIZE;
|
|
for (lplpentry = g_ripcfg.lpRouteTable; lplpentry < lplpend; lplpentry++) {
|
|
*lplpentry = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function: GetRouteTableEntry
|
|
//
|
|
// Looks for an entry with the specified address and mask, learnt using the
|
|
// specified interface. If the entry is not found, one is created.
|
|
//-----------------------------------------------------------------------------
|
|
LPHASH_TABLE_ENTRY GetRouteTableEntry(DWORD dwIndex, DWORD dwAddress,
|
|
DWORD dwNetmask) {
|
|
INT hashval;
|
|
IN_ADDR addr;
|
|
LPHASH_TABLE_ENTRY rt_entry;
|
|
LPHASH_TABLE_ENTRY prev_rt_entry;
|
|
|
|
hashval = HASH_VALUE(dwAddress);
|
|
ASSERT(hashval < HASH_TABLE_SIZE);
|
|
|
|
RIP_LOCK_ROUTETABLE();
|
|
|
|
prev_rt_entry = rt_entry = g_ripcfg.lpRouteTable[hashval];
|
|
|
|
while (rt_entry != NULL) {
|
|
if ((rt_entry->dwDestaddr == dwAddress) &&
|
|
(rt_entry->dwNetmask == dwNetmask)) {
|
|
break;
|
|
}
|
|
prev_rt_entry = rt_entry;
|
|
rt_entry = rt_entry->next;
|
|
}
|
|
|
|
if (rt_entry == NULL) {
|
|
// entry was not found, so allocate a new one
|
|
rt_entry = malloc(sizeof(HASH_TABLE_ENTRY));
|
|
if (rt_entry == NULL) {
|
|
dbgprintf("could not allocate memory for routing-table entry");
|
|
}
|
|
else {
|
|
rt_entry->next = NULL;
|
|
rt_entry->dwFlag = NEW_ENTRY;
|
|
rt_entry->dwIndex = dwIndex;
|
|
rt_entry->dwProtocol = IRE_PROTO_RIP;
|
|
rt_entry->dwDestaddr = dwAddress;
|
|
if (prev_rt_entry != NULL) {
|
|
rt_entry->prev = prev_rt_entry;
|
|
prev_rt_entry->next = rt_entry;
|
|
}
|
|
else {
|
|
rt_entry->prev = NULL;
|
|
g_ripcfg.lpRouteTable[hashval] = rt_entry;
|
|
}
|
|
|
|
InterlockedIncrement(&g_ripcfg.lpStatsTable->dwRouteCount);
|
|
}
|
|
}
|
|
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
|
|
// check_rt_entries();
|
|
|
|
return rt_entry;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function: RouteTableEntryExists
|
|
//
|
|
// This function returns TRUE if an entry to the specified address
|
|
// exists with the specified index.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL RouteTableEntryExists(DWORD dwIndex, DWORD dwAddress) {
|
|
INT hashval;
|
|
LPHASH_TABLE_ENTRY rt_entry;
|
|
|
|
hashval = HASH_VALUE(dwAddress);
|
|
|
|
RIP_LOCK_ROUTETABLE();
|
|
|
|
rt_entry = g_ripcfg.lpRouteTable[hashval];
|
|
while (rt_entry != NULL) {
|
|
if (rt_entry->dwDestaddr == dwAddress) {
|
|
break;
|
|
}
|
|
|
|
rt_entry = rt_entry->next;
|
|
}
|
|
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
|
|
return (rt_entry == NULL ? FALSE : TRUE);
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function: AddZombieRouteTableEntry
|
|
//
|
|
// This function adds a special route-table entry known as a Zombie
|
|
// route entry. In the case of border gateways which summarize attached
|
|
// subnets and send a single entry for the network, and in the case
|
|
// of routers whose interfaces have different subnet masks, the destination
|
|
// that RIP will send will be different from the destination in RIP's table.
|
|
// This makes it possible for the destination to get bounced back at RIP by
|
|
// some other router; RIP would then add an entry for the bogus route, and
|
|
// advertise the route back again, and a count to infinity would commence.
|
|
//
|
|
// Zombie entries exist to prevent this from happening:
|
|
// they have metrics of zero, so they will not be replaced
|
|
// by RIP-learnt routes (all of which have a metric of at least 1);
|
|
// they are excluded from updates sent
|
|
// they are excluded from updates written to the system routing table
|
|
// they can be timed-out
|
|
// The above conditions ensure that zombies do not interfere with the working
|
|
// of RIP, EXCEPT in the case where they prevent RIP from adding a normal entry
|
|
// for a route which was summarized in a previous update and which is therefore
|
|
// not really a RIP route at all.
|
|
//-----------------------------------------------------------------------------
|
|
DWORD AddZombieRouteTableEntry(LPRIP_ADDRESS lpaddr, DWORD dwNetwork,
|
|
DWORD dwNetmask) {
|
|
LPHASH_TABLE_ENTRY rt_entry;
|
|
|
|
rt_entry = GetRouteTableEntry(lpaddr->dwIndex, dwNetwork, dwNetmask);
|
|
if (rt_entry == NULL) {
|
|
dbgprintf("could not make entry for network in routing table");
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
// don't want to overwrite an existing entry, if there is one
|
|
if ((rt_entry->dwFlag & NEW_ENTRY) == 0 &&
|
|
(rt_entry->dwFlag & ROUTE_ZOMBIE) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// since the only reason this entry exists is because a
|
|
// subnet we are sending is being summarized or truncated, we have to
|
|
// set up values to make sure this entry is not considered in
|
|
// normal processing; (e.g. metric of 0 to make sure it is not
|
|
// replaced by a RIP-learnt route)
|
|
// however, we do allow it to be timed out
|
|
rt_entry->dwIndex = (DWORD)~0;
|
|
rt_entry->dwFlag = (GARBAGE_TIMER | ROUTE_ZOMBIE);
|
|
rt_entry->lTimeout = (LONG)DEF_GARBAGETIMEOUT;
|
|
rt_entry->dwDestaddr = dwNetwork;
|
|
rt_entry->dwNetmask = dwNetmask;
|
|
rt_entry->dwNexthop = 0;
|
|
rt_entry->dwProtocol = IRE_PROTO_OTHER;
|
|
rt_entry->dwMetric = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function: DeleteRouteTableEntry
|
|
//
|
|
// This function removes a route from the route table. Assumes
|
|
// that the route table is already locked
|
|
//-----------------------------------------------------------------------------
|
|
VOID DeleteRouteTableEntry(int pos, LPHASH_TABLE_ENTRY rt_entry) {
|
|
IN_ADDR addr;
|
|
CHAR szDest[32] = {0};
|
|
CHAR* pszTemp;
|
|
|
|
if (rt_entry == NULL) { return; }
|
|
|
|
addr.s_addr = rt_entry->dwDestaddr;
|
|
pszTemp = inet_ntoa(addr);
|
|
|
|
if (pszTemp != NULL) {
|
|
strcpy(szDest, pszTemp);
|
|
}
|
|
|
|
dbgprintf("Removing entry %d with destination IP address %s "
|
|
"from interface %d in RIP routing table",
|
|
pos, szDest, rt_entry->dwIndex);
|
|
|
|
if (rt_entry->prev != NULL) {
|
|
rt_entry->prev->next = rt_entry->next;
|
|
if (rt_entry->next != NULL) {
|
|
rt_entry->next->prev = rt_entry->prev;
|
|
}
|
|
}
|
|
else {
|
|
g_ripcfg.lpRouteTable[pos] = rt_entry->next;
|
|
if (rt_entry->next != NULL) {
|
|
rt_entry->next->prev = NULL;
|
|
}
|
|
}
|
|
|
|
InterlockedDecrement(&g_ripcfg.lpStatsTable->dwRouteCount);
|
|
|
|
// delete the route from the IP table as well
|
|
if ((rt_entry->dwFlag & ROUTE_ZOMBIE) == 0) {
|
|
UpdateSystemRouteTable(rt_entry, FALSE);
|
|
}
|
|
|
|
free(rt_entry);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void check_rt_entries() {
|
|
int pos;
|
|
LPHASH_TABLE_ENTRY rt_entry;
|
|
LPHASH_TABLE_ENTRY prev_rt_entry = NULL ;
|
|
|
|
RIP_LOCK_ROUTETABLE();
|
|
for (pos = 0; pos < HASH_TABLE_SIZE; pos++) {
|
|
rt_entry = g_ripcfg.lpRouteTable[pos];
|
|
while (rt_entry != NULL) {
|
|
if (rt_entry == rt_entry->next) {
|
|
DebugBreak();
|
|
}
|
|
if (rt_entry == rt_entry->prev) {
|
|
DebugBreak();
|
|
}
|
|
if (rt_entry->prev != NULL) {
|
|
if (rt_entry->prev != prev_rt_entry) {
|
|
DebugBreak();
|
|
}
|
|
}
|
|
prev_rt_entry = rt_entry;
|
|
rt_entry = rt_entry->next;
|
|
}
|
|
}
|
|
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
return;
|
|
}
|
|
|
|
|
|
#if 0
|
|
//-----------------------------------------------------------------------------
|
|
// Function: DumpRouteTable
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
VOID DumpRouteTable() {
|
|
INT pos;
|
|
HANDLE hRoutesDump;
|
|
LPVOID lpRoutesDump;
|
|
DWORD dwSize, dwCount;
|
|
SECURITY_ATTRIBUTES sa;
|
|
SECURITY_DESCRIPTOR sd;
|
|
LPHASH_TABLE_ENTRY rt_entry, rt_dump;
|
|
|
|
RIP_LOCK_ADDRTABLE();
|
|
|
|
// get rid of any previous dump
|
|
if (g_ripcfg.hRoutesDump != NULL) {
|
|
CloseHandle(g_ripcfg.hRoutesDump);
|
|
g_ripcfg.hRoutesDump = NULL;
|
|
}
|
|
|
|
RIP_UNLOCK_ADDRTABLE();
|
|
|
|
dwCount = 0;
|
|
|
|
RIP_LOCK_ROUTETABLE();
|
|
|
|
for (pos = 0; pos < HASH_TABLE_SIZE; pos++) {
|
|
rt_entry = g_ripcfg.lpRouteTable[pos];
|
|
while (rt_entry != NULL) {
|
|
++dwCount;
|
|
rt_entry = rt_entry->next;
|
|
}
|
|
}
|
|
|
|
// set the size to be the route table size plus the preceding count
|
|
dwSize = sizeof(DWORD) + dwCount * sizeof(HASH_TABLE_ENTRY);
|
|
|
|
// initialize security for this shared memory
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sa.bInheritHandle = FALSE;
|
|
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
return;
|
|
}
|
|
if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE)) {
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
return;
|
|
}
|
|
|
|
sa.lpSecurityDescriptor = &sd;
|
|
|
|
// request the shared memory
|
|
hRoutesDump = CreateFileMapping(INVALID_HANDLE_VALUE,
|
|
&sa, PAGE_READWRITE,
|
|
0, dwSize,
|
|
RIP_DUMP_ROUTES_NAME);
|
|
if (hRoutesDump == NULL) {
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
return;
|
|
}
|
|
|
|
// set up a pointer to the memory
|
|
lpRoutesDump = MapViewOfFile(hRoutesDump, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
|
if (lpRoutesDump == NULL) {
|
|
CloseHandle(hRoutesDump);
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
return;
|
|
}
|
|
|
|
// skip the first four bytes, which will contain the count
|
|
rt_dump = (LPHASH_TABLE_ENTRY)((LPBYTE)lpRoutesDump + sizeof(DWORD));
|
|
|
|
for (pos = 0; pos < HASH_TABLE_SIZE; pos++) {
|
|
rt_entry = g_ripcfg.lpRouteTable[pos];
|
|
while (rt_entry != NULL) {
|
|
CopyMemory(rt_dump, rt_entry, sizeof(HASH_TABLE_ENTRY));
|
|
rt_entry = rt_entry->next;
|
|
++rt_dump;
|
|
}
|
|
}
|
|
|
|
// store the count in the first DWORD
|
|
*(LPDWORD)lpRoutesDump = dwCount;
|
|
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
|
|
// let go of the memory-map
|
|
UnmapViewOfFile(lpRoutesDump);
|
|
|
|
// save the shared-memory handle
|
|
RIP_LOCK_ADDRTABLE();
|
|
|
|
RIP_UNLOCK_ADDRTABLE();
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function: ProcessRouteTableChanges
|
|
//
|
|
// Process the changes, updating metrics for routes. If necessary,
|
|
// this function will trigger an update.
|
|
// Assumes address table is locked.
|
|
//-----------------------------------------------------------------------------
|
|
void ProcessRouteTableChanges(BOOL bTriggered) {
|
|
int pos;
|
|
BOOL bNeedTriggeredUpdate;
|
|
LPHASH_TABLE_ENTRY rt_entry;
|
|
DWORD dwLastTrigger, dwMsecsTillUpdate;
|
|
DWORD dwSystime, dwSilentRIP, dwTrigger, dwTriggerFrequency;
|
|
|
|
// check_rt_entries();
|
|
|
|
RIP_LOCK_PARAMS();
|
|
|
|
dwSilentRIP = g_params.dwSilentRIP;
|
|
dwTrigger = g_params.dwTriggeredUpdates;
|
|
dwTriggerFrequency = g_params.dwMaxTriggerFrequency;
|
|
|
|
RIP_UNLOCK_PARAMS();
|
|
|
|
|
|
RIP_LOCK_ROUTETABLE();
|
|
|
|
bNeedTriggeredUpdate = FALSE;
|
|
for (pos = 0; pos < HASH_TABLE_SIZE; pos++) {
|
|
rt_entry = g_ripcfg.lpRouteTable[pos];
|
|
while (rt_entry != NULL) {
|
|
if ((rt_entry->dwFlag & ROUTE_CHANGE) == 0 &&
|
|
(rt_entry->dwFlag & ROUTE_UPDATE) == 0) {
|
|
rt_entry = rt_entry->next;
|
|
continue;
|
|
}
|
|
|
|
if ((rt_entry->dwFlag & ROUTE_CHANGE) != 0) {
|
|
bNeedTriggeredUpdate = TRUE;
|
|
}
|
|
|
|
// update if this is a RIP-learnt route
|
|
if (rt_entry->dwProtocol == IRE_PROTO_RIP) {
|
|
UpdateSystemRouteTable(rt_entry, TRUE);
|
|
}
|
|
|
|
// clear the update flag, now that the route
|
|
// has been updated in the system table
|
|
rt_entry->dwFlag &= ~ROUTE_UPDATE;
|
|
rt_entry = rt_entry->next;
|
|
}
|
|
}
|
|
|
|
dwSystime = GetTickCount();
|
|
dwLastTrigger = g_ripcfg.dwLastTriggeredUpdate;
|
|
dwMsecsTillUpdate = g_ripcfg.dwMillisecsTillFullUpdate;
|
|
|
|
// adjust the times if the clock has wrapped around past zero
|
|
if (dwSystime < dwLastTrigger) {
|
|
dwSystime += (DWORD)~0 - dwLastTrigger;
|
|
dwLastTrigger = 0;
|
|
}
|
|
|
|
// we generate a triggered update iff:
|
|
// 1. this call was made because of a response received
|
|
// 2. we are not in silent RIP mode
|
|
// 3. triggered updates are not disabled
|
|
// 4. the minimum configured interval between triggered updates
|
|
// has elapsed
|
|
// 5. the time till the next regular update is greater than the
|
|
// configured minimum interval between triggered updates
|
|
// if the system clock has wrapped around to zero, skip the condition 4;
|
|
// we know the clock has wrapped around if dwSystime is less than
|
|
// the last triggered update time
|
|
|
|
if (bTriggered && bNeedTriggeredUpdate &&
|
|
dwSilentRIP == 0 &&
|
|
dwTrigger != 0 &&
|
|
(dwSystime - dwLastTrigger) >= dwTriggerFrequency &&
|
|
dwMsecsTillUpdate >= dwTriggerFrequency) {
|
|
|
|
// update the last triggered update time
|
|
InterlockedExchange(&g_ripcfg.dwLastTriggeredUpdate, GetTickCount());
|
|
|
|
// send out the routing table, but only include changes
|
|
BroadcastRouteTableContents(bTriggered, TRUE);
|
|
|
|
}
|
|
|
|
ClearChangeFlags();
|
|
|
|
InterlockedExchange(&g_ripcfg.dwRouteChanged, 0);
|
|
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function: ClearChangeFlags
|
|
//
|
|
// This function clears all the change flags in the table after an update.
|
|
// Assumes that the routing table is locked.
|
|
//-----------------------------------------------------------------------------
|
|
VOID ClearChangeFlags() {
|
|
int pos;
|
|
LPHASH_TABLE_ENTRY rt_entry;
|
|
|
|
for (pos = 0; pos < HASH_TABLE_SIZE; pos++) {
|
|
rt_entry = g_ripcfg.lpRouteTable[pos];
|
|
while (rt_entry != NULL) {
|
|
rt_entry->dwFlag &= ~ROUTE_CHANGE;
|
|
rt_entry = rt_entry->next;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function: DoTimedOperations()
|
|
//
|
|
// This function updates the routing table entries' timers periodically,
|
|
// and handles deletion of timed-out routes.
|
|
//-----------------------------------------------------------------------------
|
|
VOID DoTimedOperations(DWORD dwMillisecsSinceLastCall) {
|
|
int pos;
|
|
IN_ADDR addr;
|
|
DWORD dwGarbageTimeout;
|
|
HASH_TABLE_ENTRY *rt_entry;
|
|
HASH_TABLE_ENTRY *rt_entry_next;
|
|
char szDest[32] = {0};
|
|
char szNexthop[32] = {0};
|
|
char* pszTemp;
|
|
|
|
// read the garbage timeout and adjust for number of times
|
|
// this routine will be called over the interval
|
|
RIP_LOCK_PARAMS();
|
|
|
|
dwGarbageTimeout = g_params.dwGarbageTimeout;
|
|
|
|
RIP_UNLOCK_PARAMS();
|
|
|
|
|
|
RIP_LOCK_ROUTETABLE();
|
|
|
|
for (pos = 0; pos < HASH_TABLE_SIZE; pos++) {
|
|
rt_entry = g_ripcfg.lpRouteTable[pos];
|
|
while (rt_entry != NULL) {
|
|
rt_entry_next = rt_entry->next;
|
|
|
|
if (rt_entry->lTimeout > (LONG)dwMillisecsSinceLastCall) {
|
|
rt_entry->lTimeout -= dwMillisecsSinceLastCall;
|
|
}
|
|
else {
|
|
|
|
// timeout is all the way down
|
|
|
|
addr.s_addr = rt_entry->dwDestaddr;
|
|
pszTemp = inet_ntoa(addr);
|
|
|
|
if (pszTemp != NULL) {
|
|
strcpy(szDest, pszTemp);
|
|
}
|
|
|
|
addr.s_addr = rt_entry->dwNexthop;
|
|
pszTemp = inet_ntoa(addr);
|
|
|
|
if (pszTemp != NULL) {
|
|
strcpy(szNexthop, pszTemp);
|
|
}
|
|
|
|
if (rt_entry->dwFlag & TIMEOUT_TIMER) {
|
|
|
|
dbgprintf("Timing out route to %s over netcard %d, "
|
|
"with next hop of %s",
|
|
szDest, rt_entry->dwIndex, szNexthop);
|
|
|
|
rt_entry->lTimeout = (LONG)dwGarbageTimeout;
|
|
rt_entry->dwFlag &= ~TIMEOUT_TIMER;
|
|
rt_entry->dwFlag |= (GARBAGE_TIMER | ROUTE_CHANGE);
|
|
rt_entry->dwMetric = METRIC_INFINITE;
|
|
InterlockedExchange(&g_ripcfg.dwRouteChanged, 1);
|
|
}
|
|
else
|
|
if (rt_entry->dwFlag & GARBAGE_TIMER) {
|
|
|
|
// time to delete this
|
|
|
|
addr.s_addr = rt_entry->dwDestaddr;
|
|
pszTemp = inet_ntoa(addr);
|
|
|
|
if (pszTemp != NULL) {
|
|
strcpy(szDest, pszTemp);
|
|
}
|
|
|
|
dbgprintf("Deleting route to %s over netcard %d "
|
|
"with next hop of %s",
|
|
szDest, rt_entry->dwIndex, szNexthop);
|
|
|
|
DeleteRouteTableEntry(pos, rt_entry);
|
|
}
|
|
}
|
|
|
|
rt_entry = rt_entry_next;
|
|
}
|
|
}
|
|
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
DWORD BroadcastRouteTableRequests() {
|
|
INT iErr;
|
|
DWORD dwSize;
|
|
LPRIP_ENTRY lpentry;
|
|
SOCKADDR_IN destaddr;
|
|
LPRIP_HEADER lpheader;
|
|
BYTE buffer[RIP_MESSAGE_SIZE];
|
|
LPRIP_ADDRESS lpaddr, lpend;
|
|
|
|
RIP_LOCK_ADDRTABLE();
|
|
|
|
if (g_ripcfg.dwAddrCount > 0) {
|
|
|
|
destaddr.sin_family = AF_INET;
|
|
destaddr.sin_port = htons(RIP_PORT);
|
|
|
|
lpheader = (LPRIP_HEADER)buffer;
|
|
lpheader->chCommand = RIP_REQUEST;
|
|
lpheader->wReserved = 0;
|
|
|
|
lpentry = (LPRIP_ENTRY)(buffer + sizeof(RIP_HEADER));
|
|
lpentry->dwAddress = 0;
|
|
lpentry->wReserved = 0;
|
|
lpentry->wAddrFamily = 0;
|
|
lpentry->dwReserved1 = 0;
|
|
lpentry->dwReserved2 = 0;
|
|
lpentry->dwMetric = htonl(METRIC_INFINITE);
|
|
|
|
dwSize = sizeof(RIP_HEADER) + sizeof(RIP_ENTRY);
|
|
|
|
lpend = g_ripcfg.lpAddrTable + g_ripcfg.dwAddrCount;
|
|
for (lpaddr = g_ripcfg.lpAddrTable; lpaddr < lpend; lpaddr++) {
|
|
|
|
// skip disabled interfaces
|
|
if (lpaddr->sock == INVALID_SOCKET) {
|
|
continue;
|
|
}
|
|
|
|
|
|
// send out broadcast requests as RIPv1 packets
|
|
lpheader->chVersion = 1;
|
|
|
|
// set the destination to the broadcast address on this subnet
|
|
destaddr.sin_addr.s_addr = (lpaddr->dwAddress |
|
|
~lpaddr->dwNetmask);
|
|
|
|
iErr = sendto(lpaddr->sock, buffer, dwSize, 0,
|
|
(LPSOCKADDR)&destaddr, sizeof(SOCKADDR_IN));
|
|
if (iErr == SOCKET_ERROR) {
|
|
dbgprintf("error %d occurred broadcasting route table request "
|
|
"on netcard %d using IP address %s",
|
|
WSAGetLastError(), lpaddr->dwIndex,
|
|
inet_ntoa(destaddr.sin_addr));
|
|
|
|
InterlockedIncrement(&lpaddr->lpstats->dwSendFailures);
|
|
|
|
RipLogInformation(RIPLOG_SENDTO_FAILED, 0, NULL, WSAGetLastError());
|
|
}
|
|
else {
|
|
InterlockedIncrement(&lpaddr->lpstats->dwRequestsSent);
|
|
}
|
|
|
|
|
|
// send out multicast requests as RIPv2 packets
|
|
lpheader->chVersion = 2;
|
|
|
|
// set the destination to the RIP multicast address on this net
|
|
destaddr.sin_addr.s_addr = RIP_MULTIADDR;
|
|
|
|
iErr = sendto(lpaddr->sock, buffer, dwSize, 0,
|
|
(LPSOCKADDR)&destaddr, sizeof(SOCKADDR_IN));
|
|
if (iErr == SOCKET_ERROR) {
|
|
dbgprintf("error %d occurred multicasting route table request "
|
|
"on netcard %d using IP address %s",
|
|
WSAGetLastError(), lpaddr->dwIndex,
|
|
inet_ntoa(destaddr.sin_addr));
|
|
|
|
InterlockedIncrement(&lpaddr->lpstats->dwSendFailures);
|
|
|
|
RipLogInformation(RIPLOG_SENDTO_FAILED, 0, NULL, WSAGetLastError());
|
|
}
|
|
else {
|
|
InterlockedIncrement(&lpaddr->lpstats->dwRequestsSent);
|
|
}
|
|
}
|
|
}
|
|
|
|
RIP_UNLOCK_ADDRTABLE();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
VOID InitUpdateBuffer(BYTE buffer[], LPRIP_ENTRY *lplpentry, LPDWORD lpdwSize) {
|
|
LPRIP_HEADER lpheader;
|
|
|
|
lpheader = (LPRIP_HEADER)buffer;
|
|
lpheader->chCommand = RIP_RESPONSE;
|
|
lpheader->chVersion = 1;
|
|
lpheader->wReserved = 0;
|
|
*lplpentry = (LPRIP_ENTRY)(buffer + sizeof(RIP_HEADER));
|
|
*lpdwSize= sizeof(RIP_HEADER);
|
|
}
|
|
|
|
|
|
|
|
VOID AddUpdateEntry(BYTE buffer[], LPRIP_ENTRY *lplpentry, LPDWORD lpdwSize,
|
|
LPRIP_ADDRESS lpaddr, LPSOCKADDR_IN lpdestaddr,
|
|
DWORD dwAddress, DWORD dwMetric) {
|
|
DWORD length;
|
|
LPRIP_ENTRY lpentry;
|
|
|
|
#ifdef ROUTE_FILTERS
|
|
|
|
DWORD dwInd = 0;
|
|
|
|
|
|
//
|
|
// run the route thru' the announce filters
|
|
//
|
|
|
|
if ( g_prfAnnounceFilters != NULL )
|
|
{
|
|
for ( dwInd = 0; dwInd < g_prfAnnounceFilters-> dwCount; dwInd++ )
|
|
{
|
|
if ( g_prfAnnounceFilters-> pdwFilter[ dwInd ] == dwAddress )
|
|
{
|
|
dbgprintf(
|
|
"Skipped route %s with next hop %s because"
|
|
"of announce filter",
|
|
inet_ntoa( *( (struct in_addr*)
|
|
&( g_prfAnnounceFilters-> pdwFilter[ dwInd ] ) ))
|
|
);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
if ((*lpdwSize + sizeof(RIP_ENTRY)) > RIP_MESSAGE_SIZE) {
|
|
length = sendto(lpaddr->sock, buffer, *lpdwSize, 0,
|
|
(LPSOCKADDR)lpdestaddr, sizeof(SOCKADDR_IN));
|
|
if (length == SOCKET_ERROR || length < *lpdwSize) {
|
|
dbgprintf("error %d sending update", WSAGetLastError());
|
|
|
|
InterlockedIncrement(&lpaddr->lpstats->dwSendFailures);
|
|
|
|
RipLogInformation(RIPLOG_SENDTO_FAILED, 0, NULL, 0);
|
|
}
|
|
else {
|
|
InterlockedIncrement(&lpaddr->lpstats->dwResponsesSent);
|
|
}
|
|
|
|
// reinitialize the buffer that was passed in
|
|
InitUpdateBuffer(buffer, lplpentry, lpdwSize);
|
|
}
|
|
|
|
lpentry = *lplpentry;
|
|
lpentry->wReserved = 0;
|
|
lpentry->wAddrFamily = htons(AF_INET);
|
|
lpentry->dwAddress = dwAddress;
|
|
lpentry->dwReserved1 = 0;
|
|
lpentry->dwReserved2 = 0;
|
|
lpentry->dwMetric = htonl(dwMetric);
|
|
|
|
*lpdwSize += sizeof(RIP_ENTRY);
|
|
|
|
++(*lplpentry);
|
|
}
|
|
|
|
|
|
|
|
VOID FinishUpdateBuffer(BYTE buffer[], LPDWORD lpdwSize,
|
|
LPRIP_ADDRESS lpaddr, LPSOCKADDR_IN lpdestaddr) {
|
|
DWORD length;
|
|
|
|
// do nothing if no entries were added
|
|
if (*lpdwSize <= sizeof(RIP_HEADER)) {
|
|
return;
|
|
}
|
|
|
|
length = sendto(lpaddr->sock, buffer, *lpdwSize, 0,
|
|
(LPSOCKADDR)lpdestaddr, sizeof(SOCKADDR_IN));
|
|
if (length == SOCKET_ERROR || length < *lpdwSize) {
|
|
dbgprintf("error %d sending update", GetLastError());
|
|
|
|
InterlockedIncrement(&lpaddr->lpstats->dwSendFailures);
|
|
|
|
RipLogInformation(RIPLOG_SENDTO_FAILED, 0, NULL, 0);
|
|
}
|
|
else {
|
|
InterlockedIncrement(&lpaddr->lpstats->dwResponsesSent);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// the following struct and three functions are used
|
|
// to implement subnet hiding. when a subnet is summarized,
|
|
// the network which is its summary is added to a list using the
|
|
// function AddToAddressList. When another subnet of the same network
|
|
// needs to be summarized, it is first searched for using the function
|
|
// IsInAddressList, and if it is found, it is not re-advertised.
|
|
// After the update is over, the list is freed.
|
|
//-------------------------------------------------------------------------
|
|
typedef struct _ADDRESS_LIST {
|
|
struct _ADDRESS_LIST *next;
|
|
DWORD dwAddress;
|
|
DWORD dwNetmask;
|
|
} ADDRESS_LIST, *LPADDRESS_LIST;
|
|
|
|
|
|
DWORD AddToAddressList(LPADDRESS_LIST *lplpList, DWORD dwAddress,
|
|
DWORD dwNetmask) {
|
|
LPADDRESS_LIST lpal;
|
|
|
|
lpal = HeapAlloc(GetProcessHeap(), 0, sizeof(ADDRESS_LIST));
|
|
if (lpal == NULL) { return ERROR_NOT_ENOUGH_MEMORY; }
|
|
|
|
lpal->dwAddress = dwAddress;
|
|
lpal->dwNetmask = dwNetmask;
|
|
lpal->next = *lplpList;
|
|
*lplpList = lpal;
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOL IsInAddressList(LPADDRESS_LIST lpList, DWORD dwAddress) {
|
|
LPADDRESS_LIST lpal;
|
|
|
|
for (lpal = lpList; lpal != NULL; lpal = lpal->next) {
|
|
if (lpal->dwAddress == dwAddress) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID FreeAddressList(LPADDRESS_LIST lpList) {
|
|
LPADDRESS_LIST lpal, lpnext;
|
|
|
|
for (lpal = lpList; lpal != NULL; lpal = lpnext) {
|
|
lpnext = lpal->next;
|
|
HeapFree(GetProcessHeap(), 0, lpal);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function: TransmitRouteTableContents
|
|
//
|
|
// Sends the route tables contents, either as unicast or broadcast
|
|
// depending on the destination address specified. This function assumes
|
|
// that the address table is locked.
|
|
//-----------------------------------------------------------------------------
|
|
VOID TransmitRouteTableContents(LPRIP_ADDRESS lpaddr,
|
|
LPSOCKADDR_IN lpdestaddr,
|
|
BOOL bChangesOnly) {
|
|
INT pos;
|
|
DWORD dwSize;
|
|
LPADDRESS_LIST lpnet, lpSummaries;
|
|
LPRIP_ENTRY lpentry;
|
|
LPHASH_TABLE_ENTRY rt_entry;
|
|
BYTE buffer[RIP_MESSAGE_SIZE];
|
|
DWORD dwNexthopNetaddr, dwDestNetaddr;
|
|
DWORD dwSplit, dwPoison, dwHost, dwDefault;
|
|
DWORD dwDestNetclassMask, dwEntryNetclassMask;
|
|
DWORD dwEntryAddr, dwDestNetclassAddr, dwEntryNetclassAddr;
|
|
|
|
dwDestNetaddr = (lpdestaddr->sin_addr.s_addr &
|
|
SubnetMask(lpdestaddr->sin_addr.s_addr));
|
|
dwDestNetclassMask = NetclassMask(lpdestaddr->sin_addr.s_addr);
|
|
dwDestNetclassAddr = (lpdestaddr->sin_addr.s_addr & dwDestNetclassMask);
|
|
|
|
RIP_LOCK_PARAMS();
|
|
dwHost = g_params.dwAnnounceHost;
|
|
dwSplit = g_params.dwSplitHorizon;
|
|
dwPoison = g_params.dwPoisonReverse;
|
|
dwDefault = g_params.dwAnnounceDefault;
|
|
RIP_UNLOCK_PARAMS();
|
|
|
|
InitUpdateBuffer(buffer, &lpentry, &dwSize);
|
|
|
|
|
|
// start out with an empty list of summarized networks
|
|
lpSummaries = NULL;
|
|
|
|
|
|
RIP_LOCK_ROUTETABLE();
|
|
|
|
|
|
#ifdef ROUTE_FILTERS
|
|
RIP_LOCK_ANNOUNCE_FILTERS();
|
|
#endif
|
|
|
|
for (pos = 0; pos < HASH_TABLE_SIZE; pos++) {
|
|
rt_entry = g_ripcfg.lpRouteTable[pos];
|
|
while (rt_entry != NULL) {
|
|
|
|
// if we're supposed to only send changes
|
|
// and this entry hasn't changed, skip it
|
|
if (bChangesOnly &&
|
|
(rt_entry->dwFlag & ROUTE_CHANGE) == 0) {
|
|
|
|
rt_entry = rt_entry->next;
|
|
continue;
|
|
}
|
|
|
|
// ignore network summary entries
|
|
if ((rt_entry->dwFlag & ROUTE_ZOMBIE) != 0) {
|
|
rt_entry = rt_entry->next;
|
|
continue;
|
|
}
|
|
|
|
// copy the destination to be advertised
|
|
dwEntryAddr = rt_entry->dwDestaddr;
|
|
|
|
// if this is the route to the network for the outgoing interface
|
|
// don't send it
|
|
//
|
|
if (dwEntryAddr == dwDestNetaddr) {
|
|
rt_entry = rt_entry->next;
|
|
continue;
|
|
}
|
|
|
|
// if host route announcements are disabled,
|
|
// and this is a host route, don't add this entry
|
|
if (dwHost == 0 &&
|
|
(rt_entry->dwFlag & ROUTE_HOST) != 0) {
|
|
rt_entry = rt_entry->next;
|
|
continue;
|
|
}
|
|
|
|
// if default route announcements are disabled
|
|
// and this is a default route, don't add this entry
|
|
if (dwDefault == 0 &&
|
|
dwEntryAddr == 0) {
|
|
rt_entry = rt_entry->next;
|
|
continue;
|
|
}
|
|
|
|
|
|
// if this update is being sent to a network different
|
|
// from the network of the destination in the route entry,
|
|
// or if the destination was truncated due to different
|
|
// subnetmask lengths, summarize the route entry's destination,
|
|
// also, if the entry is network route, we need
|
|
// to remember it so we don't re-advertise it when
|
|
// summarizing subnets
|
|
|
|
dwEntryNetclassMask = NetclassMask(dwEntryAddr);
|
|
dwEntryNetclassAddr = (dwEntryAddr & dwEntryNetclassMask);
|
|
|
|
// special case exception is default route
|
|
if (dwEntryAddr != 0 &&
|
|
(dwDestNetclassAddr != dwEntryNetclassAddr ||
|
|
dwEntryAddr == dwEntryNetclassAddr)) {
|
|
|
|
// if the network for the entry has already been
|
|
// advertised, don't advertise it again
|
|
if (IsInAddressList(lpSummaries, dwEntryNetclassAddr)) {
|
|
|
|
rt_entry = rt_entry->next;
|
|
continue;
|
|
}
|
|
|
|
// add an entry for the network to the list
|
|
// of networks used as summaries so far
|
|
AddToAddressList(&lpSummaries, dwEntryNetclassAddr,
|
|
dwEntryNetclassMask);
|
|
|
|
// now we will advertise the NETWORK, not the original address
|
|
dwEntryAddr = dwEntryNetclassAddr;
|
|
}
|
|
else
|
|
if (dwEntryAddr != 0 &&
|
|
(rt_entry->dwFlag & ROUTE_HOST) == 0 &&
|
|
lpaddr->dwNetmask < rt_entry->dwNetmask) {
|
|
|
|
// this is neither a host route nor a default route
|
|
// and the subnet mask on the outgoing interface
|
|
// is shorter than the one for the entry, so the entry
|
|
// must be truncated so it is not considered a host route
|
|
// by the routers who will receive this update
|
|
// the comparison assumes netmasks are in network byte order
|
|
|
|
dwEntryAddr &= lpaddr->dwNetmask;
|
|
|
|
// skip the entry if the truncated destination
|
|
// turns out to have been advertised already
|
|
if (IsInAddressList(lpSummaries, dwEntryAddr)) {
|
|
|
|
rt_entry = rt_entry->next;
|
|
continue;
|
|
}
|
|
|
|
AddToAddressList(&lpSummaries, dwEntryAddr, lpaddr->dwNetmask);
|
|
}
|
|
|
|
// we only do poisoned-reverse/split-horizon on RIP routes
|
|
//
|
|
if (dwSplit == 0 ||
|
|
rt_entry->dwProtocol != IRE_PROTO_RIP) {
|
|
|
|
// always add the entry in this case;
|
|
// we increment the metric for a static route
|
|
// when sending it on interfaces other than
|
|
// the interface to which the route is attached
|
|
|
|
if (lpaddr->dwIndex == rt_entry->dwIndex) {
|
|
AddUpdateEntry(buffer, &lpentry, &dwSize, lpaddr,
|
|
lpdestaddr, dwEntryAddr,
|
|
rt_entry->dwMetric);
|
|
}
|
|
else {
|
|
AddUpdateEntry(buffer, &lpentry, &dwSize, lpaddr,
|
|
lpdestaddr, dwEntryAddr,
|
|
rt_entry->dwMetric + 1);
|
|
}
|
|
}
|
|
else
|
|
if (dwSplit != 0 && dwPoison == 0) {
|
|
|
|
// don't advertise the route if this update is
|
|
// being sent to the network from which we learnt
|
|
// the route; we can tell by looking at the nexthop,
|
|
// and comparing its subnet number to the subnet number
|
|
// of the destination network
|
|
|
|
dwNexthopNetaddr = (rt_entry->dwNexthop &
|
|
SubnetMask(rt_entry->dwNexthop));
|
|
|
|
if (dwNexthopNetaddr != dwDestNetaddr) {
|
|
AddUpdateEntry(buffer, &lpentry, &dwSize, lpaddr,
|
|
lpdestaddr, dwEntryAddr,
|
|
rt_entry->dwMetric);
|
|
}
|
|
}
|
|
else
|
|
if (dwSplit != 0 && dwPoison != 0) {
|
|
|
|
// if the update is being sent to the network from which
|
|
// the route was learnt to begin with, poison any routing loops
|
|
// by saying the metric is infinite
|
|
|
|
dwNexthopNetaddr = (rt_entry->dwNexthop &
|
|
SubnetMask(rt_entry->dwNexthop));
|
|
|
|
if (dwNexthopNetaddr == dwDestNetaddr) {
|
|
// this is the case which calls for poison reverse
|
|
|
|
AddUpdateEntry(buffer, &lpentry, &dwSize, lpaddr,
|
|
lpdestaddr, dwEntryAddr,
|
|
METRIC_INFINITE);
|
|
}
|
|
else {
|
|
AddUpdateEntry(buffer, &lpentry, &dwSize, lpaddr,
|
|
lpdestaddr, dwEntryAddr,
|
|
rt_entry->dwMetric);
|
|
}
|
|
}
|
|
|
|
rt_entry = rt_entry->next;
|
|
}
|
|
}
|
|
|
|
// remember the summarized networks in case some router
|
|
// broadcasts them back at us
|
|
for (lpnet = lpSummaries; lpnet != NULL; lpnet = lpnet->next) {
|
|
AddZombieRouteTableEntry(lpaddr, lpnet->dwAddress, lpnet->dwNetmask);
|
|
}
|
|
|
|
|
|
#ifdef ROUTE_FILTERS
|
|
|
|
RIP_UNLOCK_ANNOUNCE_FILTERS();
|
|
#endif
|
|
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
|
|
// done with the list of summarized networks
|
|
FreeAddressList(lpSummaries);
|
|
|
|
FinishUpdateBuffer(buffer, &dwSize, lpaddr, lpdestaddr);
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function: BroadcastRouteTableContents
|
|
//
|
|
// This function handles both triggered updates and regular updates.
|
|
// Depending on the value of bChangesOnly, it may exclude unchanged routes
|
|
// from the update.
|
|
// Assumes the address table is locked.
|
|
//-----------------------------------------------------------------------------
|
|
DWORD BroadcastRouteTableContents(BOOL bTriggered, BOOL bChangesOnly) {
|
|
SOCKADDR_IN destaddr;
|
|
LPRIP_ADDRESS lpaddr, lpend;
|
|
|
|
|
|
destaddr.sin_family = AF_INET;
|
|
destaddr.sin_port = htons(RIP_PORT);
|
|
|
|
lpend = g_ripcfg.lpAddrTable + g_ripcfg.dwAddrCount;
|
|
for (lpaddr = g_ripcfg.lpAddrTable; lpaddr < lpend; lpaddr++) {
|
|
if (lpaddr->sock == INVALID_SOCKET) {
|
|
continue;
|
|
}
|
|
|
|
destaddr.sin_addr.s_addr = (lpaddr->dwAddress | ~lpaddr->dwNetmask);
|
|
TransmitRouteTableContents(lpaddr, &destaddr, bChangesOnly);
|
|
|
|
if (bTriggered) {
|
|
InterlockedIncrement(&lpaddr->lpstats->dwTriggeredUpdatesSent);
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifndef CHICAGO
|
|
#define POS_REGEVENT 0
|
|
#define POS_TRIGEVENT 1
|
|
#define POS_STOPEVENT 2
|
|
#define POS_LASTEVENT 3
|
|
#else
|
|
#define POS_TRIGEVENT 0
|
|
#define POS_STOPEVENT 1
|
|
#define POS_LASTEVENT 2
|
|
#endif
|
|
|
|
#define DEF_TIMEOUT (10 * 1000)
|
|
|
|
|
|
DWORD UpdateThread(LPVOID Param) {
|
|
DWORD dwErr;
|
|
HKEY hkeyParams;
|
|
HANDLE hEvents[POS_LASTEVENT];
|
|
LONG lMillisecsTillFullUpdate, lMillisecsTillRouteRefresh;
|
|
DWORD dwWaitTimeout, dwGlobalTimeout;
|
|
DWORD dwTickCount, dwTickCountBeforeWait, dwTickCountAfterWait;
|
|
DWORD dwUpdateFrequency, dwSilentRIP, dwMillisecsSinceTimedOpsDone;
|
|
|
|
#ifndef CHICAGO
|
|
dwErr = RegOpenKey(HKEY_LOCAL_MACHINE, REGKEY_RIP_PARAMS, &hkeyParams);
|
|
if (dwErr == ERROR_SUCCESS) {
|
|
hEvents[POS_REGEVENT] = CreateEvent(NULL,FALSE,FALSE,NULL);
|
|
if (hEvents[POS_REGEVENT] != NULL) {
|
|
dwErr = RegNotifyChangeKeyValue(hkeyParams, FALSE,
|
|
REG_NOTIFY_CHANGE_LAST_SET |
|
|
REG_NOTIFY_CHANGE_ATTRIBUTES |
|
|
REG_NOTIFY_CHANGE_NAME,
|
|
hEvents[POS_REGEVENT], TRUE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
hEvents[POS_STOPEVENT] = g_stopEvent;
|
|
hEvents[POS_TRIGEVENT] = g_triggerEvent;
|
|
|
|
// get the update frequency, in seconds
|
|
RIP_LOCK_PARAMS();
|
|
dwSilentRIP = g_params.dwSilentRIP;
|
|
dwUpdateFrequency = g_params.dwUpdateFrequency;
|
|
dwGlobalTimeout = g_params.dwMaxTimedOpsInterval;
|
|
RIP_UNLOCK_PARAMS();
|
|
|
|
lMillisecsTillFullUpdate = (LONG)dwUpdateFrequency;
|
|
lMillisecsTillRouteRefresh = DEF_GETROUTEFREQUENCY;
|
|
|
|
|
|
dwMillisecsSinceTimedOpsDone = 0;
|
|
|
|
while (1) {
|
|
|
|
// set the time till the next full update
|
|
InterlockedExchange(&g_ripcfg.dwMillisecsTillFullUpdate,
|
|
(DWORD)lMillisecsTillFullUpdate);
|
|
|
|
// set the time we need the next wait to last;
|
|
// it has to be the minimum of the times till there is work to do;
|
|
// uses a two-comparison sort to find the smallest of three items
|
|
dwWaitTimeout = dwGlobalTimeout;
|
|
if (dwWaitTimeout > (DWORD)lMillisecsTillFullUpdate) {
|
|
dwWaitTimeout = lMillisecsTillFullUpdate;
|
|
}
|
|
if (dwWaitTimeout > (DWORD)lMillisecsTillRouteRefresh) {
|
|
dwWaitTimeout = lMillisecsTillRouteRefresh;
|
|
}
|
|
|
|
|
|
// get the time before entering the wait
|
|
dwTickCountBeforeWait = GetTickCount();
|
|
|
|
// enter the wait
|
|
//---------------
|
|
dwErr = WaitForMultipleObjects(POS_LASTEVENT, hEvents, FALSE,
|
|
dwWaitTimeout) ;
|
|
|
|
dwTickCountAfterWait = GetTickCount();
|
|
|
|
// we have to find out how long the wait lasted, taking care
|
|
// in case the system timer wrapped around to zero
|
|
if (dwTickCountAfterWait < dwTickCountBeforeWait) {
|
|
dwTickCountAfterWait += (DWORD)~0 - dwTickCountBeforeWait;
|
|
dwTickCountBeforeWait = 0;
|
|
}
|
|
|
|
dwTickCount = dwTickCountAfterWait - dwTickCountBeforeWait;
|
|
dwMillisecsSinceTimedOpsDone += dwTickCount;
|
|
|
|
|
|
// wait returned, now see why
|
|
//---------------------------
|
|
if (dwErr == WAIT_TIMEOUT) {
|
|
|
|
// every minute we read local routes again -
|
|
// this is to deal with somebody adding
|
|
// static routes. note that deleted static routes
|
|
// get deleted every 90 seconds.
|
|
|
|
lMillisecsTillRouteRefresh -= dwWaitTimeout;
|
|
if (lMillisecsTillRouteRefresh <= 0) {
|
|
lMillisecsTillRouteRefresh = DEF_GETROUTEFREQUENCY;
|
|
|
|
}
|
|
|
|
// ProcessRouteTableChanges and BroadcastRouteTableContents
|
|
// both assume the address table is locked; lock it before
|
|
// doing timed operations, too, for good measure
|
|
|
|
RIP_LOCK_ADDRTABLE();
|
|
|
|
|
|
// update timers, passing the number of milliseconds
|
|
// since we last called DoTimedOperations
|
|
DoTimedOperations(dwMillisecsSinceTimedOpsDone);
|
|
|
|
dwMillisecsSinceTimedOpsDone = 0;
|
|
|
|
// if anything changed, process the changes
|
|
// but tell the function not to send update packets
|
|
if (g_ripcfg.dwRouteChanged != 0) {
|
|
ProcessRouteTableChanges(FALSE);
|
|
}
|
|
|
|
// update the time till the next update,
|
|
// and send the update if it is due
|
|
lMillisecsTillFullUpdate -= dwWaitTimeout;
|
|
|
|
if (lMillisecsTillFullUpdate <= 0) {
|
|
lMillisecsTillFullUpdate = dwUpdateFrequency;
|
|
|
|
// send out the periodic update
|
|
if (dwSilentRIP == 0) {
|
|
|
|
// this is not triggered, and we need to broadcast
|
|
// the entire table, instead of just the changes
|
|
|
|
BroadcastRouteTableContents(FALSE, FALSE);
|
|
}
|
|
}
|
|
|
|
RIP_UNLOCK_ADDRTABLE();
|
|
|
|
|
|
// this continue is here because there is some processing
|
|
// done below for the cases where the wait is interrupted
|
|
// before it could timeout; this skips that code
|
|
//----------------------------------------------
|
|
continue;
|
|
}
|
|
else
|
|
#ifndef CHICAGO
|
|
if (dwErr == WAIT_OBJECT_0 + POS_REGEVENT) {
|
|
|
|
// registry was changed
|
|
LoadParameters();
|
|
|
|
// get the update frequency, converted to milliseconds
|
|
RIP_LOCK_PARAMS();
|
|
|
|
dwSilentRIP = g_params.dwSilentRIP;
|
|
dwUpdateFrequency = g_params.dwUpdateFrequency;
|
|
dwGlobalTimeout = g_params.dwMaxTimedOpsInterval;
|
|
|
|
RIP_UNLOCK_PARAMS();
|
|
|
|
|
|
RegNotifyChangeKeyValue(hkeyParams, FALSE,
|
|
REG_NOTIFY_CHANGE_LAST_SET |
|
|
REG_NOTIFY_CHANGE_ATTRIBUTES |
|
|
REG_NOTIFY_CHANGE_NAME,
|
|
hEvents[POS_REGEVENT], TRUE);
|
|
|
|
}
|
|
else
|
|
#endif
|
|
if (dwErr == WAIT_OBJECT_0 + POS_TRIGEVENT) {
|
|
RIP_LOCK_ADDRTABLE();
|
|
ProcessRouteTableChanges(TRUE);
|
|
RIP_UNLOCK_ADDRTABLE();
|
|
|
|
}
|
|
else
|
|
if (dwErr == WAIT_OBJECT_0 + POS_STOPEVENT) {
|
|
// perform graceful shutdown
|
|
//
|
|
// first, set all metrics to METRIC_INFINITE - 1
|
|
// next, send out four full updates at intervals
|
|
// of between 2 and 4 seconds
|
|
int pos;
|
|
LPHASH_TABLE_ENTRY rt_entry;
|
|
|
|
RIP_LOCK_ADDRTABLE();
|
|
RIP_LOCK_ROUTETABLE();
|
|
|
|
dbgprintf("sending out final updates.");
|
|
|
|
// setting metrics to 15
|
|
for (pos = 0; pos < HASH_TABLE_SIZE; pos++) {
|
|
rt_entry = g_ripcfg.lpRouteTable[pos];
|
|
while (rt_entry != NULL) {
|
|
if (rt_entry->dwMetric != METRIC_INFINITE) {
|
|
rt_entry->dwMetric = METRIC_INFINITE - 1;
|
|
}
|
|
rt_entry = rt_entry->next;
|
|
}
|
|
}
|
|
|
|
// sending out final full updates
|
|
if (dwSilentRIP == 0) {
|
|
srand((unsigned)time(NULL));
|
|
for (pos = 0; pos < 4; pos++) {
|
|
BroadcastRouteTableContents(FALSE, FALSE);
|
|
Sleep(2000 + (int)((double)rand() / RAND_MAX * 2000.0));
|
|
}
|
|
}
|
|
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
RIP_UNLOCK_ADDRTABLE();
|
|
|
|
// break out of the infinite loop
|
|
|
|
#ifndef CHICAGO
|
|
CloseHandle(hEvents[POS_REGEVENT]);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
// these are only executed if the wait ended
|
|
// for some reason other than a timeout;
|
|
//--------------------------------------
|
|
lMillisecsTillFullUpdate -= min(lMillisecsTillFullUpdate,
|
|
(LONG)dwTickCount);
|
|
lMillisecsTillRouteRefresh -= min(lMillisecsTillRouteRefresh,
|
|
(LONG)dwTickCount);
|
|
|
|
//
|
|
// Make sure DoTimedOperations() runs at least every
|
|
// MaxTimedOpsInterval seconds.
|
|
// We grab the address table lock for good measure.
|
|
//
|
|
|
|
if (dwMillisecsSinceTimedOpsDone >= g_params.dwMaxTimedOpsInterval) {
|
|
|
|
RIP_LOCK_ADDRTABLE();
|
|
|
|
DoTimedOperations(dwMillisecsSinceTimedOpsDone);
|
|
dwMillisecsSinceTimedOpsDone = 0;
|
|
|
|
// if anything changed, process the changes
|
|
// but tell the function not to send update packets
|
|
if (g_ripcfg.dwRouteChanged != 0) {
|
|
ProcessRouteTableChanges(FALSE);
|
|
}
|
|
|
|
RIP_UNLOCK_ADDRTABLE();
|
|
}
|
|
|
|
}
|
|
dbgprintf("update thread stopping.");
|
|
SetEvent(g_updateDoneEvent);
|
|
|
|
|
|
#ifndef CHICAGO
|
|
FreeLibraryAndExitThread(g_hmodule, 0);
|
|
#endif
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Function: CleanupRouteTable
|
|
//
|
|
// Called at shutdown time - runs through all the routes in the route table
|
|
// deleting from the system the routes that were learnt through RIP.
|
|
//-----------------------------------------------------------------------------
|
|
VOID CleanupRouteTable() {
|
|
INT pos;
|
|
LPHASH_TABLE_ENTRY rt_entry, prev_rt_entry;
|
|
|
|
RIP_LOCK_ROUTETABLE();
|
|
|
|
// Walk the whole hash table - deleting all RIP added routes
|
|
// from each bucket
|
|
|
|
dbgprintf("deleting RIP routes from system table.");
|
|
|
|
for (pos = 0; pos < HASH_TABLE_SIZE; pos++) {
|
|
prev_rt_entry = rt_entry = g_ripcfg.lpRouteTable[pos];
|
|
|
|
while (rt_entry != NULL) {
|
|
prev_rt_entry = rt_entry;
|
|
rt_entry = rt_entry->next;
|
|
|
|
if (prev_rt_entry->dwProtocol == IRE_PROTO_RIP) {
|
|
// remove the route from IP's routing table
|
|
UpdateSystemRouteTable(prev_rt_entry, FALSE);
|
|
}
|
|
|
|
free(prev_rt_entry);
|
|
}
|
|
|
|
g_ripcfg.lpRouteTable[pos] = NULL;
|
|
}
|
|
|
|
RIP_UNLOCK_ROUTETABLE();
|
|
|
|
// if a route dump was made to shared memory, close the handle
|
|
RIP_LOCK_ADDRTABLE();
|
|
|
|
RIP_UNLOCK_ADDRTABLE();
|
|
}
|
|
|
|
|