如何避免通过 Linux 中的本地堆栈进行路由

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8538081/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 03:41:34  来源:igfitidea点击:

how to avoid routing through local stack in Linux

clinuxsocketsroutingnetwork-programming

提问by codewarrior

I have the following environment: 2 hosts, each with 2 Ethernet interfaces connected to eachother (like on diagram below):

我有以下环境: 2 台主机,每台主机有 2 个相互连接的以太网接口(如下图所示):

 +---------+               +---------+                     
 |      (1)+---------------+(2)      |    
 |  host1  |               |  host2  |
 |         |               |         |
 |      (3)+---------------+(4)      |
 +---------+               +---------+

I would like to write client/server socket tool that will open both client and server sockets on host1. I would like client to send TCP packets through interface (1) and server to listen on interface (3), that packets will go through host2.

我想编写客户端/服务器套接字工具,它将在 host1 上打开客户端和服务器套接字。我希望客户端通过接口 (1) 和服务器发送 TCP 数据包以在接口 (3) 上侦听,该数据包将通过 host2。

Normally Linux stack will route this packets through local TCP/IP stack without sending those to host2.

通常 Linux 堆栈将通过本地 TCP/IP 堆栈路由这些数据包,而不将它们发送到 host2。

I have tried to use SO_BINDTODEVICEoption for both server and client and it seems that server indeed is binded to interface (3) and is not listening localhost traffic. I have checked that client from host1 could not be accepted whereas client from host2 does.

我尝试对服务器和客户端使用SO_BINDTODEVICE选项,似乎服务器确实绑定到接口 (3) 并且没有监听本地主机流量。我已经检查过来自 host1 的客户端无法被接受,而来自 host2 的客户端可以接受。

Unfortunately client packets are not send out (even tcpdump on interface(1) don't see packets) through interface (1) to interface (2). Of course routing is correct (i can ping (2) from (1), (4) from (1), (4) from (3) and so on).

不幸的是,客户端数据包不会通过接口 (1) 发送到接口 (2)(即使接口 (1) 上的 tcpdump 也看不到数据包)。当然路由是正确的(我可以从(1)ping(2),从(1),(4)从(3)等等)。

My question is if this is possible to be implemented without using custom TCP/IP stack?

我的问题是这是否可以在不使用自定义 TCP/IP 堆栈的情况下实现?

Maybe I should try to change destination IP address (from client) to be from outside network (and then will be sent using default gateway from interface (1) - interface (2)) and then in postrouting change those again to original ones? Is such solution possible to work?

也许我应该尝试将目标 IP 地址(来自客户端)更改为来自外部网络(然后将使用默认网关从接口 (1) - 接口 (2) 发送),然后在路由后再次将这些地址更改为原始地址?这样的解决方案可行吗?

I am writting my application in C under Debian.

我正在 Debian 下用 C 编写我的应用程序。

Adding some more details and clarifications:

添加更多详细信息和说明:

  1. of course both pairs (1)--(2) and (3)--(4) are different subnets
  2. what I want to achieve is (1)-->(2)-->(4)-->(3)
  3. host2 is blackbox so I cant install there any packet forwarder (that will open listening socket on interface (2) and forward those to (3) through (4)) - this is exactely what I want to avoid
  1. 当然这两个对 (1)--(2) 和 (3)--(4) 是不同的子网
  2. 我想要实现的是 (1)-->(2)-->(4)-->(3)
  3. host2 是黑盒,所以我不能在那里安装任何数据包转发器(它将在接口 (2) 上打开侦听套接字并将它们转发到 (3) 到 (4)) - 这正是我想要避免的

The main problem seems to be local delivery. When I open socket on host1 and want to connect to socket, that is listening on other address of the same host kernel just uses local stack to deliver packets. See netfilter diagram below:

主要问题似乎是本地交付。当我在 host1 上打开套接字并想连接到套接字时,即侦听同一主机内核的其他地址只是使用本地堆栈来传递数据包。见下面的网络过滤器图:

 --->[1]--->[ROUTE]--->[3]--->[4]--->
             |            ^
             |            |
             |         [ROUTE]
             v            |
            [2]          [5]
             |            ^
             |            |
             v            |

Packets are going through [5] NF_IP_LOCAL_OUT and [2] NF_IP_LOCAL_IN whereas I want to force them to go through [4].

数据包正在通过 [5] NF_IP_LOCAL_OUT 和 [2] NF_IP_LOCAL_IN 而我想强制它们通过 [4]。

回答by Nikolai Fetissov

You can assign different subnets to (1)-(2) and (3)-(4) pairs, and have host2 forward the packets from (2) to (3). The client on host1 will be connecting to address of (2), so local network stack will not know that the target server is actually running locally too.

您可以将不同的子网分配给 (1)-(2) 和 (3)-(4) 对,并让 host2 将数据包从 (2) 转发到 (3)。host1 上的客户端将连接到 (2) 的地址,因此本地网络堆栈不会知道目标服务器实际上也在本地运行。

回答by ninjalj

Untested (should work, but I may have missed something):

未经测试(应该可行,但我可能错过了一些东西):

Linux has several routing tables. Table localcontains some routes that the kernel adds automatically for every IP address added to the host. You can see them with ip route show table local. Routes labeled as localindicate local routes that go through the loopback interface. You could delete that route and add a normal unicastroute to replace it:

Linux 有几个路由表。表local包含内核为添加到主机的每个 IP 地址自动添加的一些路由。您可以使用ip route show table local. 标记为本地的路由表示通过环回接口的本地路由。您可以删除该路由并添加一个普通的单播路由来替换它:

ip route del table local <ip> dev <NIC>
ip route add table local <ip> dev <NIC>
ip route flush cache

Now your 1st box will try to send IP datagrams to that IP address as if it was a remote address, e.g: it will use ARP. So, your 2nd box will have to either reply to the ARP requests if it is acting as a router or is doing proxy-ARP, or you will have to add an association to the ARP cache:

现在您的第一个盒子将尝试将 IP 数据报发送到该 IP 地址,就好像它是一个远程地址一样,例如:它将使用 ARP。因此,如果您的第二个框充当路由器或正在执行代理 ARP,则必须回复 ARP 请求,或者您必须向 ARP 缓存添加关联:

arp -s <ip> <MAC>

Then, you will probably have to disable rp_filteron the interfaces:

然后,您可能必须在接口上禁用rp_filter

echo 0 > /proc/sys/net/ipv4/conf/<NIC>/rp_filter

Them again, if this doesn't work, you could probably set up something with L2 NAT, using ebtables.

再说一次,如果这不起作用,您可能可以使用ebtables使用 L2 NAT 设置一些东西。

回答by Nikolay Bryskin

For a very similar task I'm using such script:

对于一个非常相似的任务,我正在使用这样的脚本:

ip rule add from all lookup local # add one more local table lookup rule with high pref
ip rule del pref 0 # delete default local table lookup rule
ip rout add ${ip3} via ${ip2} src ${ip1} table 100 # add correct route to some table
ip rule add from all lookup 100 pref 1000 # add rule to lookup new table before local table