Click here to Skip to main content
15,868,141 members
Articles / Desktop Programming / MFC
Article

More on using IP Helper API’s

Rate me:
Please Sign up or sign in to vote.
4.96/5 (22 votes)
4 Mar 20046 min read 237K   5.6K   120   56
A brief discussion on how to use a few of the functions in the IP Helper suite

Sample Image - IPHelper.jpg

Introduction

Having had the need/desire recently to find out the relationship between an IP address and a MAC address, I found a suite of functions that had previously gone unused by me.  When I set out to find information about their usage, the results were paltry at best.  Perhaps I did not search correctly or to exhaustion.  Nonetheless, I wanted to see how they worked.

Most of what I found kept pointing me to this thing called the Address Resolution protocol (ARP).  This is implemented as part of the IP layer of the OSI model.  When a packet needs to be delivered from one piece of hardware to another, how do the packets, which contain IP addresses, know the actual machine to deliver to?  That's where ARP comes in.  The ARP cache on the host contains IP/hardware addresses collected during previous activity.  If the cache does not contain a resolution, a broadcast message (i.e. ARP query) is then sent out by the network driver.  The host will wait two seconds for a response.  If a response is not received, the broadcast message is sent out again with a wait time of four seconds.  This continues until a response is received or the timeout is reached.  In a DHCP-type network, ARP requests are very gratuitous (i.e., they are sent out quite frequently) so as to keep the cache on each host up-to-date as possible.

Enough about ARP.  You can read about it in way more detail elsewhere.  What I wanted to do here was to briefly share my experience with the various IP Helper functions, of which ARP is a part of.  Note this article is labeled as "beginner" because there are really no surprises (there is a brief mention of threads and events but that is not relevant to the topic).  If you are already versed in the ways of networking, this article is not for you.  If you know how to call a function and can operate on simple pointers, you'll find nothing new here.

IP Helper functions

  • GetIpNetTable
  • GetIpAddrTable
  • GetIpForwardTable
  • GetIpStatistics
  • GetBestInterface
  • GetBestRoute
  • NotifyRouteChange
  • NotifyAddrChange
  • SendARP
  • GetUdpTable
  • GetIcmpStatistics
  • GetInterfaceInfo
  • GetNetworkParams
  • GetPerAdapterInfo
  • GetNumberOfInterfaces
  • GetRTTAndHopCount
  • GetTcpStatistics
  • GetTcpTable
  • GetUdpStatistics
  • Most of the functions are straightforward.  You supply a big enough buffer for the returned information, and access that information accordingly (e.g. pointer dereferencing).  Very few surprises await.  I'll go over one of the functions in detail, and leave the rest as an exercise for the interested reader.

    GetIpNetTable() is used to get the IP-to-MAC address translation table.  The returned data is a MIB_IPNETTABLE structure that contains ARP entries.  Each entry in the table is a MIB_IPNETROW structure.  Entries in this structure contain the index of the adapter (which is not an index into an array), the adapter's address, and the type of ARP entry.

    You can call the function with a stack-based buffer, but doing so with a heap-based buffer (i.e. dynamic allocation) ensures that only the amount needed is allocated.  Therefore, two calls are made to the function: one for determining the size of the buffer, and one to get the information.  This looks like:

    PBYTE pBuffer = NULL;
    ULONG ulSize  = 0; 
    
    GetIpNetTable((PMIB_IPNETTABLE) pBuffer, &ulSize, TRUE); 
    pBuffer = new BYTE[ulSize];
    if (NULL != pBuffer)
    {
        GetIpNetTable((PMIB_IPNETTABLE) pBuffer, &ulSize, TRUE);
        ...
        delete [] pBuffer;
    }

    If you did not want to use a "generic" buffer, the following will work also:

    PMIB_IPNETTABLE pIPNetTable = NULL;
    ULONG ulSize  = 0; 
    
    GetIpNetTable(pIPNetTable, &ulSize, TRUE); 
    pIPNetTable = new MIB_IPNETTABLE[ulSize];
    if (NULL != pIPNetTable)
    {
        GetIpNetTable(pIPNetTable, &ulSize, TRUE);
        ...
        delete [] pIPNetTable;
    } 

    Either way is acceptable.  I chose the former in the attached demo because the generic buffer is shared between several of the functions.

    At this point, a pointer to the data is constructed and the data is accessed.  In this example, the data is being formatted into a CString-type variable, and the result is being added to an edit box.  I used a fixed-space font so that everything would line up nicely.

    pIPNetTable = (PMIB_IPNETTABLE) m_pBuffer; 
    
    // iterate each entry in the ARP table
    for (int x = 0; x < pIPNetTable->dwNumEntries; x++)
    {
        pIPNetRow = &(pIPNetTable->table[x]); 
        
        m_strText.Format("             Index: %lu\r\n", pIPNetRow->dwIndex);
        m_edit1.ReplaceSel(m_strText); 
    
        m_strText.Format("MAC address length: %lu\r\n", pIPNetRow->dwPhysAddrLen);
        m_edit1.ReplaceSel(m_strText);
                
        m_strText.Format("       MAC address: %02x-%02x-%02x-%02x-%02x-%02x\r\n", 
            pIPNetRow->bPhysAddr[0],
            pIPNetRow->bPhysAddr[1],
            pIPNetRow->bPhysAddr[2],
            pIPNetRow->bPhysAddr[3],
            pIPNetRow->bPhysAddr[4],
            pIPNetRow->bPhysAddr[5]);
        m_edit1.ReplaceSel(m_strText);
                
        // convert the address from network-byte order to 
        // Internet standard dotted format
        ia.S_un.S_addr = pIPNetRow->dwAddr;
        m_strText.Format("        IP address: %s\r\n", inet_ntoa(ia));
        m_edit1.ReplaceSel(m_strText);
        
        // in this example, szTypes is a char* variable with values
        // Other, Invalid, Dynamic, or Other
        m_strText.Format("              Type: %s\r\n", 
                          szTypes[pIPNetRow->dwType - 1]);
        m_edit1.ReplaceSel(m_strText);
                
        m_edit1.ReplaceSel("\r\n");
    }

    Most of the other IP Helper functions behave in a similar fashion.  A few of them don't require the extra call as they operate on a fixed-sized structure.  Take GetIpStatistics() for example.  No memory to allocate.  Nothing to clean up.  It is simply called like:

    MIB_IPSTATS IPStats; 
    
    if (GetIpStatistics(&IPStats) == NO_ERROR)
    {
        ...
    }

    Another of the functions that was of interest was SendARP().  This actually queries the ARP cache for the MAC address of the specified IP address.  If a match is not found, the function returns status 31.  The function's usage looks something like:

    ULONG   ulMACAddr[2],
            ulSize = 6;
    LPBYTE  pBuffer; 
    
    if (SendARP(inet_addr(_T("207.219.70.31")), 0, 
                          ulMACAddr, &ulSize) == NO_ERROR)
    {
        pBuffer = (LPBYTE) ulMACAddr; 
    
        // access the address one byte at a time
        m_strText.Format("%02X:%02X:%02X:%02X:%02X:%02X\r\n", 
            pBuffer[0], 
            pBuffer[1], 
            pBuffer[2], 
            pBuffer[3], 
            pBuffer[4], 
            pBuffer[5]); 
    
        m_edit1.ReplaceSel(m_strText);
    }

    I'm not quite sure why the third parameter needed to be an array of ULONG variables.  I received the same result when pBuffer was declared as BYTE pBuffer[6], and pBuffer was sent as the third parameter instead of ulMACAddr.  The address stored in ulMACAddr looks like:

    ulMACAddr[0] = 0x27da5100
    ulMACAddr[1] = 0xcccccfe9 

    in memory (this is all covered in big-endian/little-endian 101).  When pBuffer accesses that one byte at a time, the result is:

    pBuffer[0] = 0x00
    pBuffer[1] = 0x51
    pBuffer[2] = 0xda
    pBuffer[3] = 0x27
    pBuffer[4] = 0xe9
    pBuffer[5] = 0xcf 

    Thus the MAC address of the machine would be 00-51-da-27-e9-cf.  Testing this on my machine using the IP address shown proved a difficult task as my local ARP cache only has two entries in it: one for the gateway, and the other for the DNS server.  Unless you have a machine on the other side of the Internet (e.g., router), you're apt to get the same result.  You can also use an IP addresses in your local subnet, but depending on your actual environment, may not be any better.

    The last function I wanted to look at is NotifyRouteChange().  This one is used so the caller can be notified of a change to the IP routing table.  The Route utility is used from a command prompt to manipulate the IP routing table.  Be careful not to delete an entry accidentally!  Testing of this function used some constructs not used in other part of the demo application, namely threads and events.  In order to use NotifyRouteChange(), an event must be created in a nonsignaled state that NotifyRouteChange() can then signal once a change has been detected.  This looked something like:

    HANDLE  h; 
    bool bDone = false; 
    
    m_hEvents[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (NULL != m_hEvents[1])
    {
        m_OverLapped.hEvent = m_hEvents[1]; 
        while (! bDone)
        {
            m_dwResult = NotifyRouteChange(&h, &m_OverLapped);
            if (ERROR_IO_PENDING != m_dwResult)        
            {
                m_strText.Format("NotifyRouteChange() failed.  Result = %lu\r\n", 
                                  m_dwResult);             
                m_edit1.ReplaceSel(m_strText);
                bDone = true;
            }
            else
            {
              m_dwResult = WaitForMultipleObjects(2, m_hEvents, 
                                                    FALSE, INFINITE);
    
              switch(m_dwResult - WAIT_OBJECT_0)
              {
                case 0:
                  bDone = true;
                  break;
                case 1:
                  m_edit1.ReplaceSel("A change was made to the route table.\r\n"); 
                  break;
              }
            }
        } 
       
        CloseHandle(m_hEvents[1]);
    }
    
    CloseHandle(m_hEvents[0]);

    While not entirely necessary, I needed to create another event (m_hEvents[0]) that could be signaled by the UI whenever a tab was changed, or the OK/Cancel button was clicked.  This allowed the events to be closed, and the secondary thread to be destroyed, properly. 

    Notes

    Several of the IP Helper functions are discussed in various levels of detail throughout other CP articles.  See the References section below for those.  This article was mainly targeted at how to use the functions (in a centralized location), rather than create yet another utility that uses them.

    References

  • Getting Addresses IP informations
  • The 'New ipconfig' and the IP Helper API
  • IP Helper API - Retrieve TCP/IP/UDP Statistics , Interface details, ARP Table and Route Table
  • Using IP Helper API’s
  • Dynamic DNS Web Service
  • xLANInfo
  • Getting active TCP/UDP connections on a box
  • License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here


    Written By
    Software Developer (Senior) Pinnacle Business Systems
    United States United States

    The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.

    HTTP 404 - File not found
    Internet Information Services

    Comments and Discussions

     
    QuestionStore MAC-Adresses in a variable and compare them with each other Pin
    Member 1124759519-Nov-14 9:05
    Member 1124759519-Nov-14 9:05 
    QuestionSuper Pin
    ns_rana1317-Mar-13 22:48
    ns_rana1317-Mar-13 22:48 
    SuggestionRe: Super Pin
    David Crow18-Mar-13 3:42
    David Crow18-Mar-13 3:42 
    GeneralRe: Super Pin
    ns_rana1319-Mar-13 18:49
    ns_rana1319-Mar-13 18:49 
    GeneralRe: Super Pin
    David Crow20-Mar-13 3:05
    David Crow20-Mar-13 3:05 
    GeneralRe: Super Pin
    ns_rana1320-Mar-13 19:17
    ns_rana1320-Mar-13 19:17 
    GeneralThanks! Pin
    shamoke_tcp10-Jan-09 8:20
    shamoke_tcp10-Jan-09 8:20 
    GeneralGood one Pin
    NiceNaidu4-Jan-09 22:21
    NiceNaidu4-Jan-09 22:21 
    GeneralTrouble on Windows Vista 32bit (probably also for Vista 64) Pin
    a.tess10-Apr-08 23:51
    a.tess10-Apr-08 23:51 
    Dear David,

    your code is very useful to understand how to use helper function on socket environement.

    I detect a trouble using the GetBestRoute(..) API in a Vista environement. This procedure return an IPForwardRow (MIB_IPFORWARDROW) that store a 0x00000000 value for dwForwardNextHop
    member struct value.
    I check the Microsoft documentation and this procedure is fully compliant with Vista. Do you know a work around on this matter.

    Thanks
    atess

    P.S.
    I don't check the situation for Vista 64bit...
    GeneralRe: Trouble on Windows Vista 32bit (probably also for Vista 64) Pin
    David Crow11-Apr-08 3:40
    David Crow11-Apr-08 3:40 
    GeneralRe: Trouble on Windows Vista 32bit (probably also for Vista 64) Pin
    a.tess8-May-08 23:38
    a.tess8-May-08 23:38 
    GeneralCan't find iphlpapi.h Pin
    Nurrizki Brahmantyo23-Jun-07 18:59
    Nurrizki Brahmantyo23-Jun-07 18:59 
    QuestionRe: Can't find iphlpapi.h Pin
    David Crow30-Jun-07 8:19
    David Crow30-Jun-07 8:19 
    QuestionHow to get the MAC from NIC Hardware Pin
    Reader Man San1-Nov-06 1:18
    professionalReader Man San1-Nov-06 1:18 
    AnswerRe: How to get the MAC from NIC Hardware Pin
    David Crow1-Nov-06 2:42
    David Crow1-Nov-06 2:42 
    GeneralRe: How to get the MAC from NIC Hardware Pin
    Reader Man San3-Nov-06 18:34
    professionalReader Man San3-Nov-06 18:34 
    GeneralAbout using the two functions: AddIPAddress and GetAdaptersInfo Pin
    Anonymous26-Dec-04 4:00
    Anonymous26-Dec-04 4:00 
    GeneralCode Question Pin
    magyarman8-Dec-04 6:38
    magyarman8-Dec-04 6:38 
    GeneralRe: Code Question Pin
    David Crow8-Dec-04 7:00
    David Crow8-Dec-04 7:00 
    QuestionSupport for IPv6 ? Pin
    Alpha Siera16-Sep-04 1:24
    Alpha Siera16-Sep-04 1:24 
    AnswerRe: Support for IPv6 ? Pin
    David Crow16-Sep-04 2:50
    David Crow16-Sep-04 2:50 
    QuestionAbout iphlpapi.dll ? Pin
    ratheeshpkr23-Aug-04 3:29
    ratheeshpkr23-Aug-04 3:29 
    AnswerRe: About iphlpapi.dll ? Pin
    David Crow23-Aug-04 4:19
    David Crow23-Aug-04 4:19 
    GeneralRe: About iphlpapi.dll ? Pin
    Bart Van Assche6-Nov-04 23:32
    Bart Van Assche6-Nov-04 23:32 
    GeneralGeting information Pin
    one_eddie4-Aug-04 0:52
    one_eddie4-Aug-04 0:52 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.