尽管有多个套接字,但所有接口都未被使用
All interfaces not being used despite multiple sockets
尽管将两个 UDP 套接字绑定到主机不同接口上的 2 个不同地址,但流量流经单个接口。
网络拓扑图如下:
2 links 在 h2 和 s1 之间
1 link 在 h1 和 s1
之间
________
| |
h2 s1_______h1
|________|
我正在 mininet 上模拟 2 台主机和一台交换机。 h1 是 运行 位于 10.0.0.1:4243 的 UDP 服务器。另一台主机有 2 个接口,ips 10.0.0.2 & 10.0.0.4。我在 h2 上制作 2 个套接字,将一个绑定到 (10.0.0.2,9999),另一个绑定到 (10.0.0.4,8888)。我是 运行 下面的代码,它应该在两个接口上交替发送数据包。
而是在两个接口上发送第一个数据包。所有后续数据包都通过单个接口发送。
客户代码(运行 on h2)
def client():
sock1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock1.bind(("10.0.0.2",9999))
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock2.bind(("10.0.0.4",8888))
while True:
text = 'The time is {}'.format(datetime.now())
data = text.encode('ascii')
sock1.sendto(data, ('10.0.0.1', 4243))
data, address = sock1.recvfrom(MAX_BYTES)
sock2.sendto(data, ('10.0.0.1', 4243))
data, address = sock2.recvfrom(MAX_BYTES)
服务器代码(运行 on h1)
def server():
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('10.0.0.1', 4243))
print('Listening at {}'.format(sock.getsockname()))
while True:
data, address = sock.recvfrom(MAX_BYTES)
text = data.decode('ascii')
print('The client at {} says {!r}'.format(address, text))
text = 'Your data was {} bytes long'.format(len(data))
data = text.encode('ascii')
sock.sendto(data, address)
一个数据包通过h2 的两个接口发送。随后的数据包都仅通过 IP 10.0.0.2 的接口发送和接收。在 wiresharking 上,一半的数据包将 src/dest IP 设置为 10.0.0.4
根据您的评论,这是我认为正在发生的事情:
- 您有冲突的默认路由
- 您已 ip_forwarding 启用允许内部路由
- 由于 #1 和 #2
,绑定调用实际上被否定了
冲突的默认路由
根据您的评论,这两个接口可能都有一个 /8。这意味着对于 h2,内核会为两者添加相同的路由,例如:
10.0.0.0/8 dev h2-eth0 proto kernel scope link src 10.0.0.2
10.0.0.0/8 dev h2-eth1 proto kernel scope link src 10.0.0.4
这意味着内核只想选择其中一个来发送所有 10.0.0.0/8 流量 to/from。您试图通过绑定调用来解决这个问题,但我不相信绑定会覆盖路由 table 行为。
ip_forwarding
如果设置了这个标志,它也会使事情复杂化,因为它可能允许通过 eth0 的 eth1 流量。
绑定
因此,将所有这些放在一起,您将套接字绑定到与各个接口关联的各个 IP,但内核只想将发往 10.0.0.1 的流量路由到一个接口,它选择了 eth0。我认为通常这会阻止 eth1 流量,但由于路由和 ip_forward 标志,eth1 流量通过 eth0 路由(在初始 ARP 数据包之后,您看到的那个数据包)。
解决方案
给h1的接口两个IP地址10.1.0.1/16和10.0.0.1/16。然后给h2-eth0 ip地址10.0.0.2/16和h2-eth1 ip地址10.1.0.2/16。然后让您的程序从 10.1.0.2 向 10.1.0.1 发送数据,从 10.0.0.2 向 10.0.0.1 发送数据。
通过隔离子网,您将防止混淆的根本原因,即路由冲突。
尽管将两个 UDP 套接字绑定到主机不同接口上的 2 个不同地址,但流量流经单个接口。
网络拓扑图如下: 2 links 在 h2 和 s1 之间 1 link 在 h1 和 s1
之间 ________
| |
h2 s1_______h1
|________|
我正在 mininet 上模拟 2 台主机和一台交换机。 h1 是 运行 位于 10.0.0.1:4243 的 UDP 服务器。另一台主机有 2 个接口,ips 10.0.0.2 & 10.0.0.4。我在 h2 上制作 2 个套接字,将一个绑定到 (10.0.0.2,9999),另一个绑定到 (10.0.0.4,8888)。我是 运行 下面的代码,它应该在两个接口上交替发送数据包。
而是在两个接口上发送第一个数据包。所有后续数据包都通过单个接口发送。
客户代码(运行 on h2)
def client():
sock1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock1.bind(("10.0.0.2",9999))
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock2.bind(("10.0.0.4",8888))
while True:
text = 'The time is {}'.format(datetime.now())
data = text.encode('ascii')
sock1.sendto(data, ('10.0.0.1', 4243))
data, address = sock1.recvfrom(MAX_BYTES)
sock2.sendto(data, ('10.0.0.1', 4243))
data, address = sock2.recvfrom(MAX_BYTES)
服务器代码(运行 on h1)
def server():
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('10.0.0.1', 4243))
print('Listening at {}'.format(sock.getsockname()))
while True:
data, address = sock.recvfrom(MAX_BYTES)
text = data.decode('ascii')
print('The client at {} says {!r}'.format(address, text))
text = 'Your data was {} bytes long'.format(len(data))
data = text.encode('ascii')
sock.sendto(data, address)
一个数据包通过h2 的两个接口发送。随后的数据包都仅通过 IP 10.0.0.2 的接口发送和接收。在 wiresharking 上,一半的数据包将 src/dest IP 设置为 10.0.0.4
根据您的评论,这是我认为正在发生的事情:
- 您有冲突的默认路由
- 您已 ip_forwarding 启用允许内部路由
- 由于 #1 和 #2 ,绑定调用实际上被否定了
冲突的默认路由
根据您的评论,这两个接口可能都有一个 /8。这意味着对于 h2,内核会为两者添加相同的路由,例如:
10.0.0.0/8 dev h2-eth0 proto kernel scope link src 10.0.0.2
10.0.0.0/8 dev h2-eth1 proto kernel scope link src 10.0.0.4
这意味着内核只想选择其中一个来发送所有 10.0.0.0/8 流量 to/from。您试图通过绑定调用来解决这个问题,但我不相信绑定会覆盖路由 table 行为。
ip_forwarding
如果设置了这个标志,它也会使事情复杂化,因为它可能允许通过 eth0 的 eth1 流量。
绑定
因此,将所有这些放在一起,您将套接字绑定到与各个接口关联的各个 IP,但内核只想将发往 10.0.0.1 的流量路由到一个接口,它选择了 eth0。我认为通常这会阻止 eth1 流量,但由于路由和 ip_forward 标志,eth1 流量通过 eth0 路由(在初始 ARP 数据包之后,您看到的那个数据包)。
解决方案
给h1的接口两个IP地址10.1.0.1/16和10.0.0.1/16。然后给h2-eth0 ip地址10.0.0.2/16和h2-eth1 ip地址10.1.0.2/16。然后让您的程序从 10.1.0.2 向 10.1.0.1 发送数据,从 10.0.0.2 向 10.0.0.1 发送数据。
通过隔离子网,您将防止混淆的根本原因,即路由冲突。