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

1921 lines
42 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//============================================================================
// Copyright (c) 1995, Microsoft Corporation
//
// File: riptest.cxx
//
// History:
// Abolade Gbadegesin Oct-16-1995 Created.
//
// Code for RIP test program
//============================================================================
extern "C" {
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <winsvc.h>
#define FD_SETSIZE 256
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <ipexport.h>
#include <ipinfo.h>
#include <llinfo.h>
#include <rtm.h>
#include <routprot.h>
#include <mprerror.h>
#include <rtutils.h>
#include <ipriprm.h>
#include <iprtrmib.h>
#include <dim.h>
#include <mprapi.h>
#include <iphlpapi.h>
#include "defs.h"
#include "riptest.h"
DWORD g_TraceID;
RIPTEST_IF_CONFIG g_cfg;
RIPTEST_IF_CONFIG g_def = {
50, // 50 routes
0x000000c0, // starting with 192.0.0.0
0x0000ffff, // using netmask 255.255.0.0
0x00000000, // and a next hop of 0
0x00000000, // and a route-tag of 0
0xffffffff, // sent to the broadcast address
0, // don't use a timeout to remove routes
2, // send version 2 packets
0, // random-sized packets
100, // use 100-millisecond packet gap
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", // all-zeroes authentication key
IPRIP_AUTHTYPE_NONE, // no authentication
262144 // and set the send and recv buffers to this size
};
DWORD g_seed;
RIPTEST_IF_BINDING g_bind;
REG_OPTION g_options[] = {
{
STR_ROUTECOUNT,
sizeof(DWORD),
&g_cfg.RIC_RouteCount,
&g_def.RIC_RouteCount,
RegGetDWORD
},
{
STR_ROUTESTART,
sizeof(DWORD),
&g_cfg.RIC_RouteStart,
&g_def.RIC_RouteStart,
RegGetAddress
},
{
STR_ROUTEMASK,
sizeof(DWORD),
&g_cfg.RIC_RouteMask,
&g_def.RIC_RouteMask,
RegGetAddress
},
{
STR_ROUTENEXTHOP,
sizeof(DWORD),
&g_cfg.RIC_RouteNexthop,
&g_def.RIC_RouteNexthop,
RegGetAddress
},
{
STR_ROUTETAG,
sizeof(DWORD),
&g_cfg.RIC_RouteTag,
&g_def.RIC_RouteTag,
RegGetDWORD
},
{
STR_ROUTETARGET,
sizeof(DWORD),
&g_cfg.RIC_RouteTarget,
&g_def.RIC_RouteTarget,
RegGetAddress
},
{
STR_ROUTETIMEOUT,
sizeof(DWORD),
&g_cfg.RIC_RouteTimeout,
&g_def.RIC_RouteTimeout,
RegGetDWORD
},
{
STR_PACKETVERSION,
sizeof(DWORD),
&g_cfg.RIC_PacketVersion,
&g_def.RIC_PacketVersion,
RegGetDWORD
},
{
STR_PACKETENTRYCOUNT,
sizeof(DWORD),
&g_cfg.RIC_PacketEntryCount,
&g_def.RIC_PacketEntryCount,
RegGetDWORD
},
{
STR_PACKETGAP,
sizeof(DWORD),
&g_cfg.RIC_PacketGap,
&g_def.RIC_PacketGap,
RegGetDWORD
},
{
STR_AUTHKEY,
IPRIP_MAX_AUTHKEY_SIZE,
g_cfg.RIC_AuthKey,
g_def.RIC_AuthKey,
RegGetBinary
},
{
STR_AUTHTYPE,
sizeof(DWORD),
&g_cfg.RIC_AuthType,
&g_def.RIC_AuthType,
RegGetDWORD
},
{
STR_SOCKBUFSIZE,
sizeof(DWORD),
&g_cfg.RIC_SockBufSize,
&g_def.RIC_SockBufSize,
RegGetDWORD
}
};
INT __cdecl
main(
INT iArgc,
PSTR ppszArgv[]
)
{
WSADATA wd;
DWORD dwErr;
//
// must be at least one argument
//
if (iArgc != 2) {
PrintUsage();
return ERROR_INVALID_PARAMETER;
}
//
// see if the user is just asking for instructions
//
if (strcmp(ppszArgv[1], "-?") == 0 || strcmp(ppszArgv[1], "/?") == 0) {
PrintUsage();
return 0;
}
//
// first and only argument is name of interface
//
mbstowcs(g_bind.RIB_Netcard, ppszArgv[1], mbstowcs(NULL, ppszArgv[1], -1));
//
// register with the Tracing DLL
//
g_TraceID = PRINTREGISTER("RipTest");
//
// startup Winsock
//
dwErr = WSAStartup(MAKEWORD(1, 1), &wd);
if (dwErr != NO_ERROR) {
PRINTDEREGISTER(g_TraceID);
return (INT)dwErr;
}
//
// get the binding for the interface over which to send routes
//
dwErr = RegGetIfBinding();
if (dwErr != NO_ERROR) {
WSACleanup();
PRINTDEREGISTER(g_TraceID);
return (INT)dwErr;
}
//
// seed the random number generator
//
g_seed = GetTickCount();
srand(g_seed);
while (TRUE) {
PRINT0("\n\nbeginning test cycle...");
//
// get the parameters for the interface,
// and quit if an error occurred or the defaults were written
//
dwErr = RegGetConfig();
if (dwErr != NO_ERROR) { break; }
//
// run one test cycle
//
dwErr = RipTest();
PRINT0("completed test cycle...");
}
WSACleanup();
PRINTDEREGISTER(g_TraceID);
return dwErr;
}
DWORD
RegGetIfBinding(
VOID
)
{
#if 1
PMIB_IPADDRTABLE AddrTable = NULL;
DWORD dwErr;
DWORD dwPrefixLength = lstrlen("\\DEVICE\\TCPIP_");
DWORD dwSize;
DWORD i;
DWORD j;
PIP_INTERFACE_INFO IfTable = NULL;
//
// Load the address table and interface table
//
do {
dwSize = 0;
dwErr = GetInterfaceInfo(IfTable, &dwSize);
if (dwErr != ERROR_INSUFFICIENT_BUFFER) {
PRINT1("error %d obtaining interface-table size", dwErr);
break;
}
IfTable = (PIP_INTERFACE_INFO)HeapAlloc(GetProcessHeap(), 0, dwSize);
if (!IfTable) {
dwErr = GetLastError();
PRINT2("error %d allocating %d-byte for interfaces", dwErr, dwSize);
dwErr = ERROR_INSUFFICIENT_BUFFER; break;
}
dwErr = GetInterfaceInfo(IfTable, &dwSize);
if (dwErr != NO_ERROR) {
PRINT1("error %d getting interface table", dwErr);
break;
}
dwSize = 0;
dwErr = GetIpAddrTable(AddrTable, &dwSize, FALSE);
if (dwErr != ERROR_INSUFFICIENT_BUFFER) {
PRINT1("error %d obtaining address-table size", dwErr);
break;
}
AddrTable = (PMIB_IPADDRTABLE)HeapAlloc(GetProcessHeap(), 0, dwSize);
if (!AddrTable) {
dwErr = GetLastError();
PRINT2("error %d allocating %d-byte for addresses", dwErr, dwSize);
dwErr = ERROR_INSUFFICIENT_BUFFER; break;
}
dwErr = GetIpAddrTable(AddrTable, &dwSize, FALSE);
if (dwErr != NO_ERROR) {
PRINT1("error %d getting address table", dwErr);
break;
}
} while(FALSE);
if (dwErr != NO_ERROR) {
if (IfTable) { HeapFree(GetProcessHeap(), 0, IfTable); }
if (AddrTable) { HeapFree(GetProcessHeap(), 0, AddrTable); }
return dwErr;
}
//
// Find the user's interface in the interface-table
//
dwErr = ERROR_INVALID_PARAMETER;
for (i = 0; i < (DWORD)IfTable->NumAdapters; i++) {
PRINT2("%d: %ls", IfTable->Adapter[i].Index, IfTable->Adapter[i].Name+dwPrefixLength);
if (lstrcmpiW(
IfTable->Adapter[i].Name+dwPrefixLength, g_bind.RIB_Netcard
) != 0) {
continue;
}
//
// We've found the interface.
// Now look in the address-table for its address.
//
for (j = 0; j < AddrTable->dwNumEntries; j++) {
PRINT2("%d: %s", AddrTable->table[j].dwIndex, INET_NTOA(AddrTable->table[j].dwAddr));
if (AddrTable->table[j].dwIndex != IfTable->Adapter[i].Index) {
continue;
}
//
// We've found the address.
//
g_bind.RIB_Address = AddrTable->table[j].dwAddr;
g_bind.RIB_Netmask = AddrTable->table[j].dwMask;
dwErr = NO_ERROR;
break;
}
if (j >= AddrTable->dwNumEntries) {
PRINT0("the address for the interface could not be found");
}
break;
}
if (i >= (DWORD)IfTable->NumAdapters) {
PRINT0("the interface specified could not be found");
}
HeapFree(GetProcessHeap(), 0, IfTable);
HeapFree(GetProcessHeap(), 0, AddrTable);
return dwErr;
#else
HKEY hkeyNetcard;
PSTR pszAddress, pszNetmask;
CHAR szNetcard[256], szValue[256];
DWORD dwErr, dwType, dwSize, dwEnableDhcp;
//
// open the TCP/IP parameters key for the interface specified
//
strcpy(szNetcard, STR_SERVICES);
strcat(szNetcard, g_bind.RIB_Netcard);
strcat(szNetcard, STR_PARAMSTCP);
dwErr = RegOpenKeyEx(
HKEY_LOCAL_MACHINE, szNetcard, 0, KEY_ALL_ACCESS, &hkeyNetcard
);
if (dwErr != NO_ERROR) {
PRINT2("error %d opening registry key %s", dwErr, szNetcard);
return dwErr;
}
do {
//
// read the dhcp key to see whether DHCP is enabled
//
dwSize = sizeof(DWORD);
dwErr = RegQueryValueEx(
hkeyNetcard, STR_ENABLEDHCP, NULL,
&dwType, (PBYTE)&dwEnableDhcp, &dwSize
);
if (dwErr != NO_ERROR) {
PRINT3(
"error %d reading value %s under key %s",
dwErr, STR_ENABLEDHCP, szNetcard
);
break;
}
if (dwEnableDhcp) {
pszAddress = STR_DHCPADDR;
pszNetmask = STR_DHCPMASK;
}
else {
pszAddress = STR_ADDRESS;
pszNetmask = STR_NETMASK;
}
//
// read the IP address and convert it
//
dwSize = sizeof(szValue);
dwErr = RegQueryValueEx(
hkeyNetcard, pszAddress, NULL,
&dwType, (PBYTE)szValue, &dwSize
);
if (dwErr != NO_ERROR) {
PRINT3(
"error %d reading value %s under key %s",
dwErr, pszAddress, szNetcard
);
break;
}
g_bind.RIB_Address = inet_addr(szValue);
PRINT2("%s == %s", pszAddress, szValue);
//
// read the network mask and convert it
//
dwSize = sizeof(szValue);
dwErr = RegQueryValueEx(
hkeyNetcard, pszNetmask, NULL,
&dwType, (PBYTE)szValue, &dwSize
);
if (dwErr != NO_ERROR) {
PRINT3(
"error %d reading value %s under key %s",
dwErr, pszNetmask, szNetcard
);
break;
}
g_bind.RIB_Netmask = inet_addr(szValue);
PRINT2("%s == %s", pszNetmask, szValue);
} while(FALSE);
RegCloseKey(hkeyNetcard);
return dwErr;
#endif
}
DWORD
RegGetConfig(
VOID
)
{
CHAR szRipTest[256];
DWORD dwErr, dwCreated;
PREG_OPTION pro, proend;
HKEY hkeyRipTest, hkeyIf;
DWORD dwNetmask, dwPrefixLength;
DWORD dwCount, dwLowestAddress, dwHighestAddress;
//
// create the RipTest key in case it doesn't exist
//
strcpy(szRipTest, STR_SERVICES);
strcat(szRipTest, STR_RIPTEST);
dwErr = RegCreateKeyEx(
HKEY_LOCAL_MACHINE, szRipTest, 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &hkeyRipTest, &dwCreated
);
if (dwErr != NO_ERROR) {
PRINT2("error %d creating registry key %s", dwErr, szRipTest);
return dwErr;
}
//
// create the key for the interface in case it doesn't exist
//
dwErr = RegCreateKeyExW(
hkeyRipTest, g_bind.RIB_Netcard, 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &hkeyIf, &dwCreated
);
RegCloseKey(hkeyRipTest);
if (dwErr != NO_ERROR) {
PRINT3(
"error %d creating subkey %S under registry key %s",
dwErr, g_bind.RIB_Netcard, szRipTest
);
return dwErr;
}
PRINT0("loading options from registry: ");
proend = g_options + (sizeof(g_options) / sizeof(REG_OPTION));
for (pro = g_options; pro < proend; pro++) {
//
// read or initialize the option
//
pro->RO_GetOpt(hkeyIf, pro);
}
RegCloseKey(hkeyIf);
//
// if the defaults were used, give the user a chance to change them
//
if (dwCreated == REG_CREATED_NEW_KEY) {
PRINT0("Default parameters have been written to the registry.");
PRINT2("Please check the key %s\\%S,", szRipTest, g_bind.RIB_Netcard);
PRINT0("modify the values if necessary, and run RIPTEST again.\n");
return ERROR_CAN_NOT_COMPLETE;
}
//
// check the route parameters for errors:
//
// make sure the class of the route is valid
//
dwLowestAddress = g_cfg.RIC_RouteStart;
if (IS_LOOPBACK_ADDR(dwLowestAddress) ||
CLASSD_ADDR(dwLowestAddress) || CLASSE_ADDR(dwLowestAddress)) {
PRINT1(
"ERROR: route %s is of an invalid network class",
INET_NTOA(dwLowestAddress)
);
return ERROR_INVALID_PARAMETER;
}
//
// make sure that from the specified starting address,
// there are enough routes in the network class to generate
// the configured number of routes
//
dwCount = g_cfg.RIC_RouteCount;
dwNetmask = g_cfg.RIC_RouteMask;
dwPrefixLength = PREFIX_LENGTH(dwNetmask);
dwLowestAddress &= dwNetmask;
dwHighestAddress = NTH_ADDRESS(dwLowestAddress, dwPrefixLength, dwCount);
if (IS_LOOPBACK_ADDR(dwHighestAddress) ||
NETCLASS_MASK(dwLowestAddress) != NETCLASS_MASK(dwHighestAddress)) {
PRINT1(
"ERROR: starting route %s is too near the end of its network class",
INET_NTOA(dwLowestAddress)
);
return ERROR_INVALID_PARAMETER;
}
//
// make sure that the authentication type is a supported value
//
if (g_cfg.RIC_AuthType != IPRIP_AUTHTYPE_NONE &&
g_cfg.RIC_AuthType != IPRIP_AUTHTYPE_SIMPLE_PASSWORD) {
PRINT1(
"ERROR: authentication type %d is not supported",
g_cfg.RIC_AuthType
);
return ERROR_INVALID_PARAMETER;
}
//
// make sure the number of packet entries isn't out-of-range
//
if (g_cfg.RIC_PacketEntryCount > 25) {
PRINT1(
"ERROR: packet-enty count %d is too large",
g_cfg.RIC_PacketEntryCount
);
return ERROR_INVALID_PARAMETER;
}
return NO_ERROR;
}
DWORD
RegGetAddress(
HKEY hKey,
PREG_OPTION pOpt
)
{
CHAR szValue[256];
DWORD dwErr, dwType, dwSize;
//
// attempt to read the value
//
dwSize = sizeof(szValue);
dwErr = RegQueryValueEx(
hKey, pOpt->RO_Name, NULL, &dwType, (PBYTE)szValue, &dwSize
);
if (dwErr != NO_ERROR) {
//
// attempt to write the default value to the registry
//
strcpy(szValue, INET_NTOA(*(PDWORD)pOpt->RO_DefVal));
dwSize = strlen(szValue) + 1;
dwType = REG_SZ;
dwErr = RegSetValueEx(
hKey, pOpt->RO_Name, NULL, dwType, (PBYTE)szValue, dwSize
);
}
//
// by now the value in the registry should be in szValue
//
*(PDWORD)pOpt->RO_OptVal = inet_addr(szValue);
PRINT2("%20s == %s", pOpt->RO_Name, szValue);
return dwErr;
}
DWORD
RegGetDWORD(
HKEY hKey,
PREG_OPTION pOpt
)
{
DWORD dwErr, dwValue, dwType, dwSize;
//
// attempt to read the value
//
dwSize = sizeof(DWORD);
dwErr = RegQueryValueEx(
hKey, pOpt->RO_Name, NULL, &dwType, (PBYTE)&dwValue, &dwSize
);
if (dwErr != NO_ERROR) {
//
// attempt to write the default value to the registry
//
dwValue = *(PDWORD)pOpt->RO_DefVal;
dwSize = sizeof(DWORD);
dwType = REG_DWORD;
dwErr = RegSetValueEx(
hKey, pOpt->RO_Name, NULL, dwType, (PBYTE)&dwValue, dwSize
);
}
//
// by now the value in the registry should be in dwValue
//
*(PDWORD)pOpt->RO_OptVal = dwValue;
PRINT2("%20s == %d", pOpt->RO_Name, dwValue);
return dwErr;
}
DWORD
RegGetBinary(
HKEY hKey,
PREG_OPTION pOpt
)
{
PBYTE pValue;
DWORD dwErr, dwType, dwSize;
//
// attempt to read the value
//
dwSize = pOpt->RO_Size;
pValue = (PBYTE)pOpt->RO_OptVal;
dwErr = RegQueryValueEx(
hKey, pOpt->RO_Name, NULL, &dwType, pValue, &dwSize
);
if (dwErr != NO_ERROR) {
//
// attempt to write the default value to the registry
//
pValue = (PBYTE)pOpt->RO_DefVal;
dwSize = pOpt->RO_Size;
dwType = REG_BINARY;
dwErr = RegSetValueEx(
hKey, pOpt->RO_Name, NULL, dwType, pValue, dwSize
);
RtlCopyMemory(pOpt->RO_OptVal, pOpt->RO_DefVal, pOpt->RO_Size);
}
{
PBYTE pb, pbend;
CHAR *psz, szValue[256], szDigits[] = "0123456789ABCDEF";
psz = szValue;
pbend = (PBYTE)pOpt->RO_OptVal + pOpt->RO_Size;
for (pb = (PBYTE)pOpt->RO_OptVal; pb < pbend; pb++) {
*psz++ = szDigits[*pb / 16];
*psz++ = szDigits[*pb % 16];
*psz++ = ':';
}
if (psz != szValue) { --psz; }
*psz = '\0';
PRINT2("%20s == %s", pOpt->RO_Name, szValue);
}
return dwErr;
}
//
// main stress function
//
DWORD
RipTest(
VOID
)
{
SOCKET sock;
SOCKADDR_IN sinaddr;
DWORD dwErr, dwMetric;
IPForwardEntry *ifelist;
LIST_ENTRY rtrlist, *ple;
PRIPTEST_ROUTER_INFO prrs;
InitializeListHead(&rtrlist);
do {
//
// create and set up socket to be used for route transmission
//
dwErr = InitializeSocket(&sock, RIPTEST_PORT);
if (dwErr != NO_ERROR) {
break;
}
//
// transmit single route request on non-RIP port,
// and build a list of responding routers
//
dwErr = DiscoverRouters(sock, &rtrlist);
if (dwErr != NO_ERROR) { closesocket(sock); break; }
//
// generate the list of routes as configured
//
dwErr = GenerateRoutes(&ifelist);
if (dwErr != NO_ERROR) { closesocket(sock); break; }
//
// re-initialize the socket, this time to the RIP port
//
closesocket(sock);
dwErr = InitializeSocket(&sock, IPRIP_PORT);
if (dwErr == SOCKET_ERROR) {
break;
}
for (dwMetric = 8; (INT)dwMetric >= 2; dwMetric -= 3) {
//
// transmit the route table with the specified metric
//
dwErr = TransmitRoutes(sock, dwMetric, ifelist);
//
// give the router time to process the advertisements:
// we allow 30 milliseconds per route, with a minimum of 15 seconds
//
Sleep(max(15000, 30 * g_cfg.RIC_RouteCount));
//
// make connections to Router on each of the responding machines,
// and use MIB api functions to retrieve the route table.
// Verify that the routes transmitted are present,
// and add the servers to the displayed statistics
//
dwErr = VerifyRouteTables(dwMetric, &rtrlist, ifelist);
}
if (g_cfg.RIC_RouteTimeout != 0) {
//
// use timeout to clear routes
//
PRINT1(
"waiting %d milliseconds for routes to timeout",
max(15000, g_cfg.RIC_RouteTimeout * 1000)
);
Sleep(max(15000, g_cfg.RIC_RouteTimeout * 1000));
}
else {
//
// send updates to clean up the routes
//
PRINT0("sending announcements to purge routes advertised");
dwErr = TransmitRoutes(sock, 16, ifelist);
Sleep(max(15000, 30 * g_cfg.RIC_RouteCount));
}
closesocket(sock);
} while(FALSE);
//
// cleanup the server list
//
while (!IsListEmpty(&rtrlist)) {
ple = RemoveHeadList(&rtrlist);
prrs = CONTAINING_RECORD(ple, RIPTEST_ROUTER_INFO, RRS_Link);
HeapFree(GetProcessHeap(), 0, prrs);
}
if (ifelist != NULL) { HeapFree(GetProcessHeap(), 0, ifelist); }
return dwErr;
}
DWORD
InitializeSocket(
SOCKET *psock,
WORD wPort
)
{
SOCKET sock;
DWORD dwErr, dwOption;
//
// create the socket
//
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
dwErr = WSAGetLastError();
PRINT1("error %d creating socket", dwErr);
return dwErr;
}
//
// enable address sharing
//
dwOption = 1;
dwErr = setsockopt(
sock,
SOL_SOCKET,
SO_REUSEADDR,
(PCCH)&dwOption,
sizeof(DWORD)
);
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
PRINT1("error %d enabling address re-use on socket", dwErr);
}
//
// enlarge the receive buffer
//
dwOption = g_cfg.RIC_SockBufSize;
dwErr = setsockopt(
sock,
SOL_SOCKET,
SO_RCVBUF,
(PCCH)&dwOption,
sizeof(DWORD)
);
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
PRINT2("error %d enlarging recv buffer to %d bytes", dwErr, dwOption);
}
//
// enlarge the send buffer
//
dwOption = g_cfg.RIC_SockBufSize;
dwErr = setsockopt(
sock,
SOL_SOCKET,
SO_SNDBUF,
(PCCH)&dwOption,
sizeof(DWORD)
);
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
PRINT2("error %d enlarging send buffer to %d bytes", dwErr, dwOption);
}
do {
SOCKADDR_IN sinaddr;
if (g_cfg.RIC_RouteTarget != IPRIP_MULTIADDR) {
//
// enable broadcasting
//
dwOption = 1;
dwErr = setsockopt(
sock,
SOL_SOCKET,
SO_BROADCAST,
(PCCH)&dwOption,
sizeof(DWORD)
);
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
PRINT1("error %d enabling broadcast on socket", dwErr);
break;
}
//
// bind the socket to the RIPTEST port
//
sinaddr.sin_family = AF_INET;
sinaddr.sin_port = htons(wPort);
sinaddr.sin_addr.s_addr = g_bind.RIB_Address;
dwErr = bind(sock, (PSOCKADDR)&sinaddr, sizeof(SOCKADDR_IN));
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
PRINT1("error %d binding socket", dwErr);
break;
}
dwErr = NO_ERROR;
}
else {
struct ip_mreq imOption;
//
// bind to the specified port
//
sinaddr.sin_family = AF_INET;
sinaddr.sin_port = htons(wPort);
sinaddr.sin_addr.s_addr = g_bind.RIB_Address;
dwErr = bind(sock, (PSOCKADDR)&sinaddr, sizeof(SOCKADDR_IN));
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
PRINT1("error %d binding socket", dwErr);
break;
}
//
// set the outgoing interface for multicasts
//
sinaddr.sin_addr.s_addr = g_bind.RIB_Address;
dwErr = setsockopt(
sock,
IPPROTO_IP,
IP_MULTICAST_IF,
(PCCH)&sinaddr.sin_addr,
sizeof(IN_ADDR)
);
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
PRINT1("error %d setting multicast interface", dwErr);
break;
}
//
// join the RIP multicast group
//
imOption.imr_multiaddr.s_addr = IPRIP_MULTIADDR;
imOption.imr_interface.s_addr = g_bind.RIB_Address;
dwErr = setsockopt(
sock,
IPPROTO_IP,
IP_ADD_MEMBERSHIP,
(PCCH)&imOption,
sizeof(struct ip_mreq)
);
if (dwErr == SOCKET_ERROR) {
dwErr = WSAGetLastError();
PRINT1("error %d joining multicast group", dwErr);
break;
}
dwErr = NO_ERROR;
}
} while(FALSE);
if (dwErr != NO_ERROR) { closesocket(sock); }
else { *psock = sock; }
return dwErr;
}
DWORD
DiscoverRouters(
SOCKET sock,
PLIST_ENTRY rtrlist
)
{
INT iLength;
PIPRIP_ENTRY pie;
PIPRIP_HEADER phdr;
SOCKADDR_IN sindest;
DWORD dwErr, dwSize;
PIPRIP_AUTHENT_ENTRY pae;
BYTE pbuf[MAX_PACKET_SIZE];
INT iErr;
FD_SET fs;
TIMEVAL tv;
DWORD dwTicks, dwTicksBefore, dwTicksAfter;
PRINT0("attempting to discover neighboring routers...");
//
// construct the RIP packet
//
phdr = (PIPRIP_HEADER)pbuf;
pie = (PIPRIP_ENTRY)(phdr + 1);
pae = (PIPRIP_AUTHENT_ENTRY)(phdr + 1);
phdr->IH_Command = IPRIP_REQUEST;
phdr->IH_Version = (CHAR)g_cfg.RIC_PacketVersion;
phdr->IH_Reserved = 0;
//
// setup the authentication entry if necessary;
// note that the code allows authentication in RIPv1 packets
//
if (g_cfg.RIC_AuthType == IPRIP_AUTHTYPE_SIMPLE_PASSWORD) {
pae->IAE_AddrFamily = ADDRFAMILY_AUTHENT;
pae->IAE_AuthType = (WORD)g_cfg.RIC_AuthType;
RtlCopyMemory(
pae->IAE_AuthKey,
g_cfg.RIC_AuthKey,
IPRIP_MAX_AUTHKEY_SIZE
);
++pie;
}
//
// setup the single packet entry; we request a meaningless address
//
pie->IE_AddrFamily = htons(AF_INET);
pie->IE_RouteTag = 0;
pie->IE_Destination = 0xccddeeff;
pie->IE_SubnetMask = 0;
pie->IE_Nexthop = 0;
pie->IE_Metric = htonl(IPRIP_INFINITE);
dwSize = (ULONG) ((PBYTE)(pie + 1) - pbuf);
//
// send the route request to the RIP port
//
PRINT1("\tsending REQUEST to %s", INET_NTOA(g_cfg.RIC_RouteTarget));
sindest.sin_family = AF_INET;
sindest.sin_port = htons(IPRIP_PORT);
sindest.sin_addr.s_addr = g_cfg.RIC_RouteTarget;
iLength = sendto(
sock, (PCCH)pbuf, dwSize, 0,
(PSOCKADDR)&sindest, sizeof(SOCKADDR_IN)
);
if (iLength == SOCKET_ERROR || (DWORD)iLength < dwSize) {
dwErr = WSAGetLastError();
PRINT1("error %d sending route request", dwErr);
return dwErr;
}
//
// wait a while before collecting responses
//
Sleep(10000);
//
// repeatedly receive for the next 10 seconds
//
tv.tv_sec = 10;
tv.tv_usec = 0;
//
// this loop executes until 10 seconds have elapsed
//
while (tv.tv_sec > 0) {
FD_ZERO(&fs);
FD_SET(sock, &fs);
//
// get the tick count beofre starting select
//
dwTicksBefore = GetTickCount();
//
// enter the call to select
//
iErr = select(0, &fs, NULL, NULL, &tv);
//
// compute the elapsed time
//
dwTicksAfter = GetTickCount();
if (dwTicksAfter < dwTicksBefore) {
dwTicks = dwTicksAfter + ((DWORD)-1 - dwTicksBefore);
}
else {
dwTicks = dwTicksAfter - dwTicksBefore;
}
//
// update the timeout
//
if (tv.tv_usec < (INT)(dwTicks % 1000) * 1000) {
//
// borrow a second from the tv_sec field
//
--tv.tv_sec;
tv.tv_usec += 1000000;
}
tv.tv_usec -= (dwTicks % 1000) * 1000;
tv.tv_sec -= (dwTicks / 1000);
//
// process any incoming packets there might be
//
if (iErr != 0 && iErr != SOCKET_ERROR && FD_ISSET(sock, &fs)) {
INT addrlen;
SOCKADDR_IN sinsrc;
PRIPTEST_ROUTER_INFO prs;
//
// receive the packet
//
addrlen = sizeof(sinsrc);
iLength = recvfrom(
sock, (PCHAR)pbuf, MAX_PACKET_SIZE, 0,
(PSOCKADDR)&sinsrc, &addrlen
);
if (iLength == 0 || iLength == SOCKET_ERROR) {
dwErr = WSAGetLastError();
PRINT1("error %d receiving packet", dwErr);
continue;
}
//
// create a list entry for the responding router
//
dwErr = CreateRouterStatsEntry(
rtrlist, sinsrc.sin_addr.s_addr, &prs
);
if (dwErr == NO_ERROR) {
PRINT2(
"\treceived RESPONSE from %s (%s)",
INET_NTOA(sinsrc.sin_addr), prs->RRS_DnsName
);
}
}
}
if (rtrlist->Flink == rtrlist) {
PRINT0("\tno neighboring routers discovered");
}
return NO_ERROR;
}
DWORD
GenerateRoutes(
IPForwardEntry **pifelist
)
{
CHAR szAddress[20];
IPForwardEntry *ifelist, *ife;
DWORD dwRouteCount, dwNetworkCount;
DWORD dwStartOffset, dwLowestAddress, dwHighestAddress;
DWORD dw, dwErr, dwAddress, dwNexthop, dwSubnetMask, dwPrefixLength;
dwNexthop = g_cfg.RIC_RouteNexthop;
dwSubnetMask = g_cfg.RIC_RouteMask;
dwPrefixLength = PREFIX_LENGTH(dwSubnetMask);
//
// find the last address in the start-address's network class
//
dwLowestAddress = g_cfg.RIC_RouteStart;
dwHighestAddress =
(CLASSA_ADDR(dwLowestAddress) ? inet_addr("126.255.255.255") :
(CLASSB_ADDR(dwLowestAddress) ? inet_addr("191.255.255.255") :
(CLASSC_ADDR(dwLowestAddress) ? inet_addr("223.255.255.255") : 0)));
//
// figure out how many networks the range can be split into
// using the specified network mask
//
dwLowestAddress &= dwSubnetMask;
dwNetworkCount = ((ntohl(dwHighestAddress) >> (32 - dwPrefixLength)) -
(ntohl(dwLowestAddress) >> (32 - dwPrefixLength)));
//
// choose a starting address for this iteration:
// we have at most K routes, and we are sending n routes,
// so the starting route must be at an offset of between 0 and (K - n)
//
dwRouteCount = g_cfg.RIC_RouteCount;
dwStartOffset = RANDOM(&g_seed, 0, (dwNetworkCount - dwRouteCount));
//
// allocate the array of routes
//
*pifelist =
ifelist = (IPForwardEntry *)HeapAlloc(
GetProcessHeap(), 0,
dwRouteCount * sizeof(IPForwardEntry)
);
if (ifelist == NULL) {
dwErr = GetLastError();
PRINT2(
"error %d allocating %d bytes for route list",
dwErr, dwRouteCount * sizeof(IPForwardEntry)
);
return dwErr;
}
RtlZeroMemory(ifelist, dwRouteCount * sizeof(IPForwardEntry));
//
// fill the table with routes
//
for (dw = dwStartOffset; dw < (dwStartOffset + dwRouteCount); dw++) {
dwAddress = NTH_ADDRESS(dwLowestAddress, dwPrefixLength, dw);
ife = ifelist + (dw - dwStartOffset);
ife->dwForwardDest = dwAddress;
ife->dwForwardMask = dwSubnetMask;
ife->dwForwardNextHop = dwNexthop;
}
return NO_ERROR;
}
DWORD
TransmitRoutes(
SOCKET sock,
DWORD dwMetric,
IPForwardEntry *ifelist
)
{
INT iLength;
WORD wRouteTag;
PIPRIP_HEADER pih;
SOCKADDR_IN sindest;
PIPRIP_ENTRY pie, pistart, piend;
PIPRIP_AUTHENT_ENTRY pae;
IPForwardEntry *ife, *ifend;
BYTE pbuf[2 * MAX_PACKET_SIZE];
DWORD dwErr, dwEntryCount;
//
// setup the message's header
//
pih = (PIPRIP_HEADER)pbuf;
pae = (PIPRIP_AUTHENT_ENTRY)(pih + 1);
pistart = (PIPRIP_ENTRY)(pih + 1);
pih->IH_Command = IPRIP_RESPONSE;
pih->IH_Version = (CHAR)g_cfg.RIC_PacketVersion;
pih->IH_Reserved = 0;
//
// setup the authentication entry if necessary;
// note that the code allows authentication in RIPv1 packets
//
if (g_cfg.RIC_AuthType == IPRIP_AUTHTYPE_SIMPLE_PASSWORD) {
pae->IAE_AddrFamily = ADDRFAMILY_AUTHENT;
pae->IAE_AuthType = (WORD)g_cfg.RIC_AuthType;
RtlCopyMemory(
pae->IAE_AuthKey,
g_cfg.RIC_AuthKey,
IPRIP_MAX_AUTHKEY_SIZE
);
++pistart;
}
//
// pick off the configured number of routes to put in the packet
//
if (g_cfg.RIC_PacketEntryCount != 0) {
dwEntryCount = g_cfg.RIC_PacketEntryCount;
}
else {
//
// choose a random number of entries
//
if (g_cfg.RIC_AuthType == IPRIP_AUTHTYPE_NONE) {
dwEntryCount = RANDOM(&g_seed, 1, 25);
}
else {
dwEntryCount = RANDOM(&g_seed, 1, 24);
}
}
wRouteTag = LOWORD(g_cfg.RIC_RouteTag);
sindest.sin_family = AF_INET;
sindest.sin_port = htons(IPRIP_PORT);
sindest.sin_addr.s_addr = g_cfg.RIC_RouteTarget;
{
DWORD dwCount;
CHAR szDest[20], szFirst[20], szLast[20];
dwCount = g_cfg.RIC_RouteCount;
strcpy(szDest, INET_NTOA(sindest.sin_addr));
strcpy(szFirst, INET_NTOA(ifelist->dwForwardDest));
strcpy(szLast, INET_NTOA((ifelist + dwCount - 1)->dwForwardDest));
PRINT5(
"sending %d routes (%s - %s) to %s using metric %d",
dwCount, szFirst, szLast, szDest, dwMetric
);
}
//
// loop filling the buffer with packets and sending it when its full
//
piend = pistart + dwEntryCount;
ifend = ifelist + g_cfg.RIC_RouteCount;
for (ife = ifelist, pie = pistart; ife < ifend; ife++, pie++) {
//
// send the current buffer if it is full
//
if (pie >= piend) {
//
// sleep for the specified packet-gap
//
Sleep(g_cfg.RIC_PacketGap);
iLength = sendto(
sock, (PCCH)pbuf, (ULONG)((PBYTE)pie - pbuf), 0,
(PSOCKADDR)&sindest, sizeof(SOCKADDR_IN)
);
if (iLength == SOCKET_ERROR || iLength < ((PBYTE)piend - pbuf)) {
dwErr = WSAGetLastError();
PRINT2(
"error %d sending packet to %s",
dwErr, INET_NTOA(sindest.sin_addr)
);
}
if (g_cfg.RIC_PacketEntryCount != 0) {
dwEntryCount = g_cfg.RIC_PacketEntryCount;
}
else {
//
// choose a random number of entries
//
if (g_cfg.RIC_AuthType == IPRIP_AUTHTYPE_NONE) {
dwEntryCount = RANDOM(&g_seed, 1, 25);
}
else {
dwEntryCount = RANDOM(&g_seed, 1, 24);
}
}
piend = pistart + dwEntryCount;
pie = pistart;
}
//
// add another entry
//
pie->IE_AddrFamily = htons(AF_INET);
pie->IE_Destination = ife->dwForwardDest;
pie->IE_Metric = htonl(dwMetric);
pie->IE_RouteTag = htons(wRouteTag);
pie->IE_Nexthop = ife->dwForwardNextHop;
pie->IE_SubnetMask = ife->dwForwardMask;
}
//
// if there is anything left, send it
//
if (pie > pistart) {
iLength = sendto(
sock, (PCCH)pbuf, (ULONG)((PBYTE)pie - pbuf), 0,
(PSOCKADDR)&sindest, sizeof(SOCKADDR_IN)
);
}
return NO_ERROR;
}
DWORD
VerifyRouteTables(
DWORD dwMetric,
PLIST_ENTRY rtrlist,
IPForwardEntry *ifelist
)
{
PLIST_ENTRY ple;
MIB_OPAQUE_QUERY roq;
MIB_OPAQUE_INFO *proi;
WCHAR pwsRouter[256];
MIB_SERVER_HANDLE hRouter;
PRIPTEST_ROUTER_INFO prrs;
MIB_IPFORWARDTABLE *ifrlist;
IPForwardEntry *ife, *ifend;
MIB_IPFORWARDROW *ifr, *ifrend;
DWORD dwInvalidMetrics, dwRoutesMissing;
DWORD dwErr, dwNumEntries, dwInSize, dwOutSize;
//
// go through the list of routers, connecting to the Router
// on each machine and querying its routing table
//
for (ple = rtrlist->Flink; ple != rtrlist; ple = ple->Flink) {
prrs = CONTAINING_RECORD(ple, RIPTEST_ROUTER_INFO, RRS_Link);
PRINT1("-----STATS FOR %s-----", prrs->RRS_DnsName);
//
// initialize the query arguments
//
mbstowcs(pwsRouter, prrs->RRS_DnsName, strlen(prrs->RRS_DnsName) + 1);
roq.dwVarId = IP_FORWARDTABLE;
dwInSize = sizeof(MIB_OPAQUE_QUERY) - sizeof(DWORD);
proi = NULL;
dwOutSize = 0;
//
// perform the query
//
dwErr = MprAdminMIBServerConnect(pwsRouter, &hRouter);
if (dwErr != NO_ERROR) {
continue;
}
dwErr = MprAdminMIBEntryGet(
hRouter, PID_IP, IPRTRMGR_PID,
(PVOID)&roq, dwInSize, (PVOID *)&proi, &dwOutSize
);
if (dwErr != NO_ERROR) {
PRINT2(
"error %d querying route table on server %s",
dwErr, prrs->RRS_DnsName
);
MprAdminMIBServerDisconnect(hRouter);
continue;
}
else
if (proi == NULL) {
PRINT1(
"empty route table retrieved from server %s",
prrs->RRS_DnsName
);
MprAdminMIBServerDisconnect(hRouter);
continue;
}
//
// look through the table of routes retrieved,
// to verify thats the routes advertised are among them
//
dwRoutesMissing = 0;
dwInvalidMetrics = 0;
ifrlist = (PMIB_IPFORWARDTABLE)(proi->rgbyData);
dwNumEntries = ifrlist->dwNumEntries;
ifend = ifelist + g_cfg.RIC_RouteCount;
for (ife = ifelist; ife < ifend; ife++) {
//
// each time we find an advertised route,
// we swap it to the end of the table;
// thus, the size of the table that we need to search
// decreases with the number of routes we have found
//
ifrend = ifrlist->table + dwNumEntries;
for (ifr = ifrlist->table; ifr < ifrend; ifr++) {
if (ife->dwForwardDest == ifr->dwForwardDest) {
if (ifr->dwForwardMetric1 == (dwMetric + 1)) {
ife->dwForwardMetric5 = ROUTE_STATUS_OK;
}
else {
//
// set the status for this route
//
++dwInvalidMetrics;
ife->dwForwardMetric5 = ROUTE_STATUS_METRIC;
PRINT3(
"\troute to %s has metric %d, expected %d",
INET_NTOA(ife->dwForwardDest),
ife->dwForwardMetric1, dwMetric + 1
);
}
//
// overwrite with the item at the end of the table;
// if we are at the end of the table, do nothing
//
if (ifr != (ifrend - 1)) { *ifr = *(ifrend - 1); }
--dwNumEntries;
break;
}
}
//
// if the item wasn't found, mark it as such
//
if (ifr >= ifrend) {
++dwRoutesMissing;
ife->dwForwardMetric5 = ROUTE_STATUS_MISSING;
PRINT1("\troute to %s missing", INET_NTOA(ife->dwForwardDest));
}
}
MprAdminMIBBufferFree(proi);
MprAdminMIBServerDisconnect(hRouter);
PRINT2("%20s == %d", "routes missing", dwRoutesMissing);
PRINT2("%20s == %d", "invalid metrics", dwInvalidMetrics);
}
return NO_ERROR;
}
DWORD
CreateRouterStatsEntry(
PLIST_ENTRY rtrlist,
DWORD dwAddress,
PRIPTEST_ROUTER_INFO *pprrs
)
{
DWORD dwErr;
PHOSTENT phe;
PRIPTEST_ROUTER_INFO prrs;
phe = gethostbyaddr((const char *)&dwAddress, sizeof(DWORD), PF_INET);
if (phe == NULL) {
dwErr = WSAGetLastError();
PRINT2(
"error %d retrieving name for host %s", dwErr, INET_NTOA(dwAddress)
);
return dwErr;
}
prrs = (PRIPTEST_ROUTER_INFO)HeapAlloc(
GetProcessHeap(), 0,
sizeof(RIPTEST_ROUTER_INFO)
);
if (prrs == NULL) {
dwErr = GetLastError();
PRINT2(
"error %d allocating %d bytes for router stats",
dwErr, sizeof(RIPTEST_ROUTER_INFO)
);
return dwErr;
}
RtlZeroMemory(prrs, sizeof(RIPTEST_ROUTER_INFO));
prrs->RRS_Address = dwAddress;
strcpy(prrs->RRS_DnsName, phe->h_name);
InsertHeadList(rtrlist, &prrs->RRS_Link);
if (pprrs != NULL) { *pprrs = prrs; }
return NO_ERROR;
}
DWORD
PrintUsage(
VOID
)
{
printf("usage: riptest [adapter_guid]");
printf("\n\te.g. riptest {73C2D5F0-A352-11D1-9043-0060089FC48B}\n");
printf("\n\tThe first time RIPTEST is run, it sets up the registry");
printf("\n\twith defaults for the specified adapter.");
printf("\n");
return NO_ERROR;
}
} // end extern "C"