无法通过SO_BINDTODEVICE在两个网卡之间的NAT执行TCP握手
我正在尝试将我的电脑连接到一个由OpenWRT管理的NAT的两侧,并通过这个NAT建立一个TCP连接:
- 我在我的第一个网卡(eth0,IP地址129.104.0.1)上运行一个DHCP服务器,并将其连接到路由器的WAN端口(IP地址129.104.0.198)
- 我将我的WiFi(wlan0,IP地址192.168.1.119)连接到NAT后面的路由器SSID
我正在使用Python和SO_BINDTODEVICE选项在服务器(eth0)和客户端(wlan0)之间通过NAT发送数据包:
对于服务器:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind((str(self.local_ip_addr),self.handler.port))
self.server.setsockopt(socket.SOL_SOCKET,25,self.iface.name+"\0")
self.server.listen(10)
while self.stopped() is False:
connect = self.server.accept()[0]
connect.settimeout(1)
connect.close()
self.server.close()
对于客户端:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, 25, self.iface.name + "\0")
sock.settimeout(1)
try:
sock.connect((self.dest,self.handler.port))
sock.close()
expect socket.timeout, socket.error as e:
return -1
我的问题是连接超时了。我在两个接口上使用Wireshark抓包,似乎问题出在客户端这边:
- wlan0向129.104.0.1发送了一个TCP SYN数据包
- 这个数据包被路由器正确地进行了NAT处理,并从129.104.0.198被eth0接收到
- eth0回复了一个SYN,ACK数据包,这个数据包也被正确地NAT回wlan0
- wlan0不理解这个SYN,ACK,并尝试重新发送第一个SYN数据包
我在想这可能和Linux内核拒绝接收来自本机地址的数据包有关,但如果有人有线索,那将非常有帮助!
编辑:我缩小了范围:这确实是一个内核问题,eth0发送的数据包被内核视为“外星人”,因为它们的源地址是本地IP。设置net.ipv4.conf.all.accept_local=1
并没有帮助,关闭net.ipv4.conf.all.rp_filter=0
也没有效果。
1 个回答
1
在查看内核源代码并添加了很多KERNEL_WARNING后,我们找到了问题的来源:在某些主流的操作系统(比如Ubuntu)中,Linux内核被设置成了路由器的模式,会丢弃那些来源地址可疑的数据包,以防止伪造地址的情况(可以在https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt和RFC3704上搜索“rp_filter”了解更多)。
如果你想允许这样的流量,你需要在你的机器上设置一些变量(以root身份):
sysctl -w net.ipv4.conf.all.accept_local=1
sysctl -w net.ipv4.conf.all.rp_filter=0
sysctl -w net.ipv4.conf.your_nic.rp_filter=0
这里的 your_nic
是接收数据包的网络接口。要注意同时修改 net.ipv4.conf.all.rp_filter
和 net.ipv4.conf.your_nic.rp_filter
,否则设置不会生效(内核默认是最严格的设置)。