无法通过SO_BINDTODEVICE在两个网卡之间的NAT执行TCP握手

1 投票
1 回答
1273 浏览
提问于 2025-04-18 08:23

我正在尝试将我的电脑连接到一个由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抓包,似乎问题出在客户端这边:

  1. wlan0向129.104.0.1发送了一个TCP SYN数据包
  2. 这个数据包被路由器正确地进行了NAT处理,并从129.104.0.198被eth0接收到
  3. eth0回复了一个SYN,ACK数据包,这个数据包也被正确地NAT回wlan0
  4. 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.txtRFC3704上搜索“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_filternet.ipv4.conf.your_nic.rp_filter,否则设置不会生效(内核默认是最严格的设置)。

撰写回答