IP Inter-network Testing Using Only a LAN

 

Environment: VC++ .NET (7)

    Software testing is never as “easy” as developers think it is, and testing network related software brings on a unique set of requirements. On several occasions, in order to test network related software products, I have had the need to simulate a TCP wan (Internet) environment, given only a LAN and a few PC’s. Commercial TCP wan simulators were too expensive and didn’t have the functionality required. What I really needed was a way to make UDP requests and TCP connections from a wide range of IP addresses,
to the software applications I was testing, from software I would write.

    Making the connection locally from simple TCP sockets applications was no problem. I needed to be able to do that from any random or specific IP address to create a wan test environment. There was not going to be a simple socket programming solution to this I thought.

    A blunt, direct simple NDIS/raw packet approach seemed the easiest at first. One problem with this approach was that a device driver/service needed to be loaded. A messy step, overkill for the simple applications I had in mind. There is a working example in the DDK, and I have used it, but it seemed too much work for what I had in mind. Another raw approach was 

    What functionality do I really need to accomplish this I asked? The key was to be able to send a packet from a workstation from any IP address to any IP address and receive the reply. It sounds so simple. The first part could easily be done with TCP raw sockets; I could send almost any packet type I needed and use any from IP address. This worked, but the server would always send the reply to the default gateway, not back to the test machine. Here again, a raw packet listen would do, and there is a way to do this with sockets and advanced ioctl functions, but I didn’t like this approach for these tools.

    I brought up a packet sniffer to gather more information. That’s where I noticed that a packet sent to a router (default gateway) is indistinguishable (to the router) from a unicast packet sent from another node directed to the router, except for the destination IP address! Non-routing nodes just drop the packets. I knew I was on to something.

    I also started searching the Microsoft SDK and found the IPHLPAPI functions. These are Microsoft specific. There I found the Add and Delete IP address functions. This was the next key if it worked, and it did.

    So now my applications can add an IP address, bind to it or use it as a “from” address and the system would use it as an ordinary ip address in socket calls! This was half the solution.

    The final key came from the packet sniffing. I needed to set the default gateway on the server running the application to test, to point to the workstation running the test applications! The server will be sending the packet to it’s default gateway, expecting it to be routed, and doesn’t care how. The test workstation will see the packet, and actually have that IP address added and will think it is a reply to that address (which it is)!! The adapter will have multiple IP addresses defined on it, one being the local LAN permanent IP address, and another, the one added.

    The functions don’t take a lot of time. Basically the method is to add an IP address, make a TCP connection or send and receive a UDP packet using that IP address, finish, then delete the IP address. I am able to populate routing and address tables with hundreds to tens of thousands of different IP addresses quickly.

    By reading the IP addresses from a file I was able to create a somewhat deterministic wan testing environment, something we were not able to do before. There are other options, like delaying the connection to simulate network latency. This was extremely valuable in tracking down spurious bugs.

    I wrote this program to test these functions. The IPHLPAPI functions are not documented well, and I had to hunt down a lot of information, so I distilled the functions I used down, and tried to make them useable and understandable. There are three main functions, IPAddAddress, IPDeleteAddress, and IPGetAllAddresses . The listing of IP addresses is unrelated to the Add/Delete functions other than I use it in this example. It also uses IPHLPAPI routines and is a natural utility function.

Notes:

    The code is pretty straightforward except for something defined as:

ULONG NTEContext  // net table entry context.

It is created on the AddIPAddress API call, which you can save, and later use to delete the IP address. But if your application didn’t add the IP address, you have to find the context somehow if you want to delete an IP address. This was hard to find in the general
case, so I created a method to hide the details.

    The routines use the first adapter for all adds and deletes, but that is easily modified for multi homed hosts.

    IP addresses exist as long as the workstation is not rebooted. They are not persistent.

    You can not delete an the primary IP address added by the regular system interface, only IP addresses that have been created with the API, or secondary addresses added in Network properties can be removed.

    I have not checked out how the Add function handles all types of netmasks. The standard ones seem to work fine.

 

    I created a simple MFC application to demonstrate the use of the of class I created using Visual Studio .NET. 


// IPADFuncs.h
// Windows IP Add/Delete/utility class definition
//      Copyrite Billco Systems 2001
//      Bill Nolde 2001   billnolde@ieee.org
#include <iphlpapi.h>

#define MAXADAPTERS 20
#define ADAPTERNUMBER 0

//  This structure is used for listing all IP addresses
typedef struct ip_z {
  char ip_address[16];
  char sub_netmask[16];
} ip_entry, *pip_entry;

class IPADFuncs
{

public:
  IPADFuncs(void);
  ~IPADFuncs(void);

int  IPV4CheckAddress   (char *pIPAddress  );
int  IPAddAddress       (char *pIPtoADD, char *pIPNetmask);
int  IPDeleteAddress    (char *iptodelete  );
int  IPGetAllAddresses  (pip_entry ppe, int nMaxEntries  );

int  nNumberOfAdapters;

private:
DWORD   IpGetAddressContext( char *pIPAddress );
BOOLEAN GetAllAdaptersInfo (void);

ULONG adapter_index [MAXADAPTERS];

};


// IPADFuncs.cpp
// Windows IP Add/Delete/utility class implementation
// Copyrite Billco Systems 2001
// Bill Nolde 2001 billnolde@ieee.org

#include "stdafx.h"
#include <winsock2.h>
#include <iphlpapi.h>
#include "IPADFuncs.h"

//--------------------------------------
IPADFuncs::IPADFuncs(void)
{
nNumberOfAdapters = 0;
GetAllAdaptersInfo ();
return;
}
//---------------------------------------
IPADFuncs::~IPADFuncs(void)
{
return;
}
//------------------------------------------------------------
// This routine adds an IP address and Netmask to an interface.
// currently, the code is hard coded for adapter 0(ADAPTERNUMBER).
// You could list the interfaces and let the user decide.
//------------------------------------------------------------

int IPADFuncs::IPAddAddress(char *pIPtoADD, char *pIPNetmask)
{
IPAddr lIPAddress; // IP address to add
IPMask lIpMask; // subnet mask for IP address

ULONG dwNTEContext;
ULONG dwNTEInstance;
int nAdapterNumber = ADAPTERNUMBER;
int nRc;

lIpMask = inet_addr(pIPNetmask);
lIPAddress = inet_addr(pIPtoADD);

//actual IpHlpApi Call
nRc = AddIPAddress(lIPAddress,
lIpMask,
adapter_index[nAdapterNumber],
&dwNTEContext,
&dwNTEInstance);
return nRc;
}
//--------------------------------------------------------------
// This routine lists all adapters. The only thing we need here
// is the adapter index for the add and delete IP functions. This
// example uses only the first adapter,0, since most PC's have
// only one adapter, but that is easily changed.
//--------------------------------------------------------------

BOOLEAN IPADFuncs::GetAllAdaptersInfo(void)
{
PIP_INTERFACE_INFO pIfTable;
ULONG dwOutBufLen ;
int nRc;

//Get Size of Table needed
dwOutBufLen = GetInterfaceInfo( NULL, &dwOutBufLen);
//Allocate space 
dwOutBufLen = ( dwOutBufLen +1) *
sizeof(IP_ADAPTER_INDEX_MAP) ;
pIfTable = (PIP_INTERFACE_INFO) malloc (
dwOutBufLen * sizeof(pIfTable) );
//actual IpHlpApi Call
nRc = GetInterfaceInfo( pIfTable, &dwOutBufLen);
if ( nRc != NO_ERROR ) {
free (pIfTable); //No adapters ??
return FALSE;
}

//Get Number of adapters and fill table with adapter Indexes
nNumberOfAdapters = pIfTable->NumAdapters;
for (int i=0; i< pIfTable->NumAdapters; i++ )
adapter_index[i] = pIfTable->Adapter[i].Index;

free (pIfTable);
return TRUE;
}
//---------------------------------------------------------
int IPADFuncs::IPDeleteAddress ( char *iptodelete )
{
DWORD dwContext;
int nRc;
dwContext = IpGetAddressContext( iptodelete );
if (dwContext == 0 ) return 1; //IP Not Found ???
nRc = DeleteIPAddress(dwContext); //actual IpHlpApi Call
if ( nRc == NO_ERROR ) return 0;
MessageBeep(10000);
return nRc;
}
//-----------------------------------------------------------
//This routine get all the IP addresses defined on all adapters
// and fills a table with the IPs'a and their Netmasks
//-----------------------------------------------------------

int IPADFuncs::IPGetAllAddresses ( pip_entry ppe,
int nMaxEntries )
{
int nRc, nNumberOfIPs;
PMIB_IPADDRTABLE pIpAddrTable;
ULONG lBufSize;
struct in_addr in;

lBufSize = nMaxEntries *
sizeof(MIB_IPADDRTABLE); //Get size and alloc a
// alloc a buffer
pIpAddrTable = (PMIB_IPADDRTABLE) malloc(lBufSize);
nRc = GetIpAddrTable (pIpAddrTable,
&lBufSize,
FALSE); //iphlpapi call
nNumberOfIPs = pIpAddrTable->dwNumEntries;
if (nNumberOfIPs > nMaxEntries)
nNumberOfIPs = nMaxEntries;

//fill table
for (int i=0; i<nNumberOfIPs; i++,++ppe) {
in.S_un.S_addr =
pIpAddrTable->table[i].dwAddr; //get address
strcpy(ppe->ip_address, inet_ntoa(in));
in.S_un.S_addr =
pIpAddrTable->table[i].dwMask; //get netmask
strcpy(ppe->sub_netmask, inet_ntoa(in));
}
free (pIpAddrTable); //clean up
return nNumberOfIPs;
}
//---------------------------------------------------------
// This is an adaptable function that can be cut up and
// used in many ways.In this case I want to retrieve an
// IP context value so I can delete an ip address.
// It is the definative IP list, including netmasks and
// contexts The core loop lists all adapters, each of which
// has a linked list of the ip addresses assigned to it.
//----------------------------------------------------------

DWORD IPADFuncs::IpGetAddressContext( char *pIPAddress )
{
PIP_ADDR_STRING paddr;
PIP_ADAPTER_INFO pAdapterInfo, pinfo;
ULONG dwBufLen, nRc;
IP_ADDRESS_STRING sIPtoFind;
DWORD dwContext;
int nAddressLength = (int) strlen(pIPAddress);

memcpy(&sIPtoFind, pIPAddress, strlen(pIPAddress));
// Allocate Memory for table
dwBufLen = GetAdaptersInfo( NULL, &dwBufLen);
dwBufLen = dwBufLen * sizeof(IP_ADAPTER_INFO);
pAdapterInfo = (PIP_ADAPTER_INFO) malloc (dwBufLen);
if ( pAdapterInfo == NULL ) {
return -1;
}
nRc = GetAdaptersInfo(pAdapterInfo,
&dwBufLen); //actual IpHlpApi Call
if ( nRc != ERROR_SUCCESS ) { //Bad Problem ??
free(pAdapterInfo);
return 0; //No match
}
pinfo = pAdapterInfo; //Adapter link list
while ( pinfo != NULL ) {
paddr = &pinfo->IpAddressList; //IP address
// link list for adapter
while ( paddr != NULL ) {
if ( memcmp(&sIPtoFind,
(void*)&paddr->IpAddress,
nAddressLength ) == 0 ) {
dwContext = paddr->Context;
free(pAdapterInfo);
return dwContext; //We Got a Match, return context
}
paddr = paddr->Next; //Next address
}
pinfo = pinfo->Next; //Next adapter
}
free(pAdapterInfo); //Clean Up
return 0; //No Match
}
//-----------------------------------------------------------
// This is a pretty thorough IP V4 address checking routine
//-----------------------------------------------------------

int IPADFuncs::IPV4CheckAddress(char *pIPAddress)
{
int nDots = 0;
ULONG dwIPCheck;
char *pTemp = pIPAddress;

if ( pTemp == NULL || *pTemp == '.' ) return 1;
while ( 1 ) {
if ( *pTemp == 0 ) break;
if ( *pTemp == '.') ++nDots;
else if ( isdigit(*pTemp) == 0 ) return 2;
++pTemp;
}
if ( nDots != 3 ) return 3;
--pTemp;
if ( *pTemp == '.' ) return 4;
if (strcmp( pIPAddress, "255.255.255.255") == 0 )
return 0;
dwIPCheck = inet_addr(pIPAddress);
if (dwIPCheck == INADDR_NONE ) return 5;
return 0;
}
//-----------------------------------------------------------

Downloads

Download demo project - 79 Kb

Download source - 3 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read