如何使TCP套接字与SO_BINDTODEVICE(针对路由表)一起工作

How to make a TCP socket work with SO_BINDTODEVICE (against routing table)

本文关键字:路由 工作 一起 BINDTODEVICE TCP 何使 套接字 SO      更新时间:2023-10-16

问题背景:

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

问题:

我正在尝试使用设置了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连接?

谢谢!

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

启用

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

对于disable,您只需要将flag设置为false,其余代码相同:

ifr.ifr_flags=true;

不要使用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之外,你也可以做同样的事情。

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

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

要允许这样的流量,你必须在你的机器上设置一些变量(作为根):

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

其中CCD_ 1是接收分组的网络接口。注意同时更改net.ipv4.conf.all.rp_filternet.ipv4.conf.your_nic.rp_filter,否则将无法工作(内核默认为最严格的设置)。