Linux:如何强制使用特定的网络接口?

Mat*_*een 18 sockets linux

这可以被认为是此早期SO问题的延续.

理想情况下,无论如何,我都希望将进程监禁到仅使用某个界面.它将进行TCP连接,发送UDP数据报和监听UDP广播.目前,我在做的是:

  1. 确定要使用的接口的IP.
  2. 创建IP策略规则,将来自接口的所有数据包路由到该IP
  3. 创建另一个IP策略规则,将来自该IP的所有数据包路由到该接口
  4. 为每个规则设置默认路由表

现在,这主要是有效的,但客户流程也必须愿意发挥作用.也就是说,它需要绑定到它想要使用的接口的特定IP,我想我也需要设置SO_BINDTODEVICE.(但是,我一直在阅读有关SO_BINDTODEVICE在使用TCP或UDP时是否实际工作的冲突信息.)幸运的是,客户端应用程序是Python,我可以扩展套接字类以透明地完成所有这些操作.但我不确定这是一个完整的解决方案,特别是在接收广播方面.

我的问题是:

  1. 请问SO_BINDTODEVICE做什么,我想在这里?或者它只对原始套接字有效吗?有人评论说," SO_BINDTODEVICE在套接字上不能保证套接字只接收到达该物理接口的线/天线的数据包." 如果确实如此,那又 SO_BINDTODEVICE什么呢?

  2. 有没有办法做到这一点,本地IP不必是唯一的?除了一个接口上的DHCP服务器可能将其分配给另一个接口正在使用的IP这一事实之外,这不会是一个问题,从而使路由表混乱.

  3. 如何仅从特定界面接收广播?绑定到特定IP似乎使它忽略广播,这是有道理的,但不是我正在寻找的.

我正在运行Ubuntu 8.04 w/Linux内核2.6.26.能够通过两个不同的接口同时访问两个不同网络上的同一子网是一个不可协商的要求,因此(大部分)免于"不要那样做".:)

Mat*_*een 6

至于我的一般问题,似乎有几种方法可以做到:

  • 复杂的方式涉及到路由表的变化和各个进程的配合。这就是我上面描述的方式。它的一个优点是它可以从用户空间工作。我在上面做了一些额外的注释,并在下面回答了我的具体问题。

  • 如果SO_BINDTODEVICE设置了,则编写一个完全忽略路由表的自定义内核模型。但是,客户端进程仍然需要调用setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev)。这个选项绝对不适合胆小的人。

  • 虚拟化流程。这可能不适合很多人,它会带来自己的一系列麻烦,主要是配置。但值得一提。

选项 1 和 2 要求流程选择加入我们希望它们工作的方式。这可以通过创建一个动态库来部分缓解,该库劫持 socket() 调用以创建套接字,然后在返回描述符之前立即将其绑定到设备。此处更详细地描述了这一点。

在做了一些研究和大量的谷歌搜索之后,我可以得出一些关于 Linux 内核 2.6.26 行为的结论。请注意,这些可能都是特定于实现的行为,甚至可能是特定于内核的。在决定基于我的单点数据实现功能之前,先测试您自己的平台。

  1. SO_BINDTODEVICE 确实做到了它所说的,至少对于UDP。

  2. 每个接口的唯一 IP 似乎是必要的,因为我们正在使用路由表。自定义内核模块可以绕过此限制。

  3. 要在特定接口上接收广播,首先使用 绑定到设备SO_BINDTODEVICE,然后使用通常的 bind() 调用绑定到广播地址。设备绑定需要先完成。然后,套接字将只接收到达该接口的广播。

我通过首先创建一个套接字来测试它。然后我使用setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev). 最后,我将其绑定到广播地址。我从另一台计算机发送了一个广播,该广播将通过非绑定接口接收。设备绑定的套接字没有收到这个广播,这是有道理的。删除setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev)呼叫,将收到广播。

还应该提到的是,您也可以setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)在这里使用。注意语义SO_REUSEADDR随着广播地址的变化而变化。具体来说,如果两个套接字都已设置,则将两个套接字绑定到同一台机器上的广播地址和相同端口是合法的SO_REUSEADDR

更新:SO_BINDTODEVICE广播似乎充满了危险,特别是接收广播帧。我观察在一个接口上收到的广播帧,但同时在另一个接口上消失了。看起来它们受本地路由表的影响,但不受 IP 策略规则的影响。但是,我不是 100% 确定这一点,如果您想继续,我只是将其作为调查点提及。所有这一切都是为了说:使用风险自负。为了节省时间,我在接口上打开了一个原始套接字并自己解析了以太网和 IP 标头。


Mat*_*een 5

经过一个艰苦的周末后,我很高兴提出一个解决方案,它可以几乎零麻烦地解决我之前讨论过的大部分问题。

有一个名为 net.ipv4.conf.all.rp_filter 的 sysctl,可以将其设置为 0 以禁用源验证:

    rp_filter - 整数
         2 - 按 RFC1812 中指定的反向路径进行源验证
             单宿主主机和存根网络的推荐选项
             路由器。可能会导致复杂的麻烦(不是无循环)
             运行缓慢且不可靠的协议(类似于 RIP)的网络,
             或使用静态路由。

         1 -(默认)较弱的 RP 过滤形式:丢弃所有数据包
             看起来像是源自直接连接的接口,但是
             是从另一个界面输入的。

         0 - 没有源验证。

也可以使用 /proc/sys/net/ipv4/conf/ <interface>/rp_filter 针对每个接口进行设置。

正如一位发帖者所解释的那样,它使 IP 路由“不确定性降低”,因为来自一个子网的数据包不能保证始终从同一接口发出。在本例中,这正是所需要的。请做更多研究以确定这是否真的是您想要的。

由于我不明白的原因,广播仍然存在问题,但我最终对这个问题感到满意,我希望它对其他人有帮助。