上一篇: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 请求。