|
|
//============================================================================
// 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 = NULL; 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"
|