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. 



Click here for larger image


// 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


Comments

  • IPHLPAPI - DeleteIPAddress on does not work on Win 98

    Posted by Legacy on 09/09/2003 12:00am

    Originally posted by: Siva Shanmugam

    I am trying to delete ip address, set static ip address for an adapter and revert static to dynamic ip address. This does not work on Win 98 since some of the apis (like AddIPAddress, DeleteIPAddress are not supported)

    Do you know if there is any way of doing this on Win 98?

    Thanks, Siva

    Reply
  • Easy Uploader

    Posted by Legacy on 07/22/2003 12:00am

    Originally posted by: Aamir Khan JAdoon

    I have an assignment to construct an application in visual C++ 6.0 that gets IP address of remote computer on the net and upload selected files and folders in the at the same path (No drive letter)as it has e.g \asd\dfg\dfg\file1.txt
    is uploaded as it is

    Reply
  • IphlpApi.dll with GetAdaptersAdresses ??

    Posted by Legacy on 04/09/2003 12:00am

    Originally posted by: Philippe

    Hi,

    I need help.
    I must try to use "GetAdaptersAdresses " as function in iphlpapi.dll to know the state of the adapters.
    It seems I can know it with the result of this function.
    But I don't have it in files iphlpapi .h, .lib, .dll ???

    Do you the right files iphlpapi .h, .lib, .dll ???

    Thanks a lot
    Philippe

    Reply
  • i am novice

    Posted by Legacy on 01/14/2003 12:00am

    Originally posted by: kiran

    i am sooy but i have just started using net.can some one or u  tell me how to determine ones i p address
    

    Reply
  • Can you send me the iphlpapi.h file and library in Kenya?

    Posted by Legacy on 09/06/2002 12:00am

    Originally posted by: David

    I think this is exactly the sort of code I am looking for right now, but unfortunately I am using a very slow connection in Kenya. Is it possible to send me the iphlpapi.h file (and library) to me at my email address? Will it work if I just paste it into the appropriate Visual C++ directories? Thanks for your help and your great code.

    David

    Reply
  • Unresolved external 'GetInterfaceInfo'

    Posted by Legacy on 08/21/2002 12:00am

    Originally posted by: Kamil

    Hi... I am trying to compile your class under Borland C++ Builder 6.0, but I am getting linker error

    [Linker Error] Unresolved external 'GetInterfaceInfo' referenced from IPADFUNCS.OBJ

    Do you know where is the problem ?

    Kamil

    well...

    I just added iphlpapi.lib to my project and everything is working ok
    :-)


    Kamil

    Reply
  • IPADFUNCS and Dialup Connections

    Posted by Legacy on 08/03/2002 12:00am

    Originally posted by: Rick

    I have used the IPADFuncs class developed by Bill Nolde to add IP addresses to PPP connections between a PC running Windows 2000 with SP2 and some government-furnished routers that interface the PC with a radio. However, the aliases (added IP addresses) seem to be clobbering the "route" table.

    When I create the PPP connection, it is set up such that the IP on the PC end of the connection is 1.2.3.4 and the IP on the router end of the connection is 1.2.3.5 (last quad of the router IP is one more than the PC IP). The "route" table entry looks like the following:
    Destination 1.2.3.5
    Mask 255.255.255.255
    Gateway 1.2.3.4
    Interface 1.2.3.4

    When I add an alias/IP address (such as 1.2.3.7) to the 1.2.3.4 IP address, the "route" table gets changed to something like the following:
    Destination 1.2.3.5
    Mask 255.255.255.255
    Gateway 1.2.3.7 (NOTE: the gateway is changed to the added IP address)
    Interface 1.2.3.4

    This prevents me from using "telnet" to access the routers through the PPP connections. I've tried manually deleting the route and adding it back with the correct IP address, but the OS complains about my attempt to add it or adds it back using the alias as the gateway (as shown above).

    Any clues as to what I might be doing wrong or how to resolve the problem?

    Reply
  • Doesn't work.

    Posted by Legacy on 04/28/2002 12:00am

    Originally posted by: Bill Xiao

    Nice to find your sample code. After unzipping your project package and running the project, there is wrong message " This application has failed to start because MFC 70.dll was not found, ... ". My computer has installed Windows XP and VS 6.0. I also can't use VC++ to open your workspace. Is there document we can find these answers?
    thanks and regards,
    bill

    Reply
  • Using AddIPAddress() API without network connection

    Posted by Legacy on 04/19/2002 12:00am

    Originally posted by: Prem

    Hi, 
    
    I am using AddIPAddress() API call in my application to
    add IP address like 127.x.y.z. This API call is working
    fine when I have a network connection. But its not working
    when I unplug the network cable.
    Is it possible to add virtual IP addresses like 127.x.y.x
    using AddIPAddress() API call without a network
    connection ? or any workaround ?

    Regards,
    Prem.

    Reply
  • It works on both Win2K AND WindowsXP

    Posted by Legacy on 04/17/2002 12:00am

    Originally posted by: Bill Nolde

    This code was tested on both Win2k and WindowsXP and has no problems. It was never tested with a dialup connection for obvious reasons and I expect it won't work. The article is about testing on a LAN which requires a Network adapter.
    Bill

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Event Date: April 15, 2014 The ability to effectively set sales goals, assign quotas and territories, bring new people on board and quickly make adjustments to the sales force is often crucial to success--and to the field experience! But for sales operations leaders, managing the administrative processes, systems, data and various departments to get it all right can often be difficult, inefficient and manually intensive. Register for this webinar and learn how you can: Align sales goals, quotas and …

  • With 81% of employees using their phones at work, companies have stopped asking: "Is corporate data leaking from personal devices?" and started asking: "How do we effectively prevent corporate data from leaking from personal devices?" The answer has not been simple. ZixOne raises the bar on BYOD security by not allowing email data to reside on the device. In addition, Zix allows employees to maintain complete control of their personal device, therefore satisfying privacy demands of valued employees and the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds