windows-nt/Source/XPSP1/NT/net/rras/ip/rip/work.c
2020-09-26 16:20:57 +08:00

7473 lines
193 KiB
C

//============================================================================
// Copyright (c) 1995, Microsoft Corporation
//
// File: work.c
//
// History:
// Abolade Gbadegesin Aug-8-1995 Created.
//
// worker function implementations
//============================================================================
#include "pchrip.h"
#pragma hdrstop
VOID
ProcessSocket(
DWORD dwAddrIndex,
PIF_TABLE_ENTRY pite,
PIF_TABLE pTable
);
VOID
EnqueueStartFullUpdate(
PIF_TABLE_ENTRY pite,
LARGE_INTEGER qwLastFullUpdateTime
);
DWORD
EnqueueDemandUpdateCheck(
PUPDATE_CONTEXT pwc
);
VOID
EnqueueDemandUpdateMessage(
DWORD dwInterfaceIndex,
DWORD dwError
);
DWORD
CountInterfaceRoutes(
DWORD dwInterfaceIndex
);
BOOL
ProcessResponseEntry(
PIF_TABLE_ENTRY pITE,
DWORD dwAddrIndex,
DWORD dwSource,
PIPRIP_ENTRY pIE,
PIPRIP_PEER_STATS pPS
);
DWORD
SendRouteOnIfList(
UPDATE_BUFFER pBufList[],
DWORD dwBufCount,
DWORD dwSendMode,
PROUTE_TABLE pSummaryTable,
PRIP_IP_ROUTE pRoute
);
//----------------------------------------------------------------------------
// Macro: RTM_ROUTE_FROM_IPRIP_ENTRY
// Macro: IPRIP_ENTRY_FROM_RTM_ROUTE
//
// These two macros are used to transfer data from an RTM route struct
// to an IPRIPv2 packet route entry, and vice versa.
// The first two bytes of an RTM route's ProtocolSpecificData array are used
// to store the route tag contained in the IPRIP packet route-entry
//----------------------------------------------------------------------------
#define RTM_ROUTE_FROM_IPRIP_ENTRY(r,i) \
(r)->RR_RoutingProtocol = PROTO_IP_RIP; \
SETROUTEMETRIC((r), ntohl((i)->IE_Metric)); \
(r)->RR_Network.N_NetNumber = (i)->IE_Destination; \
(r)->RR_Network.N_NetMask = (i)->IE_SubnetMask; \
(r)->RR_NextHopAddress.N_NetNumber = (i)->IE_Nexthop; \
(r)->RR_NextHopAddress.N_NetMask = (i)->IE_SubnetMask; \
SETROUTETAG((r), ntohs((i)->IE_RouteTag))
#define IPRIP_ENTRY_FROM_RTM_ROUTE(i,r) \
(i)->IE_AddrFamily = htons(AF_INET); \
(i)->IE_Metric = htonl(GETROUTEMETRIC(r)); \
(i)->IE_Destination = (r)->RR_Network.N_NetNumber; \
(i)->IE_SubnetMask = (r)->RR_Network.N_NetMask; \
(i)->IE_Nexthop = (r)->RR_NextHopAddress.N_NetNumber
//----------------------------------------------------------------------------
// Macro: IS_ROUTE_IN_ACCEPT_FILTER
// Macro: IS_ROUTE_IN_ANNOUNCE_FILTER
//
// The following three macros are used to search for a route
// in the accept filters and announce filters configured for an interface
// The last two macros invoke the first macro which executes the inner loop,
// since the inner loop is identical in both cases.
//----------------------------------------------------------------------------
#define IS_ROUTE_IN_FILTER(route,ret) \
(ret) = 0; \
for ( ; _pfilt < _pfiltend; _pfilt++) { \
_filt = _pfilt->RF_LoAddress; \
if (INET_CMP(route, _filt, _cmp) == 0) { (ret) = 1; break; } \
else if (_cmp > 0) { \
_filt = _pfilt->RF_HiAddress; \
if (INET_CMP(route, _filt, _cmp) <= 0) { (ret) = 1; break; }\
} \
}
#define IS_ROUTE_IN_ACCEPT_FILTER(ic,route,ret) { \
INT _cmp; \
DWORD _filt; \
PIPRIP_ROUTE_FILTER _pfilt, _pfiltend; \
_pfilt = IPRIP_IF_ACCEPT_FILTER_TABLE(ic); \
_pfiltend = _pfilt + (ic)->IC_AcceptFilterCount; \
IS_ROUTE_IN_FILTER(route,ret); \
}
#define IS_ROUTE_IN_ANNOUNCE_FILTER(ic,route,ret) { \
INT _cmp; \
DWORD _filt; \
PIPRIP_ROUTE_FILTER _pfilt, _pfiltend; \
_pfilt = IPRIP_IF_ANNOUNCE_FILTER_TABLE(ic); \
_pfiltend = _pfilt + (ic)->IC_AnnounceFilterCount; \
IS_ROUTE_IN_FILTER(route,ret); \
}
//----------------------------------------------------------------------------
// Macro: IS_PEER_IN_FILTER
//
// macro used to search the peer filters
//----------------------------------------------------------------------------
#define IS_PEER_IN_FILTER(gc,peer,ret) { \
PDWORD _pdwPeer, _pdwPeerEnd; \
(ret) = 0; \
_pdwPeer = IPRIP_GLOBAL_PEER_FILTER_TABLE(gc); \
_pdwPeerEnd = _pdwPeer + (gc)->GC_PeerFilterCount; \
for ( ; _pdwPeer < _pdwPeerEnd; _pdwPeer++) { \
if (*_pdwPeer == (peer)) { (ret) = 1; break; } \
} \
}
//----------------------------------------------------------------------------
// UPDATE BUFFER MANAGEMENT
//
// The following types and functions are used to simplify
// the transmission of routes. The system consists of the struct
// UPDATE_BUFFER, which includes a function table and a byte buffer,
// and a number of three-function update buffer routine sets.
// The sets each contain a routine to start an update buffer,
// to add a route to an update buffer, and to finish an update buffer.
//
// There are separate versions for RIPv1 mode and RIPv2 mode. The function
// InitializeUpdateBuffer sets up the function table in an update buffer
// depending on the configuration for the interface with which the buffer
// is associated. This set-up eliminates the need to check the interface
// configuration every time an entry must be added; instead, the config
// is checked a single time to set up the function table, and afterward
// the function generating the update merely calls the functions in the table.
//
// The setup also depends on the mode in which the information is being sent.
// The address to which the information is being sent is stored in the
// update buffer, since this will be required every time a route is added.
// However, when a full-update is being generated on an interface operating
// in RIPv2 mode, the destination address stored is 224.0.0.9, but the
// actual destination network is the network of the out-going interface.
// Therefore, this address is also stored since it will be needed for
// split-horizon/poison-reverse/subnet-summary processing
//----------------------------------------------------------------------------
//
// these are the modes in which routes may be transmitted
//
#define SENDMODE_FULL_UPDATE 0
#define SENDMODE_TRIGGERED_UPDATE 1
#define SENDMODE_SHUTDOWN_UPDATE 2
#define SENDMODE_GENERAL_REQUEST 3
#define SENDMODE_GENERAL_RESPONSE1 4
#define SENDMODE_GENERAL_RESPONSE2 5
#define SENDMODE_SPECIFIC_RESPONSE1 6
#define SENDMODE_SPECIFIC_RESPONSE2 7
//
// this function set is for interfaces with announcements disabled
//
DWORD
StartBufferNull(
PUPDATE_BUFFER pUB
) { return NO_ERROR; }
DWORD
AddEntryNull(
PUPDATE_BUFFER pUB,
PRIP_IP_ROUTE pRIR
) { return NO_ERROR; }
DWORD
FinishBufferNull(
PUPDATE_BUFFER pUB
) { return NO_ERROR; }
//
// this function-set is for RIPv1 interfaces
//
DWORD
StartBufferVersion1(
PUPDATE_BUFFER pUB
);
DWORD
AddEntryVersion1(
PUPDATE_BUFFER pUB,
PRIP_IP_ROUTE pRIR
);
DWORD
FinishBufferVersion1(
PUPDATE_BUFFER pUB
);
//
// this function-set is for RIPv2 interfaces
//
DWORD
StartBufferVersion2(
PUPDATE_BUFFER pUB
);
DWORD
AddEntryVersion2(
PUPDATE_BUFFER pUB,
PRIP_IP_ROUTE pRIR
);
DWORD
FinishBufferVersion2(
PUPDATE_BUFFER pUB
);
//----------------------------------------------------------------------------
// Function: InitializeUpdateBuffer
//
// this function sets up the update-buffer, writing in the functions to use
// for restarting the buffer, adding entries, and finishing the buffer.
// It also stores the destination address to which the packet is being sent,
// as well as the network and netmask for the destination
// This assumes the binding table is locked.
//----------------------------------------------------------------------------
DWORD
InitializeUpdateBuffer(
PIF_TABLE_ENTRY pITE,
DWORD dwAddrIndex,
PUPDATE_BUFFER pUB,
DWORD dwSendMode,
DWORD dwDestination,
DWORD dwCommand
) {
DWORD dwAnnounceMode;
PIPRIP_IP_ADDRESS paddr;
pUB->UB_Length = 0;
//
// save the pointer to the interface
//
pUB->UB_ITE = pITE;
pUB->UB_AddrIndex = dwAddrIndex;
paddr = IPRIP_IF_ADDRESS_TABLE(pITE->ITE_Binding) + dwAddrIndex;
pUB->UB_Socket = pITE->ITE_Sockets[dwAddrIndex];
pUB->UB_Address = paddr->IA_Address;
pUB->UB_Netmask = paddr->IA_Netmask;
//
// save the command
//
pUB->UB_Command = dwCommand;
//
// store the absolute address to which this packet is destined,
// which may differ from the address passed to sendto()
// e.g. RIPv2 packets are destined for the interface's network,
// but the address passed to sendto() is 224.0.0.9
// if the destination passed in is 0, use the broadcast address
// on the outgoing interface as the destination
//
if (dwDestination == 0) {
if(paddr->IA_Netmask == 0xffffffff)
{
TRACE0(SEND,"MASK ALL ONES");
pUB->UB_DestAddress = (paddr->IA_Address | ~(NETCLASS_MASK(paddr->IA_Address)));
}
else
{
pUB->UB_DestAddress = (paddr->IA_Address | ~paddr->IA_Netmask);
}
pUB->UB_DestNetmask = paddr->IA_Netmask;
}
else {
pUB->UB_DestAddress = dwDestination;
pUB->UB_DestNetmask = GuessSubnetMask(pUB->UB_DestAddress, NULL);
}
//
// decide on the announce mode;
// if the mode is DISABLED, we still send responses to SPECIFIC requests
// on the interface, so set the mode to RIPv1/v2 if sending a specific
// response on a disabled interface
//
dwAnnounceMode = pITE->ITE_Config->IC_AnnounceMode;
if (dwAnnounceMode == IPRIP_ANNOUNCE_DISABLED) {
if (dwSendMode == SENDMODE_SPECIFIC_RESPONSE1) {
dwAnnounceMode = IPRIP_ANNOUNCE_RIP1;
}
else
if (dwSendMode == SENDMODE_SPECIFIC_RESPONSE2) {
dwAnnounceMode = IPRIP_ANNOUNCE_RIP2;
}
}
//
// set up the function table and destination address, which
// depend on the announce-mode of the interface and on the sort
// of information being transmitted
//
switch (dwAnnounceMode) {
//
// in RIP1 mode, packets are RIP1, broadcast
//
case IPRIP_ANNOUNCE_RIP1:
pUB->UB_AddRoutine = AddEntryVersion1;
pUB->UB_StartRoutine = StartBufferVersion1;
pUB->UB_FinishRoutine = FinishBufferVersion1;
pUB->UB_Destination.sin_port = htons(IPRIP_PORT);
pUB->UB_Destination.sin_family = AF_INET;
pUB->UB_Destination.sin_addr.s_addr = pUB->UB_DestAddress;
break;
//
// in RIP1-compatible mode, packets are RIP2, broadcast,
// except in the case of a general response to a RIP1 router,
// in which case the packets are RIP1, unicast
//
case IPRIP_ANNOUNCE_RIP1_COMPAT:
if (dwSendMode == SENDMODE_GENERAL_RESPONSE1) {
pUB->UB_AddRoutine = AddEntryVersion1;
pUB->UB_StartRoutine = StartBufferVersion1;
pUB->UB_FinishRoutine = FinishBufferVersion1;
}
else {
pUB->UB_AddRoutine = AddEntryVersion2;
pUB->UB_StartRoutine = StartBufferVersion2;
pUB->UB_FinishRoutine = FinishBufferVersion2;
}
pUB->UB_Destination.sin_port = htons(IPRIP_PORT);
pUB->UB_Destination.sin_family = AF_INET;
pUB->UB_Destination.sin_addr.s_addr = pUB->UB_DestAddress;
break;
//
// in RIP2 mode, packets are RIP2, multicast, except in the case
// of a general/specific responses, in which cases messages are unicast;
// note that a RIP2-only router never sends a general response to
// a request from a RIP1 router.
//
case IPRIP_ANNOUNCE_RIP2:
pUB->UB_AddRoutine = AddEntryVersion2;
pUB->UB_StartRoutine = StartBufferVersion2;
pUB->UB_FinishRoutine = FinishBufferVersion2;
pUB->UB_Destination.sin_port = htons(IPRIP_PORT);
pUB->UB_Destination.sin_family = AF_INET;
//
// if sending to a specific destination, as a reponse
// to a request or as a full update to a unicast peer,
// set the IP address of the destination.
// Else send to multicast address.
//
if ( dwDestination != 0 ) {
pUB->UB_Destination.sin_addr.s_addr = pUB->UB_DestAddress;
}
else {
pUB->UB_Destination.sin_addr.s_addr = IPRIP_MULTIADDR;
}
break;
default:
TRACE2(
IF, "invalid announce mode on interface %d (%s)",
pITE->ITE_Index, INET_NTOA(paddr->IA_Address)
);
pUB->UB_AddRoutine = AddEntryNull;
pUB->UB_StartRoutine = StartBufferNull;
pUB->UB_FinishRoutine = FinishBufferNull;
return ERROR_INVALID_PARAMETER;
}
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: SendUpdateBuffer
//
// This function is invoked by the add-entry and finsih-buffer functions
// to send the contents of an update-buffer.
//----------------------------------------------------------------------------
DWORD
SendUpdateBuffer(
PUPDATE_BUFFER pbuf
) {
INT iLength;
DWORD dwErr;
TRACE1(SEND,"SENDING TO %s",INET_NTOA(pbuf->UB_Destination.sin_addr.s_addr));
iLength = sendto(
pbuf->UB_Socket, pbuf->UB_Buffer, pbuf->UB_Length, 0,
(PSOCKADDR)&pbuf->UB_Destination, sizeof(SOCKADDR_IN)
);
if (iLength == SOCKET_ERROR || (DWORD)iLength < pbuf->UB_Length) {
//
// an error occurred
//
CHAR szDest[20], *lpszAddr;
dwErr = WSAGetLastError();
lstrcpy(szDest, INET_NTOA(pbuf->UB_Destination.sin_addr));
lpszAddr = INET_NTOA(pbuf->UB_Address);
TRACE4(
SEND, "error %d sending update to %s on interface %d (%s)",
dwErr, szDest, pbuf->UB_ITE->ITE_Index, lpszAddr
);
LOGWARN2(SENDTO_FAILED, lpszAddr, szDest, dwErr);
InterlockedIncrement(&pbuf->UB_ITE->ITE_Stats.IS_SendFailures);
}
else {
if (pbuf->UB_Command == IPRIP_REQUEST) {
InterlockedIncrement(&pbuf->UB_ITE->ITE_Stats.IS_RequestsSent);
}
else {
InterlockedIncrement(&pbuf->UB_ITE->ITE_Stats.IS_ResponsesSent);
}
dwErr = NO_ERROR;
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: StartBufferVersion1
//
// This starts a RIPv1 update-buffer, zeroing reserved fields,
// setting the version, and setting the command field
//----------------------------------------------------------------------------
DWORD
StartBufferVersion1(
PUPDATE_BUFFER pUB
) {
PIPRIP_HEADER pHdr;
//
// set up the header
//
pHdr = (PIPRIP_HEADER)pUB->UB_Buffer;
pHdr->IH_Version = 1;
pHdr->IH_Command = (BYTE)pUB->UB_Command;
pHdr->IH_Reserved = 0;
pUB->UB_Length = sizeof(IPRIP_HEADER);
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: AddEntryVersion1
//
// This adds an entry to a RIPv1 buffer, first sending the buffer if it is full
//----------------------------------------------------------------------------
DWORD
AddEntryVersion1(
PUPDATE_BUFFER pUB,
PRIP_IP_ROUTE pRIR
) {
PIPRIP_ENTRY pie;
//
// if the buffer is full, transmit its contents and restart it
//
if ((pUB->UB_Length + sizeof(IPRIP_ENTRY)) > MAX_PACKET_SIZE) {
SendUpdateBuffer(pUB);
StartBufferVersion1(pUB);
}
//
// point to the end of the buffer
//
pie = (PIPRIP_ENTRY)(pUB->UB_Buffer + pUB->UB_Length);
IPRIP_ENTRY_FROM_RTM_ROUTE(pie, pRIR);
//
// zero out fields which are reserved in RIP1
//
pie->IE_SubnetMask = 0;
pie->IE_RouteTag = 0;
pie->IE_Nexthop = 0;
pUB->UB_Length += sizeof(IPRIP_ENTRY);
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: FinishBufferVersion1
//
// this sends the contents of a RIPv1 buffer, if any
//----------------------------------------------------------------------------
DWORD
FinishBufferVersion1(
PUPDATE_BUFFER pUB
) {
//
// send the buffer if it contains any entries
//
if (pUB->UB_Length > sizeof(IPRIP_HEADER)) {
SendUpdateBuffer(pUB);
}
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: StartBufferVersion2
//
// this starts a RIPv2 buffer
//----------------------------------------------------------------------------
DWORD
StartBufferVersion2(
PUPDATE_BUFFER pUB
) {
PIPRIP_HEADER pHdr;
PIPRIP_IF_CONFIG pic;
PIPRIP_AUTHENT_ENTRY pae;
//
// setup header
//
pHdr = (PIPRIP_HEADER)pUB->UB_Buffer;
pHdr->IH_Version = 2;
pHdr->IH_Command = (BYTE)pUB->UB_Command;
pHdr->IH_Reserved = 0;
pUB->UB_Length = sizeof(IPRIP_HEADER);
//
// see if we need to set up the authentication entry
//
pic = pUB->UB_ITE->ITE_Config;
if (pic->IC_AuthenticationType == IPRIP_AUTHTYPE_SIMPLE_PASSWORD) {
pae = (PIPRIP_AUTHENT_ENTRY)(pUB->UB_Buffer + sizeof(IPRIP_HEADER));
pae->IAE_AddrFamily = htons(ADDRFAMILY_AUTHENT);
pae->IAE_AuthType = htons((WORD)pic->IC_AuthenticationType);
CopyMemory(
pae->IAE_AuthKey,
pic->IC_AuthenticationKey,
IPRIP_MAX_AUTHKEY_SIZE
);
pUB->UB_Length += sizeof(IPRIP_AUTHENT_ENTRY);
}
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: AddEntryVersion2
//
// this adds an entry to RIPv2 buffer, first sending the buffer if it is full
//----------------------------------------------------------------------------
DWORD
AddEntryVersion2(
PUPDATE_BUFFER pUB,
PRIP_IP_ROUTE pRIR
) {
PIPRIP_ENTRY pie;
//
// send the contents if the buffer is full
//
if (pUB->UB_Length + sizeof(IPRIP_ENTRY) > MAX_PACKET_SIZE) {
SendUpdateBuffer(pUB);
StartBufferVersion2(pUB);
}
pie = (PIPRIP_ENTRY)(pUB->UB_Buffer + pUB->UB_Length);
IPRIP_ENTRY_FROM_RTM_ROUTE(pie, pRIR);
//
// for RIP routes, we assume that the route tag will be set
// in the RTM route struct already;
// for non-RIP routes, we write the route tag
// for the outgoing interface in the packet entry
//
if (pRIR->RR_RoutingProtocol == PROTO_IP_RIP) {
pie->IE_RouteTag = htons(GETROUTETAG(pRIR));
}
else {
pie->IE_RouteTag = htons(pUB->UB_ITE->ITE_Config->IC_RouteTag);
}
pUB->UB_Length += sizeof(IPRIP_ENTRY);
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: FinishBufferVersion2
//
// this sends the contents of a RIPv2 buffer, if any
//----------------------------------------------------------------------------
DWORD
FinishBufferVersion2(
PUPDATE_BUFFER pUB
) {
//
// the size above which we send depends on whether or not there
// is an authentication entry
//
if (pUB->UB_ITE->ITE_Config->IC_AuthenticationType == IPRIP_AUTHTYPE_NONE) {
if (pUB->UB_Length > sizeof(IPRIP_HEADER)) {
SendUpdateBuffer(pUB);
}
}
else {
//
// there is an authentication entry, so unless there
// is also a route entry, we will not send this last buffer
//
if (pUB->UB_Length > (sizeof(IPRIP_HEADER) +
sizeof(IPRIP_AUTHENT_ENTRY))) {
SendUpdateBuffer(pUB);
}
}
return NO_ERROR;
}
//----------------------------------------------------------------------------
// ROUTE ENUMERATION ROUTINES
//
// The following definitions simplify the enumeration of routes
// when routing information is being sent from a single source on multiple
// interfaces, for instance when a triggered update is going out on all
// interfaces, or when a full-update is being sent, or when a number
// of interfaces are being shutdown. the function InitializeGetRoute looks at
// the mode in which it is supposed to send routes, and based on that
// builds a table of functions which will be used to enumerate the routes.
// In the case of a full-update, the enumeration functions would
// go to RTM to get the information; in the case of a triggered-update, they
// would dequeue routes from the send-queue.
//----------------------------------------------------------------------------
// the following are the type definitions of the functions
// in each get-route function group
typedef DWORD (*PGETROUTE_START)(PVOID *);
typedef DWORD (*PGETROUTE_NEXT)(PVOID *, PRIP_IP_ROUTE);
typedef DWORD (*PGETROUTE_FINISH)(PVOID *);
// The following three functions handle RTM route enumeration
DWORD
RtmGetRouteStart(
PRTM_ENUM_HANDLE phEnumHandle
);
DWORD
RtmGetRouteNext(
RTM_ENUM_HANDLE hEnumHandle,
PRIP_IP_ROUTE pRoute
);
DWORD
RtmGetRouteFinish(
RTM_ENUM_HANDLE hEnumHandle
);
// The following three functions handle full-update route enumeration
// (a full-update enumerates routes from RTM)
#define FullUpdateGetRouteStart RtmGetRouteStart
#define FullUpdateGetRouteNext RtmGetRouteNext
#define FullUpdateGetRouteFinish RtmGetRouteFinish
// The following three functions handle triggered-update route enumeration
// (a triggered-update enumerates routes from the send-queue)
DWORD
TriggeredUpdateGetRouteStart(
PRTM_ENUM_HANDLE phEnumHandle
);
DWORD
TriggeredUpdateGetRouteNext(
RTM_ENUM_HANDLE hEnumHandle,
PRIP_IP_ROUTE pRoute
);
DWORD
TriggeredUpdateGetRouteFinish(
RTM_ENUM_HANDLE hEnumHandle
);
// The following three functions handle shutdown-update route enumeration.
// On shutdown, routes are enumerated from RTM, but their metrics
// are set to IPRIP_INFINITE-1 before being returned
#define ShutdownUpdateGetRouteStart RtmGetRouteStart
DWORD ShutdownUpdateGetRouteNext(RTM_ENUM_HANDLE hEnumHandle, PRIP_IP_ROUTE pRoute);
#define ShutdownUpdateGetRouteFinish RtmGetRouteFinish
// The following three functions handle general-response route enumeration
// a general response enumerates routes from RTM
#define GeneralResponseGetRouteStart RtmGetRouteStart
#define GeneralResponseGetRouteNext RtmGetRouteNext
#define GeneralResponseGetRouteFinish RtmGetRouteFinish
//----------------------------------------------------------------------------
// Function: InitializeGetRoute
//
// This functions sets up a get-route function group given the send-mode
//----------------------------------------------------------------------------
DWORD
InitializeGetRoute(
DWORD dwSendMode,
PGETROUTE_START *ppGS,
PGETROUTE_NEXT *ppGN,
PGETROUTE_FINISH *ppGF
) {
switch (dwSendMode) {
case SENDMODE_FULL_UPDATE:
*ppGS = FullUpdateGetRouteStart;
*ppGN = FullUpdateGetRouteNext;
*ppGF = FullUpdateGetRouteFinish;
break;
case SENDMODE_TRIGGERED_UPDATE:
*ppGS = TriggeredUpdateGetRouteStart;
*ppGN = TriggeredUpdateGetRouteNext;
*ppGF = TriggeredUpdateGetRouteFinish;
break;
case SENDMODE_SHUTDOWN_UPDATE:
*ppGS = ShutdownUpdateGetRouteStart;
*ppGN = ShutdownUpdateGetRouteNext;
*ppGF = ShutdownUpdateGetRouteFinish;
break;
case SENDMODE_GENERAL_RESPONSE1:
case SENDMODE_GENERAL_RESPONSE2:
*ppGS = GeneralResponseGetRouteStart;
*ppGN = GeneralResponseGetRouteNext;
*ppGF = GeneralResponseGetRouteFinish;
break;
default:
return ERROR_INVALID_PARAMETER;
break;
}
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: RtmGetRouteStart
//
// starts an enumeration of RTM routes; includes only and all best routes
// the enumeration handle is written into ppEnumerator
//----------------------------------------------------------------------------
DWORD
RtmGetRouteStart(
PRTM_ENUM_HANDLE phEnumHandle
) {
DWORD dwErr;
RTM_NET_ADDRESS rna;
RTM_IPV4_MAKE_NET_ADDRESS( &rna, 0 , 0 );
dwErr = RtmCreateDestEnum(
ig.IG_RtmHandle, RTM_VIEW_MASK_ANY,
RTM_ENUM_START | RTM_ENUM_ALL_DESTS, &rna,
RTM_BEST_PROTOCOL, phEnumHandle
);
if (dwErr != NO_ERROR) {
TRACE1( ROUTE, "error %d when creating enumeration handle", dwErr );
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: RtmGetRouteNext
//
// continues an enumeration of RTM routes
//----------------------------------------------------------------------------
DWORD
RtmGetRouteNext(
RTM_ENUM_HANDLE hEnumHandle,
PRIP_IP_ROUTE pRoute
) {
BOOL bRelDest = FALSE, bRelUcast = FALSE;
DWORD dwErr, dwNumDests = 1;
RTM_DEST_INFO rdi, rdiTemp;
char szNetwork[20], szNextHop[20];
do {
//
// Get next route
//
do {
dwErr = RtmGetEnumDests(
ig.IG_RtmHandle, hEnumHandle, &dwNumDests, &rdiTemp
);
if (dwErr == ERROR_NO_MORE_ITEMS) {
if (dwNumDests < 1) {
break;
}
dwErr = NO_ERROR;
}
else if (dwErr != NO_ERROR) {
TRACE1(ANY, "error %d enumeratings dests", dwErr);
break;
}
bRelDest = TRUE;
//
// Get route info for unicast view only
//
dwErr = RtmGetDestInfo(
ig.IG_RtmHandle, rdiTemp.DestHandle, RTM_BEST_PROTOCOL,
RTM_VIEW_MASK_UCAST, &rdi
);
if (dwErr != NO_ERROR) {
TRACE1(ANY, "error %d getting ucast info dests", dwErr);
break;
}
bRelUcast = TRUE;
//
// Check if any route info is present in the UCAST view
//
if ( ( rdi.ViewInfo[0].HoldRoute == NULL ) &&
( rdi.ViewInfo[0].Route == NULL ) )
{
//
// This destination has no info in the UCAST view
// Release all handles and get next route
//
dwErr = RtmReleaseDests(ig.IG_RtmHandle, 1, &rdi);
if (dwErr != NO_ERROR) {
TRACE3(
ANY, "error %d releasing UCAST dest %s/%d", dwErr,
szNetwork, rdi.DestAddress.NumBits
);
}
dwErr = RtmReleaseDests(ig.IG_RtmHandle, 1, &rdiTemp);
if (dwErr != NO_ERROR) {
TRACE3(
ANY, "error %d releasing dest %s/%d", dwErr,
szNetwork, rdi.DestAddress.NumBits
);
}
bRelDest = bRelUcast = FALSE;
continue;
}
//
// convert to RIP internal representation, if hold down route present
// use it as opposed to the best route.
//
dwErr = GetRouteInfo(
rdi.ViewInfo[0].HoldRoute ? rdi.ViewInfo[0].HoldRoute :
rdi.ViewInfo[0].Route,
NULL, &rdi, pRoute
);
} while (FALSE);
if (dwErr != NO_ERROR) {
break;
}
lstrcpy(szNetwork, INET_NTOA(pRoute->RR_Network.N_NetNumber));
lstrcpy(szNextHop, INET_NTOA(pRoute->RR_NextHopAddress.N_NetNumber));
//
// set metrics as appropriate
//
if ( rdi.ViewInfo[0].HoldRoute != NULL ) {
//
// help down routes are always advertized with
// metric 16
//
#if ROUTE_DBG
TRACE2(
ROUTE, "Holddown route %s/%d", szNetwork,
rdi.DestAddress.NumBits
);
#endif
SETROUTEMETRIC(pRoute, IPRIP_INFINITE);
}
else if (pRoute-> RR_RoutingProtocol != PROTO_IP_RIP) {
//
// non-RIP routes are advertised with metric 2
// TBD: This will need to be re-evaluated if/when we
// have a route redistribution policy
//
SETROUTEMETRIC(pRoute, 2);
}
} while ( FALSE );
//
// release handles as appropriate
//
if (bRelUcast) {
DWORD dwErrTemp;
dwErrTemp = RtmReleaseDests(ig.IG_RtmHandle, 1, &rdi);
if (dwErrTemp != NO_ERROR) {
TRACE3(
ANY, "error %d releasing UCAST dest %s/%d", dwErrTemp,
szNetwork, rdi.DestAddress.NumBits
);
}
}
if (bRelDest) {
DWORD dwErrTemp;
dwErrTemp = RtmReleaseDests(ig.IG_RtmHandle, 1, &rdiTemp);
if (dwErrTemp != NO_ERROR) {
TRACE3(
ANY, "error %d releasing dest %s/%d", dwErrTemp,
szNetwork, rdi.DestAddress.NumBits
);
}
}
#if ROUTE_DBG
if (dwErr == NO_ERROR) {
TRACE4(
ROUTE, "Enumerated route %s/%d via %s with metric %d",
szNetwork, rdi.DestAddress.NumBits,
szNextHop, GETROUTEMETRIC(pRoute)
);
}
#endif
return dwErr;
}
//----------------------------------------------------------------------------
// Function: RtmGetRouteFinish
//
// terminates an enumeration of RTM routes
//----------------------------------------------------------------------------
DWORD
RtmGetRouteFinish(
RTM_ENUM_HANDLE EnumHandle
) {
DWORD dwErr;
dwErr = RtmDeleteEnumHandle( ig.IG_RtmHandle, EnumHandle );
if (dwErr != NO_ERROR) {
TRACE1( ANY, "error %d closing enumeration handle", dwErr );
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: ShutdownUpdateGetRouteNext
//
// continues an enumeration of RTM routes for a shutdown-update.
// same as RtmGetRouteNext, except that metrics are set to IPRIP_INFINITE - 1
//----------------------------------------------------------------------------
DWORD
ShutdownUpdateGetRouteNext(
RTM_ENUM_HANDLE hEnumHandle,
PRIP_IP_ROUTE pRoute
) {
DWORD dwErr;
//
// during a shutdown, all non-infinite metrics are set to 15
//
dwErr = RtmGetRouteNext(hEnumHandle, pRoute);
if (dwErr == NO_ERROR && GETROUTEMETRIC(pRoute) != IPRIP_INFINITE) {
SETROUTEMETRIC(pRoute, IPRIP_INFINITE - 1);
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: TriggeredUpdateGetRouteStart
//
// starts an enumeration of routes from the send queue
// for a triggered update. nothing to do, since the caller
// of SendRoutes should have locked the send queue already
//----------------------------------------------------------------------------
DWORD
TriggeredUpdateGetRouteStart(
PRTM_ENUM_HANDLE pEnumHandle
) {
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: TriggeredUpdateGetRouteNext
//
// continues an enumeration of routes from the send-queue
//----------------------------------------------------------------------------
DWORD
TriggeredUpdateGetRouteNext(
RTM_ENUM_HANDLE EnumHandle,
PRIP_IP_ROUTE pRoute
) {
DWORD dwErr;
dwErr = DequeueSendEntry(ig.IG_SendQueue, pRoute);
if (dwErr == NO_ERROR && pRoute->RR_RoutingProtocol != PROTO_IP_RIP) {
//
// non-RIP routes are advertised with metric 2
// TBD: This will need to be re-evaluated if/when we
// have a route redistribution policy
//
SETROUTEMETRIC(pRoute, 2);
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: TriggeredUpdateGetRouteFinish
//
// terminates an enumeration of routes from the send-queue
//----------------------------------------------------------------------------
DWORD
TriggeredUpdateGetRouteFinish(
RTM_ENUM_HANDLE EnumHandle
) {
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: SendRoutes
//
// This function sends triggered updates, full-updates, shutdown-updates, and
// responses to general requests; the processing for all such output is the
// same. The source of routing information is different, however, and this
// difference is abstracted away using the route enumeration function groups
// described above.
// In the case of sending a response to a general or specific request,
// the response should be sent on a single interface using a single IP address,
// using a particular type of RIP packet; the caller can specify which
// IP address to use by setting the argument dwAddrIndex to the index of the
// desired address in the interface's IP address table, and the caller can
// specify the type of packet to use by setting the argument dwAnnounceMode
// to the corresponding IPRIP_ANNOUNCE_* constant. These arguments are only
// used for responses to requests.
//
// assumes the interface table is locked
//----------------------------------------------------------------------------
DWORD
SendRoutes(
PIF_TABLE_ENTRY pIfList[],
DWORD dwIfCount,
DWORD dwSendMode,
DWORD dwDestination,
DWORD dwAddrIndex
) {
RTM_ENUM_HANDLE Enumerator;
RIP_IP_ROUTE route;
PIPRIP_IF_CONFIG pic;
PIPRIP_IF_BINDING pib;
PIPRIP_IP_ADDRESS paddr;
DWORD i, dwErr, dwBufCount;
PDWORD pdwPeer, pdwPeerEnd;
PIF_TABLE_ENTRY *ppite, *ppitend = NULL;
PUPDATE_BUFFER pbuf, pbufend, pBufList;
PROUTE_TABLE_ENTRY prte;
ROUTE_TABLE summaryTable;
PGETROUTE_START pfnGetRouteStart;
PGETROUTE_NEXT pfnGetRouteNext;
PGETROUTE_FINISH pfnGetRouteFinish;
PLIST_ENTRY plstart, plend, phead, ple;
//
// if no interfaces, go no further
//
if (dwIfCount == 0) { return ERROR_NO_DATA; }
//
// initialize the route enumeration function table
//
dwErr = InitializeGetRoute(
dwSendMode,
&pfnGetRouteStart,
&pfnGetRouteNext,
&pfnGetRouteFinish
);
if (dwErr != NO_ERROR) { return ERROR_INVALID_PARAMETER; }
dwErr = NO_ERROR;
Enumerator = NULL;
//
// create table for summary routes
//
dwErr = CreateRouteTable(&summaryTable);
if (dwErr != 0) {
TRACE1(SEND, "error %d initializing summary table", dwErr);
return dwErr;
}
dwErr = NO_ERROR;
pBufList = NULL;
do { // breakout loop
//
// the following discussion does not apply when sending routes
// to specific destinations:
// since unicast peers may be configured on some interfaces,
// we need to allocate update buffers for those peers as well.
//
// also, we will not allocate update buffers for RIPv1 interfaces
// on which broadcast is disabled (such interfaces should have
// at least one unicast peer configured instead.)
//
// Thus, the number of update buffers may not be equal to
// the number of interfaces, and in the worst case (i.e. where
// all interfaces are RIPv1 and have broadcast disabled and have
// no unicast peers configured) there may be no update buffers at all.
//
if (dwDestination != 0) {
//
// sending to a specific destination; this only happens when
// there is a single interface in the list, for instance when
// sending a response to a general request
//
dwBufCount = dwIfCount;
}
else {
//
// we are sending a full-update, triggered-update, or
// a shutdown-update, and thus routes may be sent by
// broadcast/multicast as well as to unicast peers
//
dwBufCount = 0;
ppitend = pIfList + dwIfCount;
for (ppite = pIfList; ppite < ppitend; ppite++) {
pic = (*ppite)->ITE_Config;
pib = (*ppite)->ITE_Binding;
if (pic->IC_UnicastPeerMode != IPRIP_PEER_ONLY) {
dwBufCount += pib->IB_AddrCount;
}
if (pic->IC_UnicastPeerMode != IPRIP_PEER_DISABLED) {
dwBufCount += pic->IC_UnicastPeerCount;
}
}
}
if (dwBufCount == 0) { break; }
//
// allocate the update buffers for all interfaces
//
pBufList = RIP_ALLOC(dwBufCount * sizeof(UPDATE_BUFFER));
if (pBufList == NULL) {
dwErr = GetLastError();
TRACE2(
SEND, "error %d allocating %d bytes for update buffers",
dwErr, dwBufCount * sizeof(UPDATE_BUFFER)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// initialize the update buffers allocated; in the case of
// sending to a specific destination, initialize a buffer
// for each interface; in the case of sending updates, also
// initialize buffers for unicast peers.
//
pbuf = pBufList;
pbufend = pBufList + dwBufCount;
ppitend = pIfList + dwIfCount;
ACQUIRE_BINDING_LOCK_SHARED();
for (ppite = pIfList; ppite < ppitend; ppite++) {
if (dwDestination != 0) {
//
// sending to a specific destination
//
InitializeUpdateBuffer(
*ppite, dwAddrIndex, pbuf, dwSendMode, dwDestination,
IPRIP_RESPONSE
);
pbuf->UB_StartRoutine(pbuf);
++pbuf;
}
else {
//
// sending updates on multiple interfaces
//
pic = (*ppite)->ITE_Config;
pib = (*ppite)->ITE_Binding;
//
// if broadcast or multicast is enabled on the interface,
// and it is not configured to send only to listed peers,
// initialize the broadcast/multicast update buffer
//
if (pic->IC_UnicastPeerMode != IPRIP_PEER_ONLY) {
for (i = 0; i < pib->IB_AddrCount; i++) {
InitializeUpdateBuffer(
*ppite, i, pbuf, dwSendMode, dwDestination,
IPRIP_RESPONSE
);
pbuf->UB_StartRoutine(pbuf);
++pbuf;
}
}
if (pic->IC_UnicastPeerMode != IPRIP_PEER_DISABLED) {
//
// initialize update buffers for unicast peers, if any
//
pdwPeer = IPRIP_IF_UNICAST_PEER_TABLE(pic);
pdwPeerEnd = pdwPeer + pic->IC_UnicastPeerCount;
for ( ; pdwPeer < pdwPeerEnd; pdwPeer++) {
//
// Note: forcing peers to be on first address
//
InitializeUpdateBuffer(
*ppite, 0, pbuf, dwSendMode, *pdwPeer,
IPRIP_RESPONSE
);
pbuf->UB_StartRoutine(pbuf);
++pbuf;
}
}
}
}
RELEASE_BINDING_LOCK_SHARED();
//
// start the route enumeration
//
if ( pfnGetRouteStart(&Enumerator) == NO_ERROR ) {
//
// enumerate and transmit the routes
//
while (pfnGetRouteNext(Enumerator, &route) == NO_ERROR) {
//
// for each route, send it on each update buffer,
// subject to split-horizon/poison-reverse/subnet-summary
// pass in the summary table pointer to store summarized routes
//
dwErr = SendRouteOnIfList(
pBufList, dwBufCount, dwSendMode, &summaryTable, &route
);
}
//
// terminate the route enumeration
//
pfnGetRouteFinish(Enumerator);
//
// now send all routes which were summarized
//
plstart = summaryTable.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);
//
// shouldn't summarize when sending summary table contents
// so we pass NULL instead of a summary table pointer
//
SendRouteOnIfList(
pBufList, dwBufCount, dwSendMode, NULL, &prte->RTE_Route
);
}
}
//
// finally, write the summarized routes to RTM
//
WriteSummaryRoutes(&summaryTable, ig.IG_RtmHandle);
}
} while(FALSE);
//
// free the allocated update buffers, if any
//
if (pBufList != NULL) {
pbufend = pBufList + dwBufCount;
for (pbuf = pBufList; pbuf < pbufend; pbuf++) {
//
// send whatever might remain in the update buffer
//
pbuf->UB_FinishRoutine(pbuf);
}
RIP_FREE(pBufList);
}
//
// delete the summary table
//
DeleteRouteTable(&summaryTable);
return dwErr;
}
//----------------------------------------------------------------------------
// Function: SendRouteOnIfList
//
// this function sends a single route on all interfaces in the given
// interface list, using the update buffers in the given update buffer list
//----------------------------------------------------------------------------
DWORD
SendRouteOnIfList(
UPDATE_BUFFER pBufList[],
DWORD dwBufCount,
DWORD dwSendMode,
PROUTE_TABLE pSummaryTable,
PRIP_IP_ROUTE pRoute
) {
RIP_IP_ROUTE route;
DWORD dwFound, dwTTL;
PIF_TABLE_ENTRY pite;
PIPRIP_IF_CONFIG pic;
PUPDATE_BUFFER pbuf, pbufend;
DWORD dwDestNetwork, dwNexthopNetwork;
DWORD dwDestNetclassAddr, dwDestNetclassMask;
DWORD dwRouteNetclassAddr, dwRouteNetclassMask;
DWORD dwRouteNetwork, dwRouteNetmask, dwRouteProtocol;
#if ROUTE_DBG
CHAR szDest[32];
CHAR szDestMask[32];
CHAR szNexthop[32];
CHAR szNexthopMask[32];
CHAR szRoute[32];
CHAR szRouteMask[32];
//
// set up variables used for error and information messages
//
lstrcpy(szRoute, INET_NTOA(pRoute->RR_Network.N_NetNumber));
lstrcpy(szRouteMask, 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));
#endif
//
// we never send summary routes if they are read from RTM;
// we only send them if they are generated in the process
// of advertising actual routes on this iteration;
// we can tell the difference by checking whether we are still
// generating summary routes (i.e. if pSummaryTable is non-NULL);
// if we aren't it is time to start sending summary routes
//
if (pSummaryTable != NULL && pRoute->RR_RoutingProtocol == PROTO_IP_RIP &&
GETROUTEFLAG(pRoute) == ROUTEFLAG_SUMMARY) {
return NO_ERROR;
}
//
// get the route's network and netmask, and compute
// the route's network class address and the network class mask;
// to support supernetting, we double-check the class mask
// and use the supernet mask if necessary
//
dwRouteProtocol = pRoute->RR_RoutingProtocol;
dwRouteNetwork = pRoute->RR_Network.N_NetNumber;
dwRouteNetmask = pRoute->RR_Network.N_NetMask;
dwRouteNetclassMask = NETCLASS_MASK(dwRouteNetwork);
if (dwRouteNetclassMask > dwRouteNetmask) {
dwRouteNetclassMask = dwRouteNetmask;
}
dwRouteNetclassAddr = (dwRouteNetwork & dwRouteNetclassMask);
//
// go through each update buffer
//
pbufend = pBufList + dwBufCount;
for (pbuf = pBufList; pbuf < pbufend; pbuf++) {
pite = pbuf->UB_ITE;
pic = pite->ITE_Config;
//
// if this is a broadcast route entry, skip it
// The first condition uses the netmask information for this route,
// stored in route table, to determine if it is a broadcast route
// The second condition uses the netmask which is computed based
// on the address class
// The third condition checks if it is an all 1's broadcast
//
if ( IS_DIRECTED_BROADCAST_ADDR(dwRouteNetwork, dwRouteNetmask) ||
IS_DIRECTED_BROADCAST_ADDR(dwRouteNetwork, dwRouteNetclassMask) ||
IS_LOCAL_BROADCAST_ADDR(dwRouteNetwork) ) {
continue;
}
//
// if this is the multicast route entry, skip it
//
if ( CLASSD_ADDR( dwRouteNetwork ) || CLASSE_ADDR( dwRouteNetwork ) ) {
continue;
}
//
// If this is a loopback route, skip it.
//
if ( IS_LOOPBACK_ADDR( dwRouteNetwork ) ) {
continue;
}
//
// if this is the rotue to the outgoing interface's network,
// (e.g. the route to 10.1.1.0 on interface 10.1.1.1/255.255.255.0)
// don't include it in the update
// (clearly, we shouldn't AND the default-route's netmask (0)
// with anything and expect this to work
//
if (dwRouteNetmask &&
dwRouteNetwork == (pbuf->UB_Address & dwRouteNetmask)) {
continue;
}
//
// if announcing host routes is disabled on the interface
// and this is a host route, skip it
//
if (dwRouteNetmask == HOSTADDR_MASK &&
IPRIP_FLAG_IS_DISABLED(pic, ANNOUNCE_HOST_ROUTES)) {
continue;
}
//
// if announcing default routes is disabled
// and this is a default route, skip it
//
if (dwRouteNetwork == 0 &&
IPRIP_FLAG_IS_DISABLED(pic, ANNOUNCE_DEFAULT_ROUTES)) {
continue;
}
//
// now put the route through the announce filters
//
if (pic->IC_AnnounceFilterMode != IPRIP_FILTER_DISABLED) {
//
// discard if we are including all routes and this route is listed
// as an exception, or if we are excluding all routes and
// this route is not listed as an exception
//
IS_ROUTE_IN_ANNOUNCE_FILTER(pic, dwRouteNetwork, dwFound);
if ((pic->IC_AnnounceFilterMode == IPRIP_FILTER_INCLUDE &&
!dwFound) ||
(pic->IC_AnnounceFilterMode == IPRIP_FILTER_EXCLUDE &&
dwFound)) {
continue;
}
}
//
// SUBNET-SUMMARY PROCESSING:
//
// if the route is not on the network we are sending this to or
// if the route's mask is longer than that of the network we are
// sending to, or if the route is a network route, add it to the
// summary table instead of sending it immediately.
// default routes are excepted from summarization
//
route = *pRoute;
if (pSummaryTable != NULL && dwRouteNetwork != 0) {
//
// get the destination address to which the update is being
// sent for this interface; double-check the netclass mask
// to accomodate supernets
//
dwDestNetclassAddr = pbuf->UB_DestAddress;
dwDestNetclassMask = NETCLASS_MASK(dwDestNetclassAddr);
if (dwDestNetclassMask > pbuf->UB_DestNetmask) {
dwDestNetclassMask = pbuf->UB_DestNetmask;
}
dwDestNetclassAddr = (dwDestNetclassAddr & dwDestNetclassMask);
if ((dwRouteNetwork == dwRouteNetclassAddr &&
dwRouteNetmask == dwRouteNetclassMask) ||
dwDestNetclassAddr != dwRouteNetclassAddr) {
if ((pic->IC_AnnounceMode == IPRIP_ANNOUNCE_RIP1) ||
!IPRIP_FLAG_IS_ENABLED(pic, NO_SUBNET_SUMMARY)) {
//
// either the route is a network route,
// or the update is going to a network different
// from that of the route
//
//
// create an entry in the summary table instead of sending;
//
route.RR_Network.N_NetNumber = dwRouteNetclassAddr;
route.RR_Network.N_NetMask = dwRouteNetclassMask;
if ((dwRouteNetwork != dwRouteNetclassAddr) ||
(dwRouteNetmask != dwRouteNetclassMask)) {
route.RR_RoutingProtocol = PROTO_IP_RIP;
SETROUTEFLAG(&route, ROUTEFLAG_SUMMARY);
SETROUTETAG(&route, pic->IC_RouteTag);
}
CreateRouteEntry(
pSummaryTable, &route, pic->IC_RouteExpirationInterval,
pic->IC_RouteRemovalInterval
);
continue;
}
}
else
if (pic->IC_AnnounceMode == IPRIP_ANNOUNCE_RIP1 &&
dwRouteNetmask != HOSTADDR_MASK &&
pbuf->UB_Netmask < dwRouteNetmask) {
//
// this is neither a host route nor a default route,
// and the subnet-mask on the outgoing interface is shorter
// than that of the route, so the route's network must be
// truncated lest it be considered a host route by the routers
// who will receive this update
// only do this in RIP1 mode, since in RIP2 mode
// we can include the netmask in the route entry
//
route.RR_Network.N_NetNumber &= pbuf->UB_Netmask;
route.RR_Network.N_NetMask = pbuf->UB_Netmask;
route.RR_RoutingProtocol = PROTO_IP_RIP;
SETROUTEFLAG(&route, ROUTEFLAG_SUMMARY);
SETROUTETAG(&route, pic->IC_RouteTag);
CreateRouteEntry(
pSummaryTable, &route, pic->IC_RouteExpirationInterval,
pic->IC_RouteRemovalInterval
);
continue;
}
}
//
// Summary route checks
//
// Summary routes are to sent only on those interfaces that
// require them i.e. Interfaces on which the annouce mode is
// RIP1 or on which summarization has been explicity turned on
//
if (pSummaryTable == NULL &&
((GETROUTEFLAG(&route) & ROUTEFLAG_SUMMARY) == ROUTEFLAG_SUMMARY) &&
pic->IC_AnnounceMode != IPRIP_ANNOUNCE_RIP1 &&
IPRIP_FLAG_IS_ENABLED(pic, NO_SUBNET_SUMMARY)) {
//
// This is a summary route, and the interface over which it is
// to be sent does not require summary routes to be sent on it
//
continue;
}
//
// SPLIT-HORIZON/POISON-REVERSE PROCESSING:
//
//
// note that we only do split-horizon/poison-reverse on RIP routes
//
//
// Modification : Split-horizon/poison-reverse done for all routes
//
// if (dwRouteProtocol != PROTO_IP_RIP ||
// IPRIP_FLAG_IS_DISABLED(pic, SPLIT_HORIZON))
if (IPRIP_FLAG_IS_DISABLED(pic, SPLIT_HORIZON)) {
//
// add the entry as-is:
// sender should use us as the nexthop to this destination
//
route.RR_NextHopAddress.N_NetNumber = 0;
route.RR_NextHopAddress.N_NetMask = 0;
pbuf->UB_AddRoutine(pbuf, &route);
}
else
if (IPRIP_FLAG_IS_DISABLED(pic, POISON_REVERSE)) {
//
// if the route is being sent to the network from which
// the route was learnt, exclude the route altogether
//
dwDestNetwork = (pbuf->UB_DestAddress & pbuf->UB_DestNetmask);
dwNexthopNetwork = (route.RR_NextHopAddress.N_NetNumber &
route.RR_NextHopAddress.N_NetMask);
//
// Check if the route next hop is on the same network as the
// socket from which this RIP response is being sent.
// If so, do not include this route in the update.
// Otherwise, we may still need to do poison-reverse
// since the next-hop may be the other end of a point-to-point link
// (endpoints of such links can be on different networks)
// in which case the first test would succeed (different networks)
// but we'd still be required to perform split-horizon.
// Therefore if the outgoing interface is the one from which
// the route was learnt, we do not include this route in the update.
//
if (dwNexthopNetwork == dwDestNetwork ||
(pbuf->UB_ITE->ITE_Type == DEMAND_DIAL &&
route.RR_InterfaceID == pbuf->UB_ITE->ITE_Index)) {
continue;
}
else {
//
// sending to a different network, so sender should use
// us as the nexthop to this destination
//
route.RR_NextHopAddress.N_NetNumber = 0;
route.RR_NextHopAddress.N_NetMask = 0;
}
pbuf->UB_AddRoutine(pbuf, &route);
}
else {
//
// if the route is being sent to the network from which
// the route was learnt, include the route with infinite metric
//
dwDestNetwork = (pbuf->UB_DestAddress & pbuf->UB_DestNetmask);
dwNexthopNetwork = (route.RR_NextHopAddress.N_NetNumber &
route.RR_NextHopAddress.N_NetMask);
if (dwNexthopNetwork == dwDestNetwork ||
(pbuf->UB_ITE->ITE_Type == DEMAND_DIAL &&
route.RR_InterfaceID == pbuf->UB_ITE->ITE_Index)) {
//
// if a route is advertised with infinite metric due to
// poison-reverse and it would still be advertised with
// infinite metric in a triggered update, save bandwidth
// by excluding the route
//
if (dwSendMode == SENDMODE_TRIGGERED_UPDATE) {
continue;
}
else {
SETROUTEMETRIC(&route, IPRIP_INFINITE);
}
}
else {
//
// sending to a different network, so sender should use
// us as the nexthop to this destination
//
route.RR_NextHopAddress.N_NetNumber = 0;
route.RR_NextHopAddress.N_NetMask = 0;
}
pbuf->UB_AddRoutine(pbuf, &route);
}
//
// hold advertized destinations
//
if ((dwSendMode == SENDMODE_FULL_UPDATE) ||
(dwSendMode == SENDMODE_GENERAL_RESPONSE1) ||
(dwSendMode == SENDMODE_GENERAL_RESPONSE2)) {
//
// use the hold interval from the interface over which the
// route is over.
//
if (pite->ITE_Index == route.RR_InterfaceID) {
DWORD dwErr;
dwErr = RtmHoldDestination(
ig.IG_RtmHandle, route.hDest, RTM_VIEW_MASK_UCAST,
pic-> IC_RouteRemovalInterval * 1000
);
if (dwErr != NO_ERROR) {
TRACE1(ANY, "error %d holding dest", dwErr);
}
}
}
}
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: SendGeneralRequest
//
// This function transmits RIP requests on interface to all neighbors in
// the interfaces neighbor list. A request is also sent via broadcast or
// multicast is the neighbor list is not used exclusively.
//----------------------------------------------------------------------------
DWORD
SendGeneralRequest(
PIF_TABLE_ENTRY pite
) {
DWORD i, dwErr;
PIPRIP_ENTRY pie;
PIPRIP_IF_CONFIG pic;
PIPRIP_IF_BINDING pib;
PIPRIP_IP_ADDRESS paddr;
PDWORD pdwPeer, pdwPeerEnd;
pic = pite->ITE_Config;
pib = pite->ITE_Binding;
paddr = IPRIP_IF_ADDRESS_TABLE(pib);
ACQUIRE_BINDING_LOCK_SHARED();
do { // error breakout loop
//
// broadcast/multicast a request if not using neighbor-list only
//
if (pic->IC_UnicastPeerMode != IPRIP_PEER_ONLY) {
for (i = 0; i < pib->IB_AddrCount; i++, paddr++) {
UPDATE_BUFFER ub;
//
// initialize the update buffer
//
dwErr = InitializeUpdateBuffer(
pite, i, &ub, SENDMODE_GENERAL_REQUEST, 0,
IPRIP_REQUEST
);
ub.UB_StartRoutine(&ub);
//
// set up the general request entry
//
pie = (PIPRIP_ENTRY)(ub.UB_Buffer + ub.UB_Length);
pie->IE_AddrFamily = ADDRFAMILY_REQUEST;
pie->IE_RouteTag = 0;
pie->IE_Destination = 0;
pie->IE_SubnetMask = 0;
pie->IE_Nexthop = 0;
pie->IE_Metric = htonl(IPRIP_INFINITE);
ub.UB_Length += sizeof(IPRIP_ENTRY);
//
// send the buffer
//
ub.UB_FinishRoutine(&ub);
}
}
//
// if the list of peers is not in use, we are done
//
if (pic->IC_UnicastPeerMode == IPRIP_PEER_DISABLED) { break; }
//
// send requests to all the configured peers
//
pdwPeer = IPRIP_IF_UNICAST_PEER_TABLE(pic);
pdwPeerEnd = pdwPeer + pic->IC_UnicastPeerCount;
for ( ; pdwPeer < pdwPeerEnd; pdwPeer++) {
UPDATE_BUFFER ub;
//
// initialize the update buffer
// Note: we are forcing the peers onto the first address
//
dwErr = InitializeUpdateBuffer(
pite, 0, &ub, SENDMODE_GENERAL_REQUEST, *pdwPeer,
IPRIP_REQUEST
);
ub.UB_StartRoutine(&ub);
//
// set up the general request entry
//
pie = (PIPRIP_ENTRY)(ub.UB_Buffer + ub.UB_Length);
pie->IE_AddrFamily = ADDRFAMILY_REQUEST;
pie->IE_RouteTag = 0;
pie->IE_Destination = 0;
pie->IE_SubnetMask = 0;
pie->IE_Nexthop = 0;
pie->IE_Metric = htonl(IPRIP_INFINITE);
ub.UB_Length += sizeof(IPRIP_ENTRY);
//
// send the buffer
//
ub.UB_FinishRoutine(&ub);
}
} while(FALSE);
RELEASE_BINDING_LOCK_SHARED();
return NO_ERROR;
}
//----------------------------------------------------------------------------
// Function: AuthenticatePacket
//
// Given a RIP packet and an interface configuration block, this function
// accepts or rejects the packet based on the authentication settings
// of the interface and the authentication content of the packet.
//----------------------------------------------------------------------------
DWORD
AuthenticatePacket(
PBYTE pbuf,
PIPRIP_AUTHENT_ENTRY pae,
PIPRIP_IF_CONFIG pic,
PIPRIP_IF_STATS pis,
PIPRIP_PEER_STATS pps,
PIPRIP_ENTRY *ppie
) {
DWORD dwErr;
dwErr = NO_ERROR;
if (pic->IC_AuthenticationType == IPRIP_AUTHTYPE_NONE) {
//
// interface is not configured for authentication,
// so discard authenticated packets
//
if (pae->IAE_AddrFamily == htons(ADDRFAMILY_AUTHENT)) {
if (pis != NULL) {
InterlockedIncrement(&pis->IS_BadResponsePacketsReceived);
}
if (pps != NULL) {
InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer);
}
dwErr = ERROR_ACCESS_DENIED;
}
}
else {
//
// interface is using authentication,
// so discard unauthenticated packets
// and packets using different authentication schemes
//
if (pae->IAE_AddrFamily != htons(ADDRFAMILY_AUTHENT) ||
pae->IAE_AuthType != htons((WORD)pic->IC_AuthenticationType)) {
if (pis != NULL) {
InterlockedIncrement(&pis->IS_BadResponsePacketsReceived);
}
if (pps != NULL) {
InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer);
}
dwErr = ERROR_ACCESS_DENIED;
}
else {
//
// interface and packet are using the same authentication:
// check that the packet passes validation
//
switch(pic->IC_AuthenticationType) {
case IPRIP_AUTHTYPE_SIMPLE_PASSWORD:
//
// for simple passwords, just compare the keys
//
dwErr = (DWORD)memcmp(
pae->IAE_AuthKey, pic->IC_AuthenticationKey,
IPRIP_MAX_AUTHKEY_SIZE
);
if (dwErr != 0) { dwErr = ERROR_ACCESS_DENIED; }
break;
case IPRIP_AUTHTYPE_MD5:
//
// TBD: unimplemented unless required.
//
break;
}
//
// advance the "first entry" pointer
//
if (dwErr == NO_ERROR) { ++(*ppie); }
}
}
return dwErr;
}
//----------------------------------------------------------------------------
// Function: WorkerFunctionProcessInput
//
// This function is responsible for processing input.
// If any peer filters exist, it applies them to the routes received
// and passes the packets on to the processing functions.
//----------------------------------------------------------------------------
VOID
WorkerFunctionProcessInput(
PVOID pContext
) {
PINPUT_CONTEXT pwc;
DWORD dwErr, dwCommand;
PIPRIP_GLOBAL_CONFIG pigc;
if (!ENTER_RIP_WORKER()) { return; }
TRACE0(ENTER, "entering WorkerFunctionProcessInput");
do {
ACQUIRE_LIST_LOCK(ig.IG_RecvQueue);
dwErr = DequeueRecvEntry(ig.IG_RecvQueue, &dwCommand, (PBYTE *)&pwc);
RELEASE_LIST_LOCK(ig.IG_RecvQueue);
if (dwErr != NO_ERROR) {
TRACE1(RECEIVE, "error %d dequeueing received packet", dwErr);
break;
}
//
// call the processing function for this type of packet
//
if (dwCommand == IPRIP_REQUEST) {
ProcessRequest(pwc);
}
else
if (dwCommand == IPRIP_RESPONSE) {
DWORD dwSource, dwFound = 0;
dwSource = pwc->IC_InputSource.sin_addr.s_addr;
//
// make sure the packet is from the RIP port
//
if (pwc->IC_InputSource.sin_port != htons(IPRIP_PORT)) {
LPSTR lpszAddr = INET_NTOA(dwSource);
TRACE2(
RECEIVE, "invalid port in RESPONSE from %s on interface %d",
lpszAddr, pwc->IC_InterfaceIndex
);
LOGINFO1(INVALID_PORT, lpszAddr, NO_ERROR);
RIP_FREE(pwc);
break;
}
//
// put the packet through the peer filters since it is a response
//
ACQUIRE_GLOBAL_LOCK_SHARED();
pigc = ig.IG_Config;
if (dwCommand == IPRIP_RESPONSE &&
pigc->GC_PeerFilterMode != IPRIP_FILTER_DISABLED) {
//
// discard if this is not from a trusted peer:
// this is so if we are including only listed peers and this peer
// is not listed, or if we are excluding all listed peers
// and this peer is listed
//
IS_PEER_IN_FILTER(pigc, dwSource, dwFound);
if ((!dwFound &&
pigc->GC_PeerFilterMode == IPRIP_FILTER_INCLUDE) ||
(dwFound &&
pigc->GC_PeerFilterMode == IPRIP_FILTER_EXCLUDE)) {
LPSTR lpszAddr = INET_NTOA(dwSource);
TRACE2(
RECEIVE,
"FILTER: dropping RESPONSE from %s on interface %d",
lpszAddr, pwc->IC_InterfaceIndex
);
LOGINFO1(RESPONSE_FILTERED, lpszAddr, NO_ERROR);
RELEASE_GLOBAL_LOCK_SHARED();
RIP_FREE(pwc);
break;
}
}
RELEASE_GLOBAL_LOCK_SHARED();
ProcessResponse(pwc);
}
} while(FALSE);
TRACE0(LEAVE, "leaving WorkerFunctionProcessInput");
LEAVE_RIP_WORKER();
return;
}
//----------------------------------------------------------------------------
// Function: ProcessRequest
//
// This function handles the processing of an incoming request packet.
//----------------------------------------------------------------------------
VOID
ProcessRequest(
PVOID pContext
) {
PBYTE pbuf;
DWORD dwSize;
CHAR szSource[20];
PIPRIP_IF_STATS pis;
PIPRIP_ENTRY pie;
PIPRIP_HEADER pih;
PIPRIP_AUTHENT_ENTRY pae;
PIF_TABLE pTable;
PINPUT_CONTEXT pwc;
PIF_TABLE_ENTRY pite;
PIPRIP_IF_CONFIG pic;
PIPRIP_IP_ADDRESS paddr;
PPEER_TABLE_ENTRY ppte;
PIPRIP_PEER_STATS pps;
TRACE0(ENTER, "entering ProcessRequest");
pTable = ig.IG_IfTable;
ACQUIRE_IF_LOCK_SHARED();
do { // breakout loop
//
// retrieve the interface on which the request arrived
//
pwc = (PINPUT_CONTEXT)pContext;
pite = GetIfByIndex(pTable, pwc->IC_InterfaceIndex);
if (pite == NULL || IF_IS_INACTIVE(pite)) {
TRACE1(
REQUEST, "processing request: interface %d not found",
pwc->IC_InterfaceIndex
);
break;
}
pic = pite->ITE_Config;
paddr = IPRIP_IF_ADDRESS_TABLE(pite->ITE_Binding) + pwc->IC_AddrIndex;
pbuf = pwc->IC_InputPacket.IP_Packet;
pih = (PIPRIP_HEADER)pbuf;
pie = (PIPRIP_ENTRY)(pbuf + sizeof(IPRIP_HEADER));
pae = (PIPRIP_AUTHENT_ENTRY)pie;
pis = NULL;
pps = NULL;
lstrcpy(szSource, INET_NTOA(pwc->IC_InputSource.sin_addr));
//
// make sure this is a packet we can respond to;
// discard if this is a v1 packet and this interface is v2-only or
// if this is a v2-packet and this interface is v1-only
//
if ((pih->IH_Version != 2 &&
pic->IC_AnnounceMode == IPRIP_ANNOUNCE_RIP2)) {
CHAR szVersion[10];
LPSTR lpszAddr = INET_NTOA(paddr->IA_Address);
TRACE2(
REQUEST, "discarding non-v2 request on RIPv2 interface %d (%s)",
pite->ITE_Index, lpszAddr
);
wsprintf(szVersion, "%d", pih->IH_Version);
LOGINFO4(
PACKET_VERSION_MISMATCH, szVersion, lpszAddr, szSource, "2", 0
);
break;
}
else
if ((pih->IH_Version != 1 &&
pic->IC_AnnounceMode == IPRIP_ANNOUNCE_RIP1)) {
CHAR szVersion[10];
LPSTR lpszAddr = INET_NTOA(paddr->IA_Address);
TRACE2(
REQUEST, "discarding RIPv2 request on RIPv1 interface %d (%s)",
pite->ITE_Index, lpszAddr
);
wsprintf(szVersion, "%d", pih->IH_Version);
LOGINFO4(
PACKET_VERSION_MISMATCH, szVersion, lpszAddr, szSource, "1", 0
);
break;
}
//
// version 2 packets call for authentication processing;
//
if (pih->IH_Version == 2) {
DWORD dwErr;
dwErr = AuthenticatePacket(pbuf, pae, pic, pis, pps, &pie);
if (dwErr == ERROR_ACCESS_DENIED) {
LPSTR lpszAddr = INET_NTOA(paddr->IA_Address);
TRACE3(
REQUEST, "dropping packet from %s on interface %d (%s): authentication failed",
szSource, pite->ITE_Index, lpszAddr
);
LOGWARN2(AUTHENTICATION_FAILED, lpszAddr, szSource, NO_ERROR);
break;
}
}
//
// find the total remaining size of the packet
//
dwSize = (DWORD)(((ULONG_PTR)pbuf + pwc->IC_InputLength) - (ULONG_PTR)pie);
//
// see which kind of request this is
//
if (pie->IE_AddrFamily == ADDRFAMILY_REQUEST &&
pie->IE_Metric == htonl(IPRIP_INFINITE) &&
dwSize == sizeof(IPRIP_ENTRY)) {
//
// GENERAL REQUEST:
//
// send all routes on the interface
//
if (pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED ||
pwc->IC_InputSource.sin_port != htons(IPRIP_PORT)) {
TRACE3(
REQUEST, "responding to GENERAL REQUEST from %s on interface %d (%s)",
szSource, pite->ITE_Index, INET_NTOA(paddr->IA_Address)
);
//
// send version 2 packets in response to version 2 requests
// and send version 1 packets in response to all other requests
//
if (pih->IH_Version != 2) {
SendRoutes(
&pite, 1, SENDMODE_GENERAL_RESPONSE1,
pwc->IC_InputSource.sin_addr.s_addr, pwc->IC_AddrIndex
);
}
else {
SendRoutes(
&pite, 1, SENDMODE_GENERAL_RESPONSE2,
pwc->IC_InputSource.sin_addr.s_addr, pwc->IC_AddrIndex
);
}
InterlockedIncrement(&ig.IG_Stats.GS_TotalResponsesSent);
}
}
else
if (pic->IC_AnnounceMode == IPRIP_ANNOUNCE_DISABLED &&
pwc->IC_InputSource.sin_port == htons(IPRIP_PORT)) {
//
// SPECIFIC REQUEST:
// We are in silent mode and the request came from port 520,
// so we are not allowed to respond.
//
TRACE3(
REQUEST, "ignoring SPECIFIC REQUEST from %s on interface %d (%s)",
szSource, pite->ITE_Index, INET_NTOA(paddr->IA_Address)
);
}
else {
IP_NETWORK net;
UPDATE_BUFFER ub;
RIP_IP_ROUTE route;
PIPRIP_ENTRY piend;
RTM_NET_ADDRESS rna;
RTM_DEST_INFO rdi;
DWORD dwErr;
//
// SPECIFIC REQUEST:
// have to look up each destination in the packet
// and fill in our metric for it if it exists in RTM
//
TRACE3(
REQUEST, "responding to SPECIFIC REQUEST from %s on interface %d (%s)",
szSource, pite->ITE_Index, INET_NTOA(paddr->IA_Address)
);
//
// acquire the binding-table lock since InitializeUpdateBuffer
// needs to call GuessSubnetMask to generate a broadcast address
// to which the response will be sent
//
ACQUIRE_BINDING_LOCK_SHARED();
if (pih->IH_Version != 2) {
InitializeUpdateBuffer(
pite, pwc->IC_AddrIndex, &ub, SENDMODE_SPECIFIC_RESPONSE1,
pwc->IC_InputSource.sin_addr.s_addr, IPRIP_RESPONSE
);
}
else {
InitializeUpdateBuffer(
pite, pwc->IC_AddrIndex, &ub, SENDMODE_SPECIFIC_RESPONSE2,
pwc->IC_InputSource.sin_addr.s_addr, IPRIP_RESPONSE
);
}
//
// we must reply to the port from which the message was sent
//
ub.UB_Destination = pwc->IC_InputSource;
//
// start the update buffer
//
ub.UB_StartRoutine(&ub);
//
// query RTM for each route entry in packet
//
piend = (PIPRIP_ENTRY)(pbuf + pwc->IC_InputLength);
for ( ; pie < piend; pie++) {
//
// ignore unrecognized address families
//
if (pie->IE_AddrFamily != htons(AF_INET)) {
continue;
}
net.N_NetNumber = pie->IE_Destination;
if (pih->IH_Version == 2 && pie->IE_SubnetMask != 0) {
net.N_NetMask = pie->IE_SubnetMask;
}
else {
net.N_NetMask = GuessSubnetMask(net.N_NetNumber, NULL);
}
//
// lookup best route to the requested destination
// and get the metric
//
RTM_IPV4_SET_ADDR_AND_MASK(
&rna, net.N_NetNumber, net.N_NetMask
);
dwErr = RtmGetExactMatchDestination(
ig.IG_RtmHandle, &rna, RTM_BEST_PROTOCOL,
RTM_VIEW_MASK_UCAST, &rdi
);
if (dwErr != NO_ERROR) {
pie->IE_Metric = htonl(IPRIP_INFINITE);
}
else
{
//
// if there is no best route to this destination
// metric is INFINITE
//
if (rdi.ViewInfo[0].Route == NULL) {
pie->IE_Metric = htonl(IPRIP_INFINITE);
}
else {
dwErr = GetRouteInfo(
rdi.ViewInfo[0].Route, NULL, &rdi, &route
);
if (dwErr != NO_ERROR) {
pie->IE_Metric = htonl(IPRIP_INFINITE);
}
else {
//
// non-RIP routes are advertised with metric 2
//
pie->IE_Metric = (route.RR_RoutingProtocol == PROTO_IP_RIP ?
htonl(GETROUTEMETRIC(&route)) : htonl(2));
}
}
//
// release the dest info
//
dwErr = RtmReleaseDestInfo(ig.IG_RtmHandle, &rdi);
if (dwErr != NO_ERROR)
{
char szNet[20], szMask[20];
lstrcpy(szNet, INET_NTOA(route.RR_Network.N_NetNumber));
lstrcpy(szMask, INET_NTOA(route.RR_Network.N_NetMask));
TRACE3(
ROUTE, "error %d releasing dest %s:%s", dwErr,
szNet, szMask
);
}
}
RTM_ROUTE_FROM_IPRIP_ENTRY(&route, pie);
ub.UB_AddRoutine(&ub, &route);
}
RELEASE_BINDING_LOCK_SHARED();
//
// send the buffer now
//
ub.UB_FinishRoutine(&ub);
InterlockedIncrement(&ig.IG_Stats.GS_TotalResponsesSent);
}
} while(FALSE);
RELEASE_IF_LOCK_SHARED();
RIP_FREE(pContext);
TRACE0(LEAVE, "leaving ProcessRequest");
}
//----------------------------------------------------------------------------
// Function: ProcessResponse
//
// this function process an incoming IPRIP response packet
//----------------------------------------------------------------------------
VOID
ProcessResponse(
PVOID pContext
) {
DWORD dwSource;
PBYTE pPacket;
PIPRIP_IF_STATS pis;
PIF_TABLE pTable;
PIPRIP_HEADER pih;
BOOL bTriggerUpdate;
PIPRIP_ENTRY pie, piend;
PIPRIP_AUTHENT_ENTRY pae;
PIF_TABLE_ENTRY pite;
PIPRIP_IF_CONFIG pic;
PIPRIP_IP_ADDRESS paddr;
PINPUT_CONTEXT pwc;
PPEER_TABLE pPeers;
PPEER_TABLE_ENTRY ppte;
PIPRIP_PEER_STATS pps;
CHAR szSource[20], szNetwork[20];
LPSTR lpszTemp = NULL;
TRACE0(ENTER, "entering ProcessResponse");
bTriggerUpdate = FALSE;
pTable = ig.IG_IfTable;
pPeers = ig.IG_PeerTable;
ACQUIRE_IF_LOCK_SHARED();
do { // breakout loop
pwc = (PINPUT_CONTEXT)pContext;
//
// get pointer to receiving interface
//
pite = GetIfByIndex(pTable, pwc->IC_InterfaceIndex);
if (pite == NULL || IF_IS_INACTIVE(pite)) {
TRACE1(
RESPONSE, "processing response: interface %d not found",
pwc->IC_InterfaceIndex
);
break;
}
ZeroMemory(szSource, sizeof(szSource));
ZeroMemory(szNetwork, sizeof(szSource));
dwSource = pwc->IC_InputSource.sin_addr.s_addr;
lpszTemp = INET_NTOA(dwSource);
if (lpszTemp != NULL) { lstrcpy(szSource, lpszTemp); }
else { ZeroMemory(szSource, sizeof(szSource)); }
//
// get pointer to peer struct for sender
//
ACQUIRE_PEER_LOCK_SHARED();
ppte = GetPeerByAddress(pPeers, dwSource, GETMODE_EXACT, NULL);
if (ppte != NULL) { pps = &ppte->PTE_Stats; }
else { pps = NULL; }
RELEASE_PEER_LOCK_SHARED();
pis = &pite->ITE_Stats;
pic = pite->ITE_Config;
paddr = IPRIP_IF_ADDRESS_TABLE(pite->ITE_Binding) + pwc->IC_AddrIndex;
pPacket = pwc->IC_InputPacket.IP_Packet;
pih = (PIPRIP_HEADER)pPacket;
pie = (PIPRIP_ENTRY)(pPacket + sizeof(IPRIP_HEADER));
pae = (PIPRIP_AUTHENT_ENTRY)pie;
//
// make sure our configuration allows us to handle this packet
//
if ((pih->IH_Version != 2 &&
pic->IC_AcceptMode == IPRIP_ACCEPT_RIP2)) {
CHAR szVersion[10];
LPSTR lpszAddr = INET_NTOA(paddr->IA_Address);
InterlockedIncrement(&pis->IS_BadResponsePacketsReceived);
if (pps != NULL) {
InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer);
}
if (lpszAddr != NULL) {
TRACE2(
RESPONSE, "dropping non-v2 packet on RIPv2 interface %d (%s)",
pite->ITE_Index, lpszAddr
);
wsprintf(szVersion, "%d", pih->IH_Version);
LOGWARN4(
PACKET_VERSION_MISMATCH, szVersion, lpszAddr, szSource, "2", 0
);
}
break;
}
else
if ((pih->IH_Version != 1 &&
pic->IC_AcceptMode == IPRIP_ACCEPT_RIP1)) {
CHAR szVersion[10];
LPSTR lpszAddr = INET_NTOA(paddr->IA_Address);
InterlockedIncrement(&pis->IS_BadResponsePacketsReceived);
if (pps != NULL) {
InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer);
}
if (lpszAddr != NULL) {
TRACE2(
RESPONSE, "dropping RIPv2 packet on RIPv1 interface %d (%s)",
pite->ITE_Index, lpszAddr
);
wsprintf(szVersion, "%d", pih->IH_Version);
LOGWARN4(
PACKET_VERSION_MISMATCH, szVersion, lpszAddr, szSource, "1", 0
);
}
break;
}
//
// version 2 packets call for authentication processing;
//
if (pih->IH_Version == 2) {
DWORD dwErr;
dwErr = AuthenticatePacket(pPacket, pae, pic, pis, pps, &pie);
if (dwErr == ERROR_ACCESS_DENIED) {
LPSTR lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
RESPONSE, "dropping packet from %s on interface %d (%s): authentication failed",
szSource, pite->ITE_Index, lpszAddr
);
LOGWARN2(AUTHENTICATION_FAILED, lpszAddr, szSource, NO_ERROR);
}
break;
}
}
//
// need to lock the binding table since GuessSubnetMask will be called
// inside ProcessResponseEntry
// need to lock the global config since EnqueueSendEntry will be called
// inside ProcessResponseEntry
//
ACQUIRE_BINDING_LOCK_SHARED();
ACQUIRE_GLOBAL_LOCK_SHARED();
//
// process each entry; reserved fields must be checked for non-RIPv2
//
piend = (PIPRIP_ENTRY)(pPacket + pwc->IC_InputLength);
if (pih->IH_Version == 1) {
for ( ; pie < piend; pie++) {
//
// validate the route entry fields
//
if (pie->IE_AddrFamily != htons(AF_INET) ||
pie->IE_RouteTag != 0 || pie->IE_SubnetMask != 0 ||
pie->IE_Nexthop != 0) {
LPSTR lpszAddr;
//
// update stats on ignored entries
//
InterlockedIncrement(&pis->IS_BadResponseEntriesReceived);
if (pps != NULL) {
InterlockedIncrement(
&pps->PS_BadResponseEntriesFromPeer
);
}
lpszAddr = INET_NTOA(pie->IE_Destination);
if (lpszAddr != NULL) {
lstrcpy(szNetwork, lpszAddr);
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
LOGINFO3(
ROUTE_ENTRY_IGNORED, lpszAddr, szNetwork, szSource, 0
);
}
}
continue;
}
//
// entry is alright, process it
//
if (ProcessResponseEntry(
pite, pwc->IC_AddrIndex, dwSource, pie, pps
)) {
bTriggerUpdate = TRUE;
}
}
}
else
if (pih->IH_Version == 2) {
//
// this is a RIPv2 packet, so the reserved fields in entries
// may optionally contain information about the route;
//
for ( ; pie < piend; pie++) {
//
// validate the route entry fields
//
if (pie->IE_AddrFamily != htons(AF_INET)) {
LPSTR lpszAddr;
//
// update stats on ignored entries
//
InterlockedIncrement(&pis->IS_BadResponseEntriesReceived);
if (pps != NULL) {
InterlockedIncrement(
&pps->PS_BadResponseEntriesFromPeer
);
}
lpszAddr = INET_NTOA(pie->IE_Destination);
if (lpszAddr != NULL) {
lstrcpy(szNetwork, lpszAddr);
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
LOGINFO3(
ROUTE_ENTRY_IGNORED, lpszAddr, szNetwork, szSource, 0
);
}
}
continue;
}
//
// entry is alright, process it
//
if (ProcessResponseEntry(
pite, pwc->IC_AddrIndex, dwSource, pie, pps
)) {
bTriggerUpdate = TRUE;
}
}
}
else {
//
// this packet's version is greater than 2, so we ignore
// the contents of the reserved fields
//
for ( ; pie < piend; pie++) {
//
// validate the route entry fields
//
if (pie->IE_AddrFamily != htons(AF_INET)) {
LPSTR lpszAddr;
//
// update stats on ignored entries
//
InterlockedIncrement(&pis->IS_BadResponseEntriesReceived);
if (pps != NULL) {
InterlockedIncrement(
&pps->PS_BadResponseEntriesFromPeer
);
}
lpszAddr = INET_NTOA(pie->IE_Destination);
if (lpszAddr != NULL) {
lstrcpy(szNetwork, lpszAddr);
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
LOGINFO3(
ROUTE_ENTRY_IGNORED, lpszAddr, szNetwork, szSource, 0
);
}
}
continue;
}
//
// entry is alright, clear reserved fields and process
//
pie->IE_Nexthop = 0;
pie->IE_RouteTag = 0;
pie->IE_SubnetMask = 0;
if (ProcessResponseEntry(
pite, pwc->IC_AddrIndex, dwSource, pie, pps
)) {
bTriggerUpdate = TRUE;
}
}
}
RELEASE_GLOBAL_LOCK_SHARED();
RELEASE_BINDING_LOCK_SHARED();
//
// generate a triggered update if necessary
//
if (bTriggerUpdate) {
QueueRipWorker(WorkerFunctionStartTriggeredUpdate, NULL);
}
} while(FALSE);
RELEASE_IF_LOCK_SHARED();
RIP_FREE(pContext);
TRACE0(LEAVE, "leaving ProcessResponse");
}
//----------------------------------------------------------------------------
// Function: ProcessResponseEntry
//
// this function processes the given response packet entry, received
// on the given interface from the given source.
// If a triggered update is necessary, this function returns TRUE.
//----------------------------------------------------------------------------
BOOL
ProcessResponseEntry(
PIF_TABLE_ENTRY pITE,
DWORD dwAddrIndex,
DWORD dwSource,
PIPRIP_ENTRY pIE,
PIPRIP_PEER_STATS pPS
) {
IP_NETWORK in;
PIPRIP_IF_STATS pis;
PIPRIP_IF_CONFIG pic;
PIPRIP_IP_ADDRESS paddr;
CHAR szSource[32];
CHAR szNetmask[32];
CHAR szNexthop[32];
CHAR szNetwork[32];
BOOL bRouteExists, bRelRoute = FALSE;
RIP_IP_ROUTE route;
DWORD dwNetclassMask, dwNexthop, dwRipMetric;
DWORD dwErr, dwFlags, dwFound, dwNetwork, dwNetmask;
LPSTR lpszAddr;
RTM_NET_ADDRESS rna;
PRTM_ROUTE_INFO prri = NULL;
RTM_ROUTE_HANDLE hRtmRoute;
// TRACE0(ENTER, "entering ProcessResponseEntry");
pis = &pITE->ITE_Stats;
pic = pITE->ITE_Config;
paddr = IPRIP_IF_ADDRESS_TABLE(pITE->ITE_Binding) + dwAddrIndex;
//
// read destination and figure out subnet mask
// if mask is not given in the packet
//
dwNetwork = pIE->IE_Destination;
if (pIE->IE_SubnetMask == 0) {
dwNetmask = GuessSubnetMask(dwNetwork, &dwNetclassMask);
}
else {
//
// double-check the netclass mask, to accomodate supernets
//
dwNetmask = pIE->IE_SubnetMask;
dwNetclassMask = NETCLASS_MASK(dwNetwork);
if (dwNetclassMask > dwNetmask) {
dwNetclassMask = dwNetmask;
}
}
#if 1
dwNexthop = dwSource;
#else
// BUG 205349: using the nexthop field results in flapping
// when more than two routers are on the same network.
// The full fix is to distinguish between the source of the route
// and the nexthop of the route.
//
// read the next-hop field;
// if it is zero or it is not on the same subnet
// as the receiving interface, ignore it and use the address
// of the source as the next-hop.
// otherwise, use the address specified in the packet
// as the next-hop.
//
if (!pIE->IE_Nexthop ||
(pIE->IE_Nexthop & paddr->IA_Netmask) !=
(paddr->IA_Address & paddr->IA_Netmask)) { dwNexthop = dwSource; }
else { dwNexthop = pIE->IE_Nexthop; }
#endif
//
// set up variables used for error and information messages
//
lpszAddr = INET_NTOA(dwSource);
if (lpszAddr != NULL) { lstrcpy(szSource, lpszAddr);}
else { ZeroMemory(szSource, sizeof(szSource)); }
lpszAddr = INET_NTOA(dwNetwork);
if (lpszAddr != NULL) { lstrcpy(szNetwork, lpszAddr);}
else { ZeroMemory(szSource, sizeof(szSource)); }
lpszAddr = INET_NTOA(dwNetmask);
if (lpszAddr != NULL) { lstrcpy(szNetmask, lpszAddr);}
else { ZeroMemory(szSource, sizeof(szSource)); }
lpszAddr = INET_NTOA(dwNexthop);
if (lpszAddr != NULL) { lstrcpy(szNexthop, lpszAddr);}
else { ZeroMemory(szSource, sizeof(szSource)); }
if (pPS != NULL) {
InterlockedExchange(
&pPS->PS_LastPeerRouteTag, (DWORD)ntohs(pIE->IE_RouteTag)
);
}
do { // breakout loop
//
// make sure metric is in rational range
//
dwRipMetric = ntohl(pIE->IE_Metric);
if (dwRipMetric > IPRIP_INFINITE) {
TRACE4(
RESPONSE,
"metric == %d, ignoring route to %s via %s advertised by %s",
dwRipMetric, szNetwork, szNexthop, szSource
);
LOGWARN3(
ROUTE_METRIC_INVALID,szNetwork, szNexthop, szSource, dwRipMetric
);
break;
}
//
// make sure route is to valid address type
//
if (CLASSD_ADDR(dwNetwork) || CLASSE_ADDR(dwNetwork)) {
TRACE3(
RESPONSE,
"invalid class, ignoring route to %s via %s advertised by %s",
szNetwork, szNexthop, szSource
);
LOGINFO3(
ROUTE_CLASS_INVALID, szNetwork, szNexthop, szSource, NO_ERROR
);
break;
}
//
// make sure route is not to loopback address
//
if (IS_LOOPBACK_ADDR(dwNetwork)) {
TRACE3(
RESPONSE,
"ignoring loopback route to %s via %s advertised by %s",
szNetwork, szNexthop, szSource
);
LOGWARN3(
LOOPBACK_ROUTE_INVALID, szNetwork, szNexthop, szSource, NO_ERROR
);
break;
}
//
// make sure route it is not a broadcast route
// The first condition uses the netmask information received in the
// advertisement
// The second condition uses the netmask which is computed based
// on the address class
// The third condition checks for the all 1's broadcast
//
if ( IS_DIRECTED_BROADCAST_ADDR(dwNetwork, dwNetmask) ||
IS_DIRECTED_BROADCAST_ADDR(dwNetwork, dwNetclassMask) ||
IS_LOCAL_BROADCAST_ADDR(dwNetwork) ) {
TRACE3(
RESPONSE,
"ignoring broadcast route to %s via %s advertised by %s",
szNetwork, szNexthop, szSource
);
LOGWARN3(
BROADCAST_ROUTE_INVALID, szNetwork, szNexthop, szSource, 0
);
break;
}
//
// discard host routes if the receiving interface
// is not configured to accept host routes
//
//
// At this stage the broadcast routes have already been weeded out.
// So it is safe to assume that
// if Network address width is greater than the Netmask address
// width, then it is a host route.
// Or,
// if the dwNetmask is 255.255.255.255 then it is a host route.
//
if ( ((dwNetwork & ~dwNetmask) != 0) || (dwNetmask == HOSTADDR_MASK) ) {
//
// This is a host-route; see whether we can accept it.
//
if (IPRIP_FLAG_IS_ENABLED(pic, ACCEPT_HOST_ROUTES)) {
//
// The host route can be accepted.
// Set the mask to all-ones to ensure that
// the route can be added to the stack.
//
dwNetmask = HOSTADDR_MASK;
}
else {
//
// The host-route must be rejected.
//
TRACE3(
RESPONSE,
"ignoring host route to %s via %s advertised by %s",
szNetwork, szNexthop, szSource
);
LOGINFO3(
HOST_ROUTE_INVALID, szNetwork, szNexthop, szSource, NO_ERROR
);
break;
}
}
//
// discard default routes if the receiving interface
// is not configured to accept default routes
//
if (dwNetwork == 0 &&
IPRIP_FLAG_IS_DISABLED(pic, ACCEPT_DEFAULT_ROUTES)) {
TRACE3(
RESPONSE,
"ignoring default route to %s via %s advertised by %s",
szNetwork, szNexthop, szSource
);
LOGINFO3(
DEFAULT_ROUTE_INVALID, szNetwork, szNexthop, szSource, NO_ERROR
);
break;
}
//
// put the route through the accept filters
//
if (pic->IC_AcceptFilterMode != IPRIP_FILTER_DISABLED) {
//
// discard the route if the receiving interface is including
// all routes but this route is listed as an exception, or if
// the receiving interface is excluding all routes and this
// route is not listed as an exception
//
IS_ROUTE_IN_ACCEPT_FILTER(pic, dwNetwork, dwFound);
if ((pic->IC_AcceptFilterMode == IPRIP_FILTER_INCLUDE && !dwFound)||
(pic->IC_AcceptFilterMode == IPRIP_FILTER_EXCLUDE && dwFound)) {
TRACE3(
RESPONSE,
"ignoring filtered route to %s via %s advertised by %s",
szNetwork, szNexthop, szSource
);
LOGINFO3(
ROUTE_FILTERED, szNetwork, szNexthop, szSource, NO_ERROR
);
break;
}
}
//
// see if the route already exists in RTM's table
//
in.N_NetNumber = dwNetwork;
in.N_NetMask = dwNetmask;
RTM_IPV4_SET_ADDR_AND_MASK( &rna, dwNetwork, dwNetmask );
prri = RIP_ALLOC( RTM_SIZE_OF_ROUTE_INFO(
ig.IG_RtmProfile.MaxNextHopsInRoute
) );
if ( prri == NULL ) {
dwErr = GetLastError();
TRACE2(
ANY, "ProcessResponseEntry: error %d while allocating %d bytes",
dwErr,
RTM_SIZE_OF_ROUTE_INFO(ig.IG_RtmProfile.MaxNextHopsInRoute)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
prri-> RouteOwner = ig.IG_RtmHandle;
dwErr = RtmGetExactMatchRoute(
ig.IG_RtmHandle, &rna, RTM_MATCH_OWNER, prri, 0,
RTM_VIEW_MASK_ANY, &hRtmRoute
);
if ((dwErr != NO_ERROR) || (hRtmRoute == NULL)) {
bRouteExists = FALSE;
}
else{
bRelRoute = TRUE;
dwErr = GetRouteInfo(
hRtmRoute, prri, NULL, &route
);
if (dwErr != NO_ERROR) {
bRouteExists = FALSE;
break;
}
else {
bRouteExists = TRUE;
}
}
//
// add the cost of this interface to the metric
//
dwRipMetric = min(IPRIP_INFINITE, dwRipMetric + pic->IC_Metric);
if (dwRipMetric >= IPRIP_INFINITE && !bRouteExists) {
TRACE4(
RESPONSE,
"metric==%d, ignoring route to %s via %s advertised by %s",
IPRIP_INFINITE, szNetwork, szNexthop, szSource
);
break;
}
//
// ROUTE ADDITION/UPDATE/REMOVAL:
//
if (!bRouteExists) {
//
// NEW ROUTE:
//
// set up struct to pass to RTM
//
ZeroMemory(&route, sizeof(route));
route.RR_RoutingProtocol = PROTO_IP_RIP;
route.RR_Network = in;
SETROUTEMETRIC(&route, dwRipMetric);
route.RR_InterfaceID = pITE->ITE_Index;
route.RR_NextHopAddress.N_NetNumber = dwNexthop;
route.RR_NextHopAddress.N_NetMask = paddr->IA_Netmask;
SETROUTETAG(&route, ntohs(pIE->IE_RouteTag));
//
// add route to RTM
//
COMPUTE_ROUTE_METRIC(&route);
#if ROUTE_DBG
TRACE3(
RESPONSE,
"Adding route to %s via %s advertised by %s",
szNetwork, szNexthop, szSource
);
#endif
dwErr = AddRtmRoute(
ig.IG_RtmHandle, &route, NULL,
pic->IC_RouteExpirationInterval,
pic->IC_RouteRemovalInterval,
TRUE
);
if (dwErr != NO_ERROR) {
TRACE4(
RESPONSE,
"error %d adding route to %s via %s advertised by %s",
dwErr, szNetwork, szNexthop, szSource
);
LOGINFO3(
ADD_ROUTE_FAILED_2, szNetwork, szNexthop, szSource, dwErr
);
break;
}
InterlockedIncrement(&ig.IG_Stats.GS_SystemRouteChanges);
LOGINFO3(
NEW_ROUTE_LEARNT_1, szNetwork, szNexthop, szSource, NO_ERROR
);
}
else {
DWORD dwTimer = 0, dwChangeFlags = 0;
BOOL bTriggerUpdate = FALSE, bActive = TRUE;
//
// EXISTING ROUTE:
//
// reset time-to-live, and mark route as expiring,
// if this advertisement is from the same source
// as the existing route, and the existing route's metric
// is not already INFINITE; thus, if a route has been
// advertised as unreachable, we don't reset its time-to-live
// just because we hear an advertisement for the route
//
if (dwNexthop == route.RR_NextHopAddress.N_NetNumber &&
GETROUTEMETRIC(&route) != IPRIP_INFINITE) {
dwTimer = pic->IC_RouteExpirationInterval;
//
// if existing route was a summary route, make sure
// set the validity flag before you mark it as a
// non summary route. Fix for bug #81544
//
if ( GETROUTEFLAG( &route ) == ROUTEFLAG_SUMMARY ) {
CHAR szRouteNetwork[20], szRouteNetmask[20];
LPSTR lpszAddrTemp = INET_NTOA(route.RR_Network.N_NetNumber);
if (lpszAddrTemp != NULL) {
lstrcpy(szRouteNetwork, lpszAddrTemp);
lpszAddrTemp = INET_NTOA(route.RR_Network.N_NetMask);
if (lpszAddrTemp != NULL) {
lstrcpy(szRouteNetmask, lpszAddrTemp);
TRACE2(
RESPONSE,
"%s %s summary route to valid route",
szRouteNetwork, szRouteNetmask
);
}
}
SETROUTEFLAG( &route, ~ROUTEFLAG_SUMMARY );
}
}
//
// we only need to do further processing if
// (a) the advertised route is from the same source as
// the existing route and the metrics are different, or
// (b) the advertised route has a better metric
//
if ((dwNexthop == route.RR_NextHopAddress.N_NetNumber &&
dwRipMetric != GETROUTEMETRIC(&route)) ||
(dwRipMetric < GETROUTEMETRIC(&route))) {
//
// if the next-hop's differ, adopt the new next-hop
//
if (dwNexthop != route.RR_NextHopAddress.N_NetNumber) {
route.RR_NextHopAddress.N_NetNumber = dwNexthop;
route.RR_NextHopAddress.N_NetMask = paddr->IA_Netmask;
InterlockedIncrement(&ig.IG_Stats.GS_SystemRouteChanges);
LOGINFO2(
ROUTE_NEXTHOP_CHANGED, szNetwork, szNexthop, NO_ERROR
);
}
else {
CHAR szMetric[12];
wsprintf(szMetric, "%d", dwRipMetric);
LOGINFO3(
ROUTE_METRIC_CHANGED, szNetwork, szNexthop, szMetric, 0
);
}
//
// check the metric to decide the new time-to-live
//
if (dwRipMetric == IPRIP_INFINITE) {
//
// Delete the route
//
#if ROUTE_DBG
TRACE2(
ROUTE, "Deleting route to %s:%s", szNetwork, szNetmask
);
#endif
dwTimer = 0;
dwErr = RtmReferenceHandles(
ig.IG_RtmHandle, 1, &hRtmRoute
);
if (dwErr != NO_ERROR) {
TRACE3(
ANY, "error %d referencing route to %s:%s", dwErr,
szNetwork, szNetmask
);
break;
}
dwErr = RtmDeleteRouteToDest(
ig.IG_RtmHandle, hRtmRoute,
&dwChangeFlags
);
if (dwErr != NO_ERROR) {
TRACE3(
ANY, "error %d deleting route to %s:%s", dwErr,
szNetwork, szNetmask
);
}
break;
}
else {
//
// set the expiration flag and use the expiration TTL
//
dwTimer = pic->IC_RouteExpirationInterval;
}
//
// use the advertised metric, and set the interface ID,
// adapter index, and route tag
//
SETROUTEMETRIC(&route, dwRipMetric);
route.RR_InterfaceID = pITE->ITE_Index;
// route.RR_FamilySpecificData.FSD_AdapterIndex =
// pITE->ITE_Binding.AdapterIndex;
SETROUTETAG(&route, ntohs(pIE->IE_RouteTag));
//
// always require a triggered update if we reach here
//
bTriggerUpdate = TRUE;
}
if (dwTimer != 0) {
COMPUTE_ROUTE_METRIC(&route);
#if ROUTE_DBG
TRACE4(
RESPONSE,
"Editing route to %s via %s advertised by %s, metric %d",
szNetwork, szNexthop, szSource, dwRipMetric
);
#endif
dwErr = AddRtmRoute(
ig.IG_RtmHandle, &route, NULL, dwTimer,
pic-> IC_RouteRemovalInterval, TRUE
);
if (dwErr != NO_ERROR) {
TRACE4(
RESPONSE,
"error %d adding route to %s via %s advertised by %s",
dwErr, szNetwork, szNexthop, szSource
);
LOGINFO3(
ADD_ROUTE_FAILED_2,szNetwork,szNexthop,szSource, dwErr
);
}
}
}
} while(FALSE);
//
// if some sort of error occured, increment stats appropriately
//
if (dwErr != NO_ERROR ) {
InterlockedIncrement(&pis->IS_BadResponseEntriesReceived);
if (pPS != NULL) {
InterlockedIncrement(&pPS->PS_BadResponseEntriesFromPeer);
}
}
//
// Release the dest info structure
//
if (bRelRoute) {
dwErr = RtmReleaseRouteInfo(ig.IG_RtmHandle, prri);
if (dwErr != NO_ERROR) {
TRACE2(
ANY, "error %d releasing dest %s", dwErr, szNetwork
);
}
}
if ( prri ) {
RIP_FREE(prri);
}
//
// always return FALSE. This way no RIP route add/delete/operations
// will set of the triggered update mechanism. This mech. is set of
// by route change notifications recevied from RTMv2
//
return FALSE;
}
//----------------------------------------------------------------------------
// Function: WorkerFunctionStartFullUpdate
//
// this function initiates a full-update. It checks to see if a full-update
// is already pending, and if not, it sets the full-update-pending flag and
// schedules the full-update work item. Then it sets a flag on its interface
// indicating a full-update should be generated on the interface.
//----------------------------------------------------------------------------
VOID
WorkerFunctionStartFullUpdate(
PVOID pContext,
BOOLEAN bNotUsed
) {
DWORD dwIndex;
PIF_TABLE pTable;
PIF_TABLE_ENTRY pite;
PIPRIP_IF_CONFIG pic;
if (!ENTER_RIP_API()) { return; }
TRACE0(ENTER, "entering WorkerFunctionStartFullUpdate");
pTable = ig.IG_IfTable;
ACQUIRE_IF_LOCK_SHARED();
EnterCriticalSection(&pTable->IT_CS);
do { // breakout loop
//
// retrieve the interface on which the full-update will be sent
//
dwIndex = PtrToUlong(pContext);
pite = GetIfByIndex(pTable, dwIndex);
if (pite == NULL) {
TRACE1(
SEND, "starting full-update: interface %d not found", dwIndex
);
break;
}
//
// if the interface is no longer active, do nothing
//
if (IF_IS_INACTIVE(pite)) {
pite->ITE_Flags &= ~ITEFLAG_FULL_UPDATE_INQUEUE;
break;
}
//
// do nothing if a full-update is already pending
//
if (IF_FULL_UPDATE_PENDING(pite)) { break; }
//
// only do full-updates on periodic-update interfaces
// and don't do full-updates on interfaces configured to be silent;
//
if (pite->ITE_Config->IC_UpdateMode != IPRIP_UPDATE_PERIODIC ||
pite->ITE_Config->IC_AnnounceMode == IPRIP_ANNOUNCE_DISABLED) {
pite->ITE_Flags &= ~ITEFLAG_FULL_UPDATE_INQUEUE;
break;
}
//
// set the full update flags on the interface;
//
pite->ITE_Flags |= ITEFLAG_FULL_UPDATE_PENDING;
//
// if there is no full-update pending,
// queue the full-update finishing function
//
if (!IPRIP_FULL_UPDATE_PENDING(pTable)) {
DWORD dwRand;
//
// set the global full-update-pending flag
//
pTable->IT_Flags |= IPRIP_FLAG_FULL_UPDATE_PENDING;
//
// we need a random interval between 1 and 5 seconds
//
dwRand = GetTickCount();
dwRand = RtlRandom(&dwRand);
dwRand = 1000 + (DWORD)((double)dwRand / MAXLONG * (4.0 * 1000));
//
// Schedule a full update
//
if (!ChangeTimerQueueTimer(
ig.IG_TimerQueueHandle, pTable->IT_FinishFullUpdateTimer,
dwRand, 10000000)) {
TRACE1(
SEND, "error %d setting finish full update timer",
GetLastError()
);
}
}
} while(FALSE);
LeaveCriticalSection(&pTable->IT_CS);
RELEASE_IF_LOCK_SHARED();
TRACE0(LEAVE, "leaving WorkerFunctionStartFullUpdate");
LEAVE_RIP_API();
}
//----------------------------------------------------------------------------
// Function: EnqueueStartFullUpdate
//
// This function is called to enqueue the next start-full-update event
// for the given interface. The interface's state is updated as necessary.
// It assumes that the following locks have been acquired:
// IT_RWL - shared
// IT_CS - exclusive
// TimerQueue lock - exclusive
//----------------------------------------------------------------------------
VOID
EnqueueStartFullUpdate(
PIF_TABLE_ENTRY pite,
LARGE_INTEGER qwLastFullUpdateTime
) {
//
// set last-full-update time
//
if (!ChangeTimerQueueTimer(
ig.IG_TimerQueueHandle, pite->ITE_FullOrDemandUpdateTimer,
RipSecsToMilliSecs(pite->ITE_Config->IC_FullUpdateInterval),
10000000
)) {
TRACE1(
SEND, "error %d updating start full update timer",
GetLastError()
);
pite->ITE_Flags &= ~ITEFLAG_FULL_UPDATE_INQUEUE;
}
}
//----------------------------------------------------------------------------
// Function: WorkerFunctionFinishFullUpdate
//
// This function sends a full-update on every interface which has the
// full-update pending flag set, and schedules the next full-update on each
// interface.
//----------------------------------------------------------------------------
VOID
WorkerFunctionFinishFullUpdate(
PVOID pContext,
BOOLEAN bNotUsed
) {
PIF_TABLE pTable;
PIPRIP_IF_CONFIG pic;
PLIST_ENTRY ple, phead;
DWORD dwErr, dwIndex, dwIfCount;
LARGE_INTEGER qwCurrentTime;
PIF_TABLE_ENTRY pite, *ppite, *ppitend, *pIfList;
if (!ENTER_RIP_API()) { return; }
TRACE0(ENTER, "entering WorkerFunctionFinishFullUpdate");
pTable = ig.IG_IfTable;
ACQUIRE_IF_LOCK_SHARED();
EnterCriticalSection(&pTable->IT_CS);
pIfList = NULL;
ppite = NULL;
do {
//
// first count how many there are
//
dwIfCount = 0;
phead = &pTable->IT_ListByAddress;
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress);
if (IF_IS_ACTIVE(pite) && IF_FULL_UPDATE_PENDING(pite)) {
pic = pite->ITE_Config;
if (pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC &&
pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED) {
++dwIfCount;
}
}
}
if (dwIfCount == 0) {
TRACE0(SEND, "finishing full-update: no interfaces");
break;
}
//
// then make memory for the interface pointers
//
pIfList = RIP_ALLOC(dwIfCount * sizeof(PIF_TABLE_ENTRY));
if (pIfList == NULL) {
dwErr = GetLastError();
TRACE2(
SEND, "error code %d allocating %d bytes for interface list",
dwErr, dwIfCount * sizeof(PIF_TABLE_ENTRY)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
//
// enqueue the next full-update for each interface
//
RipQuerySystemTime(&qwCurrentTime);
pTable->IT_LastUpdateTime = qwCurrentTime;
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
pite=CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress);
if (IF_IS_ACTIVE(pite) && IF_FULL_UPDATE_PENDING(pite)) {
pic = pite->ITE_Config;
if (pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC &&
pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED) {
EnqueueStartFullUpdate(pite, qwCurrentTime);
}
}
}
break;
}
//
// and copy the interface pointers to the memory allocated
//
ppitend = pIfList + dwIfCount;
for (ple = phead->Flink, ppite = pIfList;
ple != phead && ppite < ppitend; ple = ple->Flink) {
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress);
if (IF_IS_ACTIVE(pite) && IF_FULL_UPDATE_PENDING(pite)) {
pic = pite->ITE_Config;
if (pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC &&
pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED) {
*ppite++ = pite;
}
}
}
//
// send the updates
//
TRACE1(SEND, "sending full-updates on %d interfaces", dwIfCount);
SendRoutes(pIfList, dwIfCount, SENDMODE_FULL_UPDATE, 0, 0);
//
// enqueue the next full-update for each interface
//
RipQuerySystemTime(&qwCurrentTime);
pTable->IT_LastUpdateTime = qwCurrentTime;
for (ppite = pIfList; ppite < ppitend; ppite++) {
EnqueueStartFullUpdate(*ppite, qwCurrentTime);
}
//
// free the memory allocated for the interface pointers
//
RIP_FREE(pIfList);
} while(FALSE);
//
// clear the full-update pending flags
//
phead = &pTable->IT_ListByAddress;
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress);
pite->ITE_Flags &= ~ITEFLAG_FULL_UPDATE_PENDING;
}
pTable->IT_Flags &= ~IPRIP_FLAG_FULL_UPDATE_PENDING;
LeaveCriticalSection(&pTable->IT_CS);
RELEASE_IF_LOCK_SHARED();
TRACE0(LEAVE, "leaving WorkerFunctionFinishFullUpdate");
LEAVE_RIP_API();
}
//----------------------------------------------------------------------------
// Function: FinishTriggeredUpdate
//
// This function is responsible for sending out a triggered update
// on all interfaces which have triggered updates enabled.
// No triggered updates are sent on interfaces which already have
// a full-update pending.
// Assumes interface table is locked for reading or writing,
// and update-lock (IT_CS) is held.
//----------------------------------------------------------------------------
VOID
FinishTriggeredUpdate(
) {
PIF_TABLE pTable;
PIPRIP_IF_STATS pis;
DWORD dwErr, dwIfCount;
PIPRIP_IF_CONFIG pic = NULL;
PLIST_ENTRY ple, phead;
LARGE_INTEGER qwCurrentTime;
PIF_TABLE_ENTRY pite, *ppite, *ppitend, *pIfList;
pTable = ig.IG_IfTable;
//
// we lock the send queue now so that no routes are added
// until the existing ones are transmitted
//
ACQUIRE_LIST_LOCK(ig.IG_SendQueue);
do { // breakout loop
//
// count the interfaces on which the triggered update will be sent
//
dwIfCount = 0;
phead = &pTable->IT_ListByAddress;
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress);
pic = pite->ITE_Config;
if (IF_IS_ACTIVE(pite) && !IF_FULL_UPDATE_PENDING(pite) &&
pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC &&
pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED &&
IPRIP_FLAG_IS_ENABLED(pic, TRIGGERED_UPDATES)) {
++dwIfCount;
}
}
if (dwIfCount == 0) {
TRACE0(SEND, "finishing triggered-update: no interfaces");
break;
}
//
// allocate memory to hold the interface pointers
//
pIfList = RIP_ALLOC(dwIfCount * sizeof(PIF_TABLE_ENTRY));
if (pIfList == NULL) {
dwErr = GetLastError();
TRACE2(
SEND, "error code %d allocating %d bytes for interface list",
dwErr, dwIfCount * sizeof(PIF_TABLE_ENTRY)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// copy the interface pointers to the allocated memory
//
ppitend = pIfList + dwIfCount;
for (ple = phead->Flink, ppite = pIfList;
ple != phead && ppite < ppitend; ple = ple->Flink) {
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress);
pic = pite->ITE_Config;
if (IF_IS_ACTIVE(pite) && !IF_FULL_UPDATE_PENDING(pite) &&
pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC &&
pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED &&
IPRIP_FLAG_IS_ENABLED(pic, TRIGGERED_UPDATES)) {
*ppite++ = pite;
}
}
//
// send the triggered-update routes
//
TRACE1(SEND, "sending triggered-updates on %d interfaces", dwIfCount);
SendRoutes(pIfList, dwIfCount, SENDMODE_TRIGGERED_UPDATE, 0, 0);
//
// update the statistics for each interface
//
for (ppite = pIfList; ppite < ppitend; ppite++) {
pis = &(*ppite)->ITE_Stats;
InterlockedIncrement(&pis->IS_TriggeredUpdatesSent);
}
//
// update the last time at which an update was sent
//
RipQuerySystemTime(&pTable->IT_LastUpdateTime);
//
// free the memory allocated for the interfaces
//
RIP_FREE(pIfList);
} while (FALSE);
//
// make sure send-queue is empty
//
FlushSendQueue(ig.IG_SendQueue);
RELEASE_LIST_LOCK(ig.IG_SendQueue);
pTable->IT_Flags &= ~IPRIP_FLAG_TRIGGERED_UPDATE_PENDING;
return;
}
//----------------------------------------------------------------------------
// Function: WorkerFunctionStartTriggeredUpdate
//
// This function checks to see if the minimum interval between triggered
// updates has elapsed, and if so, sends a triggered update. Otherwise,
// it schedules the triggered update to be sent, and sets flags to indicate
// that a triggered update is pending
//----------------------------------------------------------------------------
VOID
WorkerFunctionStartTriggeredUpdate(
PVOID pContext
) {
PIF_TABLE pTable;
LARGE_INTEGER qwCurrentTime, qwSoonestTriggerTime;
if (!ENTER_RIP_WORKER()) { return; }
TRACE0(ENTER, "entering WorkerFunctionStartTriggeredUpdate");
pTable = ig.IG_IfTable;
ACQUIRE_IF_LOCK_SHARED();
EnterCriticalSection(&pTable->IT_CS);
//
// if triggered update is not pending, queue a triggered update
//
if (!IPRIP_TRIGGERED_UPDATE_PENDING(pTable)) {
//
// figure out when is the soonest time a triggered update
// can be sent, based on the configured minimum interval
// between triggered updates (in seconds) and the last time
// a triggered update was generated (in 100-nanosecond units)
//
ACQUIRE_GLOBAL_LOCK_SHARED();
qwSoonestTriggerTime.HighPart = 0;
qwSoonestTriggerTime.LowPart =
ig.IG_Config->GC_MinTriggeredUpdateInterval;
RipSecsToSystemTime(&qwSoonestTriggerTime);
RELEASE_GLOBAL_LOCK_SHARED();
qwSoonestTriggerTime = RtlLargeIntegerAdd(
qwSoonestTriggerTime,
pTable->IT_LastUpdateTime
);
RipQuerySystemTime(&qwCurrentTime);
//
// figure out if clock has been set backward, by comparing
// the current time against the last update time
//
if (RtlLargeIntegerLessThan(
qwCurrentTime, pTable->IT_LastUpdateTime
)) {
//
// Send triggered update anyway, since there is no way
// to figure out the if minimum time between updates has
// elapsed
//
FinishTriggeredUpdate();
}
else if (RtlLargeIntegerLessThan(qwCurrentTime, qwSoonestTriggerTime)) {
//
// must defer the triggered update
//
qwSoonestTriggerTime = RtlLargeIntegerSubtract(
qwSoonestTriggerTime, qwCurrentTime
);
RipSystemTimeToMillisecs(&qwSoonestTriggerTime);
if (!ChangeTimerQueueTimer(
ig.IG_TimerQueueHandle,
pTable->IT_FinishTriggeredUpdateTimer,
qwSoonestTriggerTime.LowPart, 10000000
)) {
TRACE1(
SEND, "error %d updating finish update timer",
GetLastError()
);
}
else {
pTable->IT_Flags |= IPRIP_FLAG_TRIGGERED_UPDATE_PENDING;
}
}
else {
//
// the minimum time between triggered updates has elapsed,
// so send the triggered update now
//
FinishTriggeredUpdate();
}
}
LeaveCriticalSection(&pTable->IT_CS);
RELEASE_IF_LOCK_SHARED();
TRACE0(LEAVE, "leaving WorkerFunctionStartTriggeredUpdate");
LEAVE_RIP_WORKER();
}
//----------------------------------------------------------------------------
// Function: WorkerFunctionFinishTriggeredUpdate
//
// This function generates a triggered update on all interfaces which
// do not have triggered updates disabled.
//----------------------------------------------------------------------------
VOID
WorkerFunctionFinishTriggeredUpdate(
PVOID pContext,
BOOLEAN bNotUsed
) {
PIF_TABLE pTable;
if (!ENTER_RIP_API()) { return; }
TRACE0(ENTER, "entering WorkerFunctionFinishTriggeredUpdate");
pTable = ig.IG_IfTable;
ACQUIRE_IF_LOCK_SHARED();
EnterCriticalSection(&pTable->IT_CS);
FinishTriggeredUpdate();
LeaveCriticalSection(&pTable->IT_CS);
RELEASE_IF_LOCK_SHARED();
TRACE0(LEAVE, "leaving WorkerFunctionFinishTriggeredUpdate");
LEAVE_RIP_API();
return;
}
//----------------------------------------------------------------------------
// Function: WorkerFunctionStartDemandUpdate
//
// This function initiates a demand-update on the speficied interface,
// sending a general request on the interface. It then schedules a work-item
// to report back to Router Manager when the update is done
//----------------------------------------------------------------------------
VOID
WorkerFunctionStartDemandUpdate(
PVOID pContext
) {
PIF_TABLE pTable;
RIP_IP_ROUTE route;
PUPDATE_CONTEXT pwc;
PIF_TABLE_ENTRY pite;
DWORD dwErr, dwIndex;
if (!ENTER_RIP_WORKER()) { return; }
TRACE0(ENTER, "entering WorkerFunctionStartDemandUpdate");
pTable = ig.IG_IfTable;
ACQUIRE_IF_LOCK_SHARED();
do { // breakout loop
//
// retrieve the interface on which to perform the demand update
//
dwIndex = PtrToUlong(pContext);
pite = GetIfByIndex(pTable, dwIndex);
if (pite == NULL) {
TRACE1(SEND, "demand-update: interface %d not found", dwIndex);
break;
}
//
// make sure interface is active and has demand-updates enabled
//
if (IF_IS_INACTIVE(pite)) {
TRACE1(SEND, "demand-update: interface %d not active", dwIndex);
EnqueueDemandUpdateMessage(dwIndex, ERROR_CAN_NOT_COMPLETE);
break;
}
else
if (pite->ITE_Config->IC_UpdateMode != IPRIP_UPDATE_DEMAND) {
TRACE1(SEND, "demand-updates disabled on interface %d ", dwIndex);
EnqueueDemandUpdateMessage(dwIndex, ERROR_CAN_NOT_COMPLETE);
break;
}
//
// setup the update context
//
pwc = RIP_ALLOC(sizeof(UPDATE_CONTEXT));
if (pwc == NULL) {
dwErr = GetLastError();
TRACE2(
SEND, "error %d allocating %d bytes",
dwErr, sizeof(UPDATE_CONTEXT)
);
EnqueueDemandUpdateMessage(dwIndex, dwErr);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
pwc->UC_InterfaceIndex = dwIndex;
pwc->UC_RetryCount = 1;
pwc->UC_RouteCount = 0;
//
// Create a timer for the demand update checks
//
if (!CreateTimerQueueTimer(
&pite->ITE_FullOrDemandUpdateTimer,
ig.IG_TimerQueueHandle,
WorkerFunctionFinishDemandUpdate, (PVOID)pwc,
5000, 5000, 0
)) {
EnqueueDemandUpdateMessage(dwIndex, GetLastError());
}
//
// request routing tables from neighbors
//
SendGeneralRequest(pite);
} while (FALSE);
RELEASE_IF_LOCK_SHARED();
TRACE0(LEAVE, "leaving WorkerFunctionStartDemandUpdate");
LEAVE_RIP_WORKER();
}
//----------------------------------------------------------------------------
// Function: WorkerFunctionFinishDemandUpdate
//
// This function queues a message informing the Router Manager that
// the demand-update requested is complete
//----------------------------------------------------------------------------
VOID
WorkerFunctionFinishDemandUpdate(
PVOID pContext,
BOOLEAN bNotUsed
) {
PIF_TABLE pTable;
PUPDATE_CONTEXT pwc;
PIF_TABLE_ENTRY pite;
DWORD dwErr, dwIndex, dwRouteCount;
if (pContext == NULL) { return; }
if (!ENTER_RIP_API()) { return; }
TRACE0(ENTER, "entering WorkerFunctionFinishDemandUpdate");
//
// get the update context
//
pwc = (PUPDATE_CONTEXT)pContext;
dwIndex = pwc->UC_InterfaceIndex;
pTable = ig.IG_IfTable;
ACQUIRE_IF_LOCK_SHARED();
do {
//
// retrieve the interface being updated
//
pite = GetIfByIndex(pTable, dwIndex);
if (pite == NULL) {
EnqueueDemandUpdateMessage(dwIndex, ERROR_CAN_NOT_COMPLETE);
break;
}
//
// report failure if the interface is no longer active
//
if (!IF_IS_ACTIVE(pite)) {
EnqueueDemandUpdateMessage(dwIndex, ERROR_CAN_NOT_COMPLETE);
break;
}
//
// get a count of the routes now on the interface
//
dwRouteCount = CountInterfaceRoutes(dwIndex);
//
// if there are still no routes, send another request
// unless we have sent the maximum number of requests
//
if (dwRouteCount == 0 && ++pwc->UC_RetryCount < MAX_UPDATE_REQUESTS) {
SendGeneralRequest(pite);
break;
}
//
// if the number of routes has not changed in the last 5 seconds,
// tell the router manager that the update is complete;
// otherwise, update the route count and enqueue another check
//
if (pwc->UC_RouteCount == dwRouteCount) {
EnqueueDemandUpdateMessage(dwIndex, NO_ERROR);
RIP_FREE(pwc);
if (!DeleteTimerQueueTimer(
ig.IG_TimerQueueHandle, pite->ITE_FullOrDemandUpdateTimer,
NULL)) {
TRACE1(
SEND, "error %d deleting demand update timer",
GetLastError()
);
}
pite->ITE_FullOrDemandUpdateTimer = NULL;
}
else {
pwc->UC_RouteCount = dwRouteCount;
}
} while(FALSE);
RELEASE_IF_LOCK_SHARED();
TRACE0(LEAVE, "leaving WorkerFunctionFinishDemandUpdate");
LEAVE_RIP_API();
}
//----------------------------------------------------------------------------
// Function: CountInterfaceRoutes
//
// Returns a count of the RIP routes associated with the specified interface
//----------------------------------------------------------------------------
DWORD
CountInterfaceRoutes(
DWORD dwInterfaceIndex
) {
HANDLE hRouteEnum;
PHANDLE phRoutes = NULL;
DWORD dwHandles, dwFlags, i, dwErr, dwCount = 0;
dwErr = RtmCreateRouteEnum(
ig.IG_RtmHandle, NULL, RTM_VIEW_MASK_UCAST,
RTM_ENUM_OWN_ROUTES, NULL, RTM_MATCH_INTERFACE,
NULL, dwInterfaceIndex, &hRouteEnum
);
if (dwErr != NO_ERROR) {
TRACE1(
ANY, "CountInterfaceRoutes : error %d creating enum handle",
dwErr
);
return 0;
}
//
// allocate handle array large enough to hold max handles in an
// enum
//
phRoutes = RIP_ALLOC(ig.IG_RtmProfile.MaxHandlesInEnum * sizeof(HANDLE));
if ( phRoutes == NULL ) {
dwErr = GetLastError();
TRACE2(
ANY, "CountInterfaceRoutes: error %d while allocating %d bytes"
" to hold max handles in an enum",
dwErr, ig.IG_RtmProfile.MaxHandlesInEnum * sizeof(HANDLE)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
return 0;
}
do
{
dwHandles = ig.IG_RtmProfile.MaxHandlesInEnum;
dwErr = RtmGetEnumRoutes(
ig.IG_RtmHandle, hRouteEnum, &dwHandles, phRoutes
);
for ( i = 0; i < dwHandles; i++ )
{
//
// Release all route handles
//
dwErr = RtmReleaseRoutes(ig.IG_RtmHandle, 1, &phRoutes[i]);
if (dwErr != NO_ERROR) {
TRACE1(
ANY, "CountInterfaceRoutes : error %d releasing routes",
dwErr
);
}
}
dwCount += dwHandles;
} while (dwErr == NO_ERROR);
//
// close enum handle
//
dwErr = RtmDeleteEnumHandle(ig.IG_RtmHandle, hRouteEnum);
if (dwErr != NO_ERROR) {
TRACE1(
ANY, "CountInterfaceRoutes : error %d closing enum handle", dwErr
);
}
if ( phRoutes ) {
RIP_FREE(phRoutes);
}
return dwCount;
}
//----------------------------------------------------------------------------
// Function: EnqueueDemandUpdateMessage
//
// This function posts a message to IPRIP's Router Manager event queue
// indicating the status of a demand update request.
//----------------------------------------------------------------------------
VOID
EnqueueDemandUpdateMessage(
DWORD dwInterfaceIndex,
DWORD dwError
) {
MESSAGE msg;
PUPDATE_COMPLETE_MESSAGE pmsg;
//
// set up an UPDATE_COMPLETE message
//
pmsg = &msg.UpdateCompleteMessage;
pmsg->UpdateType = RF_DEMAND_UPDATE_ROUTES;
pmsg->UpdateStatus = dwError;
pmsg->InterfaceIndex = dwInterfaceIndex;
ACQUIRE_LIST_LOCK(ig.IG_EventQueue);
EnqueueEvent(ig.IG_EventQueue, UPDATE_COMPLETE, msg);
SetEvent(ig.IG_EventEvent);
RELEASE_LIST_LOCK(ig.IG_EventQueue);
}
//----------------------------------------------------------------------------
// Function: WorkerFunctionProcessRtmMessage
//
// This function handles messages from RTM about new or expired routes.
//----------------------------------------------------------------------------
VOID
WorkerFunctionProcessRtmMessage(
PVOID pContext
) {
DWORD dwErr, dwFlags, dwNumDests, dwSize;
PIF_TABLE pTable;
BOOL bTriggerUpdate, bDone = FALSE;
PIF_TABLE_ENTRY pite;
PIPRIP_IF_CONFIG pic;
RIP_IP_ROUTE route;
PRTM_DEST_INFO prdi;
CHAR szNetwork[32], szNexthop[32];
if (!ENTER_RIP_WORKER()) { return; }
TRACE0(ENTER, "entering WorkerFunctionProcessRtmMessage");
pTable = ig.IG_IfTable;
//
// allocate a buffer for retrieving the dest info
//
dwSize = RTM_SIZE_OF_DEST_INFO( ig.IG_RtmProfile.NumberOfViews );
prdi = (PRTM_DEST_INFO) RIP_ALLOC( dwSize );
if ( prdi == NULL ) {
dwErr = GetLastError();
TRACE2(
ROUTE, "error %d allocating %d bytes for dest info buffers",
dwErr, dwSize
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
LEAVE_RIP_WORKER();
return;
}
//
// Acquire locks
//
ACQUIRE_IF_LOCK_SHARED();
ACQUIRE_GLOBAL_LOCK_SHARED();
ACQUIRE_LIST_LOCK(ig.IG_SendQueue);
bTriggerUpdate = FALSE;
//
// loop dequeueing messages until RTM says there are no more left
//
while (!bDone) {
//
// Retrieve route changes
//
dwNumDests = 1;
dwErr = RtmGetChangedDests(
ig.IG_RtmHandle, ig.IG_RtmNotifHandle, &dwNumDests, prdi
);
if ((dwErr != NO_ERROR) && (dwErr != ERROR_NO_MORE_ITEMS)) {
TRACE1(ROUTE, "error %d retrieving changed dests", dwErr);
break;
}
//
// check if there are any more changed dests
//
if (dwErr == ERROR_NO_MORE_ITEMS) { bDone = TRUE; }
if (dwNumDests < 1) { break; }
if ((prdi-> ViewInfo[0].HoldRoute != NULL) ||
(prdi-> ViewInfo[0].Route != NULL)) {
ZeroMemory(&route, sizeof(RIP_IP_ROUTE));
//
// For each route change check if you have a held down route.
// if so get the info for the held down route since that is
// the one to be advertized.
//
// N.B. RIP summary routes are not advertized via the route
// change processing mechanism.
//
dwErr = GetRouteInfo(
(prdi-> ViewInfo[0].HoldRoute != NULL) ?
prdi-> ViewInfo[0].HoldRoute : prdi-> ViewInfo[0].Route,
NULL, prdi, &route
);
if (dwErr == NO_ERROR) {
//
// do not advertize RIP summary routes
//
if ((route.RR_RoutingProtocol != PROTO_IP_RIP) ||
(GETROUTEFLAG(&route) & ROUTEFLAG_SUMMARY) !=
ROUTEFLAG_SUMMARY) {
//
// held down routes are advertized with INFINITE metric
//
if (prdi-> ViewInfo[0].HoldRoute != NULL) {
SETROUTEMETRIC(&route, IPRIP_INFINITE);
}
EnqueueSendEntry( ig.IG_SendQueue, &route );
bTriggerUpdate = TRUE;
}
#if ROUTE_DBG
else if (route.RR_RoutingProtocol == PROTO_IP_RIP) {
TRACE0(ROUTE, "Ignoring route change caused by RIP summary route");
}
#endif
}
}
//
// release the destination info
//
dwErr = RtmReleaseChangedDests(
ig.IG_RtmHandle, ig.IG_RtmNotifHandle, 1, prdi
);
if (dwErr != NO_ERROR) {
TRACE1(ANY, "error %d releasing changed dests", dwErr);
}
}
if (prdi) { RIP_FREE(prdi); }
//
// queue a triggered update now if necessary
//
if (bTriggerUpdate) {
QueueRipWorker(WorkerFunctionStartTriggeredUpdate, NULL);
}
RELEASE_LIST_LOCK(ig.IG_SendQueue);
RELEASE_GLOBAL_LOCK_SHARED();
RELEASE_IF_LOCK_SHARED();
TRACE0(LEAVE, "leaving WorkerFunctionProcessRtmMessage");
LEAVE_RIP_WORKER();
}
//----------------------------------------------------------------------------
// Function: WorkerFunctionActivateInterface
//
// This function sends out the initial general request on an interface.
//----------------------------------------------------------------------------
VOID
WorkerFunctionActivateInterface(
PVOID pContext
) {
PIF_TABLE pTable;
UPDATE_BUFFER ub;
PIPRIP_ENTRY pEntry;
PIF_TABLE_ENTRY pite;
PIPRIP_IF_CONFIG pic;
PIPRIP_IF_BINDING pib;
PIPRIP_IP_ADDRESS paddr;
SOCKADDR_IN sinDest;
DWORD i, dwErr, dwIndex;
LARGE_INTEGER qwCurrentTime;
if (!ENTER_RIP_WORKER()) { return; }
TRACE0(ENTER, "entering WorkerFunctionActivateInterface");
pTable = ig.IG_IfTable;
ACQUIRE_IF_LOCK_SHARED();
do { // breakout loop
//
// retrieve the interface to be activated
//
dwIndex = PtrToUlong(pContext);
pite = GetIfByIndex(pTable, dwIndex);
if (pite == NULL) {
TRACE1(IF, "activating interface: interface %d not found", dwIndex);
break;
}
pic = pite->ITE_Config;
pib = pite->ITE_Binding;
//
// If binding is NULL, assume that interface has been
// deativated. This check has been introduced as a consequence
// of WorkerFunctionDeactivateInterface being made synchronous.
// As a result, by the time this function is invoked an interface
// that was in the process of being activated could have been
// deactivated.
//
// The change to synchronous deactivate was made
// to accomadate demand dial interfaces that could get connected
// and disconnected immeditately, causing the above behaviour
//
if ( pib == NULL ) {
TRACE1( IF, "activating interface %d: Binding not found", dwIndex );
break;
}
paddr = IPRIP_IF_ADDRESS_TABLE(pib);
//
// request input notification on the interface's sockets
//
if (pic->IC_AcceptMode != IPRIP_ACCEPT_DISABLED) {
for (i = 0; i < pib->IB_AddrCount; i++) {
dwErr = WSAEventSelect(
pite->ITE_Sockets[i], ig.IG_IpripInputEvent,
FD_READ
);
if (dwErr != NO_ERROR) {
LPSTR lpszAddr = INET_NTOA(paddr[i].IA_Address);
if (lpszAddr != NULL) {
TRACE3(
IF, "WSAEventSelect returned %d for interface %d (%s)",
dwErr, dwIndex, lpszAddr
);
LOGERR1(EVENTSELECT_FAILED, lpszAddr, 0);
}
}
}
}
//
// if interface is silent or interface does demand-udpates,
// no initial request is sent on it
//
if (pic->IC_UpdateMode != IPRIP_UPDATE_PERIODIC ||
pic->IC_AnnounceMode == IPRIP_ANNOUNCE_DISABLED) {
//
// configured to be silent, do nothing
//
break;
}
//
// send general request to neighboring routers
//
SendGeneralRequest(pite);
//
// create timer for periodic updates, if required.
//
EnterCriticalSection(&pTable->IT_CS);
if (pite->ITE_FullOrDemandUpdateTimer == NULL) {
if (!CreateTimerQueueTimer(
&pite->ITE_FullOrDemandUpdateTimer,
ig.IG_TimerQueueHandle,
WorkerFunctionStartFullUpdate, pContext,
RipSecsToMilliSecs(pic->IC_FullUpdateInterval),
10000000, 0
)) {
dwErr = GetLastError();
TRACE1(IF, "error %d returned by CreateTimerQueueTimer", dwErr);
break;
}
else {
pite->ITE_Flags |= ITEFLAG_FULL_UPDATE_INQUEUE;
}
}
else {
RipQuerySystemTime(&qwCurrentTime);
EnqueueStartFullUpdate(pite, qwCurrentTime);
}
LeaveCriticalSection(&pTable->IT_CS);
} while(FALSE);
RELEASE_IF_LOCK_SHARED();
TRACE0(LEAVE, "leaving WorkerFunctionActivateInterface");
LEAVE_RIP_WORKER();
}
//----------------------------------------------------------------------------
// Function: WorkerFunctionDeactivateInterface
//
// This function generates shutdown update on the given interface, and
// removes from RTM all RIP-learnt routes associated with the interface.
// Assumes the interface table has already been exclusively locked
//----------------------------------------------------------------------------
VOID
WorkerFunctionDeactivateInterface(
PVOID pContext
) {
UPDATE_BUFFER ub;
PIF_TABLE pTable;
RIP_IP_ROUTE route;
HANDLE hEnumerator;
PHANDLE phRoutes = NULL;
BOOL bTriggerUpdate;
PIF_TABLE_ENTRY pite;
PIPRIP_IF_CONFIG pic;
PIPRIP_IF_BINDING pib;
PIPRIP_IP_ADDRESS paddr;
DWORD i, dwErr, dwFlags, dwIndex, dwHandles;
TRACE0(ENTER, "entering WorkerFunctionDeactivateInterface");
dwIndex = PtrToUlong(pContext);
bTriggerUpdate = FALSE;
pTable = ig.IG_IfTable;
do { // breakout loop
//
// find the interface to be deactivated
//
pite = GetIfByIndex(pTable, dwIndex);
if (pite == NULL) {
TRACE1(
IF, "de-activating interface: interface %d not found", dwIndex
);
break;
}
pib = pite->ITE_Binding;
paddr = IPRIP_IF_ADDRESS_TABLE(pib);
//
// if graceful shutdown is on and demand-update is off,
// send the graceful-shutdown update
//
if (pite->ITE_Config->IC_UpdateMode == IPRIP_UPDATE_PERIODIC &&
IPRIP_FLAG_IS_ENABLED(pite->ITE_Config, GRACEFUL_SHUTDOWN)) {
//
// transmit all RTM routes with non-infinite metrics set to 15
//
if (pite->ITE_Config->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED) {
SendRoutes(&pite, 1, SENDMODE_SHUTDOWN_UPDATE, 0, 0);
}
}
//
// this function is called either because an interface
// that was active (bound and enabled) is either no longer enabled
// or is no longer bound. We complete the deactivation differently
// depending on which of these is the case
//
if (!IF_IS_BOUND(pite) ) {
//
// the interface was bound, but isn't anymore.
// close the socket for the interface
//
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 enabled, but isn't anymore.
// tell WinSock to stop notifying us of input
//
for (i = 0; i < pib->IB_AddrCount; i++) {
WSAEventSelect(pite->ITE_Sockets[i], ig.IG_IpripInputEvent, 0);
}
}
//
// if full updates pending/queued on this interface, cancel them.
//
pite-> ITE_Flags &= ~ITEFLAG_FULL_UPDATE_PENDING;
pite-> ITE_Flags &= ~ITEFLAG_FULL_UPDATE_INQUEUE;
//
// if we're announcing routes over this interface,
// delete the periodic announcement timer
//
if (pite->ITE_Config->IC_UpdateMode == IPRIP_UPDATE_PERIODIC &&
pite->ITE_Config->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED) {
if (!DeleteTimerQueueTimer(
ig.IG_TimerQueueHandle,
pite->ITE_FullOrDemandUpdateTimer,
NULL)) {
TRACE1(
ANY, "error %d deleting update timer", GetLastError()
);
}
pite->ITE_FullOrDemandUpdateTimer = NULL;
}
//
// we're done if graceful shutdown is disabled
// or if this is a demand-update interface
//
if (pite->ITE_Config->IC_UpdateMode != IPRIP_UPDATE_PERIODIC ||
IPRIP_FLAG_IS_DISABLED(pite->ITE_Config, GRACEFUL_SHUTDOWN)) {
break;
}
//
// move the routes learnt on this interface to the send-queue
// and set their metrics to 16
//
dwErr = RtmCreateRouteEnum(
ig.IG_RtmHandle, NULL, RTM_VIEW_MASK_ANY,
RTM_ENUM_OWN_ROUTES, NULL, RTM_MATCH_INTERFACE, NULL,
pite->ITE_Index, &hEnumerator
);
if (dwErr != NO_ERROR) {
TRACE1(
ANY, "WorkerFunctionDeactivateInterface: error %d creating"
" enum handle", dwErr
);
break;
}
//
// allocate handle array large enough to hold max handles in an
// enum
//
phRoutes = RIP_ALLOC(ig.IG_RtmProfile.MaxHandlesInEnum*sizeof(HANDLE));
if ( phRoutes == NULL ) {
dwErr = GetLastError();
TRACE2(
ANY, "WorkerFunctionDeactivateInterface: error %d "
"while allocating %d bytes to hold max handles in an enum",
dwErr, ig.IG_RtmProfile.MaxHandlesInEnum * sizeof(HANDLE)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// find all RIP routes learnt on this interface
//
ACQUIRE_GLOBAL_LOCK_SHARED();
ACQUIRE_LIST_LOCK(ig.IG_SendQueue);
do {
dwHandles = ig.IG_RtmProfile.MaxHandlesInEnum;
dwErr = RtmGetEnumRoutes(
ig.IG_RtmHandle, hEnumerator, &dwHandles, phRoutes
);
for ( i = 0; i < dwHandles; i++ ) {
if (GetRouteInfo(
phRoutes[i], NULL, NULL, &route
) == NO_ERROR) {
//
// set the route's metric to infinite
//
SETROUTEMETRIC(&route, IPRIP_INFINITE);
//
// add the route to the send-queue
//
EnqueueSendEntry(ig.IG_SendQueue, &route);
bTriggerUpdate = TRUE;
}
if (RtmDeleteRouteToDest(
ig.IG_RtmHandle, phRoutes[i], &dwFlags
) != NO_ERROR) {
//
// If delete is successful, this is automatic
//
if (RtmReleaseRoutes(
ig.IG_RtmHandle, 1, &phRoutes[i]
) != NO_ERROR) {
TRACE1(
ANY, "WorkerFunctionDeactivateInterface: "
"error %d releasing route handles", dwErr
);
}
}
}
} while ( dwErr == NO_ERROR );
//
// close the enm handle
//
dwErr = RtmDeleteEnumHandle(ig.IG_RtmHandle, hEnumerator);
if (dwErr != NO_ERROR) {
TRACE1(
ANY, "WorkerFunctionDeactivateInterface: error %d "
"closing enum handle", dwErr
);
}
RELEASE_LIST_LOCK(ig.IG_SendQueue);
RELEASE_GLOBAL_LOCK_SHARED();
//
// queue a triggered-update work-item for the other active interfaces
//
if (bTriggerUpdate) {
dwErr = QueueRipWorker(WorkerFunctionStartTriggeredUpdate, NULL);
if (dwErr != NO_ERROR) {
TRACE1(
IF, "error %d queueing triggered update work-item", dwErr
);
LOGERR0(QUEUE_WORKER_FAILED, dwErr);
}
}
} while(FALSE);
if ( phRoutes ) {
RIP_FREE(phRoutes);
}
TRACE0(LEAVE, "leaving WorkerFunctionDeactivateInterface");
}
//----------------------------------------------------------------------------
// Function: WorkerFunctionFinishStopProtocol
//
// This function is called when IPRIP is stopping; it sends out shutdown
// updates on all interfaces and removes all RIP routes from RTM
//----------------------------------------------------------------------------
VOID
WorkerFunctionFinishStopProtocol(
PVOID pContext
) {
MESSAGE msg = {0, 0, 0};
LONG lThreadCount;
PIF_TABLE pTable;
PIPRIP_IF_CONFIG pic;
PLIST_ENTRY ple, phead;
DWORD dwErr, dwIfCount;
PIF_TABLE_ENTRY pite, *ppite, *ppitend, *pIfList;
HANDLE WaitHandle;
TRACE0(ENTER, "entering WorkerFunctionFinishStopProtocol");
//
// NOTE: since this is called while the router is stopping,
// there is no need for it to use ENTER_RIP_WORKER()/LEAVE_RIP_WORKER()
//
lThreadCount = PtrToUlong(pContext);
//
// waits for input thread and timer thread to stop,
// and also waits for API callers and worker functions to finish
//
while (lThreadCount-- > 0) {
WaitForSingleObject(ig.IG_ActivitySemaphore, INFINITE);
}
//
// deregister the events set with NtdllWait thread and delete the
// timer queue registered with NtdllTimer thread.
// These calls should not be inside IG_CS lock and must be done
// after all the threads have stopped.
//
WaitHandle = InterlockedExchangePointer(&ig.IG_IpripInputEventHandle, NULL) ;
if (WaitHandle) {
UnregisterWaitEx( WaitHandle, INVALID_HANDLE_VALUE ) ;
}
if (ig.IG_TimerQueueHandle) {
DeleteTimerQueueEx(ig.IG_TimerQueueHandle, INVALID_HANDLE_VALUE);
}
//
// we enter the critical section and leave, just to be sure that
// all threads have quit their calls to LeaveRipWorker()
//
EnterCriticalSection(&ig.IG_CS);
LeaveCriticalSection(&ig.IG_CS);
TRACE0(STOP, "all threads stopped, now performing graceful shutdown");
pTable = ig.IG_IfTable;
ACQUIRE_IF_LOCK_EXCLUSIVE();
//
// send out graceful shutdown updates on all active interfaces
//
do {
phead = &pTable->IT_ListByAddress;
//
// first count the interfaces on which graceful shutdown is enabled
//
dwIfCount = 0;
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress);
pic = pite->ITE_Config;
if (IF_IS_ACTIVE(pite) &&
pite->ITE_Binding &&
pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC &&
pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED &&
IPRIP_FLAG_IS_ENABLED(pic, GRACEFUL_SHUTDOWN)) {
++dwIfCount;
}
}
if (dwIfCount == 0) { break; }
//
// allocate space for the interface pointers
//
pIfList = RIP_ALLOC(dwIfCount * sizeof(PIF_TABLE_ENTRY));
if (pIfList == NULL) {
dwErr = GetLastError();
TRACE2(
STOP, "shutdown: error %d allocating %d bytes for interfaces",
dwErr, dwIfCount * sizeof(PIF_TABLE_ENTRY)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// copy the interface pointers into the space allocated
//
ppitend = pIfList + dwIfCount;
for (ple = phead->Flink, ppite = pIfList;
ple != phead && ppite < ppitend; ple = ple->Flink) {
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress);
pic = pite->ITE_Config;
if (IF_IS_ACTIVE(pite) &&
pite->ITE_Binding &&
pic->IC_UpdateMode == IPRIP_UPDATE_PERIODIC &&
pic->IC_AnnounceMode != IPRIP_ANNOUNCE_DISABLED &&
IPRIP_FLAG_IS_ENABLED(pic, GRACEFUL_SHUTDOWN)) {
*ppite++ = pite;
}
}
//
// pass the array of interfaces to SendRoutes
//
TRACE1(STOP, "sending shutdown updates on %d interfaces", dwIfCount);
SendRoutes(pIfList, dwIfCount, SENDMODE_SHUTDOWN_UPDATE, 0, 0);
//
// free the array of interfaces
//
RIP_FREE(pIfList);
} while(FALSE);
RELEASE_IF_LOCK_EXCLUSIVE();
//
// delete all IPRIP routes from RTM
//
for (ple = phead->Flink; ple != phead; ple = ple->Flink)
{
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress);
BlockDeleteRoutesOnInterface(
ig.IG_RtmHandle, pite-> ITE_Index
);
}
//
// cleanup the global structures
//
TRACE0(STOP, "IPRIP is cleaning up resources");
ProtocolCleanup(TRUE);
LOGINFO0(IPRIP_STOPPED, NO_ERROR);
//
// let the Router Manager know that we are done
//
ACQUIRE_LIST_LOCK(ig.IG_EventQueue);
EnqueueEvent(ig.IG_EventQueue, ROUTER_STOPPED, msg);
SetEvent(ig.IG_EventEvent);
RELEASE_LIST_LOCK(ig.IG_EventQueue);
return;
}
VOID
PrintGlobalStats(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
);
VOID
PrintGlobalConfig(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
);
VOID
PrintIfStats(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
);
VOID
PrintIfConfig(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
);
VOID
PrintIfBinding(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
);
VOID
PrintPeerStats(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
);
#define ClearScreen(h) { \
DWORD _dwin,_dwout; \
COORD _c = {0, 0}; \
CONSOLE_SCREEN_BUFFER_INFO _csbi; \
GetConsoleScreenBufferInfo(h,&_csbi); \
_dwin = _csbi.dwSize.X * _csbi.dwSize.Y; \
FillConsoleOutputCharacter(h,' ',_dwin,_c,&_dwout); \
}
VOID
WorkerFunctionMibDisplay(
PVOID pContext,
BOOLEAN bNotUsed
) {
COORD c;
HANDLE hConsole = NULL;
DWORD dwErr, dwTraceID;
DWORD dwExactSize, dwInSize, dwOutSize;
IPRIP_MIB_GET_INPUT_DATA imgid;
PIPRIP_MIB_GET_OUTPUT_DATA pimgod;
LARGE_INTEGER qwNextDisplay, qwCurrentTime;
if (!ENTER_RIP_API()) { return; }
TraceGetConsole(ig.IG_MibTraceID, &hConsole);
if (hConsole == NULL) {
LEAVE_RIP_WORKER();
return;
}
ClearScreen(hConsole);
c.X = c.Y = 0;
dwInSize = sizeof(imgid);
imgid.IMGID_TypeID = IPRIP_GLOBAL_STATS_ID;
pimgod = NULL;
//
// get size of the first entry in the first table
//
dwErr = MibGetFirst(dwInSize, &imgid, &dwOutSize, pimgod);
if (dwErr == ERROR_INSUFFICIENT_BUFFER) {
//
// allocate a buffer, and set its size
//
pimgod = RIP_ALLOC(dwOutSize);
//
// perform the query again
//
dwErr = MibGetFirst(dwInSize, &imgid, &dwOutSize, pimgod);
}
//
// now that we have the first element in the first table,
// we can enumerate the elements in the remaining tables using GetNext
//
while (dwErr == NO_ERROR) {
//
// print the current element and set up the query
// for the next element (the display functions change imgid
// so that it can be used to query the next element)
//
switch(pimgod->IMGOD_TypeID) {
case IPRIP_GLOBAL_STATS_ID:
PrintGlobalStats(hConsole, &c, &imgid, pimgod);
break;
case IPRIP_GLOBAL_CONFIG_ID:
PrintGlobalConfig(hConsole,&c, &imgid, pimgod);
break;
case IPRIP_IF_CONFIG_ID:
PrintIfConfig(hConsole, &c, &imgid, pimgod);
break;
case IPRIP_IF_BINDING_ID:
PrintIfBinding(hConsole, &c, &imgid, pimgod);
break;
case IPRIP_IF_STATS_ID:
PrintIfStats(hConsole, &c, &imgid, pimgod);
break;
case IPRIP_PEER_STATS_ID:
PrintPeerStats(hConsole, &c, &imgid, pimgod);
break;
default:
break;
}
RIP_FREE(pimgod);
pimgod = NULL;
dwOutSize = 0;
//
// move to the next line on the console
//
++c.Y;
//
// query the next MIB element
//
dwErr = MibGetNext(dwInSize, &imgid, &dwOutSize, pimgod);
if (dwErr == ERROR_INSUFFICIENT_BUFFER) {
//
// allocate a new buffer, and set its size
//
pimgod = RIP_ALLOC(dwOutSize);
//
// perform the query again
//
dwErr = MibGetNext(dwInSize, &imgid, &dwOutSize, pimgod);
}
}
//
// if memory was allocated, free it now
//
if (pimgod != NULL) { RIP_FREE(pimgod); }
LEAVE_RIP_API();
}
#define WriteLine(h,c,fmt,arg) { \
DWORD _dw; \
CHAR _sz[200], _fmt[200]; \
wsprintf(_fmt,"%-100s",fmt); \
wsprintf(_sz,_fmt,arg); \
WriteConsoleOutputCharacter(h,_sz,lstrlen(_sz),c,&_dw); \
++(c).Y; \
}
VOID
PrintGlobalStats(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
) {
PIPRIP_GLOBAL_STATS pgs;
pgs = (PIPRIP_GLOBAL_STATS)pimgod->IMGOD_Buffer;
WriteLine(
hConsole, *pc, "System Route Changes: %d",
pgs->GS_SystemRouteChanges
);
WriteLine(
hConsole, *pc, "Total Responses Sent: %d",
pgs->GS_TotalResponsesSent
);
pimgid->IMGID_TypeID = IPRIP_GLOBAL_STATS_ID;
}
VOID
PrintGlobalConfig(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
) {
PIPRIP_GLOBAL_CONFIG pgc;
PDWORD pdwPeer, pdwPeerEnd;
CHAR szFilter[32];
LPSTR lpszAddr = NULL;
pgc = (PIPRIP_GLOBAL_CONFIG)pimgod->IMGOD_Buffer;
switch (pgc->GC_PeerFilterMode) {
case IPRIP_FILTER_DISABLED:
lstrcpy(szFilter, "disabled"); break;
case IPRIP_FILTER_INCLUDE:
lstrcpy(szFilter, "include all"); break;
case IPRIP_FILTER_EXCLUDE:
lstrcpy(szFilter, "exclude all"); break;
default:
lstrcpy(szFilter, "invalid"); break;
}
WriteLine(
hConsole, *pc, "Logging Level: %d",
pgc->GC_LoggingLevel
);
WriteLine(
hConsole, *pc, "Max Receive Queue Size: %d bytes",
pgc->GC_MaxRecvQueueSize
);
WriteLine(
hConsole, *pc, "Max Send Queue Size: %d bytes",
pgc->GC_MaxSendQueueSize
);
WriteLine(
hConsole, *pc, "Min Triggered Update interval: %d seconds",
pgc->GC_MinTriggeredUpdateInterval
);
WriteLine(
hConsole, *pc, "Peer Filter Mode: %s",
szFilter
);
WriteLine(
hConsole, *pc, "Peer Filter Count: %d",
pgc->GC_PeerFilterCount
);
pdwPeer = IPRIP_GLOBAL_PEER_FILTER_TABLE(pgc);
pdwPeerEnd = pdwPeer + pgc->GC_PeerFilterCount;
for ( ; pdwPeer < pdwPeerEnd; pdwPeer++) {
lpszAddr = INET_NTOA(*pdwPeer);
if (lpszAddr != NULL) {
WriteLine(
hConsole, *pc, " %s",
lpszAddr
);
}
}
pimgid->IMGID_TypeID = IPRIP_GLOBAL_CONFIG_ID;
}
VOID
PrintIfStats(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
) {
PIPRIP_IF_STATS pis;
pis = (PIPRIP_IF_STATS)pimgod->IMGOD_Buffer;
WriteLine(
hConsole, *pc, "Interface Index: %d",
pimgod->IMGOD_IfIndex
);
WriteLine(
hConsole, *pc, "Send Failures: %d",
pis->IS_SendFailures
);
WriteLine(
hConsole, *pc, "Receive Failures: %d",
pis->IS_ReceiveFailures
);
WriteLine(
hConsole, *pc, "Requests Sent: %d",
pis->IS_RequestsSent
);
WriteLine(
hConsole, *pc, "Requests Received: %d",
pis->IS_RequestsReceived
);
WriteLine(
hConsole, *pc, "Responses Sent: %d",
pis->IS_ResponsesSent
);
WriteLine(
hConsole, *pc, "Responses Received: %d",
pis->IS_ResponsesReceived
);
WriteLine(
hConsole, *pc, "Bad Response Packets Received: %d",
pis->IS_BadResponsePacketsReceived
);
WriteLine(
hConsole, *pc, "Bad Response Entries Received: %d",
pis->IS_BadResponseEntriesReceived
);
WriteLine(
hConsole, *pc, "Triggered Updates Sent: %d",
pis->IS_TriggeredUpdatesSent
);
pimgid->IMGID_TypeID = IPRIP_IF_STATS_ID;
pimgid->IMGID_IfIndex = pimgod->IMGOD_IfIndex;
}
VOID
PrintIfConfig(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
) {
PIPRIP_IF_CONFIG pic;
PDWORD pdwPeer, pdwPeerEnd;
PIPRIP_ROUTE_FILTER pfilt, pfiltend;
CHAR szAuthType[24], szAuthKey[64];
CHAR szPeer[20], szAccept[20], szAnnounce[20], szFilter[64];
CHAR szUpdateMode[24], szAcceptMode[24], szAnnounceMode[24];
LPSTR lpszAddr = NULL;
pic = (PIPRIP_IF_CONFIG)pimgod->IMGOD_Buffer;
switch (pic->IC_UpdateMode) {
case IPRIP_UPDATE_PERIODIC:
lstrcpy(szUpdateMode, "periodic");
break;
case IPRIP_UPDATE_DEMAND:
lstrcpy(szUpdateMode, "demand");
break;
default:
lstrcpy(szUpdateMode, "invalid");
break;
}
switch (pic->IC_AcceptMode) {
case IPRIP_ACCEPT_DISABLED:
lstrcpy(szAcceptMode, "disabled");
break;
case IPRIP_ACCEPT_RIP1:
lstrcpy(szAcceptMode, "RIP1");
break;
case IPRIP_ACCEPT_RIP1_COMPAT:
lstrcpy(szAcceptMode, "RIP1 compatible");
break;
case IPRIP_ACCEPT_RIP2:
lstrcpy(szAcceptMode, "RIP2");
break;
default:
lstrcpy(szAcceptMode, "invalid");
break;
}
switch(pic->IC_AnnounceMode) {
case IPRIP_ANNOUNCE_DISABLED:
lstrcpy(szAnnounceMode, "disabled");
break;
case IPRIP_ANNOUNCE_RIP1:
lstrcpy(szAnnounceMode, "RIP1");
break;
case IPRIP_ANNOUNCE_RIP1_COMPAT:
lstrcpy(szAnnounceMode, "RIP1 compatible");
break;
case IPRIP_ANNOUNCE_RIP2:
lstrcpy(szAnnounceMode, "RIP2");
break;
default:
lstrcpy(szAnnounceMode, "invalid");
break;
}
switch (pic->IC_AuthenticationType) {
case IPRIP_AUTHTYPE_NONE:
lstrcpy(szAuthType, "none");
break;
case IPRIP_AUTHTYPE_SIMPLE_PASSWORD:
lstrcpy(szAuthType, "simple password");
break;
case IPRIP_AUTHTYPE_MD5:
lstrcpy(szAuthType, "MD5");
break;
default:
lstrcpy(szAuthType, "invalid");
break;
}
{
PSTR psz;
CHAR szDigits[] = "0123456789ABCDEF";
PBYTE pb, pbend;
psz = szAuthKey;
pbend = pic->IC_AuthenticationKey + IPRIP_MAX_AUTHKEY_SIZE;
for (pb = pic->IC_AuthenticationKey; pb < pbend; pb++) {
*psz++ = szDigits[*pb / 16];
*psz++ = szDigits[*pb % 16];
*psz++ = '-';
}
*(--psz) = '\0';
}
switch (pic->IC_UnicastPeerMode) {
case IPRIP_PEER_DISABLED:
lstrcpy(szPeer, "disabled"); break;
case IPRIP_PEER_ALSO:
lstrcpy(szPeer, "also"); break;
case IPRIP_PEER_ONLY:
lstrcpy(szPeer, "only"); break;
default:
lstrcpy(szPeer, "invalid"); break;
}
switch (pic->IC_AcceptFilterMode) {
case IPRIP_FILTER_DISABLED:
lstrcpy(szAccept, "disabled"); break;
case IPRIP_FILTER_INCLUDE:
lstrcpy(szAccept, "include all"); break;
case IPRIP_FILTER_EXCLUDE:
lstrcpy(szAccept, "exclude all"); break;
default:
lstrcpy(szAccept, "invalid"); break;
}
switch (pic->IC_AnnounceFilterMode) {
case IPRIP_FILTER_DISABLED:
lstrcpy(szAnnounce, "disabled"); break;
case IPRIP_FILTER_INCLUDE:
lstrcpy(szAnnounce, "include all"); break;
case IPRIP_FILTER_EXCLUDE:
lstrcpy(szAnnounce, "exclude all"); break;
default:
lstrcpy(szAnnounce, "invalid"); break;
}
WriteLine(
hConsole, *pc, "Interface Index: %d",
pimgod->IMGOD_IfIndex
);
WriteLine(
hConsole, *pc, "Metric: %d",
pic->IC_Metric
);
WriteLine(
hConsole, *pc, "Update Mode: %s",
szUpdateMode
);
WriteLine(
hConsole, *pc, "Accept Mode: %s",
szAcceptMode
);
WriteLine(
hConsole, *pc, "Announce Mode: %s",
szAnnounceMode
);
WriteLine(
hConsole, *pc, "Accept Host Routes: %s",
(IPRIP_FLAG_IS_ENABLED(pic, ACCEPT_HOST_ROUTES) ? "enabled" : "disabled")
);
WriteLine(
hConsole, *pc, "Announce Host Routes: %s",
(IPRIP_FLAG_IS_ENABLED(pic, ANNOUNCE_HOST_ROUTES) ? "enabled" : "disabled")
);
WriteLine(
hConsole, *pc, "Accept Default Routes: %s",
(IPRIP_FLAG_IS_ENABLED(pic, ACCEPT_DEFAULT_ROUTES) ? "enabled" : "disabled")
);
WriteLine(
hConsole, *pc, "Announce Default Routes: %s",
(IPRIP_FLAG_IS_ENABLED(pic, ANNOUNCE_DEFAULT_ROUTES) ? "enabled" : "disabled")
);
WriteLine(
hConsole, *pc, "Split Horizon: %s",
(IPRIP_FLAG_IS_ENABLED(pic, SPLIT_HORIZON) ? "enabled" : "disabled")
);
WriteLine(
hConsole, *pc, "Poison Reverse: %s",
(IPRIP_FLAG_IS_ENABLED(pic, POISON_REVERSE) ? "enabled" : "disabled")
);
WriteLine(
hConsole, *pc, "Graceful Shutdown: %s",
(IPRIP_FLAG_IS_ENABLED(pic, GRACEFUL_SHUTDOWN) ? "enabled" : "disabled")
);
WriteLine(
hConsole, *pc, "Triggered Updates: %s",
(IPRIP_FLAG_IS_ENABLED(pic, TRIGGERED_UPDATES) ? "enabled" : "disabled")
);
WriteLine(
hConsole, *pc, "Overwrite Static Routes: %s",
(IPRIP_FLAG_IS_ENABLED(pic, OVERWRITE_STATIC_ROUTES) ? "enabled" : "disabled")
);
WriteLine(
hConsole, *pc, "Route Expiration Interval: %d seconds",
pic->IC_RouteExpirationInterval
);
WriteLine(
hConsole, *pc, "Route Removal Interval: %d seconds",
pic->IC_RouteRemovalInterval
);
WriteLine(
hConsole, *pc, "Full Update Interval: %d seconds",
pic->IC_FullUpdateInterval
);
WriteLine(
hConsole, *pc, "Authentication Type: %s",
szAuthType
);
WriteLine(
hConsole, *pc, "Authentication Key: %s",
szAuthKey
);
WriteLine(
hConsole, *pc, "Route Tag: %d",
pic->IC_RouteTag
);
WriteLine(
hConsole, *pc, "Unicast Peer Mode: %s",
szPeer
);
WriteLine(
hConsole, *pc, "Accept Filter Mode: %s",
szAccept
);
WriteLine(
hConsole, *pc, "Announce Filter Mode: %s",
szAnnounce
);
WriteLine(
hConsole, *pc, "Unicast Peer Count: %d",
pic->IC_UnicastPeerCount
);
pdwPeer = IPRIP_IF_UNICAST_PEER_TABLE(pic);
pdwPeerEnd = pdwPeer + pic->IC_UnicastPeerCount;
for ( ; pdwPeer < pdwPeerEnd; pdwPeer++) {
lpszAddr = INET_NTOA(*pdwPeer);
if (lpszAddr != NULL) {
WriteLine(
hConsole, *pc, " %s",
lpszAddr
);
}
}
WriteLine(
hConsole, *pc, "Accept Filter Count: %d",
pic->IC_AcceptFilterCount
);
pfilt = IPRIP_IF_ACCEPT_FILTER_TABLE(pic);
pfiltend = pfilt + pic->IC_AcceptFilterCount;
for ( ; pfilt < pfiltend; pfilt++) {
lpszAddr = INET_NTOA(pfilt->RF_LoAddress);
if (lpszAddr != NULL) {
lstrcpy(szFilter, lpszAddr);
strcat(szFilter, " - ");
lpszAddr = INET_NTOA(pfilt->RF_HiAddress);
if (lpszAddr != NULL) {
strcat(szFilter, INET_NTOA(pfilt->RF_HiAddress));
WriteLine(
hConsole, *pc, " %s",
szFilter
);
}
}
}
WriteLine(
hConsole, *pc, "Announce Filter Count: %d",
pic->IC_AnnounceFilterCount
);
pfilt = IPRIP_IF_ANNOUNCE_FILTER_TABLE(pic);
pfiltend = pfilt + pic->IC_AnnounceFilterCount;
for ( ; pfilt < pfiltend; pfilt++) {
lpszAddr = INET_NTOA(pfilt->RF_LoAddress);
if (lpszAddr != NULL) {
lstrcpy(szFilter, lpszAddr);
strcat(szFilter, " - ");
lpszAddr = INET_NTOA(pfilt->RF_HiAddress);
if (lpszAddr != NULL) {
strcat(szFilter, INET_NTOA(pfilt->RF_HiAddress));
WriteLine(
hConsole, *pc, " %s",
szFilter
);
}
}
}
pimgid->IMGID_TypeID = IPRIP_IF_CONFIG_ID;
pimgid->IMGID_IfIndex = pimgod->IMGOD_IfIndex;
}
VOID
PrintIfBinding(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
) {
DWORD i;
CHAR szAddr[64];
PIPRIP_IF_BINDING pib;
PIPRIP_IP_ADDRESS paddr;
LPSTR lpszAddr = NULL;
pib = (PIPRIP_IF_BINDING) pimgod->IMGOD_Buffer;
paddr = IPRIP_IF_ADDRESS_TABLE(pib);
WriteLine(
hConsole, *pc, "Interface Index: %d",
pimgod->IMGOD_IfIndex
);
WriteLine(
hConsole, *pc, "Address Count: %d",
pib->IB_AddrCount
);
for (i = 0; i < pib->IB_AddrCount; i++, paddr++) {
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
lstrcpy(szAddr, lpszAddr);
lstrcat(szAddr, " - ");
lpszAddr = INET_NTOA(paddr->IA_Netmask);
if (lpszAddr != NULL) {
lstrcat(szAddr, lpszAddr);
WriteLine(
hConsole, *pc, "Address Entry: %s",
szAddr
);
}
}
}
pimgid->IMGID_TypeID = IPRIP_IF_BINDING_ID;
pimgid->IMGID_IfIndex = pimgod->IMGOD_IfIndex;
}
VOID
PrintPeerStats(
HANDLE hConsole,
PCOORD pc,
PIPRIP_MIB_GET_INPUT_DATA pimgid,
PIPRIP_MIB_GET_OUTPUT_DATA pimgod
) {
PIPRIP_PEER_STATS pps;
LPSTR lpszAddr = INET_NTOA(pimgod->IMGOD_PeerAddress);
pps = (PIPRIP_PEER_STATS)pimgod->IMGOD_Buffer;
if (lpszAddr != NULL) {
WriteLine(
hConsole, *pc, "Peer Address: %s",
lpszAddr
);
}
else {
WriteLine(
hConsole, *pc, "Peer Address: Failed inet_ntoa conv ",
lpszAddr
);
}
WriteLine(
hConsole, *pc, "Last Peer Route Tag: %d",
pps->PS_LastPeerRouteTag
);
WriteLine(
hConsole, *pc, "Last Peer Update Tick-Count %d ticks",
pps->PS_LastPeerUpdateTickCount
);
WriteLine(
hConsole, *pc, "Bad Response Packets From Peer: %d",
pps->PS_BadResponsePacketsFromPeer
);
WriteLine(
hConsole, *pc, "Bad Response Entries From Peer: %d",
pps->PS_BadResponseEntriesFromPeer
);
pimgid->IMGID_TypeID = IPRIP_PEER_STATS_ID;
pimgid->IMGID_PeerAddress = pimgod->IMGOD_PeerAddress;
}
//----------------------------------------------------------------------------
// Function: CallbackFunctionNetworkEvents
//
// This function queues a worker function to process the input packets.
// It registers a ntdll wait event at the end so that only one thread can
// be processing the input packets.
//----------------------------------------------------------------------------
VOID
CallbackFunctionNetworkEvents (
PVOID pContext,
BOOLEAN NotUsed
) {
HANDLE WaitHandle;
//
// enter/leaveRipApi should be called to make sure that rip dll is around
//
if (!ENTER_RIP_API()) { return; }
//
// set the pointer to NULL, so that Unregister wont be called
//
WaitHandle = InterlockedExchangePointer(&ig.IG_IpripInputEventHandle, NULL);
if (WaitHandle)
UnregisterWaitEx( WaitHandle, NULL ) ;
QueueRipWorker(WorkerFunctionNetworkEvents,pContext);
LEAVE_RIP_API();
}
//----------------------------------------------------------------------------
// Function: ProcessNetworkEvents
//
// This function enumerates the input events on each interface
// and processes any incoming input packets
//----------------------------------------------------------------------------
VOID
WorkerFunctionNetworkEvents (
PVOID pContext
) {
DWORD i, dwErr;
PIF_TABLE pTable;
PIPRIP_IF_CONFIG pic;
PIPRIP_IF_BINDING pib;
PIF_TABLE_ENTRY pite;
PLIST_ENTRY ple, phead;
WSANETWORKEVENTS wsane;
PIPRIP_IP_ADDRESS paddr;
LPSTR lpszAddr = NULL;
if (!ENTER_RIP_WORKER()) { return; }
pTable = ig.IG_IfTable;
ACQUIRE_IF_LOCK_SHARED();
//
// go through the list of active interfaces
// processing sockets which are read-ready
//
phead = &pTable->IT_ListByAddress;
for (ple = phead->Flink; ple != phead; ple = ple->Flink) {
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, ITE_LinkByAddress);
pic = pite->ITE_Config;
if (pic->IC_AcceptMode == IPRIP_ACCEPT_DISABLED) { continue; }
pib = pite->ITE_Binding;
paddr = IPRIP_IF_ADDRESS_TABLE(pib);
for (i = 0; i < pib->IB_AddrCount; i++, paddr++) {
if (pite->ITE_Sockets[i] == INVALID_SOCKET) { continue; }
//
// enumerate network events to see whether
// any packets have arrived on this interface
//
dwErr = WSAEnumNetworkEvents(pite->ITE_Sockets[i], NULL, &wsane);
if (dwErr != NO_ERROR) {
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
RECEIVE, "error %d checking for input on interface %d (%s)",
dwErr, pite->ITE_Index, lpszAddr
);
LOGWARN1(ENUM_NETWORK_EVENTS_FAILED, lpszAddr, dwErr);
}
continue;
}
//
// see if the input bit is set
//
if (!(wsane.lNetworkEvents & FD_READ)) { continue; }
//
// the input flag is set, now see if there was an error
//
if (wsane.iErrorCode[FD_READ_BIT] != NO_ERROR) {
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
RECEIVE, "error %d in input record for interface %d (%s)",
wsane.iErrorCode[FD_READ_BIT], pite->ITE_Index, lpszAddr
);
LOGWARN1(INPUT_RECORD_ERROR, lpszAddr, dwErr);
}
continue;
}
//
// there is no error, so process the socket
//
ProcessSocket(i, pite, pTable);
}
}
RELEASE_IF_LOCK_SHARED();
//
// if dll is not stopping, register input event with NtdllWait thread again
//
if (ig.IG_Status != IPRIP_STATUS_STOPPING) {
if (! RegisterWaitForSingleObject(
&ig.IG_IpripInputEventHandle,
ig.IG_IpripInputEvent,
CallbackFunctionNetworkEvents,
NULL,
INFINITE,
(WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE)
)) {
dwErr = GetLastError();
TRACE1(START,
"error %d registering input event with NtdllWait thread",
dwErr);
LOGERR0(REGISTER_WAIT_FAILED, dwErr);
}
}
LEAVE_RIP_WORKER();
}
//----------------------------------------------------------------------------
// Function: ProcessSocket
//
// This function receives the message on the given socket and queues it
// for processing if the configuration on the receiving interface allows it.
//----------------------------------------------------------------------------
VOID
ProcessSocket(
DWORD dwAddrIndex,
PIF_TABLE_ENTRY pite,
PIF_TABLE pTable
) {
SOCKET sock;
PPEER_TABLE pPeers;
IPRIP_PACKET pkt;
PBYTE pInputPacket;
CHAR szSrcaddr[20];
LPSTR lpszAddr = NULL;
PIPRIP_HEADER pih;
PINPUT_CONTEXT pwc;
PIPRIP_IF_STATS pis;
PIPRIP_IF_CONFIG pic;
PIPRIP_IF_BINDING pib;
PIPRIP_IP_ADDRESS paddr;
PIPRIP_PEER_STATS pps;
PPEER_TABLE_ENTRY ppte;
DWORD dwErr, dwSrcaddr;
SOCKADDR_IN sinInputSource;
INT i, iInputLength, iAddrLength;
pwc = NULL;
pis = &pite->ITE_Stats;
pic = pite->ITE_Config;
sock = pite->ITE_Sockets[dwAddrIndex];
pib = pite->ITE_Binding;
paddr = IPRIP_IF_ADDRESS_TABLE(pib) + dwAddrIndex;
pPeers = ig.IG_PeerTable;
do {
pInputPacket = pkt.IP_Packet;
//
// read the incoming packet
//
iAddrLength = sizeof(SOCKADDR_IN);
iInputLength = recvfrom(
sock, pInputPacket, MAX_PACKET_SIZE, 0,
(PSOCKADDR)&sinInputSource, &iAddrLength
);
if (iInputLength == 0 || iInputLength == SOCKET_ERROR) {
dwErr = WSAGetLastError();
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
RECEIVE, "error %d receiving packet on interface %d (%s)",
dwErr, pite->ITE_Index, lpszAddr
);
LOGERR1(RECVFROM_FAILED, lpszAddr, dwErr);
}
InterlockedIncrement(&pis->IS_ReceiveFailures);
break;
}
dwSrcaddr = sinInputSource.sin_addr.s_addr;
//
// ignore the packet if it is from a local address
//
if (GetIfByAddress(pTable, dwSrcaddr, GETMODE_EXACT, NULL) != NULL) {
break;
}
lpszAddr = INET_NTOA(dwSrcaddr);
if (lpszAddr != NULL) { lstrcpy(szSrcaddr, lpszAddr); }
else { ZeroMemory(szSrcaddr, sizeof(szSrcaddr)); }
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE4(
RECEIVE, "received %d-byte packet from %s on interface %d (%s)",
iInputLength, szSrcaddr, pite->ITE_Index, lpszAddr
);
}
//
// the packet must contain at least one entry
//
if (iInputLength < MIN_PACKET_SIZE) {
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE4(
RECEIVE,
"%d-byte packet from %s on interface %d (%s) is too small",
iInputLength, szSrcaddr, pite->ITE_Index, lpszAddr
);
LOGWARN2(PACKET_TOO_SMALL, lpszAddr, szSrcaddr, NO_ERROR);
}
break;
}
//
// find out which peer sent this, or create a new peer
//
ACQUIRE_PEER_LOCK_EXCLUSIVE();
dwErr = CreatePeerEntry(pPeers, dwSrcaddr, &ppte);
if (dwErr == NO_ERROR) {
pps = &ppte->PTE_Stats;
}
else {
pps = NULL;
//
// not a serious error, so go on
//
TRACE2(
RECEIVE, "error %d creating peer statistics entry for %s",
dwErr, szSrcaddr
);
}
RELEASE_PEER_LOCK_EXCLUSIVE();
ACQUIRE_PEER_LOCK_SHARED();
//
// place a template over the packet
//
pih = (PIPRIP_HEADER)pInputPacket;
//
// update the peer statistics
//
if (pps != NULL) {
InterlockedExchange(
&pps->PS_LastPeerUpdateTickCount, GetTickCount()
);
InterlockedExchange(
&pps->PS_LastPeerUpdateVersion, (DWORD)pih->IH_Version
);
}
//
// discard if the version is invalid, or if the packet is
// a RIPv1 packet and the reserved field in the header is non-zero
//
if (pih->IH_Version == 0) {
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
RECEIVE, "invalid version packet from %s on interface %d (%s)",
szSrcaddr, pite->ITE_Index, lpszAddr
);
LOGWARNDATA2(
PACKET_VERSION_INVALID, lpszAddr, szSrcaddr,
iInputLength, pInputPacket
);
}
if (pps != NULL) {
InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer);
}
RELEASE_PEER_LOCK_SHARED();
break;
}
else
if (pih->IH_Version == 1 && pih->IH_Reserved != 0) {
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE3(
RECEIVE, "invalid packet header from %s on interface %d (%s)",
szSrcaddr, pite->ITE_Index, lpszAddr
);
LOGWARNDATA2(
PACKET_HEADER_CORRUPT, lpszAddr, szSrcaddr,
iInputLength, pInputPacket
);
}
if (pps != NULL) {
InterlockedIncrement(&pps->PS_BadResponsePacketsFromPeer);
}
RELEASE_PEER_LOCK_SHARED();
break;
}
RELEASE_PEER_LOCK_SHARED();
//
// make sure command field is valid, and
// update statistics on received packets
//
if (pih->IH_Command == IPRIP_REQUEST) {
InterlockedIncrement(&pis->IS_RequestsReceived);
}
else
if (pih->IH_Command == IPRIP_RESPONSE) {
InterlockedIncrement(&pis->IS_ResponsesReceived);
}
else {
break;
}
//
// allocate and initialize a work-context to be queued
// and update the receive queue size
//
pwc = RIP_ALLOC(sizeof(INPUT_CONTEXT));
if (pwc == NULL) {
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE4(
RECEIVE,
"error %d allocating %d bytes for packet on interface %d (%s)",
GetLastError(), sizeof(INPUT_CONTEXT), pite->ITE_Index,
lpszAddr
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
}
break;
}
pwc->IC_InterfaceIndex = pite->ITE_Index;
pwc->IC_AddrIndex = dwAddrIndex;
pwc->IC_InputSource = sinInputSource;
pwc->IC_InputLength = iInputLength;
pwc->IC_InputPacket = pkt;
//
// enqueue the packet and source-address as a recv-queue entry
//
ACQUIRE_GLOBAL_LOCK_SHARED();
ACQUIRE_LIST_LOCK(ig.IG_RecvQueue);
dwErr = EnqueueRecvEntry(
ig.IG_RecvQueue, pih->IH_Command, (PBYTE)pwc
);
RELEASE_LIST_LOCK(ig.IG_RecvQueue);
RELEASE_GLOBAL_LOCK_SHARED();
if (dwErr != NO_ERROR) {
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE4(
RECEIVE,
"error %d queueing data for packet from %s on interface %d (%s)",
dwErr, szSrcaddr, pite->ITE_Index, lpszAddr
);
}
break;
}
//
// enqueue the work-item to process the packet
//
dwErr = QueueRipWorker(WorkerFunctionProcessInput, NULL);
if (dwErr != NO_ERROR) {
PLIST_ENTRY phead;
lpszAddr = INET_NTOA(paddr->IA_Address);
if (lpszAddr != NULL) {
TRACE4(
RECEIVE,
"error %d queueing work-item for packet from %s on interface %d (%s)",
dwErr, szSrcaddr, pite->ITE_Index, lpszAddr
);
LOGERR0(QUEUE_WORKER_FAILED, dwErr);
}
//
// remove the data that was queued for processing
//
ACQUIRE_LIST_LOCK(ig.IG_RecvQueue);
phead = &ig.IG_RecvQueue->LL_Head;
RemoveTailList(phead);
ig.IG_RecvQueueSize -= sizeof(RECV_QUEUE_ENTRY);
RELEASE_LIST_LOCK(ig.IG_RecvQueue);
break;
}
return;
} while(FALSE);
//
// some cleanup is required if an error brought us here
//
if (pwc != NULL) { RIP_FREE(pwc); }
return;
}
DWORD
ProcessRtmNotification(
RTM_ENTITY_HANDLE hRtmHandle, // not used
RTM_EVENT_TYPE retEventType,
PVOID pvContext1, // not used
PVOID pvContext2 // not used
) {
DWORD dwErr;
TRACE1(ROUTE, "ENTERED ProcessRtmNotification, event %d", retEventType );
if (!ENTER_RIP_API()) { return ERROR_CAN_NOT_COMPLETE; }
//
// only route change notifications are processed
//
if (retEventType == RTM_CHANGE_NOTIFICATION) {
QueueRipWorker(WorkerFunctionProcessRtmMessage, (PVOID)retEventType);
dwErr = NO_ERROR;
}
else {
dwErr = ERROR_NOT_SUPPORTED;
}
LEAVE_RIP_API();
TRACE1(ROUTE, "LEAVING ProcessRtmNotification %d", dwErr);
return dwErr;
}
DWORD
BlockDeleteRoutesOnInterface (
IN HANDLE hRtmHandle,
IN DWORD dwIfIndex
)
/*++
Routine Description :
This routine deletes all the routes learnt by the protocol
over the specified interface.
Parameters :
hRtmHandle - Entity registration handle
dwIfIndex - Interface over which routes are to be deleted
Return Value :
--*/
{
HANDLE hRtmEnum;
PHANDLE phRoutes = NULL;
DWORD dwHandles, dwFlags, i, dwErr;
dwErr = RtmCreateRouteEnum(
hRtmHandle, NULL, RTM_VIEW_MASK_ANY, RTM_ENUM_OWN_ROUTES,
NULL, RTM_MATCH_INTERFACE, NULL, dwIfIndex, &hRtmEnum
);
if ( dwErr != NO_ERROR ) {
TRACE1(
ANY, "BlockDeleteRoutesOnInterface: Error %d creating handle",
dwErr
);
return dwErr;
}
//
// allocate handle array large enough to hold max handles in an
// enum
//
phRoutes = RIP_ALLOC(ig.IG_RtmProfile.MaxHandlesInEnum * sizeof(HANDLE));
if ( phRoutes == NULL ) {
dwErr = GetLastError();
TRACE2(
ANY, "BlockDeleteRoutesOnInterface: error %d while "
"allocating %d bytes to hold max handles in an enum",
dwErr, ig.IG_RtmProfile.MaxHandlesInEnum * sizeof(HANDLE)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
return dwErr;
}
do {
dwHandles = ig.IG_RtmProfile.MaxHandlesInEnum;
dwErr = RtmGetEnumRoutes(
hRtmHandle, hRtmEnum, &dwHandles, phRoutes
);
for ( i = 0; i < dwHandles; i++ )
{
if ( RtmDeleteRouteToDest(
hRtmHandle, phRoutes[i], &dwFlags
) != NO_ERROR ) {
//
// If delete is successful, this is automatic
//
TRACE2(
ANY, "BlockDeleteRoutesOnInterface : error %d deleting"
" routes on interface %d", dwErr, dwIfIndex
);
dwErr = RtmReleaseRoutes(hRtmHandle, 1, &phRoutes[i]);
if (dwErr != NO_ERROR) {
TRACE1(ANY, "error %d releasing route", dwErr);
}
}
}
} while (dwErr == NO_ERROR);
//
// close enum handles
//
dwErr = RtmDeleteEnumHandle(hRtmHandle, hRtmEnum);
if (dwErr != NO_ERROR) {
TRACE1(
ANY, "BlockDeleteRoutesOnInterface : error %d closing enum handle",
dwErr
);
}
if ( phRoutes ) {
RIP_FREE(phRoutes);
}
return NO_ERROR;
}