如何使用SO_BINDTODEVICE使TCP套接字工作(绕过路由表)

3

问题背景:

在我们的机器上,有多个网络接口连接到不同的网络。存在可能重叠的IP地址(例如,两台不同的机器在两个不同的网络中具有相同的IP地址)。因此,当我们想要连接特定对等方时,我们需要指定它的IP地址以及我们的网络接口,以便正确地连接到该网络。 我们希望编写一个能够通过TCP与特定对等方建立连接的C/C++应用程序。

问题:

我正在尝试使用已设置SO_BINDTODEVICE的套接字进行TCP连接。以下是简化的片段:

sockfd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, interface_name,
    strlen(interface_name));
connect(sockfd, (sockaddr *) &serv_addr, sizeof(serv_addr));

我知道struct ifreq,但似乎只有其中的第一个字段(ifr_name字段)被使用。因此,我只能传递接口的名称。
  • 如果强制接口与路由表中的接口相同,则一切正常。
  • 如果强制接口不同,则(使用Wireshark测试):
    1. SYN从强制接口发送到所需的对等方。
    2. 在强制接口上收到所需对等方的SYN,ACK。
    3. 未从强制接口发送ACK,因此未建立连接。(并返回步骤2。)

如何检查我们的系统拒绝了SYN、ACK或ACK的位置?以及如何正确地强制TCP套接字使用特定接口进行连接(与路由表相反)?

也许还有其他更方便的方法来在所需的接口上创建TCP连接吗?

谢谢!

3个回答

1
我知道这可能不是您想要的答案,但您可以禁用其他接口,只启用您想要的网络。在您的情况下,似乎需要所有接口,但我认为这种方法可以帮助其他人。您可以使用以下命令启用/禁用网络接口:

启用

ifr.ifr_flags = true; 
strcpy(ifr.ifr_name, "eth0"); //you could put any interface name beside "eth0" 
res = ioctl(sockfd, SIOCSIFFLAGS, &ifr);

如果要禁用它,只需要将标志设置为false,其余代码保持不变:

ifr.ifr_flags = false;


0

不要使用SO_BINDTODEVICE。它并非所有平台都支持,而且有更简单的方法。

相反,将套接字绑定到所需连接到远程端的正确网络上的本地IP地址。

例如,

sockfd = socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = 0; //Auto-determine port.
sin.sin_addr.s_addr = inet_addr("192.168.1.123"); //Your IP address on same network as peer you want to connect to

bind(sockfd, (sockaddr*)&sin, sizeof(sin));

接着调用connect方法。

对于服务器端,你需要做同样的事情,只不过要指定一个端口而不是0,然后调用listen而不是connect。


谢谢。这是一个很好的解决方案,但是我忘了提到我们的机器在每个网络中都有相同的地址。抱歉。 - user1700143
我很不想说这个,因为我讨厌人们质疑你的问题而不是回答它,但是你为什么在多个网络上有相同的IP呢?这听起来像是一个令人困惑的噩梦来调试。 - syplex
抱歉耽搁了。由于可扩展性的原因,我们希望拥有许多相同的网络。拥有许多接口的机器是调度程序。 - user1700143
在Windows系统上,bind()函数只影响传入的数据包。 - Jorge Fuentes González
@JorgeFuentesGonzález,实际上这是不正确的。根据MSDN的说法,“如果应用程序想要在具有多个网络接口和本地IP地址的本地计算机上选择特定的本地IP地址,则使用bind before send[...]”。https://msdn.microsoft.com/en-us/library/windows/desktop/ms737550%28v=vs.85%29.aspx - syplex
@syplex 刚刚在另一个帖子里和另一个人谈到了同样的问题。忘记我也在这里说过了。问题是在旧版本的Windows中这是不可能的,但自从Windows Vista以来它可以正常工作(我没有意识到Windows Vista升级,因为我误读了另一篇文章)。 - Jorge Fuentes González

0

这是内核配置问题 - 在许多发行版中,默认配置为在这种特定情况下拒绝传入的数据包。

我在另一个类似问题的答案中找到了解决此问题的方法:

To allow such traffic you have to set some variables on your machine (as 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

where your_nic is the network interface receiving the packet. Beware to change both net.ipv4.conf.all.rp_filter and net.ipv4.conf.your_nic.rp_filter, it will not work otherwise (the kernel defaults to the most restrictive setting).


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接