Edit (Add/Remove/Modify) ARP Table

Introduction

This is a tool to display and modify the IP-to-Physical address translation tables used by ARP (Address Resolution Protocol), such as Windows command-line arp.exe. This tool will do two things. It displays ARP tables' entries and adds/modifies/removes ARP entries, the two things done by requesting an SNMP (Simple Network Management Protocol) extension library.

Access IP and MAC Addresses Through SNM

You can read and modify an ARP table through SNMP, by requesting SNMP to get/set object information. SNMP does requests and responses through MIB (Management Information Base). MIB is like a tree; MIB has all the manageable objects you will use. For more information, see RFC1213; also, check the files at %SystemRoot%\system32\*.mib. The file that has entries you will use is %SystemRoot%\system32\ mib_ii.mib; you can open and see it by using notepad.exe. Here is an MIB ipNetToMediaEntry entry:

ipNetToMediaEntry OBJECT-TYPE
   SYNTAX  IpNetToMediaEntry
   ACCESS  not-accessible
   STATUS  mandatory
   DESCRIPTION
      "Each entry contains one IpAddress to 'physical'
      address equivalence."
   INDEX   { ipNetToMediaIfIndex,
      ipNetToMediaNetAddress }
   ::= { ipNetToMediaTable 1 }

MIB accesses the objects by OID (Object Identifier) number. Every object has a number; also, child objects have both the parent object number and its number. The numbers are separated by a dot ".". For example, if the parent object has the number "1" and the child object has the number "3", the child object's OID number is "1.3", and the child of a child may be "1.3.6", ... "1.3.6.1.2.1", and so on.

Here is simple sketch for the nodes tree:

Initialize the Class

I used the SnmpExtensionQuery function to resolve SNMP requests, but before using it, you must call the SnmpExtensionInit function to initialize the SNMP extension agent DLL. The two functions exist in the Microsoft library inetmib1.dll, so at the class construction, I will load the this library and get the addresses of these functions, then call SnmpExtensionInit to initialize the SNMP extension agent DLL.

Here is a construction of a CARP class:

///////////////////////////////////////////////////////////////////
// Construction/Destruction
///////////////////////////////////////////////////////////////////

CARP::CARP()
{
   // Load dynamic library: inetmib1.dll
   hMIBLibrary    = LoadLibrary(TEXT("inetmib1.dll"));

   // If library loaded, get addresses of (SnmpExtensionInit,
   // pfnSnmpExtensionQuery) functions
   if (hMIBLibrary)
   {
      pfnSnmpExtensionInit    = (PFNSNMPEXTENSIONINIT)
         GetProcAddress(hMIBLibrary, "SnmpExtensionInit");
      pfnSnmpExtensionQuery   = (PFNSNMPEXTENSIONQUERY)
         GetProcAddress(hMIBLibrary, "SnmpExtensionQuery");

      // If success get addresses and initialize SNMP,
      // bInitialized = true
      if (pfnSnmpExtensionInit && pfnSnmpExtensionQuery)
      {
         HANDLE               hPollForTrapEvent;
         AsnObjectIdentifier  aoiSupportedView;

            bInitialized = pfnSnmpExtensionInit(0,
               &hPollForTrapEvent, &aoiSupportedView);
      }
   }
   else
   {
      // If fail to get addresses, bInitialized = false
      bInitialized = FALSE;
      AfxMessageBox(_T("Load library fail"));
   }
}

Get ARP Entries

The GetEntries function gets ARP entries such as arp.exe -a. It takes three Parameters: pTable Pointer to array of arpTable struct that will filled by IP and MAC addresses TableLength Length of the array AdapterIndex NIC Adapter index number I'm used three OIDs to get ARP table entries: OID[0] = "1.3.6.1.2.1.4.22.1.1", to get the interface index of the entry, and compare it with AdapterIndex parameter OID[1] = "1.3.6.1.2.1.4.22.1.2", to get the IP and MAC addresses OID[2] = "1.3.6.1.2.1.4.22.1.4", to get the entry type (Static or Dynamic).

//-----------------------------------------------------------------
// Function: GetEntries: Read ARP table for specific NIC interface.
//
// Parameters:
//    pTable          Pointer to array of arpTable struct
//    TableLength     Length of the array
//    AdapterIndex    NIC Adapter index number
//
// Returns:
//                    Number of read ARP entries
//-----------------------------------------------------------------
int CARP::GetEntries(arpTable* pTable, int TableLength,
                     int AdapterIndex)
{
   // Be sure initialize SNMP true
   if (!bInitialized)
      return 0;

   SnmpVarBindList     SVBList[3];
   SnmpVarBind         SVBVars[3];
   UINT                OID[3][10];
   AsnInteger32        aiErrorStatus[3], aiErrorIndex[3];
   AsnObjectIdentifier  AsnOID0 = {sizeof(OID[0])/sizeof(UINT),
                                   OID[0]};
   AsnObjectIdentifier  AsnOID1 = {sizeof(OID[1])/sizeof(UINT),
                                   OID[1]};
   AsnObjectIdentifier  AsnOID2 = {sizeof(OID[2])/sizeof(UINT),
                                   OID[2]};
   unsigned long        pIPAddress;
   unsigned long        pMACAddress;
   int                  iEntries;


   //--------------------------------------------------------------
   //    Fill array of 3 OIDs
   //
   //    OID[0]    :    "1.3.6.1.2.1.4.22.1.1", ipNetToMediaIfIndex
   //                The interface on which this entry's
   //                equivalence is effective
   //
   //    OID[1]    :    "1.3.6.1.2.1.4.22.1.2",
   //                   ipNetToMediaPhysAddress
   //                The media-dependent 'physical' address
   //
   //    OID[2]    :    "1.3.6.1.2.1.4.22.1.4", ipNetToMediaType
   //                Entry type: 1:Other,
   //                            2:Invalid(Remove),
   //                            3:Dynamic,
   //                            4:Static
   //
   for (int count=0; count<3; count++)
   {
      OID[count][0] = 1;
      OID[count][1] = 3;
      OID[count][2] = 6;
      OID[count][3] = 1;
      OID[count][4] = 2;
      OID[count][5] = 1;
      OID[count][6] = 4;
      OID[count][7] = 22;
      OID[count][8] = 1;

      switch(count)
      {
      case 0:
         // Adapter interface
         OID[count][9] = 1;
         break;

      case 1:
         // MAC address
         OID[count][9] = 2;
         break;

      case 2:
         // Entry Type
         OID[count][9] = 4;
         break;
      }
   }

   ZeroMemory(pTable, sizeof(arpTable)*TableLength);

   SVBList[0].len  = 1;
   SVBList[0].list = &SVBVars[0];
   SnmpUtilOidCpy(&SVBVars[0].name, &AsnOID0);

   SVBList[1].len  = 1;
   SVBList[1].list = &SVBVars[1];
   SnmpUtilOidCpy(&SVBVars[1].name, &AsnOID1);

   SVBList[2].len  = 1;
   SVBList[2].list = &SVBVars[2];
   SnmpUtilOidCpy(&SVBVars[2].name, &AsnOID2);

   iEntries        = 0;
   do
   {
      aiErrorStatus[0] = 0;
      aiErrorIndex[0]  = 0;
      aiErrorStatus[1] = 0;
      aiErrorIndex[1]  = 0;
      aiErrorStatus[2] = 0;
      aiErrorIndex[2]  = 0;

      // Query information of 3 OIDs
      if (pfnSnmpExtensionQuery(SNMP_PDU_GETNEXT, &SVBList[0],
         &aiErrorStatus[0], &aiErrorIndex[0]))
         if (pfnSnmpExtensionQuery(SNMP_PDU_GETNEXT, &SVBList[1],
            &aiErrorStatus[1], &aiErrorIndex[1]))
            if (pfnSnmpExtensionQuery(SNMP_PDU_GETNEXT, &SVBList[2],
               &aiErrorStatus[2], &aiErrorIndex[2]))
               if (aiErrorStatus[0] == SNMP_ERRORSTATUS_NOERROR &&
                  aiErrorStatus[1]  == SNMP_ERRORSTATUS_NOERROR &&
                  // Check for error
                  aiErrorStatus[2]  == SNMP_ERRORSTATUS_NOERROR)
               {
                  //-----------------------------------------------
                  // From MSDN Help: http://msdn2.microsoft.com/
                  //                 en-us/library/aa378021.aspx
                  // 
                  // If the extension agent cannot resolve the
                  // variable bindings on a Get Next request, it
                  // must change the name field of the SnmpVarBind
                  // structure to the value of the object
                  // identifier immediately following that of the
                  // currently supported MIB subtree view.
                  // For example, if the extension agent supports
                  // view ".1.3.6.1.4.1.77.1", a Get Next
                  // request on ".1.3.6.1.4.1.77.1.5.1" would
                  // result in a modified name field of
                  // ".1.3.6.1.4.1.77.2".
                  // This signals the SNMP service to continue the
                  // attempt to resolve the variable bindings
                  // with other extension agents
                  //-----------------------------------------------

                  if(SnmpUtilOidNCmp(&SVBVars[0].name, &AsnOID0,
                     AsnOID0.idLength)) 
                     break;
                  if(SnmpUtilOidNCmp(&SVBVars[1].name, &AsnOID1,
                     AsnOID1.idLength))
                     break;
                  if(SnmpUtilOidNCmp(&SVBVars[2].name, &AsnOID2,
                     AsnOID2.idLength))
                     break;

                        // Verify selected Adapter interface
                        if (AdapterIndex == SVBList[0].list
                           ->value.asnValue.number)
                        {
                           // pIPAddress get pointer ro IP Address
                           pIPAddress = (unsigned long)SVBList[1].list
                              ->name.ids;
                           pTable[iEntries].IPAddress[0] =
                              *(unsigned char *)(pIPAddress + 44);
                           pTable[iEntries].IPAddress[1] =
                              *(unsigned char *)(pIPAddress + 48);
                           pTable[iEntries].IPAddress[2] =
                              *(unsigned char *)(pIPAddress + 52);
                           pTable[iEntries].IPAddress[3] =
                              *(unsigned char *)(pIPAddress + 56);

                           // pIPAddress get pointer ro MAC Address
                           pMACAddress =
                              (unsigned long)SVBList[1].list
                              ->value.asnValue.string.stream;
                           if (pMACAddress)
                           {
                              pTable[iEntries].MACAddress[0] =
                                 *(unsigned char *)(pMACAddress + 0);
                              pTable[iEntries].MACAddress[1] =
                                 *(unsigned char *)(pMACAddress + 1);
                              pTable[iEntries].MACAddress[2] =
                                 *(unsigned char *)(pMACAddress + 2);
                              pTable[iEntries].MACAddress[3] =
                                 *(unsigned char *)(pMACAddress + 3);
                              pTable[iEntries].MACAddress[4] =
                                 *(unsigned char *)(pMACAddress + 4);
                              pTable[iEntries].MACAddress[5] =
                                 *(unsigned char *)(pMACAddress + 5);
                           }

                           // Entry Type
                           pTable[iEntries].Type =
                              (unsigned long)SVBList[2].list
                              ->value.asnValue.number;

                           // Type must be one of (1, 2, 3, 4)
                            if (pTable[iEntries].Type>=1 &&
                                pTable[iEntries].Type<=4)
                              // Move to next array position
                              iEntries++;
                  }
               }
               else
                  break;    // If error exit do-while
    }
   while(iEntries < TableLength);

   // Frees the memory allocated for the specified object identifiers
   SnmpUtilOidFree(&SVBVars[2].name);
   SnmpUtilOidFree(&SVBVars[1].name);
   SnmpUtilOidFree(&SVBVars[0].name);

   return iEntries;    // Return number of Entries
}

Edit (Add/Remove/Modify) ARP Table

Edit ARP Entries

The EditEntry function adds/modifies/removes ARP entries, adds a dynamic entry such as arp.exe 1.2.3.4 11-22-33-44-55-66, static entry like arp.exe -s 1.2.3.4 11-22-33-44-55-66, or removes an entry such as arp.exe -d 1.2.3.4. When you add a new entry, if the entry IP address not exist in the ARP table, the entry will be added at table; otherwise, if the entry IP address already exists in the ARP table, the entry will be updated. To remove the entry, you need only the IP address to identify the entry on the NIC interface; the MAC address is not needed in the remove operation.

The function takes four parameters:

  • IPAddress Array of 4 BYTES
  • Four octs of IP Address MACAddress Array of 4 BYTES
  • Six octs of MAC Address Type Entry type (2:Remove, 3:Dynamic, 4:Static)
  • AdapterIndex NIC Adapter index number

I've used four OIDs to set ARP table entries:

  • OID[0] = "1.3.6.1.2.1.4.22.1.1", to set the interface index of the entry
  • OID[1] = "1.3.6.1.2.1.4.22.1.2", to set MAC address
  • OID[3] = "1.3.6.1.2.1.4.22.1.3", to set IP address
  • OID[2] = "1.3.6.1.2.1.4.22.1.4", to set the entry type (Static or Dynamic), or remove the entry
//-----------------------------------------------------------------
// Function:       EditEntry: Add/Modify/Remove ARP entry for
//                 specific NIC interface.
//
// Parameters:
//    IPAddress    Array of 4 BYTES, 4 octs of IP Address
//    MACAddress   Array of 4 BYTES, 6 octs of MAC Address
//    Type         Entry type (2:Remove, 3:Dynamic, 4:Static)
//    AdapterIndex NIC Adapter index number
//
// Returns:
//                 TRUE if set successfully, FALSE otherwise.
//-----------------------------------------------------------------
BOOL CARP::EditEntry(unsigned char IPAddress[4], unsigned char
                     MACAddress[6], unsigned long Type,
                     int AdapterIndex)
{
   if (!bInitialized)
        return 0;

   SnmpVarBindList SVBList;
   SnmpVarBind     SVBVars[4];
   UINT            OID[4][10];
   AsnInteger32    aiErrorStatus, aiErrorIndex;
   BOOL            bReturn = FALSE;

   //--------------------------------------------------------------
   //    Fill array of 4 OIDs
   //    
   //    OID[0]    :    "1.3.6.1.2.1.4.22.1.1", ipNetToMediaIfIndex
   //                The interface on which this entry's equivalence
   //                is effective
   //
   //    OID[1]    :    "1.3.6.1.2.1.4.22.1.2", ipNetToMediaPhysAddress
   //                The media-dependent 'physical' address
   //
   //    OID[2]    :    "1.3.6.1.2.1.4.22.1.3", ipNetToMediaNetAddress
   //                The IpAddress corresponding to
   //                the media-dependent 'physical' address
   //
   //    OID[3]    :    "1.3.6.1.2.1.4.22.1.4", ipNetToMediaType
   //                Entry type: 1:Other,
   //                            2:Invalid(Remove),
   //                            3:Dynamic,
   //                            4:Static
   //--------------------------------------------------------------
   for (int count=0; count<4; count++)
   {
      OID[count][0] = 1;
      OID[count][1] = 3;
      OID[count][2] = 6;
      OID[count][3] = 1;
      OID[count][4] = 2;
      OID[count][5] = 1;
      OID[count][6] = 4;
      OID[count][7] = 22;
      OID[count][8] = 1;
      OID[count][9] = 1 + count;

      switch(count)
      {
      case 0:
         //    OID[0]    :    "1.3.6.1.2.1.4.22.1.1",
         //                   ipNetToMediaIfIndex
         //                The interface on which this entry's
         //                equivalence is effective
         SVBVars[count].value.asnType         = ASN_INTEGER;
         SVBVars[count].value.asnValue.number = AdapterIndex;
         break;

      case 1:
         //    OID[1]    :    "1.3.6.1.2.1.4.22.1.2",
         //                   ipNetToMediaPhysAddress
         //                The media-dependent 'physical' address
         SVBVars[count].value.asnType                = ASN_OCTETSTRING;
         SVBVars[count].value.asnValue.string.stream = MACAddress;
         // MAC Address length
         SVBVars[count].value.asnValue.string.length = 6;
         SVBVars[count].value.asnValue.string.dynamic= FALSE;
         break;

      case 2:
         //    OID[2]    :    "1.3.6.1.2.1.4.22.1.3",
         //                   ipNetToMediaNetAddress
         //                The IpAddress corresponding to the
         //                media-dependent 'physical' address
         SVBVars[count].value.asnType                = ASN_IPADDRESS;
         SVBVars[count].value.asnValue.string.stream = IPAddress;
         // IP Address length
         SVBVars[count].value.asnValue.string.length = 4;
         SVBVars[count].value.asnValue.string.dynamic= FALSE;
         break;

      case 3:
         //    OID[3]    :    "1.3.6.1.2.1.4.22.1.4", ipNetToMediaType
         //                Entry type: 2:Remove, 3:Dynamic, 4:Static
         SVBVars[count].value.asnType                = ASN_INTEGER;
         SVBVars[count].value.asnValue.number        = Type;
         break;
      }
      AsnObjectIdentifier    AsnOID =
         {sizeof(OID[count])/sizeof(UINT), OID[count]};
      SnmpUtilOidCpy(&SVBVars[count].name, &AsnOID);
    }

   SVBList.len   = 4;
   SVBList.list  = SVBVars;

   aiErrorStatus = 0;
   aiErrorIndex  = 0;

   // Set information of entry (4 OIDs)
   if (pfnSnmpExtensionQuery(SNMP_PDU_SET, &SVBList,
                             &aiErrorStatus, &aiErrorIndex))
      if (aiErrorStatus == SNMP_ERRORSTATUS_NOERROR)
         bReturn = TRUE; // If success set bReturn = true

   // Frees the memory allocated for the specified object identifiers
   SnmpUtilOidFree(&SVBVars[3].name);
   SnmpUtilOidFree(&SVBVars[2].name);
   SnmpUtilOidFree(&SVBVars[1].name);
   SnmpUtilOidFree(&SVBVars[0].name);

   // TRUE if set successfully, FALSE otherwise.
   return bReturn;
}


About the Author

Usama El-Mokadem

Programming Languages: C/C++, Assembly (x86, Win32), and Visual Basic.

Brainbench: 4611463
Homepage: musama.tripod.com
E-Mail: musama@hotmail.com

Downloads

Comments

  • There are no comments yet. Be the first to comment!

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

Top White Papers and Webcasts

  • Live Event Date: May 6, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT While you likely have very good reasons for remaining on WinXP after end of support -- an estimated 20-30% of worldwide devices still are -- the bottom line is your security risk is now significant. In the absence of security patches, attackers will certainly turn their attention to this new opportunity. Join Lumension Vice President Paul Zimski in this one-hour webcast to discuss risk and, more importantly, 5 pragmatic risk mitigation techniques …

  • Download the Information Governance Survey Benchmark Report to gain insights that can help you further establish business value in your Records and Information Management (RIM) program and across your entire organization. Discover how your peers in the industry are dealing with this evolving information lifecycle management environment and uncover key insights such as: 87% of organizations surveyed have a RIM program in place 8% measure compliance 64% cannot get employees to "let go" of information for …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds