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