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.
 
 
 
 
 
 

3808 lines
88 KiB

//============================================================================
// Copyright (c) 1995, Microsoft Corporation
//
// File: table.c
//
// History:
// Abolade Gbadegesin Aug-8-1995 Created.
//
// V Raman Oct-3-1996
// Added code to create/delete/wait on
// ITE_DeactivateEvent. Also added code to set
// ITE_Flags when deactivate is pending.
//
// V Raman Oct-27-1996
// Removed deactivate event and made
// DeactivateInterface synchronous
//
// interface table and peer table implementation
//============================================================================
#include "pchrip.h"
#pragma hdrstop
DWORD CreateIfSocket(PIF_TABLE_ENTRY pITE);
DWORD DeleteIfSocket(PIF_TABLE_ENTRY pITE);
DWORD InsertIfByAddress(PIF_TABLE pTable, PIF_TABLE_ENTRY pITE);
DWORD InsertPeerByAddress(PPEER_TABLE pTable, PPEER_TABLE_ENTRY pPTE);
DWORD AddNeighborToIfConfig(DWORD dwRemoteAddress, PIF_TABLE_ENTRY pite);
//----------------------------------------------------------------------------
// Function: CreateIfTable
//
// initializes an interface table
//----------------------------------------------------------------------------
DWORD
CreateIfTable(
PIF_TABLE pTable
) {
DWORD dwErr;
PLIST_ENTRY phead, plstart, plend;
//
// initialize the multiple-reader/single-writer synchronization object
//
dwErr = CreateReadWriteLock(&pTable->IT_RWL);
if (dwErr != NO_ERROR) {
TRACE1(IF, "error %d creating read-write-lock", dwErr);
return dwErr;
}
//
// initialize the hash table
//
plstart = pTable->IT_HashTableByIndex;
plend = plstart + IF_HASHTABLE_SIZE;
for (phead = plstart; phead < plend; phead++) {
InitializeListHead(phead);
}
//
// initialize the lists ordered by address and by index
//
InitializeListHead(&pTable->IT_ListByAddress);
InitializeListHead(&pTable->IT_ListByIndex);
//
// initialize the table's critical section
//
try {
InitializeCriticalSection(&pTable->IT_CS);
}
except(EXCEPTION_EXECUTE_HANDLER) {
dwErr = GetExceptionCode();
}
//
// Create timers for full updates and for triggered updates
//
if (!CreateTimerQueueTimer(
&pTable->IT_FinishFullUpdateTimer,
ig.IG_TimerQueueHandle,
WorkerFunctionFinishFullUpdate, NULL,
10000000, 10000000, 0
)) {
dwErr = GetLastError();
TRACE1(IF, "error %d creating finish full update timer", dwErr);
return dwErr;
}
if (!CreateTimerQueueTimer(
&pTable->IT_FinishTriggeredUpdateTimer,
ig.IG_TimerQueueHandle,
WorkerFunctionFinishTriggeredUpdate, NULL,
10000000, 10000000, 0
)) {
dwErr = GetLastError();
TRACE1(IF, "error %d creating finish triggered update timer", dwErr);
return dwErr;
}
//
// initialize remainder of struct
//
if (dwErr == NO_ERROR) {
pTable->IT_Created = 0x12345678;
pTable->IT_Flags = 0;
pTable->IT_LastUpdateTime.LowPart =
pTable->IT_LastUpdateTime.HighPart = 0;
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: DeleteIfTable
//
// frees resources used by an interface table.
// this assumes the table is locked for writing
//----------------------------------------------------------------------------
DWORD
DeleteIfTable(
PIF_TABLE pTable
) {
DWORD dwIndex;
PIF_TABLE_ENTRY pite;
PLIST_ENTRY ple, plend, phead;
//
// free memory for all existing interfaces
//
plend = pTable->IT_HashTableByIndex + IF_HASHTABLE_SIZE;
for (ple = plend - IF_HASHTABLE_SIZE; ple < plend; ple++) {
while (!IsListEmpty(ple)) {
phead = RemoveHeadList(ple);
pite = CONTAINING_RECORD(phead, IF_TABLE_ENTRY, ITE_HTLinkByIndex);
if (IF_IS_BOUND(pite)) {
DeleteIfSocket(pite);
if (IF_IS_ENABLED(pite)) {
RemoveEntryList(&pite->ITE_LinkByAddress);
}
RIP_FREE(pite->ITE_Binding);
}
RIP_FREE(pite->ITE_Config);
RIP_FREE(pite);
}
}
//
// delete synchronization objects
//
DeleteCriticalSection(&pTable->IT_CS);
DeleteReadWriteLock(&pTable->IT_RWL);
pTable->IT_Created = 0;
pTable->IT_Flags = 0;
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: CreateIfEntry
//
// inserts an entry into the interface table.
// this assumes the table is locked for writing
//----------------------------------------------------------------------------
DWORD
CreateIfEntry(
PIF_TABLE pTable,
DWORD dwIndex,
NET_INTERFACE_TYPE dwIfType,
PIPRIP_IF_CONFIG pConfig,
PIF_TABLE_ENTRY *ppEntry
) {
DWORD dwErr, dwSize;
PIF_TABLE_ENTRY pite;
PLIST_ENTRY ple, phead;
PIPRIP_IF_CONFIG picsrc, picdst;
if (ppEntry != NULL) { *ppEntry = NULL; }
dwErr = NO_ERROR;
do {
//
// fail if the interface exists
//
pite = GetIfByIndex(pTable, dwIndex);
if (pite != NULL) {
pite = NULL;
TRACE1(IF, "interface %d already exists", dwIndex);
dwErr = ERROR_INVALID_PARAMETER;
break;
}
//
// allocate memory for the new interface
//
pite = RIP_ALLOC(sizeof(IF_TABLE_ENTRY));
if (pite == NULL) {
dwErr = GetLastError();
TRACE3(
ANY, "error %d allocating %d bytes for interface %d",
dwErr, sizeof(IF_TABLE_ENTRY), dwIndex
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// initialize interface fields
//
pite->ITE_Index = dwIndex;
pite->ITE_Type = dwIfType;
//
// Change semantics to come up in UNBOUND-DISABLED state
//
//pite->ITE_Flags = ITEFLAG_ENABLED;
pite-> ITE_Flags = 0;
pite->ITE_Config = NULL;
pite->ITE_Binding = NULL;
pite->ITE_Sockets = NULL;
pite->ITE_FullOrDemandUpdateTimer = NULL;
picsrc = (PIPRIP_IF_CONFIG)pConfig;
dwSize = IPRIP_IF_CONFIG_SIZE(picsrc);
//
// validate the configuration parameters
//
dwErr = ValidateIfConfig(pConfig);
if (dwErr != NO_ERROR) {
TRACE1(IF, "invalid config specified for interface %d", dwIndex);
break;
}
//
// allocate space to hold the interface configuration
//
pite->ITE_Config = picdst = RIP_ALLOC(dwSize);
if (picdst == NULL) {
dwErr = GetLastError();
TRACE3(
IF, "error %d allocating %d bytes for interface %d config",
dwErr, dwSize, dwIndex
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// copy the configuration
//
CopyMemory(picdst, picsrc, dwSize);
//
// initialize the binding information and interface stats
//
pite->ITE_Binding = NULL;
ZeroMemory(&pite->ITE_Stats, sizeof(IPRIP_IF_STATS));
//
// insert the interface in the hash table
//
InsertHeadList(
pTable->IT_HashTableByIndex + IF_HASHVALUE(dwIndex),
&pite->ITE_HTLinkByIndex
);
//
// insert the interface in the list ordered by index
//
phead = &pTable->IT_ListByIndex;
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
PIF_TABLE_ENTRY ptemp;
ptemp = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByIndex);
if (pite->ITE_Index < ptemp->ITE_Index) { break; }
}
InsertTailList(ple, &pite->ITE_LinkByIndex);
if (ppEntry != NULL) { *ppEntry = pite; }
} while(FALSE);
if (dwErr != NO_ERROR && pite != NULL) {
if (pite->ITE_Config != NULL) { RIP_FREE(pite->ITE_Config); }
RIP_FREE(pite);
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: DeleteIfEntry
//
// removes an entry from the interface table.
// this assumes the table is locked for writing
//----------------------------------------------------------------------------
DWORD
DeleteIfEntry(
PIF_TABLE pTable,
DWORD dwIndex
) {
PIF_TABLE_ENTRY pite;
//
// find the interface if it exists
//
pite = GetIfByIndex(pTable, dwIndex);
if (pite == NULL) {
TRACE1(IF, "could not find interface %d", dwIndex);
return ERROR_INVALID_PARAMETER;
}
//
// cleanup the socket depending on its state
//
if (IF_IS_BOUND(pite)) {
DeleteIfSocket(pite);
if (IF_IS_ENABLED(pite)) {
RemoveEntryList(&pite->ITE_LinkByAddress);
}
RIP_FREE(pite->ITE_Binding);
}
//
// remove it from the list ordered by index
// as well as from the hash table
//
RemoveEntryList(&pite->ITE_LinkByIndex);
RemoveEntryList(&pite->ITE_HTLinkByIndex);
RIP_FREE(pite->ITE_Config);
RIP_FREE(pite);
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: ValidateIfConfig
//
// Checks the parameters in an IPRIP_IF_CONFIG structure.
//----------------------------------------------------------------------------
DWORD
ValidateIfConfig(
PIPRIP_IF_CONFIG pic
) {
CHAR szStr[12];
if (pic->IC_Metric > IPRIP_INFINITE) {
TRACE1(
IF, "Invalid interface metric %d specified",
pic->IC_Metric
);
_ltoa(pic->IC_Metric, szStr, 10);
LOGERR2(
INVALID_IF_CONFIG, "Metric", szStr, ERROR_INVALID_PARAMETER
);
return ERROR_INVALID_PARAMETER;
}
if (pic->IC_UpdateMode != IPRIP_UPDATE_PERIODIC &&
pic->IC_UpdateMode != IPRIP_UPDATE_DEMAND) {
TRACE1(
IF, "Invalid update mode %d specified",
pic->IC_UpdateMode
);
_ltoa(pic->IC_UpdateMode, szStr, 10);
LOGERR2(
INVALID_IF_CONFIG, "Update Mode", szStr,
ERROR_INVALID_PARAMETER
);
return ERROR_INVALID_PARAMETER;
}
if (pic->IC_AcceptMode != IPRIP_ACCEPT_DISABLED &&
pic->IC_AcceptMode != IPRIP_ACCEPT_RIP1 &&
pic->IC_AcceptMode != IPRIP_ACCEPT_RIP1_COMPAT &&
pic->IC_AcceptMode != IPRIP_ACCEPT_RIP2) {
TRACE1(
IF, "Invalid accept mode %d specified",
pic->IC_AcceptMode
);
_ltoa(pic->IC_AcceptMode, szStr, 10);
LOGERR2(
INVALID_IF_CONFIG, "Accept Mode", szStr,
ERROR_INVALID_PARAMETER
);
return ERROR_INVALID_PARAMETER;
}
if (pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED &&
pic->IC_AnnounceMode != IPRIP_ANNOUNCE_RIP1 &&
pic->IC_AnnounceMode != IPRIP_ANNOUNCE_RIP1_COMPAT &&
pic->IC_AnnounceMode != IPRIP_ANNOUNCE_RIP2) {
TRACE1(
IF, "Invalid announce mode %d specified",
pic->IC_AnnounceMode
);
_ltoa(pic->IC_AnnounceMode, szStr, 10);
LOGERR2(
INVALID_IF_CONFIG, "Announce Mode", szStr,
ERROR_INVALID_PARAMETER
);
return ERROR_INVALID_PARAMETER;
}
if (pic->IC_AuthenticationType != IPRIP_AUTHTYPE_NONE &&
pic->IC_AuthenticationType != IPRIP_AUTHTYPE_SIMPLE_PASSWORD) {
TRACE1(
IF, "Invalid authentication type %d specified",
pic->IC_AuthenticationType
);
_ltoa(pic->IC_AuthenticationType, szStr, 10);
LOGERR2(
INVALID_IF_CONFIG, "Authentication Type", szStr,
ERROR_INVALID_PARAMETER
);
return ERROR_INVALID_PARAMETER;
}
if (pic->IC_UnicastPeerMode != IPRIP_PEER_DISABLED &&
pic->IC_UnicastPeerMode != IPRIP_PEER_ALSO &&
pic->IC_UnicastPeerMode != IPRIP_PEER_ONLY) {
TRACE1(
IF, "Invalid unicast peer mode %d specified",
pic->IC_UnicastPeerMode
);
_ltoa(pic->IC_UnicastPeerMode, szStr, 10);
LOGERR2(
INVALID_IF_CONFIG, "unicast peer mode", szStr,
ERROR_INVALID_PARAMETER
);
return ERROR_INVALID_PARAMETER;
}
if (pic->IC_AcceptFilterMode != IPRIP_FILTER_DISABLED &&
pic->IC_AcceptFilterMode != IPRIP_FILTER_INCLUDE &&
pic->IC_AcceptFilterMode != IPRIP_FILTER_EXCLUDE) {
TRACE1(
IF, "Invalid accept filter mode %d specified",
pic->IC_AcceptFilterMode
);
_ltoa(pic->IC_AcceptFilterMode, szStr, 10);
LOGERR2(
INVALID_IF_CONFIG, "Accept filter mode", szStr,
ERROR_INVALID_PARAMETER
);
return ERROR_INVALID_PARAMETER;
}
if (pic->IC_AnnounceFilterMode != IPRIP_FILTER_DISABLED &&
pic->IC_AnnounceFilterMode != IPRIP_FILTER_INCLUDE &&
pic->IC_AnnounceFilterMode != IPRIP_FILTER_EXCLUDE) {
TRACE1(
IF, "Invalid announce filter mode %d specified",
pic->IC_AnnounceFilterMode
);
_ltoa(pic->IC_AnnounceFilterMode, szStr, 10);
LOGERR2(
INVALID_IF_CONFIG, "Announce filter mode", szStr,
ERROR_INVALID_PARAMETER
);
return ERROR_INVALID_PARAMETER;
}
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: BindIfEntry
//
// Updates the binding information for the specified interface.
// Assumes interface table is locked for writing
//----------------------------------------------------------------------------
DWORD
BindIfEntry(
PIF_TABLE pTable,
DWORD dwIndex,
PIP_ADAPTER_BINDING_INFO pBinding
) {
DWORD i, j, dwErr = NO_ERROR, dwSize;
PIF_TABLE_ENTRY pite;
PIPRIP_IF_BINDING pib;
PIPRIP_IP_ADDRESS paddr;
PIP_ADAPTER_BINDING_INFO piabi;
BOOL bFound;
pib = NULL;
do {
//
// retrieve the interface entry
//
pite = GetIfByIndex(pTable, dwIndex);
if (pite == NULL) {
dwErr = ERROR_INVALID_PARAMETER;
break;
}
//
// If the interface is already bound, check to see if he is giving
// us a different binding. If he is, then it is an error. Otherwise
// we shall be obliging and not complain too much
//
if (IF_IS_BOUND(pite)) {
TRACE1(IF, "interface %d is already bound", dwIndex);
pib = pite->ITE_Binding;
if(pib->IB_AddrCount != pBinding->AddressCount)
{
TRACE1(IF, "interface %d is bound and has different binding",dwIndex);
dwErr = ERROR_INVALID_PARAMETER;
break;
}
paddr = IPRIP_IF_ADDRESS_TABLE(pib);
for(i = 0; i < pBinding->AddressCount; i++)
{
bFound = FALSE;
for(j = 0; j < pib->IB_AddrCount; j++)
{
if((paddr[j].IA_Address == pBinding->Address[i].Address) &&
(paddr[j].IA_Netmask == pBinding->Address[i].Mask))
{
bFound = TRUE;
break;
}
}
if(!bFound)
{
TRACE1(IF,"interface %d is bound and has different binding",dwIndex);
dwErr = ERROR_INVALID_PARAMETER;
break;
}
}
//
// At this time we have dwErr as either NO_ERROR or
// ERROR_INVALID_PARAMETER. Either case we can break here
// since we are done
//
break;
}
//
// make sure there is at least one address
//
if (pBinding->AddressCount == 0) { break; }
dwSize = sizeof(IPRIP_IF_BINDING) +
pBinding->AddressCount * sizeof(IPRIP_IP_ADDRESS);
//
// allocate memory to store the binding
// in our format
//
pib = RIP_ALLOC(dwSize);
if (pib == NULL) {
dwErr = GetLastError();
TRACE3(
IF, "error %d allocating %d bytes for binding on interface %d",
dwErr, dwSize, dwIndex
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// convert the binding into our format
//
pib->IB_AddrCount = pBinding->AddressCount;
paddr = IPRIP_IF_ADDRESS_TABLE(pib);
for (i = 0; i < pib->IB_AddrCount; i++, paddr++) {
paddr->IA_Address = pBinding->Address[i].Address;
paddr->IA_Netmask = pBinding->Address[i].Mask;
}
//
// save the binding in the interface entry
//
pite->ITE_Binding = pib;
#if 0
//
// for demand dial interfaces add neighbor
//
if ( pite-> ITE_Type == DEMAND_DIAL ) {
dwErr = AddNeighborToIfConfig( pBinding-> RemoteAddress, pite );
if ( dwErr != NO_ERROR ) { break ; }
}
#endif
//
// create sockets for interface's addresses
//
dwErr = CreateIfSocket(pite);
if (dwErr != NO_ERROR) {
TRACE2(
IF, "error %d creating sockets for interface %d", dwErr, dwIndex
);
break;
}
//
// mark the interface as being bound
//
pite->ITE_Flags |= ITEFLAG_BOUND;
//
// we save the binding information in a private table
// so it can be quickly accessed and searched when we are
// trying to guess subnet masks given IP addresses;
//
ACQUIRE_BINDING_LOCK_EXCLUSIVE();
dwErr = CreateBindingEntry(ig.IG_BindingTable, pib);
RELEASE_BINDING_LOCK_EXCLUSIVE();
//
// if interface is also enabled, it is now active
// so queue activation work-item
//
if (IF_IS_ENABLED(pite)) {
//
// place interface on the list of active interfaces
//
dwErr = InsertIfByAddress(pTable, pite);
if (dwErr != NO_ERROR) {
TRACE2(
IF, "error %d inserting interface %d in active list",
dwErr, dwIndex
);
pite->ITE_Flags &= ~ITEFLAG_BOUND;
DeleteIfSocket(pite);
break;
}
//
// queue the work-item to send initial request
//
dwErr = QueueRipWorker(
WorkerFunctionActivateInterface, (PVOID)UlongToPtr(dwIndex)
);
if (dwErr != NO_ERROR) {
TRACE2(
IF, "error %d queuing work-item for interface %d",
dwErr, dwIndex
);
LOGERR0(QUEUE_WORKER_FAILED, dwErr);
RemoveEntryList(&pite->ITE_LinkByAddress);
pite->ITE_Flags &= ~ITEFLAG_BOUND;
DeleteIfSocket(pite);
break;
}
}
} while(FALSE);
if (dwErr != NO_ERROR) {
if (pib) { RIP_FREE(pib); }
if (pite) { pite->ITE_Binding = NULL; }
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: UnBindIfEntry
//
// removes the binding for the specified interface.
// assumes the interface table is locked for writing.
//----------------------------------------------------------------------------
DWORD
UnBindIfEntry(
PIF_TABLE pTable,
DWORD dwIndex
) {
DWORD dwErr;
PIF_TABLE_ENTRY pite;
do {
//
// retrieve the interface specified
//
pite = GetIfByIndex(pTable, dwIndex);
if (pite == NULL) {
dwErr = ERROR_INVALID_PARAMETER;
break;
}
//
// quit if the interface is already unbound
//
if (IF_IS_UNBOUND(pite)) {
dwErr = ERROR_INVALID_PARAMETER;
TRACE1(
IF, "interface %d is already unbound", dwIndex
);
break;
}
//
// clear the "bound" flag
//
pite->ITE_Flags &= ~ITEFLAG_BOUND;
//
// if the interface isn't enabled, close the socket for the interface;
// if the interface is enabled, that means it was active
// and we must queue the deactivation work-item
//
if (!IF_IS_ENABLED(pite)) {
DeleteIfSocket(pite);
ACQUIRE_BINDING_LOCK_EXCLUSIVE();
dwErr = DeleteBindingEntry(ig.IG_BindingTable, pite->ITE_Binding);
RELEASE_BINDING_LOCK_EXCLUSIVE();
RIP_FREE(pite->ITE_Binding);
pite->ITE_Binding = NULL;
}
else {
//
// the interface was active, so deactivate it
//
// remove from active list
//
RemoveEntryList(&pite->ITE_LinkByAddress);
WorkerFunctionDeactivateInterface( (PVOID)UlongToPtr(dwIndex));
//
// close the socket ourselves if required
//
if ( pite-> ITE_Binding ) {
DeleteIfSocket(pite);
ACQUIRE_BINDING_LOCK_EXCLUSIVE();
dwErr = DeleteBindingEntry(
ig.IG_BindingTable, pite->ITE_Binding
);
RELEASE_BINDING_LOCK_EXCLUSIVE();
RIP_FREE(pite->ITE_Binding);
pite->ITE_Binding = NULL;
}
else {
dwErr = NO_ERROR;
}
}
} while(FALSE);
return dwErr;
}
//----------------------------------------------------------------------------
// Function: EnableIfEntry
//
// configures an interface for RIP activity, including setting up
// a socket and linking the interface into the list ordered by address.
// this assumes the table is locked for writing
//----------------------------------------------------------------------------
DWORD
EnableIfEntry(
PIF_TABLE pTable,
DWORD dwIndex
) {
DWORD dwErr;
PLIST_ENTRY ple, phead;
PIF_TABLE_ENTRY pite;
do {
//
// retrieve the interface
//
pite = GetIfByIndex(pTable, dwIndex);
if (pite == NULL) {
TRACE1(IF, "could not find interface %d",dwIndex);
dwErr = ERROR_INVALID_PARAMETER;
break;
}
//
// quit if the interface is already enabled
//
if (IF_IS_ENABLED(pite)) {
TRACE1(IF, "interface %d is already enabled", dwIndex);
dwErr = NO_ERROR;
break;
}
pite->ITE_Flags |= ITEFLAG_ENABLED;
//
// if interface is already bound, it is now active,
// so queue the interface activation work-item
//
if (IF_IS_BOUND(pite)) {
//
// place interface on the list of active interfaces
//
dwErr = InsertIfByAddress(pTable, pite);
if (dwErr != NO_ERROR) {
TRACE2(
IF, "error %d inserting interface %d in active list",
dwErr, dwIndex
);
pite->ITE_Flags &= ~ITEFLAG_ENABLED;
break;
}
//
// queue the work-item to send initial request
//
dwErr = QueueRipWorker(
WorkerFunctionActivateInterface, (PVOID)UlongToPtr(dwIndex)
);
if (dwErr != NO_ERROR) {
TRACE2(
IF, "error %d queuing work-item for interface %d",
dwErr, dwIndex
);
LOGERR0(QUEUE_WORKER_FAILED, dwErr);
RemoveEntryList(&pite->ITE_LinkByAddress);
pite->ITE_Flags &= ~ITEFLAG_ENABLED;
break;
}
}
dwErr = NO_ERROR;
} while(FALSE);
return dwErr;
}
//----------------------------------------------------------------------------
// Function: ConfigureIfEntry
//
// modifies the configuration for an already-existing interface
// this assumes the table is locked for writing
//----------------------------------------------------------------------------
DWORD
ConfigureIfEntry(
PIF_TABLE pTable,
DWORD dwIndex,
PIPRIP_IF_CONFIG pConfig
) {
DWORD dwErr, dwSize;
PIF_TABLE_ENTRY pite;
PIPRIP_IF_CONFIG picsrc, picdst;
dwErr = NO_ERROR;
do {
//
// retrieve the interface to be configured
//
pite = GetIfByIndex(pTable, dwIndex);
if (pite == NULL) {
TRACE1(IF, "could not find interface %d", dwIndex);
dwErr = ERROR_INVALID_PARAMETER;
break;
}
//
// get the size of the new configuration
//
picsrc = (PIPRIP_IF_CONFIG)pConfig;
dwSize = IPRIP_IF_CONFIG_SIZE(picsrc);
//
// validate the new configuration
//
dwErr = ValidateIfConfig(picsrc);
if (dwErr != NO_ERROR) {
TRACE1(IF, "invalid config specified for interface %d", dwIndex);
break;
}
//
// allocate space to hold the new configuration
//
picdst = RIP_ALLOC(dwSize);
if (picdst == NULL) {
dwErr = GetLastError();
TRACE3(
IF, "error %d allocating %d bytes for interface %d config",
dwErr, dwSize, dwIndex
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// copy the new configuration, and free the old one
//
CopyMemory(picdst, picsrc, dwSize);
if (pite->ITE_Config != NULL) { RIP_FREE(pite->ITE_Config); }
pite->ITE_Config = picdst;
//
// if the interface is bound, re-initialize the interface
//
if (IF_IS_BOUND(pite)) {
//
// close the sockets and set them up again
//
dwErr = DeleteIfSocket(pite);
dwErr = CreateIfSocket(pite);
if (dwErr != NO_ERROR) {
TRACE2(
IF, "error %d creating sockets for interface %d",
dwErr, dwIndex
);
break;
}
//
// re-activate the interface if it is active
//
if (IF_IS_ENABLED(pite)) {
//
// queue the work-item to activate the interface
//
dwErr = QueueRipWorker(
WorkerFunctionActivateInterface, (PVOID)UlongToPtr(dwIndex)
);
if (dwErr != NO_ERROR) {
TRACE2(
IF, "error %d queueing work-item for interface %d",
dwErr, dwIndex
);
LOGERR0(QUEUE_WORKER_FAILED, dwErr);
break;
}
}
}
dwErr = NO_ERROR;
} while(FALSE);
return dwErr;
}
//----------------------------------------------------------------------------
// Function: DisableIfEntry
//
// stops RIP activity on an interface, removing the interface
// from the list of interfaces ordered by address.
// this assumes the table is locked for writing
//----------------------------------------------------------------------------
DWORD
DisableIfEntry(
PIF_TABLE pTable,
DWORD dwIndex
) {
DWORD dwErr;
PIF_TABLE_ENTRY pite;
do {
//
// retrieve the interface to be disabled
//
pite = GetIfByIndex(pTable, dwIndex);
if (pite == NULL) {
TRACE1(IF, "could not find interface %d", dwIndex);
dwErr = ERROR_INVALID_PARAMETER;
break;
}
//
// quit if already disabled
//
if (IF_IS_DISABLED(pite)) {
TRACE1(IF, "interface %d is already disabled", dwIndex);
dwErr = ERROR_INVALID_PARAMETER;
break;
}
//
// clear the enabled flag
//
pite->ITE_Flags &= ~ITEFLAG_ENABLED;
//
// if this interface was not bound, clearing the flag is enough;
// if the interface was bound (and therefore active),
// deactivate it here
//
if (IF_IS_BOUND(pite)) {
//
// remove from active list
//
RemoveEntryList(&pite->ITE_LinkByAddress);
//
// execute the work-item to send final updates
//
WorkerFunctionDeactivateInterface( (PVOID) UlongToPtr(dwIndex) );
}
dwErr = NO_ERROR;
} while(FALSE);
return dwErr;
}
//----------------------------------------------------------------------------
// Function: CreateIfSocket
//
// creates sockets for an interface, setting it up according to
// the configuration including in the interface control block.
// this assumes the table containing the interface is locked for writing
//----------------------------------------------------------------------------
DWORD
CreateIfSocket(
PIF_TABLE_ENTRY pite
) {
SOCKADDR_IN sinsock;
PIPRIP_IF_CONFIG pic;
PIPRIP_IF_BINDING pib;
PIPRIP_IP_ADDRESS paddr;
DWORD i, dwErr, dwOption, dwIndex;
LPSTR lpszAddr;
pic = pite->ITE_Config;
pib = pite->ITE_Binding;
dwIndex = pite->ITE_Index;
//
// allocate an array of sockets
//
pite->ITE_Sockets = RIP_ALLOC(pib->IB_AddrCount * sizeof(SOCKET));
if (pite->ITE_Sockets == NULL) {
dwErr = GetLastError();
TRACE3(
IF, "error %d allocating %d bytes for interface %d sockets",
dwErr, pib->IB_AddrCount * sizeof(SOCKET), dwIndex
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
return dwErr;
}
//
// initialize the array of sockets
//
for (i = 0; i < pib->IB_AddrCount; i++) {
pite->ITE_Sockets[i] = INVALID_SOCKET;
}
//
// create sockets for each address in the binding
//
paddr = IPRIP_IF_ADDRESS_TABLE(pib);
for (i = 0; i < pib->IB_AddrCount; i++, paddr++) {
//
// create the socket
//
pite->ITE_Sockets[i] = WSASocket(
AF_INET, SOCK_DGRAM, 0, NULL, 0, 0
);
if (pite->ITE_Sockets[i] == INVALID_SOCKET) {
dwErr = WSAGetLastError();
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
IF, "error %d creating socket for interface %d (%s)",
dwErr, dwIndex, lpszAddr
);
LOGERR1(CREATE_SOCKET_FAILED_2, lpszAddr, dwErr);
}
break;
}
//
// try to allow re-use of this address
//
dwOption = 1;
dwErr = setsockopt(
pite->ITE_Sockets[i], SOL_SOCKET, SO_REUSEADDR,
(PBYTE)&dwOption, sizeof(dwOption)
);
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
IF, "error %d setting re-use flag for interface %d (%s)",
dwErr, dwIndex, INET_NTOA(paddr->IA_Address)
);
}
}
//
// enable broadcasting if not exclusively RIP2 mode,
// or if there are any unicast peers configured
//
if (pic->IC_AcceptMode == IPRIP_ACCEPT_RIP1 ||
pic->IC_AcceptMode == IPRIP_ACCEPT_RIP1_COMPAT ||
pic->IC_AnnounceMode == IPRIP_ANNOUNCE_RIP1 ||
pic->IC_AnnounceMode == IPRIP_ANNOUNCE_RIP1_COMPAT ||
(pic->IC_UnicastPeerMode != IPRIP_PEER_DISABLED &&
pic->IC_UnicastPeerCount != 0)) {
//
// make sure broadcasting is enabled for this socket
//
dwOption = 1;
dwErr = setsockopt(
pite->ITE_Sockets[i], SOL_SOCKET, SO_BROADCAST,
(PBYTE)&dwOption, sizeof(dwOption)
);
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
IF, "error %d enabling broadcast on interface %d (%s)",
dwErr, dwIndex, lpszAddr
);
LOGERR1(ENABLE_BROADCAST_FAILED, lpszAddr, dwErr);
}
break;
}
}
//
// bind the socket to the RIP port
//
sinsock.sin_family = AF_INET;
sinsock.sin_port = htons(IPRIP_PORT);
sinsock.sin_addr.s_addr = paddr->IA_Address;
dwErr = bind(
pite->ITE_Sockets[i], (LPSOCKADDR)&sinsock,
sizeof(SOCKADDR_IN)
);
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
IF, "error %d binding on socket for interface %d (%s)",
dwErr, dwIndex, lpszAddr
);
LOGERR1(BIND_FAILED, lpszAddr, dwErr);
}
break;
}
//
// enable multicasting if not exclusively RIP1/RIP1-compatible mode
//
if (pic->IC_AcceptMode == IPRIP_ACCEPT_RIP2 ||
pic->IC_AcceptMode == IPRIP_ACCEPT_RIP1_COMPAT ||
pic->IC_AnnounceMode == IPRIP_ANNOUNCE_RIP2) {
struct ip_mreq imOption;
//
// set the interface from which multicasts must be sent
//
sinsock.sin_addr.s_addr = paddr->IA_Address;
dwErr = setsockopt(
pite->ITE_Sockets[i], IPPROTO_IP, IP_MULTICAST_IF,
(PBYTE)&sinsock.sin_addr, sizeof(IN_ADDR)
);
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
IF, "error %d setting interface %d (%s) as multicast",
dwErr, dwIndex, lpszAddr
);
LOGERR1(SET_MCAST_IF_FAILED, lpszAddr, dwErr);
}
break;
}
//
// join the IPRIP multicast group
//
imOption.imr_multiaddr.s_addr = IPRIP_MULTIADDR;
imOption.imr_interface.s_addr = paddr->IA_Address;
dwErr = setsockopt(
pite->ITE_Sockets[i], IPPROTO_IP, IP_ADD_MEMBERSHIP,
(PBYTE)&imOption, sizeof(imOption)
);
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
IF, "error %d enabling multicast on interface %d (%s)",
dwErr, dwIndex, lpszAddr
);
LOGERR1(JOIN_GROUP_FAILED, lpszAddr, dwErr);
}
break;
}
}
dwErr = NO_ERROR;
}
if (i < pib->IB_AddrCount) {
//
// something failed if we are here
//
DeleteIfSocket(pite);
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: DeleteIfSocket
//
// closes the sockets used by an interface, if any.
// assumes that the interface is active, and that the interface table
// is locked for writing
//----------------------------------------------------------------------------
DWORD
DeleteIfSocket(
PIF_TABLE_ENTRY pite
) {
DWORD i;
for (i = 0; i < pite->ITE_Binding->IB_AddrCount; i++) {
if (pite->ITE_Sockets[i] != INVALID_SOCKET) {
if (closesocket(pite->ITE_Sockets[i]) == SOCKET_ERROR) {
TRACE1(IF, "error %d closing socket", WSAGetLastError());
}
pite->ITE_Sockets[i] = INVALID_SOCKET;
}
}
RIP_FREE(pite->ITE_Sockets);
pite->ITE_Sockets = NULL;
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: GetIfByIndex
//
// returns the interface with the given index.
// assumes the table is locked for reading or writing
//----------------------------------------------------------------------------
PIF_TABLE_ENTRY
GetIfByIndex(
PIF_TABLE pTable,
DWORD dwIndex
) {
PIF_TABLE_ENTRY pite = NULL;
PLIST_ENTRY phead, ple;
phead = pTable->IT_HashTableByIndex + IF_HASHVALUE(dwIndex);
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_HTLinkByIndex);
if (pite->ITE_Index == dwIndex) {
break;
}
}
if (ple == phead) { return NULL; }
else { return pite; }
}
//----------------------------------------------------------------------------
// Function: GetIfByAddress
//
// returns the interface bound to the given address.
// assumes the table is locked for reading or writing
//----------------------------------------------------------------------------
PIF_TABLE_ENTRY
GetIfByAddress(
PIF_TABLE pTable,
DWORD dwAddress,
DWORD dwGetMode,
PDWORD pdwErr
) {
DWORD i;
PIPRIP_IF_BINDING pib;
PLIST_ENTRY phead, pfl;
PIPRIP_IP_ADDRESS paddr;
PIF_TABLE_ENTRY pite, piterec;
if (pdwErr != NULL) { *pdwErr = NO_ERROR; }
phead = &pTable->IT_ListByAddress;
pite = NULL;
//
// return record at head of list if mode is GetFirst
//
if (dwGetMode == GETMODE_FIRST) {
if (phead->Flink == phead) {
if (pdwErr != NULL) { *pdwErr = ERROR_NO_MORE_ITEMS; }
return NULL;
}
else {
pfl = phead->Flink;
return CONTAINING_RECORD(pfl, IF_TABLE_ENTRY, ITE_LinkByAddress);
}
}
//
// search for the entry
//
for (pfl = phead->Flink; pfl != phead; pfl = pfl->Flink) {
piterec = CONTAINING_RECORD(pfl, IF_TABLE_ENTRY, ITE_LinkByAddress);
pib = piterec->ITE_Binding;
paddr = IPRIP_IF_ADDRESS_TABLE(pib);
for (i = 0; i < pib->IB_AddrCount; i++, paddr++) {
if (dwAddress == paddr->IA_Address) { pite = piterec; break; }
}
if (pite) { break; }
}
//
// return record after the one found if mode is GetNext
//
if (dwGetMode == GETMODE_NEXT && pite != NULL) {
pfl = &pite->ITE_LinkByAddress;
//
// if entry found is last one, return NULL,
// otherwise, return the following entry
//
if (pfl->Flink == phead) {
if (pdwErr != NULL) { *pdwErr = ERROR_NO_MORE_ITEMS; }
pite = NULL;
}
else {
pfl = pfl->Flink;
pite = CONTAINING_RECORD(pfl, IF_TABLE_ENTRY, ITE_LinkByAddress);
}
}
//
// if the interface wasn't found, this will still be NULL
//
return pite;
}
//----------------------------------------------------------------------------
// Function: GetIfByListIndex
//
// This function is similar to GetIfByAddress in that it supports
// three modes of retrieval, but it is different in that it looks
// in the list of interfaces sorted by index.
//----------------------------------------------------------------------------
PIF_TABLE_ENTRY
GetIfByListIndex(
PIF_TABLE pTable,
DWORD dwIndex,
DWORD dwGetMode,
PDWORD pdwErr
) {
PIF_TABLE_ENTRY pite;
PLIST_ENTRY ple, phead;
if (pdwErr != NULL) { *pdwErr = NO_ERROR; }
phead = &pTable->IT_ListByIndex;
pite = NULL;
//
// return record at head of list if mode is GETMODE_FIRST;
// if list is empty, return NULL.
//
if (dwGetMode == GETMODE_FIRST) {
if (phead->Flink == phead) {
if (pdwErr != NULL) { *pdwErr = ERROR_NO_MORE_ITEMS; }
return NULL;
}
else {
ple = phead->Flink;
return CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByIndex);
}
}
//
// get the entry requested
//
pite = GetIfByIndex(pTable, dwIndex);
//
// if mode is GETMODE_NEXT, return the item after the one retrieved
//
if (dwGetMode == GETMODE_NEXT && pite != NULL) {
ple = &pite->ITE_LinkByIndex;
//
// if entry found is last one, return NULL,
// otherwise return the following entry
//
if (ple->Flink == phead) {
if (pdwErr != NULL) { *pdwErr = ERROR_NO_MORE_ITEMS; }
pite = NULL;
}
else {
ple = ple->Flink;
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByIndex);
}
}
return pite;
}
//----------------------------------------------------------------------------
// Function: InsertIfByAddress
//
// inserts the given interface into the list of interfaces sorted by address.
// assumes the table is locked for writing
//----------------------------------------------------------------------------
DWORD
InsertIfByAddress(
PIF_TABLE pTable,
PIF_TABLE_ENTRY pITE
) {
INT cmp;
PIF_TABLE_ENTRY pite;
PIPRIP_IP_ADDRESS paddr;
DWORD dwAddress, dwITEAddress;
PLIST_ENTRY phead, pfl;
phead = &pTable->IT_ListByAddress;
paddr = IPRIP_IF_ADDRESS_TABLE(pITE->ITE_Binding);
dwAddress = paddr->IA_Address;
//
// search for the insertion point
//
for (pfl = phead->Flink; pfl != phead; pfl = pfl->Flink) {
pite = CONTAINING_RECORD(pfl, IF_TABLE_ENTRY, ITE_LinkByAddress);
paddr = IPRIP_IF_ADDRESS_TABLE(pite->ITE_Binding);
dwITEAddress = paddr->IA_Address;
if (INET_CMP(dwAddress, dwITEAddress, cmp) < 0) { break; }
else if (cmp == 0) { return ERROR_ALREADY_EXISTS; }
}
InsertTailList(pfl, &pITE->ITE_LinkByAddress);
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: AddNeighborToIfConfig
//
// Adds a unicast neighbor to an interface config block.
//----------------------------------------------------------------------------
DWORD
AddNeighborToIfConfig(
DWORD dwRemoteAddress,
PIF_TABLE_ENTRY pite
) {
BOOL bFound = FALSE;
DWORD dwErr = (DWORD) -1,
dwSize = 0,
dwCnt = 0;
PDWORD pdwAddr = NULL;
PIPRIP_IF_CONFIG pic = NULL,
picNew = NULL;
do
{
pic = pite-> ITE_Config;
//
// verify neighbor is not aready present
//
pdwAddr = IPRIP_IF_UNICAST_PEER_TABLE( pic );
for ( dwCnt = 0; dwCnt < pic-> IC_UnicastPeerCount; dwCnt++ )
{
if ( dwRemoteAddress == pdwAddr[ dwCnt ] )
{
bFound = TRUE;
break;
}
}
//
// entry exits, enable unicast peer mode and quit
//
if ( bFound )
{
LPSTR lpszAddr = INET_NTOA( dwRemoteAddress );
pic-> IC_UnicastPeerMode = IPRIP_PEER_ALSO;
dwErr = NO_ERROR;
if (lpszAddr != NULL) {
TRACE2(
IF,
"Unicast neighbor %s already present in configuration on interface %d",
lpszAddr, pite-> ITE_Index
);
}
break;
}
//
// allocate new config block
//
dwSize = IPRIP_IF_CONFIG_SIZE( pic ) + sizeof( DWORD );
picNew = RIP_ALLOC( dwSize );
if ( picNew == NULL )
{
dwErr = GetLastError();
TRACE3(
IF, "error %d allocating %d bytes for configuration on interface %d",
dwErr, dwSize, pite-> ITE_Index
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// copy base structure
//
CopyMemory( picNew, pic, sizeof( IPRIP_IF_CONFIG ) );
//
// copy uicast peer table
//
CopyMemory(
IPRIP_IF_UNICAST_PEER_TABLE( picNew ),
IPRIP_IF_UNICAST_PEER_TABLE( pic ),
pic-> IC_UnicastPeerCount * sizeof( DWORD )
);
//
// add new neighbor and set unicast neighbor mode
//
pdwAddr = IPRIP_IF_UNICAST_PEER_TABLE( picNew );
pdwAddr[ picNew-> IC_UnicastPeerCount++ ] = dwRemoteAddress;
picNew-> IC_UnicastPeerMode = IPRIP_PEER_ALSO;
//
// Copy accept and annouce filters
//
CopyMemory(
IPRIP_IF_ACCEPT_FILTER_TABLE( picNew ),
IPRIP_IF_ACCEPT_FILTER_TABLE( pic ),
pic-> IC_AcceptFilterCount * sizeof( IPRIP_IP_ADDRESS )
);
CopyMemory(
IPRIP_IF_ANNOUNCE_FILTER_TABLE( picNew ),
IPRIP_IF_ANNOUNCE_FILTER_TABLE( pic ),
pic-> IC_AnnounceFilterCount * sizeof( IPRIP_IP_ADDRESS )
);
//
// save the new config
//
pite-> ITE_Config = picNew;
RIP_FREE( pic );
dwErr = NO_ERROR;
} while ( FALSE );
return dwErr;
}
//----------------------------------------------------------------------------
// Function: CreatePeerTable
//
// initializes the given peer table
//----------------------------------------------------------------------------
DWORD
CreatePeerTable(
PPEER_TABLE pTable
) {
DWORD dwErr;
PLIST_ENTRY ple, plstart, plend;
//
// initialize the hash table of peers
//
plstart = pTable->PT_HashTableByAddress;
plend = plstart + PEER_HASHTABLE_SIZE;
for (ple = plstart; ple < plend; ple++) {
InitializeListHead(ple);
}
//
// initialize the list of peers ordered by address
//
InitializeListHead(&pTable->PT_ListByAddress);
//
// initialize the multiple-read/single-write synchronization object
//
dwErr = CreateReadWriteLock(&pTable->PT_RWL);
if (dwErr == NO_ERROR) {
pTable->PT_Created = 0x12345678;
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: DeletePeerTable
//
// frees the resources used by the given peer table
// assumes the table is locked for writing
//----------------------------------------------------------------------------
DWORD
DeletePeerTable(
PPEER_TABLE pTable
) {
PLIST_ENTRY ple, phead;
PPEER_TABLE_ENTRY ppte;
//
// empty the hash table of peer stats structures
//
phead = &pTable->PT_ListByAddress;
while (!IsListEmpty(phead)) {
ple = RemoveHeadList(phead);
ppte = CONTAINING_RECORD(ple, PEER_TABLE_ENTRY, PTE_LinkByAddress);
RIP_FREE(ppte);
}
//
// delete the table's synchronization object
//
DeleteReadWriteLock(&pTable->PT_RWL);
pTable->PT_Created = 0;
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: CreatePeerEntry
//
// creates an entry in the given table for a peer with the given address
// assumes the table is locked for writing
//----------------------------------------------------------------------------
DWORD
CreatePeerEntry(
PPEER_TABLE pTable,
DWORD dwAddress,
PPEER_TABLE_ENTRY *ppEntry
) {
DWORD dwErr;
PLIST_ENTRY ple, phead;
PPEER_TABLE_ENTRY ppte;
if (ppEntry != NULL) { *ppEntry = NULL; }
//
// make sure the entry does not already exist
//
ppte = GetPeerByAddress(pTable, dwAddress, GETMODE_EXACT, NULL);
if (ppte != NULL) {
if (ppEntry != NULL) { *ppEntry = ppte; }
return NO_ERROR;
}
//
// allocate memory for the new peer entry
//
ppte = RIP_ALLOC(sizeof(PEER_TABLE_ENTRY));
if (ppte == NULL) {
LPSTR lpszAddr = INET_NTOA(dwAddress);
dwErr = GetLastError();
if (lpszAddr != NULL) {
TRACE3(
IF, "error %d allocating %d bytes for peer %s entry",
dwErr, sizeof(PEER_TABLE_ENTRY), lpszAddr
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
}
return dwErr;
}
//
// initialize the fields
//
ppte->PTE_Address = dwAddress;
ZeroMemory(&ppte->PTE_Stats, sizeof(IPRIP_PEER_STATS));
//
// insert the peer stats entry in the hash table
//
phead = pTable->PT_HashTableByAddress + PEER_HASHVALUE(dwAddress);
InsertHeadList(phead, &ppte->PTE_HTLinkByAddress);
//
// insert the entry in the list sorted by address
//
dwErr = InsertPeerByAddress(pTable, ppte);
if (ppEntry != NULL) { *ppEntry = ppte; }
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: DeletePeerEntry
//
// deletes the entry for the peer with the given address
// assumes the table is locked for writing
//----------------------------------------------------------------------------
DWORD
DeletePeerEntry(
PPEER_TABLE pTable,
DWORD dwAddress
) {
PPEER_TABLE_ENTRY ppte;
//
// quit if the entry cannot be found
//
ppte = GetPeerByAddress(pTable, dwAddress, GETMODE_EXACT, NULL);
if (ppte == NULL) { return ERROR_INVALID_PARAMETER; }
//
// remove the entry from the hash-table
// and from the list sorted by address
//
RemoveEntryList(&ppte->PTE_LinkByAddress);
RemoveEntryList(&ppte->PTE_HTLinkByAddress);
RIP_FREE(ppte);
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: GetPeerByAddress
//
// returns the entry for the peer with the given address
// assumes the table is locked for reading or writing
//----------------------------------------------------------------------------
PPEER_TABLE_ENTRY
GetPeerByAddress(
PPEER_TABLE pTable,
DWORD dwAddress,
DWORD dwGetMode,
PDWORD pdwErr
) {
PLIST_ENTRY phead, pfl;
PPEER_TABLE_ENTRY ppte, ppterec;
if (pdwErr != NULL) { *pdwErr = NO_ERROR; }
//
// return head of list if in GetFirst mode
//
if (dwGetMode == GETMODE_FIRST) {
phead = &pTable->PT_ListByAddress;
if (phead->Flink == phead) {
if (pdwErr != NULL) { *pdwErr = ERROR_NO_MORE_ITEMS; }
return NULL;
}
else {
pfl = phead->Flink;
return CONTAINING_RECORD(pfl, PEER_TABLE_ENTRY, PTE_LinkByAddress);
}
}
phead = pTable->PT_HashTableByAddress + PEER_HASHVALUE(dwAddress);
ppte = NULL;
//
// search for the entry
//
for (pfl = phead->Flink; pfl != phead; pfl = pfl->Flink) {
ppterec = CONTAINING_RECORD(pfl, PEER_TABLE_ENTRY, PTE_HTLinkByAddress);
if (ppterec->PTE_Address == dwAddress) { ppte = ppterec; break; }
}
//
// return entry after the one found if in GetNext mode
//
if (dwGetMode == GETMODE_NEXT && ppte != NULL) {
phead = &pTable->PT_ListByAddress;
pfl = &ppte->PTE_LinkByAddress;
//
// return NULL if entry is last one
//
if (pfl->Flink == phead) {
if (pdwErr != NULL) { *pdwErr = ERROR_NO_MORE_ITEMS; }
return NULL;
}
else {
pfl = pfl->Flink;
return CONTAINING_RECORD(pfl, PEER_TABLE_ENTRY, PTE_LinkByAddress);
}
}
//
// if the peer wasn't found, this will still be NULL
//
return ppte;
}
//----------------------------------------------------------------------------
// Function: InsertPeerByAddress
//
// inserts the given entry into the list of peers sorted by address
// assumes the table is locked for writing
//----------------------------------------------------------------------------
DWORD
InsertPeerByAddress(
PPEER_TABLE pTable,
PPEER_TABLE_ENTRY pPTE
) {
INT cmp;
PPEER_TABLE_ENTRY ppte;
DWORD dwAddress, dwPTEAddress;
PLIST_ENTRY phead, pfl;
dwAddress = pPTE->PTE_Address;
phead = &pTable->PT_ListByAddress;
//
// search for the peer entry
//
for (pfl = phead->Flink; pfl != phead; pfl = pfl->Flink) {
ppte = CONTAINING_RECORD(pfl, PEER_TABLE_ENTRY, PTE_LinkByAddress);
dwPTEAddress = ppte->PTE_Address;
if (INET_CMP(dwAddress, dwPTEAddress, cmp) < 0) { break; }
else if (cmp == 0) { return ERROR_ALREADY_EXISTS; }
}
InsertTailList(pfl, &pPTE->PTE_LinkByAddress);
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: CreateRouteTable
//
// Initializes a route table. Note that no synchronization is provided.
//----------------------------------------------------------------------------
DWORD
CreateRouteTable(
PROUTE_TABLE pTable
) {
PLIST_ENTRY plstart, plend, ple;
//
// initialize the hash table buckets
//
plstart = pTable->RT_HashTableByNetwork;
plend = plstart + ROUTE_HASHTABLE_SIZE;
for (ple = plstart; ple < plend; ple++) {
InitializeListHead(ple);
}
pTable->RT_Created = 0x12345678;
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: DeleteRouteTable
//
// Removes all entries from a route table and frees resources used.
//----------------------------------------------------------------------------
DWORD
DeleteRouteTable(
PROUTE_TABLE pTable
) {
PROUTE_TABLE_ENTRY prte;
PLIST_ENTRY ple, plend, phead;
//
// empty the hash-table buckets
//
plend = pTable->RT_HashTableByNetwork + ROUTE_HASHTABLE_SIZE;
for (ple = plend - ROUTE_HASHTABLE_SIZE; ple < plend; ple++) {
while (!IsListEmpty(ple)) {
phead = RemoveHeadList(ple);
prte = CONTAINING_RECORD(phead, ROUTE_TABLE_ENTRY, RTE_Link);
RIP_FREE(prte);
}
}
pTable->RT_Created = 0;
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: WriteSummaryRoutes
//
// Writes to RTM all entries which are marked as summary routes.
//----------------------------------------------------------------------------
DWORD
WriteSummaryRoutes(
PROUTE_TABLE pTable,
HANDLE hRtmHandle
) {
DWORD dwFlags, dwErr;
PRIP_IP_ROUTE prir;
PROUTE_TABLE_ENTRY prte;
PLIST_ENTRY ple, phead, plstart, plend;
BOOL bRelDest = FALSE, bRelRoute = FALSE;
RTM_NET_ADDRESS rna;
RTM_DEST_INFO rdi;
PRTM_ROUTE_INFO prri;
CHAR szNetwork[20], szNetmask[20];
//
// allocate route info structure
//
prri = RIP_ALLOC(
RTM_SIZE_OF_ROUTE_INFO( ig.IG_RtmProfile.MaxNextHopsInRoute )
);
if (prri == NULL)
{
dwErr = GetLastError();
TRACE2(
ANY, "error %d allocated %d bytes in WriteSummaryRoutes",
dwErr, RTM_SIZE_OF_ROUTE_INFO(ig.IG_RtmProfile.MaxNextHopsInRoute)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
return dwErr;
}
//
// go through each bucket writing routes
//
plstart = pTable->RT_HashTableByNetwork;
plend = plstart + ROUTE_HASHTABLE_SIZE;
for (phead = plstart; phead < plend; phead++) {
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
prte = CONTAINING_RECORD(ple, ROUTE_TABLE_ENTRY, RTE_Link);
prir = &prte->RTE_Route;
bRelDest = bRelRoute = FALSE;
do {
//
// if a valid route exists do not overwrite it with
// a summary route
//
RTM_IPV4_SET_ADDR_AND_MASK(
&rna, prir-> RR_Network.N_NetNumber,
prir-> RR_Network.N_NetMask
);
dwErr = RtmGetExactMatchDestination(
hRtmHandle, &rna, RTM_BEST_PROTOCOL,
RTM_VIEW_MASK_UCAST, &rdi
);
if (dwErr == NO_ERROR)
{
bRelDest = TRUE;
//
// Get info for the best route to this destination
//
dwErr = RtmGetRouteInfo(
hRtmHandle, rdi.ViewInfo[0].Route,
prri, NULL
);
if (dwErr != NO_ERROR)
{
TRACE1(
ANY, "error %d getting route info in"
"WriteSummaryRoutes", dwErr
);
break;
}
bRelRoute = TRUE;
//
// Check if this route is active. If it is skip
// adding an inactive summary route
//
if (!(prri-> Flags & RTM_ROUTE_FLAGS_INACTIVE)) {
lstrcpy(szNetwork, INET_NTOA(prir-> RR_Network.N_NetNumber));
lstrcpy(szNetmask, INET_NTOA(prir-> RR_Network.N_NetMask));
TRACE2(
ROUTE,
"Route %s %s not overwritten in summary route addition",
szNetwork, szNetmask
);
break;
}
}
//
// you reach here only if you don't have an active
// route to the summary route's destination
//
//
// if this is a summary entry (i.e. is a RIP route
// with the summary entry set)
//
if (prir->RR_RoutingProtocol == PROTO_IP_RIP &&
GETROUTEFLAG(prir) == ROUTEFLAG_SUMMARY) {
LPSTR lpszAddr;
COMPUTE_ROUTE_METRIC(prir);
dwErr = AddRtmRoute(
hRtmHandle, prir, NULL, prte->RTE_TTL,
prte->RTE_HoldTTL, FALSE
);
lpszAddr = INET_NTOA(prir-> RR_Network.N_NetNumber);
if (lpszAddr != NULL) {
lstrcpy(szNetwork, lpszAddr );
lpszAddr = INET_NTOA(prir-> RR_Network.N_NetMask);
if (lpszAddr != NULL) {
lstrcpy(szNetmask, INET_NTOA(prir-> RR_Network.N_NetMask));
#if ROUTE_DBG
TRACE2(
ROUTE, "Adding summary route %s %s", szNetwork,
szNetmask
);
#endif
if (dwErr != NO_ERROR) {
LPSTR lpszNexthop =
INET_NTOA(prir->RR_NextHopAddress.N_NetNumber);
if (lpszNexthop != NULL) {
TRACE4(
ROUTE,
"error %d writing summary route to %s:%s via %s",
dwErr, szNetwork, szNetmask, lpszNexthop
);
LOGWARN2(
ADD_ROUTE_FAILED_1, szNetwork, lpszNexthop, dwErr
);
}
}
}
}
}
} while (FALSE);
if (dwErr != NO_ERROR) {
//
// in case one of the INET_NTOA statements failed above, just
// trace the fact that there was an error
//
TRACE1(
ROUTE,
"error %d writing summary route",
dwErr
);
}
//
// release handles as required
//
if (bRelRoute) {
dwErr = RtmReleaseRouteInfo(hRtmHandle, prri);
if (dwErr != NO_ERROR) {
TRACE1(
ANY, "error %d releasing route info in"
" WriteSummaryRoutes", dwErr
);
}
}
if (bRelDest) {
dwErr = RtmReleaseDestInfo(hRtmHandle, &rdi);
if (dwErr != NO_ERROR) {
TRACE1(
ANY, "error %d releasing route info in"
" WriteSummaryRoutes", dwErr
);
}
}
}
}
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: CreateRouteEntry
//
// Makes an entry in the route table for the given route.
//----------------------------------------------------------------------------
DWORD
CreateRouteEntry(
PROUTE_TABLE pTable,
PRIP_IP_ROUTE pRoute,
DWORD dwTTL,
DWORD dwHoldTTL
) {
DWORD dwErr;
PLIST_ENTRY ple;
PROUTE_TABLE_ENTRY prte;
//
// see if the entry exists first
//
if ((prte = GetRouteByRoute(pTable, pRoute)) != NULL) {
//
// just update the metric if the new route has a lower one
//
if (GETROUTEMETRIC(&prte->RTE_Route) > GETROUTEMETRIC(pRoute)) {
SETROUTEMETRIC(&prte->RTE_Route, GETROUTEMETRIC(pRoute));
}
return NO_ERROR;
}
//
// allocate space for the new route
//
prte = RIP_ALLOC(sizeof(ROUTE_TABLE_ENTRY));
if (prte == NULL) {
dwErr = GetLastError();
TRACE2(
ANY, "error %d allocating %d bytes for route table entry",
dwErr, sizeof(ROUTE_TABLE_ENTRY)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
return dwErr;
}
//
// initialize the entry's fields and copy the actual route structure
//
prte->RTE_TTL = dwTTL;
prte->RTE_HoldTTL = dwHoldTTL;
CopyMemory(&prte->RTE_Route, pRoute, sizeof(RIP_IP_ROUTE));
//
// insert the route in the hash table
//
ple = pTable->RT_HashTableByNetwork +
ROUTE_HASHVALUE(pRoute->RR_Network.N_NetNumber);
InsertHeadList(ple, &prte->RTE_Link);
#if ROUTE_DBG
{
LPSTR lpszAddr;
char szNet[20], szMask[20];
lpszAddr = INET_NTOA(pRoute-> RR_Network.N_NetNumber);
if (lpszAddr != NULL) {
lstrcpy(szNet, lpszAddr);
lpszAddr = INET_NTOA(pRoute-> RR_Network.N_NetMask);
if (lpszAddr != NULL) {
lstrcpy(szMask, lpszAddr);
lpszAddr = INET_NTOA(pRoute-> RR_NextHopAddress.N_NetNumber);
if (lpszAddr != NULL) {
TRACE4(
ROUTE, "Creating summary route : Route %s %s via %s"
"on interface %d",
szNet, szMask, lpszAddr, pRoute-> RR_InterfaceID
);
}
}
}
}
#endif
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: DeleteRouteEntry
//
// Remove the entry which matches the given route.
//----------------------------------------------------------------------------
DWORD
DeleteRouteEntry(
PROUTE_TABLE pTable,
PRIP_IP_ROUTE pRoute
) {
PROUTE_TABLE_ENTRY prte;
//
// find the route to be deleted
//
prte = GetRouteByRoute(pTable, pRoute);
if (prte == NULL) { return ERROR_INVALID_PARAMETER; }
//
// remove it from the hash table and free the memory it used
//
RemoveEntryList(&prte->RTE_Link);
RIP_FREE(prte);
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: GetRouteByRoute
//
// Searches for the route entry which matches the given route, if any,
// and returns a pointer to it if it is found.
//----------------------------------------------------------------------------
PROUTE_TABLE_ENTRY
GetRouteByRoute(
PROUTE_TABLE pTable,
PRIP_IP_ROUTE pRoute
) {
DWORD dwNetNumber;
PLIST_ENTRY phead, pfl;
PROUTE_TABLE_ENTRY prte, prterec;
//
// get the net number to be found and find the corresponding bucket
//
prte = NULL;
dwNetNumber = pRoute->RR_Network.N_NetNumber;
phead = pTable->RT_HashTableByNetwork + ROUTE_HASHVALUE(dwNetNumber);
//
// search the bucket for the route
//
for (pfl = phead->Flink; pfl != phead; pfl = pfl->Flink) {
prterec = CONTAINING_RECORD(pfl, ROUTE_TABLE_ENTRY, RTE_Link);
if (prterec->RTE_Route.RR_Network.N_NetNumber == dwNetNumber) {
prte = prterec; break;
}
}
//
// if the route wasn't found, this will still be NULL
//
return prte;
}
//----------------------------------------------------------------------------
// Function: CreateBindingTable
//
// Initializes a table of bindings.
//----------------------------------------------------------------------------
DWORD
CreateBindingTable(
PBINDING_TABLE pTable
) {
DWORD dwErr;
PLIST_ENTRY plend, ple;
//
// initialize the hash table of bindings
//
plend = pTable->BT_HashTableByNetwork + BINDING_HASHTABLE_SIZE;
for (ple = plend - BINDING_HASHTABLE_SIZE; ple < plend; ple++) {
InitializeListHead(ple);
}
//
// initialize the table's synchronization object
//
dwErr = CreateReadWriteLock(&pTable->BT_RWL);
if (dwErr == NO_ERROR) {
pTable->BT_Created = 0x12345678;
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: DeleteBindingTable
//
// Cleans up resources used by a binding table.
//----------------------------------------------------------------------------
DWORD
DeleteBindingTable(
PBINDING_TABLE pTable
) {
PBINDING_TABLE_ENTRY pbte;
PLIST_ENTRY plend, ple, phead;
//
// destroy the synchronization object
//
DeleteReadWriteLock(&pTable->BT_RWL);
//
// empty the hash-table buckets
//
plend = pTable->BT_HashTableByNetwork + BINDING_HASHTABLE_SIZE;
for (ple = plend - BINDING_HASHTABLE_SIZE; ple < plend; ple++) {
while (!IsListEmpty(ple)) {
phead = RemoveHeadList(ple);
pbte = CONTAINING_RECORD(phead, BINDING_TABLE_ENTRY, BTE_Link);
RIP_FREE(pbte);
}
}
pTable->BT_Created = 0;
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: CreateBindingEntry
//
// Adds a binding to the table.
// assumes the binding table is locked for writing
//----------------------------------------------------------------------------
DWORD
CreateBindingEntry(
PBINDING_TABLE pTable,
PIPRIP_IF_BINDING pib
) {
INT cmp;
PLIST_ENTRY ple, phead;
PIPRIP_IP_ADDRESS paddr;
PBINDING_TABLE_ENTRY pbte;
DWORD i, dwErr, dwAddress, dwNetmask, dwNetwork;
//
// go through the IP addresses in the interface binding,
// adding each to the binding table
//
paddr = IPRIP_IF_ADDRESS_TABLE(pib);
for (i = 0; i < pib->IB_AddrCount; i++, paddr++) {
dwAddress = paddr->IA_Address;
dwNetmask = paddr->IA_Netmask;
//
// compute the network part of the binding
//
dwNetwork = (dwAddress & NETCLASS_MASK(dwAddress));
//
// get the hash bucket to which the binding belongs,
// and find the insertion point in the bucket
//
phead = pTable->BT_HashTableByNetwork + BINDING_HASHVALUE(dwNetwork);
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
pbte = CONTAINING_RECORD(ple, BINDING_TABLE_ENTRY, BTE_Link);
INET_CMP(dwNetwork, pbte->BTE_Network, cmp);
if (cmp < 0) { break; }
else
if (cmp > 0) { continue; }
//
// the network parts are equal; further compare
// against the IP address fields
//
INET_CMP(dwAddress, pbte->BTE_Address, cmp);
if (cmp < 0) { break; }
else
if (cmp > 0) { continue; }
//
// the addresses are also equal; return an error
//
return ERROR_ALREADY_EXISTS;
}
//
// we now have the insertion point, so create the new item
//
pbte = RIP_ALLOC(sizeof(BINDING_TABLE_ENTRY));
if (pbte == NULL) {
dwErr = GetLastError();
TRACE2(
IF, "error %d allocating %d bytes for binding entry",
dwErr, sizeof(BINDING_TABLE_ENTRY)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
return dwErr;
}
pbte->BTE_Address = dwAddress;
pbte->BTE_Network = dwNetwork;
pbte->BTE_Netmask = dwNetmask;
//
// insert the entry
//
InsertTailList(ple, &pbte->BTE_Link);
}
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: DeleteBindingEntry
//
// Removes a binding from the table.
// assumes the binding table is locked for writing
//----------------------------------------------------------------------------
DWORD
DeleteBindingEntry(
PBINDING_TABLE pTable,
PIPRIP_IF_BINDING pib
) {
PLIST_ENTRY ple, phead;
PIPRIP_IP_ADDRESS paddr;
PBINDING_TABLE_ENTRY pbte;
DWORD i, dwNetwork, dwAddress, dwNetmask;
paddr = IPRIP_IF_ADDRESS_TABLE(pib);
for (i = 0; i < pib->IB_AddrCount; i++, paddr++) {
dwAddress = paddr->IA_Address;
dwNetmask = paddr->IA_Netmask;
//
// get the hash bucket to be searched
//
dwNetwork = (dwAddress & NETCLASS_MASK(dwAddress));
phead = pTable->BT_HashTableByNetwork + BINDING_HASHVALUE(dwNetwork);
//
// search the bucket for the binding specified
//
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
pbte = CONTAINING_RECORD(ple, BINDING_TABLE_ENTRY, BTE_Link);
if (dwAddress != pbte->BTE_Address ||
dwNetmask != pbte->BTE_Netmask) {
continue;
}
//
// the entry to be deleted has been found;
// remove it from the list and free its memory
//
RemoveEntryList(&pbte->BTE_Link);
RIP_FREE(pbte);
break;
}
}
return NO_ERROR;
}
//---------------------------------------------------------------------------
// Function: GuessSubnetMask
//
// This function attempts to deduce the subnet mask of an IP address
// based on the configured addresses and masks on the local host.
// assumes the binding table is locked for reading or writing
//---------------------------------------------------------------------------
DWORD
GuessSubnetMask(
DWORD dwAddress,
PDWORD pdwNetclassMask
) {
INT cmp;
PLIST_ENTRY ple, phead;
PBINDING_TABLE_ENTRY pbte;
DWORD dwNetwork, dwNetmask, dwGuessMask;
//
// the mask for a default route (0.0.0.0) is zero
//
if (dwAddress == 0) {
if (pdwNetclassMask != NULL) { *pdwNetclassMask = 0; }
return 0;
}
//
// the mask for the broadcast route is all-ones (255.255.255.255)
//
if (dwAddress == INADDR_BROADCAST) {
if (pdwNetclassMask != NULL) { *pdwNetclassMask = INADDR_BROADCAST; }
return INADDR_BROADCAST;
}
//
// otherwise, we start with the network-class mask
//
dwGuessMask = dwNetmask = NETCLASS_MASK(dwAddress);
if (pdwNetclassMask != NULL) { *pdwNetclassMask = dwNetmask; }
//
// if the route is a network route, we're done
//
if ((dwAddress & ~dwNetmask) == 0) { return dwNetmask; }
//
// otherwise, search through the bindings table
// to see if one is on the same network as this address
//
dwNetwork = (dwAddress & dwNetmask);
phead = ig.IG_BindingTable->BT_HashTableByNetwork +
BINDING_HASHVALUE(dwNetwork);
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
pbte = CONTAINING_RECORD(ple, BINDING_TABLE_ENTRY, BTE_Link);
INET_CMP(dwNetwork, pbte->BTE_Network, cmp);
if (cmp < 0) { break; }
else
if (cmp > 0) { continue; }
//
// this entry is on the same network as the address passed in
// so see if the entry's netmask matches the address;
// if it does, we're done; otherwise, save this mask
// as a guess, and keep looking.
// note that this exhaustive search is the only way we can
// reliably guess masks for supernets
//
if ((dwAddress & pbte->BTE_Netmask) ==
(pbte->BTE_Address & pbte->BTE_Netmask)) {
return pbte->BTE_Netmask;
}
dwGuessMask = pbte->BTE_Netmask;
}
//
// return whatever has been our best guess so far
//
return dwGuessMask;
}
DWORD
AddRtmRoute(
RTM_ENTITY_HANDLE hRtmHandle,
PRIP_IP_ROUTE prir,
RTM_NEXTHOP_HANDLE hNextHop OPTIONAL,
DWORD dwTimeOut,
DWORD dwHoldTime,
BOOL bActive
)
/*++
Routine Description :
This function adds a route to the RTMv2 database. In addition it
creates the nexthop if one is not specified (via hNextHop), based
on the next hop i/f and address specified in the RIP route.
Parameters :
hRtmHandle - Entity registration handle
prir - RIP route to be added
hNextHop - Handle to the next hop to be used for the route
dwTimeout - Route timeout interval
dwHoldTime - Route holddown interval (after delete)
bActive - TRUE if the route being added is an active route,
FALSE otherwise (in RIP's case for summary routes)
Return Value :
NO_ERROR - Success
Rtm error code - Otherwise
Environment :
Invoked from ProcessRouteEntry and WriteSummaryRoutes
--*/
{
BOOL bRelDest = FALSE;
DWORD dwErr, dwChangeFlags = 0;
RTM_DEST_INFO rdi;
RTM_NEXTHOP_INFO rni;
RTM_ROUTE_INFO rri;
RTM_NET_ADDRESS rna;
CHAR szNetwork[20], szNetmask[20], szNextHop[20], szNextHopmask[20];
do {
//
// char strings used to print IP address/mask info
// Used in error cases only
//
lstrcpy(szNetwork, INET_NTOA(prir-> RR_Network.N_NetNumber));
lstrcpy(szNetmask, INET_NTOA(prir-> RR_Network.N_NetMask));
lstrcpy(szNextHop, INET_NTOA(prir-> RR_NextHopAddress.N_NetNumber));
lstrcpy(szNextHopmask, INET_NTOA(prir-> RR_NextHopAddress.N_NetMask));
//
// Zero out the next hop and route memory
//
ZeroMemory(&rni, sizeof(RTM_NEXTHOP_INFO));
ZeroMemory(&rri, sizeof(RTM_ROUTE_INFO));
if (hNextHop == NULL) {
//
// Find next hop.
//
rni.InterfaceIndex = prir-> RR_InterfaceID;
RTM_IPV4_SET_ADDR_AND_MASK(
&rni.NextHopAddress, prir-> RR_NextHopAddress.N_NetNumber,
IPV4_SOURCE_MASK
);
//
// Save the nexthop mask in the entity specific info
//
*((PDWORD)&rni.EntitySpecificInfo) = prir-> RR_NextHopAddress.N_NetMask;
rni.NextHopOwner = hRtmHandle;
dwErr = RtmFindNextHop(hRtmHandle, &rni, &hNextHop, NULL);
if (dwErr == ERROR_NOT_FOUND) {
//
// Next hop not found. Create one
//
dwErr = RtmAddNextHop(
hRtmHandle, &rni, &hNextHop, &dwChangeFlags
);
if (dwErr != NO_ERROR) {
TRACE3(
ROUTE, "error %d creating next hop %s %s",
dwErr, szNextHop, szNextHopmask
);
break;
}
}
else if (dwErr != NO_ERROR) {
TRACE3(
ANY, "error %d finding next hop %s %s", dwErr,
szNextHop, szNextHopmask
);
break;
}
}
//
// Build route info structure
//
RTM_IPV4_SET_ADDR_AND_MASK(
&rna, prir-> RR_Network.N_NetNumber, prir-> RR_Network.N_NetMask
);
rri.PrefInfo.Metric = prir-> RR_FamilySpecificData.FSD_Metric1;
rri.BelongsToViews = RTM_VIEW_MASK_UCAST | RTM_VIEW_MASK_MCAST;
//
// set entity specific info
//
SETRIPTAG(&rri, GETROUTETAG(prir));
SETRIPFLAG(&rri, GETROUTEFLAG(prir));
//
// Set next hop info
//
rri.NextHopsList.NumNextHops = 1;
rri.NextHopsList.NextHops[0] = hNextHop;
rri.Neighbour = hNextHop;
//
// Call into router manager to set preference info
//
ig.IG_SupportFunctions.ValidateRoute(PROTO_IP_RIP, &rri, &rna);
//
// if this is an inactive route,
// - set route flag to inactive.
// - set the views for route to none
//
if ( !bActive ) {
rri.Flags1 = 0;
rri.Flags = RTM_ROUTE_FLAGS_INACTIVE;
rri.BelongsToViews = 0;
}
//
// Add route to dest, convert timeout to milliseconds
//
dwChangeFlags = RTM_ROUTE_CHANGE_FIRST;
dwErr = RtmAddRouteToDest(
hRtmHandle, NULL, &rna, &rri, dwTimeOut * 1000, NULL,
0, NULL, &dwChangeFlags
);
if ( dwErr != NO_ERROR ) {
TRACE4(
ANY, "error %d adding route %s %s via %s",
dwErr, szNetwork, szNetmask, szNextHop
);
break;
}
if ( bActive )
{
//
// Hold destination if this is an active route
//
dwErr = RtmGetExactMatchDestination(
hRtmHandle, &rna, RTM_BEST_PROTOCOL,
RTM_VIEW_MASK_UCAST, &rdi
);
if ( dwErr != NO_ERROR ) {
TRACE3(
ANY, "error %d getting just added destination %s:%s",
dwErr, szNetwork, szNetmask
);
break;
}
bRelDest = TRUE;
dwErr = RtmHoldDestination(
hRtmHandle, rdi.DestHandle, RTM_VIEW_MASK_UCAST,
dwHoldTime * 1000
);
if ( dwErr != NO_ERROR ) {
TRACE3(
ANY, "error %d failed to hold destination %s %s",
dwErr, szNetwork, szNetmask
);
break;
}
}
} while(FALSE);
//
// release acquired handles
//
if ( bRelDest ) {
dwErr = RtmReleaseDestInfo( hRtmHandle, &rdi );
if ( dwErr != NO_ERROR ) {
TRACE3(
ANY, "error %d failed to relase just added destination %s %s",
dwErr, szNetwork, szNetmask
);
}
}
return dwErr;
}
DWORD
GetRouteInfo(
IN RTM_ROUTE_HANDLE hRoute,
IN PRTM_ROUTE_INFO pInRouteInfo OPTIONAL,
IN PRTM_DEST_INFO pInDestInfo OPTIONAL,
OUT PRIP_IP_ROUTE pRoute
)
/*++
Routine Description:
Wrapper for filling out the OSPF_RTMv2_ROUTE by retrieving various
RTM infos.
Arguments:
hRoute
pInRouteInfo
pInDestInfo
pRoute
Return Value:
RTM error code
--*/
{
DWORD dwErr;
RTM_ROUTE_INFO RouteInfo, *pRouteInfo;
RTM_ENTITY_INFO EntityInfo, *pEntityInfo;
RTM_DEST_INFO DestInfo, *pDestInfo;
RTM_NEXTHOP_INFO NextHopInfo, *pNextHopInfo;
pRouteInfo = NULL;
pEntityInfo = NULL;
pDestInfo = NULL;
pNextHopInfo = NULL;
do
{
ZeroMemory(pRoute, sizeof(RIP_IP_ROUTE));
//
// If the user hasnt already given us the route info, get it
//
if ( pInRouteInfo == NULL )
{
dwErr = RtmGetRouteInfo(
ig.IG_RtmHandle, hRoute, &RouteInfo, NULL
);
if ( dwErr != NO_ERROR )
{
TRACE1(
ANY, "GetRouteInfo: Error %d from RtmGetRouteInfo\n", dwErr
);
break;
}
pRouteInfo = &RouteInfo;
}
else
{
pRouteInfo = pInRouteInfo;
}
//
// If the user hasnt given us the dest info, get it
//
if ( pInDestInfo == NULL )
{
dwErr = RtmGetDestInfo(
ig.IG_RtmHandle, pRouteInfo->DestHandle,
0, RTM_VIEW_MASK_UCAST, &DestInfo
);
if ( dwErr != NO_ERROR )
{
TRACE1(
ANY, "GetRouteInfo: Error %d from RtmGetDestInfo\n", dwErr
);
break;
}
pDestInfo = &DestInfo;
}
else
{
pDestInfo = pInDestInfo;
}
//
// Get owner info if the protocol is not us
//
if ( pRouteInfo-> RouteOwner != ig.IG_RtmHandle )
{
dwErr = RtmGetEntityInfo(
ig.IG_RtmHandle, pRouteInfo->RouteOwner, &EntityInfo
);
if ( dwErr != NO_ERROR )
{
TRACE1(
ANY, "GetRouteInfo: Error %d from RtmGetEntityInfo\n",
dwErr
);
break;
}
pEntityInfo = &EntityInfo;
}
//
// Get the info about the first next hop
//
dwErr = RtmGetNextHopInfo(
ig.IG_RtmHandle,
pRouteInfo->NextHopsList.NextHops[0],
&NextHopInfo
);
if ( dwErr != NO_ERROR )
{
TRACE1(
ANY, "GetRouteInfo: Error %d from RtmGetEntityInfo\n",
dwErr
);
break;
}
pNextHopInfo = &NextHopInfo;
//
// Now copy out all the info.
// First, the route info
//
pRoute-> RR_FamilySpecificData.FSD_Metric1 =
pRoute-> RR_FamilySpecificData.FSD_Metric =
pRouteInfo-> PrefInfo.Metric;
//
// copy out the protocol id from the entity info
//
if ( pEntityInfo != NULL )
{
pRoute-> RR_RoutingProtocol = pEntityInfo->EntityId.EntityProtocolId;
}
else
{
//
// this is a RIP route
//
pRoute-> RR_RoutingProtocol = PROTO_IP_RIP;
SETROUTEFLAG(pRoute, GETRIPFLAG(pRouteInfo));
SETROUTETAG(pRoute, GETRIPTAG(pRouteInfo));
}
//
// Copy out the dest info
//
RTM_IPV4_GET_ADDR_AND_MASK(
pRoute->RR_Network.N_NetNumber,
pRoute->RR_Network.N_NetMask,
&(pDestInfo->DestAddress)
);
pRoute-> hDest = pDestInfo-> DestHandle;
//
// Copy out the next hop info
//
RTM_IPV4_GET_ADDR_AND_MASK(
pRoute->RR_NextHopAddress.N_NetNumber,
pRoute->RR_NextHopAddress.N_NetMask,
&(pNextHopInfo->NextHopAddress)
);
//
// retrive saved next hop mask
//
pRoute-> RR_NextHopAddress.N_NetMask =
*((PDWORD)&pNextHopInfo-> EntitySpecificInfo);
pRoute-> RR_InterfaceID = pNextHopInfo->InterfaceIndex;
#if 0
{
char szNet[20], szMask[20], szNextHop[20], szNextHopMask[20];
lstrcpy(szNet, INET_NTOA(pRoute-> RR_Network.N_NetNumber));
lstrcpy(szMask, INET_NTOA(pRoute-> RR_Network.N_NetMask));
lstrcpy(szNextHop, INET_NTOA(pRoute-> RR_NextHopAddress.N_NetNumber));
lstrcpy(szNextHopMask, INET_NTOA(pRoute-> RR_NextHopAddress.N_NetMask));
TRACE5(
ROUTE, "GetRouteInfo : Route %s %s via %s %s on interface %d",
szNet, szMask, szNextHop, szNextHopMask,
pRoute-> RR_InterfaceID
);
TRACE3(
ROUTE, "Has metric %d, flag %x, tag %d",
GETROUTEMETRIC(pRoute), GETROUTEFLAG(pRoute),
GETROUTETAG(pRoute)
);
TRACE2(
ROUTE, "Protocol %d, original flag %d",
pRoute-> RR_RoutingProtocol, GETRIPFLAG(pRouteInfo)
);
}
#endif
} while( FALSE );
//
// Release the relevant infos
//
if ( pNextHopInfo != NULL )
{
RtmReleaseNextHopInfo( ig.IG_RtmHandle, pNextHopInfo );
}
if ( pEntityInfo != NULL )
{
RtmReleaseEntityInfo( ig.IG_RtmHandle, pEntityInfo );
}
//
// Release the route and dest infos only if we were not passed them
// in AND we successfully retrieved them
//
if ( ( pInDestInfo == NULL ) && ( pDestInfo != NULL ) )
{
RtmReleaseDestInfo( ig.IG_RtmHandle, pDestInfo );
}
if( ( pInRouteInfo == NULL ) && ( pRouteInfo != NULL ) )
{
RtmReleaseRouteInfo( ig.IG_RtmHandle, pRouteInfo );
}
return NO_ERROR;
}