【问题标题】:how to avoid routing through local stack in Linux如何避免在 Linux 中通过本地堆栈进行路由
【发布时间】:2011-12-16 17:46:14
【问题描述】:

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

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

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

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

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

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

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

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

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

添加更多细节和说明:

  1. 当然,(1)--(2) 和 (3)--(4) 对都是不同的子网
  2. 我想要实现的是(1)-->(2)-->(4)-->(3)
  3. host2 是黑盒,所以我不能在那里安装任何数据包转发器(它将打开接口 (2) 上的侦听套接字并将它们转发到 (3) 到 (4)) - 这正是我想要避免的

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

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

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

【问题讨论】:

  • 只是为了理解您希望您的消息路径为 (1) --> (2) --> (4) --> (3) ?
  • 您当然不需要为此定制 TCP 堆栈。不确定它是否是最好的方法,但您可以在 host2 上进行一些网络地址转换,将一些唯一的 IP 地址映射到接口 (3) 的地址,并确保从 host2 到 (3) 的数据包通过 (4 )。 Host1 然后会连接到那个唯一的 IP 地址。
  • 如果一个套接字在另一台机器上从 (1) 连接到 (2),而另一个套接字在第一台机器上连接 (4) 到 (3),为什么 Linux TCP 堆栈会跳过通过网络? TCP 堆栈如何可能知道这种不寻常的拓扑结构以及它如何可能知道您的意图?
  • 这样想:你想让你的妻子(在隔壁房间)知道晚饭准备好了,但她生你的气,不和你说话。所以你决定给你妈妈打电话,让她告诉她的邻居给你的妻子打电话。你拨你妈妈的号码。现在... 你认为哪个电话会响?您认为电话公司会让您妻子的电话响铃的假设是否合理?
  • 让我们添加 IP 地址以进行澄清。假设接口 (1) 的 IP 地址为 192.168.1.1,接口 (3) 的 IP 地址为 192.168.10.1。当我打开套接字并想连接 192.168.10.1 时,内核会知道它是 host1 上其他接口的本地地址。如果 Linux 内核是电话公司,它只知道我想给我的妻子打电话。关键是如何强迫它打电话给我妈妈,先打电话给邻居而不是直接打电话。

标签: c linux sockets routing network-programming


【解决方案1】:

未经测试(应该可以,但我可能遗漏了一些东西):

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

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

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

arp -s <ip> <MAC>

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

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

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

【讨论】:

  • 你的意思是unicast,而不是第二行ip 上的local
  • @NikolaiNFetissov: local 用于表名。我实际上并没有在任何一个命令中指定路由的类型,对于第一个命令,IP 可能足以选择要删除的唯一路由,第二个 unicast 是默认路由类型。
  • @NikolaiNFetissov:是的,我测试了那部分,数据包在tcpdump -ni eth0 上可见。我没有测试整个设置,我太懒了。无论如何,OP 可能既不需要ARP 部分也不需要rp_filter 一个。
  • @ninjalj:你是如何将客户端套接字绑定到特定接口的?我试图修改本地路由表,但我的客户端数据包仍然被内核丢弃在某个地方(在接口 (1) 上看不到任何内容)。
  • @codewarrior:您不需要绑定到接口,路由会处理所有事情。至少IP数据报应该使用指定的接口发送,我没有设置第二个系统,所以没有测试接收部分,启用log_martians并查看内核日志。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 其实(1)-(2)和(3)-(4)之间有不同的子网。要使用此解决方案,我将需要 host2 上的一些转发器,它将侦听来自接口 (2) 的数据包并通过接口 (4) 发送到接口 (3)。 Host2 是黑盒,所以我不能在那里安装任何其他软件。无论如何,来自 host1 的客户端将连接到在接口 (2) 上侦听的服务器,这正是我想要避免的。
    • 您需要 something 来反映从接口 (1) 发送到接口 (3) 的数据包 - 一台充当路由器的 pc,一个带有 nat 的 linksys 垃圾箱设置,任何东西。发挥创意。
    • 问题是再次从host1发送数据包。即使路由设置为通过设置为接口 (2) 的默认网关发送所有数据包,也会导致发送到同一设备的第二个接口的数据包不会使用默认网关进行路由。如果我将客户端套接字显式绑定到干扰 (1) 数据包被内核丢弃(即使 tcpdump 在接口 (1) 上运行也看不到数据包)。所以问题似乎不在于路由本身,而在于本地堆栈交付。
    • 那个某事需要要么做网络地址转换,要么做代理,所以客户端的目的地址不在同一台机器上。
    猜你喜欢
    • 2014-04-17
    • 1970-01-01
    • 1970-01-01
    • 2010-12-04
    • 2010-11-30
    • 2014-02-26
    • 2012-09-23
    • 2015-08-20
    • 2011-11-23
    相关资源
    最近更新 更多