IP Helper API: ARP, Routing, and Network Statistics

Thursday Sep 30th 2004 by Alex Gusev
Share:

Manage routing tables and display network statisics on different network protocols.

Well, this article is the last in the series ("IP Helper API: Retrieving Basic Information" and "IP Helper API: Managing IP Addresses") dedicated to the IP Helper API. We have covered most of the API calls. Now, we will talk mainly about ARP, forwarding of IP requests, and network statistics.

Retrieving ARP Information

In the previous articles, we touched a bit about how to get basic Address Resolution Protocol (ARP) information. In addition, you had the following functions defined:

  • CreateIpNetEntry
  • SetIpNetEntry
  • DeleteIpNetEntry
  • FlushIpNetTable
  • CreateProxyArpEntry
  • DeleteProxyArpEntry
  • SendARP

They are used to create, delete, or modify ARP table entries. In each case, you should provide a valid MIB_IPNETROW struct. You also can manage proxy ARP records. All these functions may be quite useful; for example, when you're developing applications for devices that do not support IP, but are using Windows CE as a front end to access the network.

Besides, the SendARP function deserves our attention. It allows you to retrieve the MAC address of the destination IP. When might you need it? The first thing rising in my head is raw sockets. You also can use this request for authentication purposes. Below, you'll find a simple example of SendARP usage:

void CNWParamsDlg::OnSendARP()
{
   IPAddr dstAddr = inet_addr("10.168.242.170");
   ULONG MacAddr[2];
   ULONG PhyAddrLen = 6;
   memset(MacAddr,0,sizeof(MacAddr));
   DWORD dwRes = SendARP(dstAddr,0,MacAddr,&PhyAddrLen);

   if ( dwRes == NO_ERROR )
   {
      m_ListBox.ResetContent();

      BYTE *pAddr = (BYTE*)MacAddr;
      CString sTmp, sMAC;
      for (int i = 0; i < PhyAddrLen - 1; i++,pAddr++)
      {
         sTmp.Format(L"%02X:",*pAddr);
         sMAC += sTmp;
      }
      if ( PhyAddrLen )
      {
         sTmp.Format(L"%02X",*pAddr);
         sMAC += sTmp;
      }
      if ( !sMAC.IsEmpty() )
      {
         m_ListBox.AddString(sMAC);
      }
   }
}

Note, that SendARP send requests to local network, so all calls with outer IP will fail.

Utilizing Routing Mechanism

Let's move now to other networking issue. Most of IP requests are forwarded to their destinations. With IP Helper API, you have rich capabilities to manage the way how datagrams are routed over the network. You have the following functions to manage the IP routing table, and to obtain other routing information:

  • GetIpForwardTable—to get the contents of IP forwarding table
  • CreateIpForwardEntry—to create entry
  • DeleteIpForwardEntry—to delete entry
  • SetIpForwardEntry—to modify entry
  • GetBestRoute—to get the best route to a specified destination address

Let's start from GetIpForwardTable. This function returns the contents of IP routing table. Similar to other IP Helper tables, that's an array of MIB_IPFORWARDROW structs. This struct contains different forwarding information, which describes an IP network route. We will use it in our test only few fields. For more details, see RFC 1354. So, here is code snippet:

void CNWParamsDlg::ShowForwardRows(PMIB_IPFORWARDTABLE
                                   pIpForwardTable)
{
   m_ListBox.ResetContent();

   CString sTmp;
   sTmp.Format(L"NumEntries: %d",pIpForwardTable->dwNumEntries);
   m_ListBox.AddString(sTmp);

   in_addr addr;
   TCHAR szBuffer[128];
   for (int i = 0; i < pIpForwardTable->dwNumEntries; i++)
   {
      MIB_IFROW mibRow;
      memset(&mibRow,0,sizeof(mibRow));
      mibRow.dwIndex = pIpForwardTable->table[i].dwForwardIfIndex;
      GetIfEntry(&mibRow);
      sTmp.Format(L"Idx: %lu Name: %s",
                  pIpForwardTable->table[i].dwForwardIfIndex,
                  mibRow.wszName);
      m_ListBox.AddString(sTmp);

      addr.S_un.S_addr = pIpForwardTable->table[i].dwForwardDest;
      char *szAddr = inet_ntoa(addr);
      memset(szBuffer,0,sizeof(szBuffer));
      MultiByteToWideChar(CP_ACP,0,szAddr,strlen(szAddr),szBuffer,
                          sizeof(szBuffer));
      sTmp.Format(L"   Dest IP: %s", szBuffer);
      m_ListBox.AddString(sTmp);

      addr.S_un.S_addr = pIpForwardTable->table[i].dwForwardMask;
      szAddr = inet_ntoa(addr);
      memset(szBuffer,0,sizeof(szBuffer));
      MultiByteToWideChar(CP_ACP,0,szAddr,strlen(szAddr),szBuffer,
                          sizeof(szBuffer));
      sTmp.Format(L"   Subnet Mask: %s", szBuffer);
      m_ListBox.AddString(sTmp);

      sTmp = L"   Type:";
      switch (pIpForwardTable->table[i].dwForwardType)
      {
      case MIB_IPROUTE_TYPE_OTHER:
         sTmp += L" OTHER";
         break;
      case MIB_IPROUTE_TYPE_INVALID:
         sTmp += L" INVALID";
         break;
      case MIB_IPROUTE_TYPE_DIRECT:      // Local
         sTmp += L" DIRECT";
         break;
      case MIB_IPROUTE_TYPE_INDIRECT:    // Remote
         sTmp += L" INDIRECT";
         break;
      }
      m_ListBox.AddString(sTmp);
   }
}

void CNWParamsDlg::OnGetIpForwardTable()
{
   PMIB_IPFORWARDTABLE pIpForwardTable = 0;
   ULONG dwSize = 0;
   DWORD dwRes = GetIpForwardTable(pIpForwardTable, &dwSize, FALSE);

   if ( dwRes == ERROR_INSUFFICIENT_BUFFER )
   {
      pIpForwardTable = (PMIB_IPFORWARDTABLE) new BYTE[dwSize];
      dwRes = GetIpForwardTable(pIpForwardTable, &dwSize, FALSE);
      if ( dwRes == NO_ERROR )
         ShowForwardRows(pIpForwardTable);
      delete pIpForwardTable;
   }
}

As you can see, the test code displays correspondend the interface index and name, destination host IP, and subnet mask and route type. Once again, all details are documented in RFC 1354, so we will not touch them here. But, anyway, the information returned gives your application enough feeding to process.

The next three functions—CreateIpForwardEntry, DeleteIpForwardEntry, and SetIpForwardEntry—are used to manage IP routing table. Their usage is illustrated in the following sample. It changes default gateway value. Please note that calling only SetIpForwardEntry will not change it, but rather add new one. Due to this reason, the code below first removes all suitable records and then creates a new entry with the desired properties:

void CNWParamsDlg::OnDefaultGateway()
{
   PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
   PMIB_IPFORWARDROW pRow              = NULL;
   ULONG dwSize                        = 0;
   BOOL bOrder                         = FALSE;
   DWORD dwStatus                      = 0;
   DWORD NewGateway = 0xAADDBBCC;      // CC.BB.DD.AA

   dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
   if (dwStatus == ERROR_INSUFFICIENT_BUFFER)
   {
      pIpForwardTable = (PMIB_IPFORWARDTABLE)new BYTE[dwSize];
      dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize,
                                   bOrder);
   }

   if (dwStatus != NO_ERROR )
   {
      if (pIpForwardTable)
         delete pIpForwardTable;
      return;
   }

   for (unsigned long int i=0; i <
        pIpForwardTable->dwNumEntries; i++)
   {
      if (pIpForwardTable->table[i].dwForwardDest == 0)
      {
         // The default gateway has been found
         if (!pRow)
         {
            // Allocate some memory to store the row in - this is
            // easier than filling in the row structure and helps
            // ensure that only the gateway address is changed.
            pRow = (PMIB_IPFORWARDROW)
                   new BYTE[sizeof(MIB_IPFORWARDROW)];
            // Copy the row
            memcpy(pRow, &(pIpForwardTable->table[i]),
                   sizeof(MIB_IPFORWARDROW));
         }

         // Delete the old default gateway entry
         dwStatus = DeleteIpForwardEntry(&(pIpForwardTable->
                                         table[i]));

         if (dwStatus != NO_ERROR)
         {
            TRACE(L"Could not delete old gateway\n");
            break;
         }
      }
   }

   // Set the nexthop field to our new gateway - all the other
   // properties of the route will be the same as they were
   // previously.
   pRow->dwForwardNextHop = NewGateway;

   // Create a new route entry for the default gateway.
   dwStatus = CreateIpForwardEntry(pRow);

   if (dwStatus == NO_ERROR)
      TRACE(L"Gateway changed successfully\n");
   else if (dwStatus == ERROR_INVALID_PARAMETER)
      TRACE(L"Invalid Parameter\n");
   else
      TRACE(L"Failure\n");

   // Free resources
   if (pIpForwardTable)
      delete pIpForwardTable;
   if (pRow)
      delete pRow;
}

Default gateway has dwForwardDest field equals to zero, so that's the criteria sample code uses to detect it. Finally, dwForwardNextHop is set to a new value, and all is done!

Several words about GetBestRoute function. Similar to GetBestInterface, this function returns the best route for specified destination IP address. On succesfull return, the last parameter keeps MIB_IPFORWARDROW variable with info regarding found route. The next snippet show this all:

void CNWParamsDlg::OnBestRoute()
{
   MIB_IPFORWARDROW BestRoute;
   DWORD dwRes =
      GetBestRoute(inet_addr("A.B.C.D"),0,&BestRoute);
   TRACE(L"Best Route: %d",BestRoute.dwForwardDest);

   CString sTmp;
   MIB_IFROW mibRow;
   memset(&mibRow,0,sizeof(mibRow));
   mibRow.dwIndex = BestRoute.dwForwardIfIndex;
   GetIfEntry(&mibRow);
   sTmp.Format(L"Idx: %lu Name: %s", BestRoute.dwForwardIfIndex,
               mibRow.wszName);
   m_ListBox.ResetContent();
   m_ListBox.AddString(sTmp);
}

It's really simple. You're providing destination address and optionally local one. You get back the best route for it. That's it!

IP Protocols Statistics

If you develop an application which needs to monitor network, say some kind of sniffer or whatever it is, you may want to know some statistical information about different network protocols. IP Helper API has subset of correspondend functions, which deal with ICMP, IP, TCP, and UPD statistics respectively:

  • GetIpStatistics
  • GetIcmpStatistics
  • GetTcpStatistics
  • GetUdpStatistics
  • SetIpStatistics
  • SetTcpEntry
  • SetIpTTL

You may find a detailed description of each one in SDK help; here, I just want to note that they give you a lot of info about the network state and traffic. Your application may use all this to analyze networking processes.

Download

Download the accompanying code's zip file here.

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers' lives in the mobile jungles a little bit simpler.

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved