首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

SNMP 操作 ARP 表:讀取/新增/修改/刪除 IP-MAC 映射 (inetmib1.dll 實作)

     編輯:關於VC++

前言

Windows 系統管理代理 (SNMP Extension Agent) 透過 inetmib1.dll 公開 MIB-II 物件,包含 ARP 表 (ipNetToMediaTable)。本文示範載入 DLL、初始化 SNMP 擴展代理、呼叫 SnmpExtensionQuery 進行 GETNEXT 遍歷讀取 ARP 條目,以及 SET 操作新增/修改/刪除條目,封裝為 CARP 類可直接整合。

MIB-II ARP 表結構 (RFC 1213)

ipNetToMediaEntry:
    ipNetToMediaIfIndex      INTEGER  -- 介面索引 (1.3.6.1.2.1.4.22.1.1)
    ipNetToMediaPhysAddress  PhysAddress -- MAC 位址 (1.3.6.1.2.1.4.22.1.2)
    ipNetToMediaNetAddress   IpAddress  -- IP 位址 (1.3.6.1.2.1.4.22.1.3)
    ipNetToMediaType         INTEGER    -- 類型: 1=Other, 2=Invalid(刪除), 3=Dynamic, 4=Static (1.3.6.1.2.1.4.22.1.4)
INDEX { ipNetToMediaIfIndex, ipNetToMediaNetAddress }

OID 陣列定義

// 讀取用 3 個 OID
UINT OID_READ[3][10] = {
    {1,3,6,1,2,1,4,22,1,1}, // IfIndex
    {1,3,6,1,2,1,4,22,1,2}, // PhysAddress (MAC)
    {1,3,6,1,2,1,4,22,1,4}  // Type
};

// 寫入用 4 個 OID (含 NetAddress)
UINT OID_WRITE[4][10] = {
    {1,3,6,1,2,1,4,22,1,1}, // IfIndex
    {1,3,6,1,2,1,4,22,1,2}, // PhysAddress
    {1,3,6,1,2,1,4,22,1,3}, // NetAddress (IP)
    {1,3,6,1,2,1,4,22,1,4}  // Type
};

類別 CARP 核心實作

建構函式:載入 DLL 與初始化

CARP::CARP()
{
    hMIBLibrary = LoadLibrary(_T("inetmib1.dll"));
    if (hMIBLibrary)
    {
        pfnSnmpExtensionInit = (PFNSNMPEXTENSIONINIT)GetProcAddress(hMIBLibrary, "SnmpExtensionInit");
        pfnSnmpExtensionQuery = (PFNSNMPEXTENSIONQUERY)GetProcAddress(hMIBLibrary, "SnmpExtensionQuery");
        if (pfnSnmpExtensionInit && pfnSnmpExtensionQuery)
        {
            HANDLE hTrapEvent; AsnObjectIdentifier aoiView;
            bInitialized = pfnSnmpExtensionInit(0, &hTrapEvent, &aoiView);
        }
    }
}

讀取 ARP 表 GetEntries

int CARP::GetEntries(ARP_TABLE* pTable, int maxCount, int adapterIndex)
{
    if (!bInitialized) return 0;

    SnmpVarBindList vbl[3]; SnmpVarBind vb[3];
    AsnObjectIdentifier oid[3];
    AsnInteger32 errStatus[3], errIndex[3];

    // 綁定 3 個 OID
    for (int i=0; i<3; ++i) {
        vbl[i].len = 1; vbl[i].list = &vb[i];
        SnmpUtilOidCpy(&vb[i].name, &oid[i]); // oid[i] 從 OID_READ 填入
    }

    int count = 0;
    do {
        // 重置錯誤碼
        for (int i=0; i<3; ++i) errStatus[i] = errIndex[i] = 0;

        // GETNEXT 遍歷
        if (!pfnSnmpExtensionQuery(SNMP_PDU_GETNEXT, &vbl[0], &errStatus[0], &errIndex[0])) break;
        if (!pfnSnmpExtensionQuery(SNMP_PDU_GETNEXT, &vbl[1], &errStatus[1], &errIndex[1])) break;
        if (!pfnSnmpExtensionQuery(SNMP_PDU_GETNEXT, &vbl[2], &errStatus[2], &errIndex[2])) break;

        if (errStatus[0]!=SNMP_ERRORSTATUS_NOERROR || errStatus[1]!=SNMP_ERRORSTATUS_NOERROR || errStatus[2]!=SNMP_ERRORSTATUS_NOERROR) break;

        // 檢查 OID 是否仍在 ipNetToMediaTable 子樹下
        if (SnmpUtilOidNCmp(&vb[0].name, &oid[0], oid[0].idLength)) break;
        if (SnmpUtilOidNCmp(&vb[1].name, &oid[1], oid[1].idLength)) break;
        if (SnmpUtilOidNCmp(&vb[2].name, &oid[2], oid[2].idLength)) break;

        // 比對介面索引
        if (vb[0].list->value.asnValue.number == adapterIndex)
        {
            // 從 OID 尾端解析 IP 位址 (最後 4 個 sub-id)
            ULONG* ipIds = vb[1].list->name.ids + vb[1].list->name.idLength - 4;
            pTable[count].ip[0] = (BYTE)ipIds[0]; // 注意:原文碼有硬編碼偏移 44/48/52/56,此處改為通用解析
            pTable[count].ip[1] = (BYTE)ipIds[1];
            pTable[count].ip[2] = (BYTE)ipIds[2];
            pTable[count].ip[3] = (BYTE)ipIds[3];

            // MAC 位址從 PhysAddress 取得
            BYTE* mac = (BYTE*)vb[1].list->value.asnValue.string.stream;
            if (mac) memcpy(pTable[count].mac, mac, 6);

            pTable[count].type = vb[2].list->value.asnValue.number; // 1~4
            if (pTable[count].type >= 1 && pTable[count].type <= 4) count++;
        }
    } while (count < maxCount);

    // 釋放 OID 記憶體
    for (int i=2; i>=0; --i) SnmpUtilOidFree(&vb[i].name);
    return count;
}

修改 ARP 表 EditEntry (新增/修改/刪除)

BOOL CARP::EditEntry(BYTE ip[4], BYTE mac[6], ULONG type, int adapterIndex)
{
    if (!bInitialized) return FALSE;

    SnmpVarBindList vbl; SnmpVarBind vb[4];
    AsnObjectIdentifier oid[4];
    AsnInteger32 errStatus, errIndex;

    for (int i=0; i<4; ++i) {
        vb[i].name = oid[i]; // 從 OID_WRITE 填入
        switch (i) {
            case 0: // IfIndex
                vb[i].value.asnType = ASN_INTEGER;
                vb[i].value.asnValue.number = adapterIndex; break;
            case 1: // PhysAddress (MAC)
                vb[i].value.asnType = ASN_OCTETSTRING;
                vb[i].value.asnValue.string.stream = (BYTE*)mac;
                vb[i].value.asnValue.string.length = 6;
                vb[i].value.asnValue.string.dynamic = FALSE; break;
            case 2: // NetAddress (IP)
                vb[i].value.asnType = ASN_IPADDRESS;
                vb[i].value.asnValue.string.stream = (BYTE*)ip;
                vb[i].value.asnValue.string.length = 4;
                vb[i].value.asnValue.string.dynamic = FALSE; break;
            case 3: // Type
                vb[i].value.asnType = ASN_INTEGER;
                vb[i].value.asnValue.number = type; break; // 2=刪除, 3=動態, 4=靜態
        }
        SnmpUtilOidCpy(&vb[i].name, &oid[i]);
    }
    vbl.len = 4; vbl.list = vb;

    errStatus = errIndex = 0;
    BOOL ok = pfnSnmpExtensionQuery(SNMP_PDU_SET, &vbl, &errStatus, &errIndex) &&
              (errStatus == SNMP_ERRORSTATUS_NOERROR);

    for (int i=3; i>=0; --i) SnmpUtilOidFree(&vb[i].name);
    return ok;
}

使用範例

CARP arp;
ARP_TABLE table[100];
int n = arp.GetEntries(table, 100, 1); // 介面索引 1 (通常為第一張實體 NIC)
for (int i=0; i

擴充:路由表、介面表、TCP 連線表

同樣透過 inetmib1.dll 存取 MIB-II 其他表:

  • ipRouteTable (1.3.6.1.2.1.4.21) — 路由表
  • ifTable (1.3.6.1.2.1.2.2) — 介面統計
  • tcpConnTable (1.3.6.1.2.1.6.13) — TCP 連線
  • udpTable (1.3.6.1.2.1.7.5) — UDP 端口

完整 OID 樹見原文附錄 (RFC 1213 節錄)。

注意事項

議題說明
權限修改 ARP/路由表需管理員權限
SNMP 服務需啟用「SNMP 服務」 (Windows 功能) 並設定 Community Name (預設 public)
介面索引可透過 GetAdaptersInfo 或 iphlpapi 取得正確 IfIndex
動態條目老化動態 ARP 會隨時間老化消失,靜態條目永久保留直到手動刪除
現代替代GetIpNetTable/SetIpNetEntry (IPHLPAPI) 更直接、無需 SNMP 服務

結語

透過 SNMP 擴展代理操作 ARP 表展示了 Windows 管理儀器的底層機制。雖現代開發推薦使用 IPHLPAPI (GetIpNetTable 等),但理解 MIB-II、OID、SNMP PDU (GETNEXT/SET) 流程對網管系統開發、跨平台協定實作極有價值。

本文配套原始碼含 CARP 類完整實作與測試主程式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved