Python能够在打开套接字时选择哪个网络适配器吗?

27

运行Python应用程序的目标机器将拥有三个可用网络接口。一般来说,这三个网络将大不相同,但是有可能其中两个在类似的网络上。

在下面的示例中,我无法控制ETH 2上的目标地址(因为它是预先配置的系统),因此我被迫以编程方式选择要使用的适配器。

我相当确定这将涉及操作系统如何处理连接路由的问题。我的希望是,会有一种平台无关的方法来使用Python解决这个问题,因为有可能需要在Windows 7和Linux机器上运行此应用程序。

示例代码

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.0.2', 8000)) # Which device will this connect to??

正常情况

  • ETH 0 源地址:192.168.0.1
  • ETH 0 目标地址:192.168.0.2
  • ETH 1 源地址:10.20.30.1
  • ETH 1 目标地址:10.20.30.2
  • ETH 2 源地址:60.50.40.1
  • ETH 2 目标地址:60.50.40.1

可能出现问题的情况

  • ETH 0 源地址:192.168.0.1
  • ETH 0 目标地址:192.168.0.2
  • ETH 1 源地址:10.20.30.1
  • ETH 1 目标地址:10.20.30.2
  • ETH 2 源地址:192.168.0.3
  • ETH 2 目标地址:192.168.0.2

额外信息
适配器 ETH0、1 和 2 连接到不同的物理网络。


我们在谈论什么类型的套接字?你的两个192.168.0.x是指相同的网络还是使用相同的RFC1918地址空间的两个不同网络? - themel
@themel:我没有做过太多远离“快乐路径”的套接字编程,所以我恐怕不理解你的第一个问题。请查看我的编辑以回答你的第二个问题。 - Adam Lewis
3个回答

26

对于Windows我了解不多,但在Linux上,通常直到进行路由决策之后才选择接口,因此你通常不能指定数据包离开的接口。

但是,在Linux上,你可以使用SO_BINDTODEVICE(参见man 7 socket)选项。这将把套接字绑定到一个设备,但只有root用户才能在套接字上设置此选项。


刚刚检查了一下,Python套接字库没有定义SO_BINDTODEVICE,但你可以从socket.h中获取它:

# from socket.h
# define SO_BINDTODEVICE 25

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, 25, 'eth0')

另请参阅:


1
好吧,这正是我不想听到的(关于路由)。好消息是目标系统现在很可能会是Linux。感谢您提供的示例和链接,两者都非常有帮助。 - Adam Lewis
3
你可以使用socket.SO_BINDTODEVICE代替原始值'25'。 - Gearoid Murphy
为什么Curl Windows不支持特定的“--interface”参数? - CS QGB
不太正确。需要做类似这样的事情:s.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, 'eth0\0'.encode('utf-8')) - Jim Fell

19

在Windows上,如果您知道要使用的接口的IP地址,只需在连接之前绑定到该地址即可。在Linux上,可以使用socket选项SO_BINDTODEVICE(JimB建议使用),似乎这也是一个特权调用。

例如,在Windows上:

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('192.168.0.1', 0))
s.connect(('...'))

在Windows下,绑定源地址会选择与该设备具有相同IP地址的接口,即使该IP地址具有更高的路由度量成本。但Linux下不起作用,因为它总是使用所选设备的IP地址覆盖源地址。路由仅基于目标地址完成。唯一的例外似乎是将源地址设置为127.0.0.1,那么Linux将阻止这些数据包离开该计算机。


2
地址是系统范围的,绑定到特定地址不会选择数据包离开的接口。该决策在路由过程中进行。 - JimB
2
问题是如何选择网络适配器,而 bind 并不能做到这一点。Bind 仅为套接字设置地址/端口。接收数据包的接口很可能是您想要的接口,但是您的出站数据包将通过路由确定的接口离开,通常是具有默认网关的接口。 - JimB
是的,我知道那一点。我只是假设如果你设置了源地址,路由子系统会选择路由表中至少有一条返回路径的条目。如果仅基于目标地址完成路由选择,那么这个方法将无法实现。 - Lloyd Macrohon
我也在研究这个(我正在收集一些交换机和笔记本电脑)。当你测试时,尝试使用除本地主机以外的其他数据点。 - Adam Lewis
1
Linux可以基于源地址进行路由决策,这是策略路由的一部分,详见ip rule - ninjalj
显示剩余5条评论

-1

SO_BINDTODEVICE听起来很合理,但通常您会通过绑定的IP地址间接选择设备。更经常的情况是,您只需绑定到'',以绑定到机器的所有地址。


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