基于NFQUEUE+WinDivert的透明TCP/UDP代理故障排查求助
首先,先明确IP_INADDRERROR的核心含义:这个内核错误通常表示收到的IP包存在地址解析或有效性问题——比如IP头格式错误、源/目的地址非法、包长度字段异常导致内核无法正确解析地址,或者路由配置不匹配。结合你的架构和代码,我整理了几个优先级最高的排查方向:
1. 服务器端伪造地址的路由配置缺失
你的客户端把所有目标包的目的地址改成了10.20.30.40,但如果服务器上没有配置这个地址,内核会认为这是一个非本地地址,尝试转发却找不到对应路由,最终触发IP_INADDRERROR并丢弃包。
快速验证修复:在Debian服务器上执行以下命令,把伪造地址绑定到回环网卡:
ip addr add 10.20.30.40/32 dev lo
这样内核会识别这个地址为本地地址,将包交给NFQUEUE的用户空间代理处理,而不是尝试转发。
2. 客户端TCP选项的长度字段未正确设置
看你客户端TCP包修改的代码:
let mut option = TcpOption::new(); option.kind = 253; option.data.append(&mut packet.destination.octets().to_vec()); option.data.append(&mut tcp.destination.to_be_bytes().to_vec()); tcp.options.push(option);
TCP选项的length字段是必须手动设置的(除非packedit库有自动计算逻辑)——它的值是1字节(kind) + data长度。你这里的data是6字节(4字节IP+2字节端口),所以option.length应该设为7。如果这个字段缺失或错误,会导致TCP头总长度计算错误,进而IP包的payload长度异常,内核解析时就会触发地址错误。
修复代码:
let mut option = TcpOption::new(); option.kind = 253; option.data.append(&mut packet.destination.octets().to_vec()); option.data.append(&mut tcp.destination.to_be_bytes().to_vec()); option.length = 1 + option.data.len() as u8; // 手动设置选项长度 tcp.options.push(option);
3. 服务器端包转发逻辑不完整
你贴出的服务器代码只绑定了NFQUEUE队列,但核心的handle函数(以及util模块)没有实现细节。这是关键问题:
- 服务器需要从TCP选项或UDP payload中提取原始的目的IP和端口
- 修改IP包的目的地址/端口为原始值,重新计算校验和后转发到真实服务器
- 接收真实服务器的响应包,再把源地址改回
10.20.30.40、源端口改回2054,同时把客户端的原始IP/端口存入TCP选项或UDP payload,最后发回客户端
如果服务器没有做这些步骤,NFQUEUE收到的包要么被直接丢弃,要么处理后不符合内核要求,必然触发IP_INADDRERROR。
4. UDP包修改后的长度校验
客户端UDP处理中,你在payload前添加了6字节的原始地址/端口信息,但需要确认:
udp.recalculate_all()是否正确更新了UDP头的length字段(应为8字节头 + 新payload长度)packet.recalculate_all()是否正确更新了IP包的总长度(IP头长度 + UDP包总长度)
如果这两个长度字段计算错误,内核会认为包结构损坏,无法正确解析IP地址,进而抛出错误。
5. 抓包验证包结构
最后,通过抓包确认包的实际结构是否符合预期:
- 服务器端执行
tcpdump -i any host 10.20.30.40 and port 2054 -nnvv,检查收到的包的IP头长度、总长度、TCP/UDP选项/payload是否正确 - 客户端用Wireshark抓包,对比修改前后的包结构,确认地址、端口、校验和、长度字段均符合要求
内容的提问来源于stack exchange,提问作者warmBy

