Source code of Windows XP (NT5)
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

//****************************************************************************
//
// 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();
}