上一篇:WindowsAPI|每天了解幾個winAPI接口之網絡配置相關文檔Iphlpapi.h詳細分析六
如果有錯誤歡迎指正批評,在此只作為科普和參考。
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um\iphlpapi.h
文章目錄
- CreateIpNetEntry:把某個 IPv4 地址強行綁定到指定 MAC(或更新現有綁定)
- 函數原型(簡化)
- 關鍵參數
- 典型使用場景
- 注意事項
- 簡單代碼示例
- ARP 緩存條目是什么
- 為什么需要這張紙條?
- 一條 ARP 緩存長什么樣?
- 存在哪里?
- 生命周期
- SetIpNetEntry:**用來“修改”一條已經存在的 ARP 緩存條目**
- 功能一句話
- 與 CreateIpNetEntry 的區別
- 調用要點
- 最小示例
- 常見錯誤碼
- DeleteIpNetEntry:**把本機 ARP 緩存中的一條**(且僅一條)**已有條目刪除**
- 函數原型
- 關鍵約束
- 使用流程(典型)
- 最小示例
- 與 FlushIpNetTable 的區別
- FlushIpNetTable:
- 函數原型
- 行為要點
- 典型場景
- 最小示例
- 與 DeleteIpNetEntry 的關系
- CreateProxyArpEntry:**讓本機在指定接口上“假裝”自己擁有某個 IP(段),從而替別人回答 ARP 請求——這就是 Proxy ARP(代理 ARP)。**
- 1. 什么是 Proxy ARP?
- 2. 函數原型
- 3. 結果表現
- 4. 典型場景
- 5. 注意事項
- 6. 最小示例
- DeleteProxyArpEntry:撤銷之前通過 `CreateProxyArpEntry` 建立的 Proxy ARP 代理范圍
- 函數原型
- 成功后的效果
- 常見返回值
- 最小示例
CreateIpNetEntry:把某個 IPv4 地址強行綁定到指定 MAC(或更新現有綁定)
//////////////////////////////////////////////////////////////////////////////
// //
// Used to create, modify or delete an ARP entry. In all cases the dwIndex //
// dwAddr field MUST BE SPECIFIED. //
// For a set, the complete MIB_IPNETROW structure must be specified //
// //
//////////////////////////////////////////////////////////////////////////////IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
CreateIpNetEntry(_In_ PMIB_IPNETROW pArpEntry);
CreateIpNetEntry
是 Windows IP 幫助庫(iphlpapi.dll
)導出的一個 API 函數,用于 創建或修改本機的 ARP 緩存條目。它的核心功能是:往 IPv4 鄰居緩存(ARP 表)里“寫”一個條目,實現 IP 地址到 MAC 地址的靜態映射。
函數原型(簡化)
DWORD CreateIpNetEntry(_In_ const MIB_IPNETROW *pArpEntry
);
- 返回值:
DWORD
NO_ERROR
(0)表示成功;- 其他 Win32 錯誤碼表示失敗,常見如
ERROR_INVALID_PARAMETER
、ERROR_NOT_SUPPORTED
等。
關鍵參數
typedef struct _MIB_IPNETROW {DWORD dwIndex; // 對應本地接口的索引(LUID),**必須正確**DWORD dwPhysAddrLen;BYTE bPhysAddr[MAXLEN_PHYSADDR]; // 目標 MAC 地址DWORD dwAddr; // 目標 IPv4 地址(網絡字節序),**必須指定**DWORD dwType; // 條目類型:static(4)、dynamic(3) 等
} MIB_IPNETROW, *PMIB_IPNETROW;
- dwIndex:通過
GetAdaptersInfo
或GetIfTable
等 API 獲取,指明要把 ARP 條目綁到哪塊網卡。 - dwAddr:要映射的 IPv4 地址(如 192.168.1.100)。
- bPhysAddr:對應的 MAC 地址(如 00-11-22-33-44-55)。
- dwType:
MIB_IPNET_TYPE_STATIC
(4)→ 靜態條目,重啟后仍保留,除非手動刪除。MIB_IPNET_TYPE_DYNAMIC
(3)→ 動態條目,隨 ARP 老化機制自動過期。
通常創建“永久”映射時設為STATIC
。
典型使用場景
- 手動添加靜態 ARP 條目
避免局域網內目標主機被 ARP 欺騙,或為了在腳本里預先綁定關鍵服務器。 - 網絡診斷/管理工具
如自己寫個“arp -s”替代品,或集成到網管軟件里。 - 驅動或安全軟件
在驅動層之上快速寫入靜態鄰居條目,防止某些攻擊。
注意事項
- 權限:調用進程需具備 管理員權限(
SeLoadDriverPrivilege
或SeManageVolumePrivilege
在某些系統上也可能要求)。 - IPv4 Only:該 API 只操作 IPv4 ARP 緩存。IPv6 鄰居緩存請用
CreateUnicastIpAddressEntry
或SetIpNetEntry2
。 - 已有條目:如果
(dwIndex, dwAddr)
已存在,函數會更新該條目(相當于 Set 操作)。 - 刪除條目:用
DeleteIpNetEntry
或FlushIpNetTable
。
簡單代碼示例
MIB_IPNETROW arp = {0};
arp.dwIndex = IfIndex; // 網卡索引
arp.dwAddr = inet_addr("192.168.1.100");
arp.dwPhysAddrLen = 6;
memcpy(arp.bPhysAddr, "\x00\x11\x22\x33\x44\x55", 6);
arp.dwType = MIB_IPNET_TYPE_STATIC; // 靜態DWORD ret = CreateIpNetEntry(&arp);
if (ret == NO_ERROR) {printf("ARP entry added.\n");
} else {printf("Error: %lu\n", ret);
}
一句話總結:
CreateIpNetEntry
就是“以編程方式執行 arp -s
”的 Win32 API,用來把某個 IPv4 地址強行綁定到指定 MAC(或更新現有綁定)。
ARP 緩存條目是什么
ARP 緩存條目(ARP Cache Entry)就是操作系統在內存里“記的一張小紙條”:
把某個 IP 地址暫時或永久地對應到某個 MAC 地址。
為什么需要這張紙條?
局域網通信時,數據鏈路層(以太網/Wi-Fi)只看 MAC 地址(形如 00:11:22:33:44:55)。
但應用程序只知道 IP 地址(如 192.168.1.10)。
于是操作系統必須在發送數據包前,先回答:
“192.168.1.10 對應的 MAC 是多少?”
這個“查表 + 找不到就廣播詢問”的過程就是 ARP(Address Resolution Protocol)。
為了避免每次都廣播,操作系統把“問到的答案”緩存起來——這就是 ARP 緩存條目。
一條 ARP 緩存長什么樣?
在 Windows 上用 arp -a
可以看到:
Internet Address Physical Address Type
192.168.1.1 00-11-22-33-44-55 dynamic
192.168.1.100 66-77-88-99-aa-bb static
- Internet Address:目標 IPv4 地址
- Physical Address:對方的 MAC 地址
- Type
dynamic
:動態條目,默認存活 2 分鐘~10 分鐘(可配置),超時自動刪除。static
:靜態條目,除非手動刪除或重啟,否則長期保留。
存在哪里?
- Windows:內核的一塊內存數據結構(可通過
GetIpNetTable
API 讀取)。 - Linux:
/proc/net/arp
或ip neigh
命令查看。
生命周期
- 首次通信 → 發 ARP 請求廣播 → 收到 ARP 應答 → 寫入動態條目。
- 繼續通信 → 每用一次就刷新“老化計時器”。
- 超時未用 → 動態條目被刪除。
- 管理員手動 → 用
arp -s
或CreateIpNetEntry
創建靜態條目,永不過期。
一句話總結:
ARP 緩存條目就是“IP 與 MAC 的映射關系表里的某一行”,讓操作系統在局域網里發數據包時,不必每次都重新問“誰有 IP x.x.x.x?”
SetIpNetEntry:用來“修改”一條已經存在的 ARP 緩存條目
IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
SetIpNetEntry(_In_ PMIB_IPNETROW pArpEntry);
SetIpNetEntry
與剛才介紹的 CreateIpNetEntry
同屬 iphlpapi.dll,用來“修改”一條已經存在的 ARP 緩存條目,而不是新建一條。
功能一句話
把本地 ARP 表里已有的 (接口, IPv4) 對應關系更新成新的 MAC 或新的類型(dynamic / static)。
與 CreateIpNetEntry 的區別
API | 條目不存在時的行為 | 條目已存在時的行為 | 典型用途 |
---|---|---|---|
CreateIpNetEntry | 新建 | 更新 | 相當于 arp -s |
SetIpNetEntry | 返回錯誤 (ERROR_NOT_FOUND ) | 更新 | 只改已有條目 |
調用要點
- 必須保證
MIB_IPNETROW
中dwIndex
(接口索引)dwAddr
(IPv4 地址)
與現有條目 完全一致,否則函數會失敗。
- 可以修改:
- 目標 MAC (
bPhysAddr
) - 條目類型 (
dwType
):dynamic → static 或反之
- 目標 MAC (
- 權限:同樣需要管理員權限。
最小示例
MIB_IPNETROW row = {0};
// 1. 先取出現有條目(這里僅演示,實際用 GetIpNetTable 找到)
row.dwIndex = 7; // 本機接口索引
row.dwAddr = inet_addr("192.168.1.1");// 要改的 IP
memcpy(row.bPhysAddr, "\xAA\xBB\xCC\xDD\xEE\xFF", 6);
row.dwPhysAddrLen = 6;
row.dwType = MIB_IPNET_TYPE_STATIC; // 把 dynamic 改成 staticDWORD ret = SetIpNetEntry(&row);
if (ret == NO_ERROR) {printf("ARP entry updated.\n");
} else {printf("Error: %lu\n", ret);
}
常見錯誤碼
ERROR_NOT_FOUND
(1168)
→ 指定的 (接口, IP) 組合在 ARP 表里不存在。ERROR_INVALID_PARAMETER
(87)
→ 參數不合法(MAC 長度、接口索引、地址格式等)。
一句話總結:
SetIpNetEntry
就是“只能改、不能建”的 ARP 表編輯 API;當你想 更新 某條已有鄰居條目的 MAC 或類型時就用它。
DeleteIpNetEntry:把本機 ARP 緩存中的一條(且僅一條)已有條目刪除
IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
DeleteIpNetEntry(_In_ PMIB_IPNETROW pArpEntry);
DeleteIpNetEntry
是 iphlpapi.dll 提供的 API,用于把本機 ARP 緩存中的一條(且僅一條)已有條目刪除。
換句話說,它是“以編程方式執行 arp -d <IP>
”的函數版本。
函數原型
DWORD WINAPI DeleteIpNetEntry(_In_ PMIB_IPNETROW pArpEntry
);
- 返回值
NO_ERROR
(0) 刪除成功ERROR_NOT_FOUND
(1168) 指定條目不存在ERROR_INVALID_PARAMETER
參數非法- 其他 Win32 錯誤碼
關鍵約束
- 必須提供
dwIndex
(本地接口索引)dwAddr
(IPv4 地址,網絡字節序)
這兩個字段合在一起充當“主鍵”,精確匹配要刪的那一行。
- 其余字段可忽略
函數只看(dwIndex, dwAddr)
,MAC 地址、類型等字段不會被比對。 - 權限
需要管理員權限;非特權進程調用會失敗(ERROR_ACCESS_DENIED
)。
使用流程(典型)
- 先用
GetIpNetTable
枚舉當前 ARP 表,找到要刪的條目,把其dwIndex
和dwAddr
拷出來。 - 填充一個
MIB_IPNETROW
,僅填這兩個關鍵字段即可。 - 調用
DeleteIpNetEntry
。
最小示例
MIB_IPNETROW entry = {0};
entry.dwIndex = 7; // 網卡接口索引
entry.dwAddr = inet_addr("192.168.1.100"); // 要刪除的 IPDWORD err = DeleteIpNetEntry(&entry);
if (err == NO_ERROR) {printf("ARP entry deleted.\n");
} else {printf("Delete failed: %lu\n", err);
}
與 FlushIpNetTable 的區別
DeleteIpNetEntry
刪除單條指定(接口, IP)
的條目。FlushIpNetTable
把整張接口的 ARP 表全部清空(暴力版arp -d *
)。
一句話總結:
DeleteIpNetEntry
就是“精確制導”地刪掉某個接口下某個 IPv4 地址對應的 ARP 緩存行;條目必須已存在,否則返回 ERROR_NOT_FOUND
。
FlushIpNetTable:
IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
FlushIpNetTable(_In_ DWORD dwIfIndex);
FlushIpNetTable
是 iphlpapi.dll 提供的“一鍵清空”接口——
把指定網卡接口(由 dwIfIndex
標識)的整張 IPv4 ARP 緩存表全部刷掉,相當于在該接口上執行 arp -d *
(Windows 無此命令行,但效果類似)。
函數原型
DWORD WINAPI FlushIpNetTable(_In_ DWORD dwIfIndex // 要清空的接口索引
);
- 返回值
NO_ERROR
(0) 成功ERROR_INVALID_PARAMETER
:接口索引無效- 其他 Win32 錯誤碼(如權限不足)
行為要點
- 作用范圍
僅影響dwIfIndex
對應的接口;其他接口的 ARP 表保持不變。 - 清空內容
該接口下 所有動態條目 立即被刪除;靜態條目 不受影響(靜態條目需用DeleteIpNetEntry
或重啟才能移除)。 - 觸發后續 ARP 廣播
清空后,如果進程再次訪問這些 IP,系統會重新發起 ARP 請求廣播。 - 權限
需要管理員權限;普通進程返回ERROR_ACCESS_DENIED
。
典型場景
- 網絡故障排查:懷疑 ARP 緩存被污染,先整體清掉再觀察。
- DHCP/網絡切換:接口剛拿到新的網段地址,清掉舊網段殘留的條目。
- 安全/測試工具:模擬“ARP 表刷新”。
最小示例
DWORD dwIfIndex = 7; // 例:有線網卡接口索引
DWORD err = FlushIpNetTable(dwIfIndex);
if (err == NO_ERROR) {printf("Interface %lu ARP table flushed.\n", dwIfIndex);
} else {printf("Flush failed: %lu\n", err);
}
與 DeleteIpNetEntry 的關系
API | 粒度 | 影響靜態條目? |
---|---|---|
DeleteIpNetEntry | 單條(IP + 接口) | 可以刪除 |
FlushIpNetTable | 整接口所有動態條目 | 不刪除靜態 |
一句話總結:
FlushIpNetTable
就是“把某塊網卡的 IPv4 ARP 緩存瞬間格式化”,只留下靜態條目,動態鄰居全部強制失效并重新解析。
CreateProxyArpEntry:讓本機在指定接口上“假裝”自己擁有某個 IP(段),從而替別人回答 ARP 請求——這就是 Proxy ARP(代理 ARP)。
//////////////////////////////////////////////////////////////////////////////
// //
// Used to create or delete a Proxy ARP entry. The dwIndex is the index of //
// the interface on which to PARP for the dwAddress. If the interface is //
// of a type that doesnt support ARP, e.g. PPP, then the call will fail //
// //
//////////////////////////////////////////////////////////////////////////////IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
CreateProxyArpEntry(_In_ DWORD dwAddress,_In_ DWORD dwMask,_In_ DWORD dwIfIndex);
CreateProxyArpEntry 作用一句話:讓本機在指定接口上“假裝”自己擁有某個 IP(段),從而替別人回答 ARP 請求——這就是 Proxy ARP(代理 ARP)。
1. 什么是 Proxy ARP?
- 當主機 A 想訪問 IP
X
,但X
并不在本子網,按常理 A 應該先把包發給網關。 - 如果網關上啟用了 Proxy ARP,它會回應 A 的 ARP 請求:
“IPX
的 MAC 就是我的 MAC”。
于是 A 把數據直接發到網關 MAC,再由網關轉發到真正的X
,整個過程對 A 完全透明。
2. 函數原型
DWORD WINAPI CreateProxyArpEntry(_In_ DWORD dwAddress, // 起始 IPv4 地址(網絡字節序)_In_ DWORD dwMask, // 子網掩碼(決定范圍)_In_ DWORD dwIfIndex // 在哪個接口上代答
);
dwAddress
+dwMask
共同描述 要代理的 IP 范圍。
例如192.168.1.0
+255.255.255.0
→ 代理整個/24
。dwIfIndex
必須是支持 ARP 的接口(以太網、Wi-Fi 等);PPP/隧道接口會返回ERROR_NOT_SUPPORTED
。
3. 結果表現
調用成功后,內核在該接口上:
- 接收 任何落在這個 IP 范圍內的 ARP 請求;
- 立即回發 ARP Reply,源 MAC 填本機接口 MAC;
- 后續數據包由本機按路由表轉發給真正的目標。
4. 典型場景
- 無感知網關
老設備不懂子網,只能廣播 ARP。網關用 Proxy ARP 讓它以為所有地址都在本地 LAN。 - 地址遷移/透明遷移
臨時把某段地址代理到新網關,無需修改終端配置。 - VPN/撥號服務器
讓遠程客戶端仿佛直接位于公司局域網。
5. 注意事項
- 僅 IPv4;IPv6 用 NDP Proxy。
- 需要 管理員權限。
- 刪除用
DeleteProxyArpEntry
(參數完全一致)。 - 不會自動加路由,必須確保本機能把流量正確轉發到真正目的地,否則雖有 ARP 回復,數據仍會黑洞。
6. 最小示例
DWORD dwAddr = inet_addr("192.168.10.0"); // 起始地址
DWORD dwMask = inet_addr("255.255.255.0"); // /24
DWORD dwIdx = 7; // 以太網接口索引DWORD err = CreateProxyArpEntry(dwAddr, dwMask, dwIdx);
if (err == NO_ERROR)printf("Proxy ARP enabled on 192.168.10.0/24\n");
elseprintf("Error: %lu\n", err);
一句話再總結:
CreateProxyArpEntry
讓指定網卡“撒謊”說自己擁有某一段 IP,從而充當透明的 ARP 代理,把 ARP 請求全部攬到自己身上,再負責把數據包真正送出去。
DeleteProxyArpEntry:撤銷之前通過 CreateProxyArpEntry
建立的 Proxy ARP 代理范圍
IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
DeleteProxyArpEntry(_In_ DWORD dwAddress,_In_ DWORD dwMask,_In_ DWORD dwIfIndex);
DeleteProxyArpEntry
是 CreateProxyArpEntry
的逆向操作:
- 功能:在指定接口上,撤銷之前通過
CreateProxyArpEntry
建立的 Proxy ARP 代理范圍。 - 參數必須與創建時完全一致(
dwAddress
、dwMask
、dwIfIndex
),否則系統找不到對應條目而返回ERROR_NOT_FOUND
。
函數原型
DWORD WINAPI DeleteProxyArpEntry(_In_ DWORD dwAddress, // 起始 IPv4 地址(網絡字節序)_In_ DWORD dwMask, // 子網掩碼_In_ DWORD dwIfIndex // 接口索引
);
成功后的效果
- 該接口停止為指定 IP 段代答 ARP 請求。
- 終端下次 ARP 該段地址時,將收不到代理回復,只能得到真實主機或網關的響應(若存在)。
- 對已建立的流量連接無影響,僅對新 ARP 請求生效。
常見返回值
值 | 含義 |
---|---|
NO_ERROR (0) | 刪除成功 |
ERROR_NOT_FOUND (1168) | 指定 (地址/掩碼/接口) 的組合不存在 |
ERROR_INVALID_PARAMETER | 參數非法 |
ERROR_ACCESS_DENIED | 權限不足(需管理員) |
最小示例
// 撤銷先前對 192.168.10.0/24 的 Proxy ARP
DWORD addr = inet_addr("192.168.10.0");
DWORD mask = inet_addr("255.255.255.0");
DWORD idx = 7; // 對應接口DWORD err = DeleteProxyArpEntry(addr, mask, idx);
if (err == NO_ERROR)printf("Proxy ARP entry removed.\n");
elseprintf("Failed: %lu\n", err);
一句話總結:
DeleteProxyArpEntry
用來“注銷” Proxy ARP 代理范圍,讓接口不再冒充指定網段的 IP 地址去回應 ARP 請求。