Mininet 脚本从虚拟机的 IP 而不是主机的 IP 发送流量

Mininet script sending traffic from virtual machine's IP instead of host machines'

在 python3/mininet 脚本中,我有一个经过测试的有效主机及其 IP 地址字典。 对于每个键 - 通过迭代 dictOfAllHostsAndIPs.keys() - 我在每个模拟主机的终端上执行脚本

for host in dictOfAllHostsAndIPs.keys():
    host.cmd(os.system( "python3 ./traffic_generator.py %s" % <my_args>))

这个脚本将随机数据包发送到从模拟网络上的 IP 列表中随机选择的 IP 地址 -- 它使用 echo 非常随机地生成流量,将一些数据通过管道传输到 netcat,netcat 将其分发到通常是未使用:

os.system("echo -n '%s'" % data_string + " | nc %s 1299" % str(random_ip)))

我的问题是,当我在网络上的每台交换机上启动数据包嗅探器(主要是 tshark)时,在我看到的 TCP 帧数据的结果日志中第一行它们都来自我的 VM 的 IP (192.168.119.133) 而不是主机 (10.0.0.XX),并且在 ethernet/source 行中我总是得到 MAC 地址VM 本身而不是虚拟机。

# frame# /time(epoch) /     source ip → dest ip    /   ports: src → dest
>       8 0.063202633 192.168.119.133 → 10.0.0.1     TCP 74 40370 → 1299

> Ethernet II, Src: VMware_34:8e:de (00:0c:29:34:8e:de), Dst:
> VMware_fa:1a:ac (00:50:56:fa:1a:ac)
>     Destination: VMware_fa:1a:ac (00:50:56:fa:1a:ac)
>         Address: VMware_fa:1a:ac (00:50:56:fa:1a:ac)
>         
>     Source: VMware_34:8e:de (00:0c:29:34:8e:de)
>         Address: VMware_34:8e:de (00:0c:29:34:8e:de)

我在日志中看到的帧的源部分中唯一发生变化的是 VM 发送数据包的端口,它始终在 35000 和 49999 之间(或者可能是 50k 等等)。 (见上面日志的第一行,ports: src

我最初假设每个正在使用的端口都专用于从不同的模拟主机发送流量,但它们太多了而且它们不会保持不变,所以这显然不是案例.

没有立即意识到这一点(其他一切看起来都还不错:数据包目的地和我需要检查我的研究项目的所有其他参数),然后我实现了代码的模块化、线程重构。所以现在我查看我的程序的不同版本的日志,它们都有相同的问题,无论我如何 运行 流量生成脚本(旧版本)或 trafficGenerator 对象的实例化(新版本),所以这个问题不依赖于我的脚本结构的这个方面。

我多次检查并摆弄我的电话,四次检查我迭代的列表(然后是字典,然后再次列出),以便让各个机器 运行 流量生成代码与for hosts in list(net.get(hosts)): host.cmd(<action>) 最新版本是这样的:

    listOfTGs = []
### Create a list of threads for parallel execution
    listOfThreads = []
    for host in dictOfAllHostsAndIPs.keys():
###     Create a trafficGenerator object on the mininet machine, then call commands on host
        listOfTGs.append(tg.TrafficGenerator(durationFromArgument, host.name, callable_ips_list, net))
    for tg_instance in listOfTGs:
        listOfThreads.append(tg_instance.threadedExecution())
    print(listOfThreads)
    for th in listOfThreads:
        th.start()

我可以确认(经过 maaaany 仔细的基于打印的调试时间后)生成流量的操作与所有其他操作一样,对每个模拟主机并行执行一次,每个主机的正确标识可见在标准输出中。而且,我在嗅探数据包时获得的 IP 是 VM 本身之一,而不是 应该 出现的主机之一。在其他研究人员的论文中,我看到他们在嗅探分析中正确地使用了它,所以这不仅仅是“mininet 这样做的事情”(为了确定,我调查了它)。

TL;DR:我的问题是发送的数据包源是我启动 mininet 仿真的 VM 本身,而不是我称之为流量生成过程的被仿真主机。

我想我看到了源代码中发生的事情,但我没有 运行 框架来确认它。

看起来 mininet 为每个节点安装了一个 NAT rule

    self.cmd( 'iptables -I FORWARD',
              '-i', self.localIntf, '-d', self.subnet, '-j DROP' )
    self.cmd( 'iptables -A FORWARD',
              '-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' )
    self.cmd( 'iptables -A FORWARD',
              '-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' )
    self.cmd( 'iptables -t nat -A POSTROUTING',
              '-s', self.subnet, "'!'", '-d', self.subnet,
              '-j MASQUERADE' )

     # Instruct the kernel to perform forwarding
    self.cmd( 'sysctl net.ipv4.ip_forward=1' )

所以基本上,如果流量的目的地在节点的子网之外,则应用 NAT (MASQUAERADE) 规则。假设这也发生在根网络命名空间中,那么所有出站流量都经过 NAT(即:采用最后一跳的 IP)。

如何证明这一点

  1. 设置你的 mininet 网络。
  2. 使用 iptables -t nat -L 查看您的根命名空间 NAT 规则。如果您看到 MASQUERADE 条目,那么这就是您遇到问题的 NAT 规则
  3. 您应该能够看到带有 ip netns list 的各个命名空间。我认为它们应该被标记为 numbers.
  4. 为该列表中的每个命名空间输入 ip netns exec $NETNSNAME iptables -t nat -L 。遵守更多 NAT 规则。

如何测试 mininet 是否在根网络命名空间中执行命令

除了 NAT 规则之外,问题的另一个可能来源是 mininet 不是从节点的网络命名空间而是从根命名空间执行命令。这应该也很容易测试。

  1. 设置你的 mininet 网络。
  2. 您应该能够看到带有 ip netns list 的各个命名空间。我认为它们应该被标记为 numbers.
  3. 为该列表中的每个命名空间输入 ip netns exec $NETNSNAME ip addr show。观察 IP 地址。
  4. 对于与您希望从中发送流量的节点对应的名称空间,运行 以下内容:ip netns exec $NETNSNAME bash -c "echo -n $DATASTRING | nc $RANDOMIP 1299" 您已经为 DATASTRING 和 RANDOMIP 设置了 BASH 变量。
  5. 像之前一样观察路况。

如果您观察到的流量仍然具有 VM 的源 IP,则很可能是 NAT 规则导致了问题。如果它有节点的 IP,那么 mininet 没有在正确的命名空间中执行命令。