├── .gitignore ├── README.md ├── SUMMARY.md ├── _images ├── basic_topo.png ├── mn.png └── typical_scenario.png ├── advanced ├── README.md ├── config_file.md ├── controller.md ├── dpctl.md ├── interaction.md └── nox.md ├── basic_usage ├── README.md ├── information.md ├── network.md └── node.md ├── code_structure ├── README.md ├── core.md ├── docs.md ├── install.md ├── logic.md ├── others.md └── runtime.md ├── cover.jpg ├── cover_small.jpg ├── introduction ├── README.md ├── feature.md └── install.md ├── module_link ├── README.md ├── intf.md ├── link.md ├── tcintf.md └── tclink.md ├── module_net ├── README.md ├── mininet.md └── mininetwithcontrolnet.md ├── module_node ├── README.md ├── base.md ├── controller.md ├── host.md └── switch.md ├── module_others ├── clean.md ├── cli.md ├── log.md ├── moduledeps.md ├── term.md └── util.md ├── module_topo ├── README.md ├── lineartopo.md ├── multigraph.md ├── singleswitch.md ├── singleswitchreversed.md └── topo.md ├── operation ├── README.md ├── cmd_summary.md ├── link.md ├── mac.md ├── namespace.md ├── other.md ├── start_parameter.md ├── test.md ├── topology.md ├── type.md └── xterm.md └── runtime_and_example ├── README.md ├── baresshd.md ├── example.md └── mn.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Node rules: 2 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 3 | .grunt 4 | 5 | ## Dependency directory 6 | ## Commenting this out is preferred by some people, see 7 | ## https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 8 | node_modules 9 | 10 | # Book build output 11 | _book 12 | 13 | # eBook build output 14 | *.epub 15 | *.mobi 16 | *.pdf 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mininet 应用与源码剖析 2 | ============ 3 | [Mininet](http://mininet.org) 是研究软件定义网络,进行快速验证的高效模拟平台。 4 | 5 | 本书的一到三章介绍 Mininet 的安装和使用;第四章介绍一些高级功能;五到七章分析 Mininet 的源码实现。 6 | 7 | 最新版本在线阅读:[GitBook](https://www.gitbooks.io/book/yeasy/mininet_book)。 8 | 9 | 本书源码在 Github 上维护,欢迎参与: [https://github.com/yeasy/mininet_book](https://github.com/yeasy/mininet_book)。 10 | 11 | 感谢所有的 [贡献者](https://github.com/yeasy/mininet_book/graphs/contributors)。 12 | 13 | ## 更新历史: 14 | 15 | * 0.6: 2013-12-16 16 | * 完善对 topo 模块、net 模块等的分析。 17 | * 0.5: 2013-12-11 18 | * 补充对部分 example 文件的分析; 19 | * 增加对 clean 模块的分析。 20 | * 0.4: 2013-11-18 21 | * 补充对 link 和 node 库的核心代码分析; 22 | 补 充运行文件分析。 23 | * 0.3: 2013-11-16 24 | * 完成运行文件分析。 25 | * 0.2: 2013-11-15 26 | * 完成库文件分析。 27 | * 0.1: 2013-10-11 28 | * 完成代码结构分析。 29 | 30 | ## 参加步骤 31 | * 在 GitHub 上 `fork` 到自己的仓库,如 `user/mininet_book`,然后 `clone` 到本地,并设置用户信息。 32 | ``` 33 | $ git clone git@github.com:user/mininet_book.git 34 | $ cd mininet_book 35 | $ git config user.name "User" 36 | $ git config user.email user@email.com 37 | ``` 38 | * 修改代码后提交,并推送到自己的仓库。 39 | ``` 40 | $ #do some change on the content 41 | $ git commit -am "Fix issue #1: change helo to hello" 42 | $ git push 43 | ``` 44 | * 在 GitHub 网站上提交 pull request。 45 | * 定期使用项目仓库内容更新自己仓库内容。 46 | ``` 47 | $ git remote add upstream https://github.com/yeasy/mininet_book 48 | $ git fetch upstream 49 | $ git checkout master 50 | $ git rebase upstream/master 51 | $ git push -f origin master 52 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [前言](README.md) 4 | * [概述](introduction/README.md) 5 | * [主要特性](introduction/feature.md) 6 | * [安装](introduction/install.md) 7 | * [基本使用](basic_usage/README.md) 8 | * [创建网络](basic_usage/network.md) 9 | * [查看信息](basic_usage/information.md) 10 | * [节点操作](basic_usage/node.md) 11 | * [常用操作](operation/README.md) 12 | * [快捷测试](operation/test.md) 13 | * [自定义拓扑](operation/topology.md) 14 | * [使用友好的 MAC 编号](operation/mac.md) 15 | * [使用 XTerm](operation/xterm.md) 16 | * [链路操作](operation/link.md) 17 | * [指定交换机跟控制器类型](operation/type.md) 18 | * [名字空间](operation/namespace.md) 19 | * [启动参数总结](operation/start_parameter.md) 20 | * [常用命令总结](operation/cmd_summary.md) 21 | * [其他操作](operation/other.md) 22 | * [高级功能](advanced/README.md) 23 | * [使用 dpctl](advanced/dpctl.md) 24 | * [控制器](advanced/controller.md) 25 | * [交换机与控制器交互](advanced/interaction.md) 26 | * [使用 NOX](advanced/nox.md) 27 | * [外部读取配置命令](advanced/config_file.md) 28 | * [代码结构](code_structure/README.md) 29 | * [运行相关](code_structure/runtime.md) 30 | * [安装相关](code_structure/install.md) 31 | * [核心代码](code_structure/core.md) 32 | * [说明文件](code_structure/docs.md) 33 | * [其它](code_structure/others.md) 34 | * [整体逻辑功能](code_structure/logic.md) 35 | * [mininet.link 模块](module_link/README.md) 36 | * [mininet.link.Intf](module_link/intf.md) 37 | * [mininet.link.Link](module_link/link.md) 38 | * [mininet.link.TCIntf](module_link/tcintf.md) 39 | * [mininet.link.TCLink](module_link/tclink.md) 40 | * [mininet.node 模块](module_node/README.md) 41 | * [基类](module_node/base.md) 42 | * [主机类](module_node/host.md) 43 | * [控制器类](module_node/controller.md) 44 | * [交换机类](module_node/switch.md) 45 | * [mininet.net 模块](module_net/README.md) 46 | * [mininet.net.Mininet](module_net/mininet.md) 47 | * [mininet.net.MininetWithControlNet](module_net/mininetwithcontrolnet.md) 48 | * [mininet.topo 模块](module_topo/README.md) 49 | * [mininet.topo.MultiGraph](module_topo/multigraph.md) 50 | * [mininet.topo.Topo](module_topo/topo.md) 51 | * [mininet.topo.LinearTopo](module_topo/lineartopo.md) 52 | * [mininet.topo.SingleSwitchTopo](module_topo/singleswitch.md) 53 | * [mininet.topo.SingleSwitchReversedTopo](module_topo/singleswitchreversed.md) 54 | * [其它模块](module_others/README.md) 55 | * [mininet.cli 模块](module_others/cli.md) 56 | * [mininet.clean 模块](module_others/clean.md) 57 | * [mininet.log 模块](module_others/log.md) 58 | * [mininet.moduledeps 模块](module_others/moduledeps.md) 59 | * [mininet.term 模块](module_others/term.md) 60 | * [mininet.util 模块](module_others/util.md) 61 | * [运行代码和示例](runtime_and_example/README.md) 62 | * [mn](runtime_and_example/mn.md) 63 | * [示例程序](runtime_and_example/example.md) 64 | 65 | -------------------------------------------------------------------------------- /_images/basic_topo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasy/mininet_book/c63d989b2d44dd88da9f3c42dc261f1e025c21bd/_images/basic_topo.png -------------------------------------------------------------------------------- /_images/mn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasy/mininet_book/c63d989b2d44dd88da9f3c42dc261f1e025c21bd/_images/mn.png -------------------------------------------------------------------------------- /_images/typical_scenario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasy/mininet_book/c63d989b2d44dd88da9f3c42dc261f1e025c21bd/_images/typical_scenario.png -------------------------------------------------------------------------------- /advanced/README.md: -------------------------------------------------------------------------------- 1 | # 高级功能 2 | 3 | 本章将通过一个具体管理 Openflow switch 的例子来介绍一些比较高级的命令。 4 | 5 | 首先,启动 Mininet,执行 6 | ``` 7 | sudo mn --topo single,3 --mac --switch ovsk --controller remote 8 | ``` 9 | 生成一个小的网络,三台主机连到一台交换机上,交换机为 OpenvSwitch 交换机,指定 remote 类型控制器(默认为本地)。 10 | -------------------------------------------------------------------------------- /advanced/config_file.md: -------------------------------------------------------------------------------- 1 | ## 外部读取配置命令 2 | 可以写到一个文件中,用 Mininet 直接调用。例如脚本文件名为 my_cli_script,则可以执行 3 | ``` 4 | mininet> source my_cli_script 5 | ``` 6 | 或者 7 | ``` 8 | # mn --pre my_cli_script 9 | ``` 10 | -------------------------------------------------------------------------------- /advanced/controller.md: -------------------------------------------------------------------------------- 1 | ## 控制器 2 | 通过执行 3 | ``` 4 | sudo mn --controller=remote --ip=[controller IP] --port=[controller listening port] 5 | ``` 6 | 可以连接到控制器。 7 | -------------------------------------------------------------------------------- /advanced/dpctl.md: -------------------------------------------------------------------------------- 1 | ## 使用 dpctl 2 | 执行 3 | ``` 4 | dpctl show tcp:127.0.0.1:6634 5 | ``` 6 | 可以查看到交换机的端口等基本情况,其中 tcp 端口 6634 是默认的交换机监听端口。 7 | 8 | 执行 9 | ``` 10 | dpctl dump-flows tcp:127.0.0.1:6634 11 | ``` 12 | 可以看到更详细的流表信息。 13 | 此时,流表为空,执行 `h2 ping h3` 无法得到响应。因此我们需要通过 dpctl 手动添加流表项,实现转发。 14 | 命令为 15 | ``` 16 | dpctl add-flow tcp:127.0.0.1:6634 in_port=1,actions=output:2 17 | dpctl add-flow tcp:127.0.0.1:6634 in_port=2,actions=output:1 18 | ``` 19 | 此时查看流表可以看到新的转发信息,同时可以在 h2 和 h3 之间 ping 通。 20 | 21 | ## 使用ovs-ofctl 22 | 执行 23 | ``` 24 | sh ovs-ofctl add-flow s1 action=normal 25 | ``` 26 | 将交换机s1的转发行为设置为normal,流表将会自动更新。 27 | 28 | 执行 29 | ``` 30 | sh ovs-ofctl del-flows s1 31 | ``` 32 | 将交换机s1的流表清空。 33 | 34 | 35 | 我们可以自定义在不同网络层之间的流匹配。 36 | 37 | 执行 38 | ``` 39 | sh ovs-ofctl add-flow s1 priority=500, in_port=1, actions=output:2 40 | sh ovs-ofctl add-flow s1 priority=500, in_port=2, actions=output:1 41 | ``` 42 | 自定义第一层的流匹配规则,根据port进行匹配。 43 | 44 | 执行 45 | ``` 46 | sh ovs-ofctl add-flow s1 dl_src=00:00:00:00:00:01, dl_dst=00:00:00:00:00:02, actions=output:2 47 | sh ovs-ofctl add-flow s1 dl_src=00:00:00:00:00:02, dl_dst=00:00:00:00:00:01, actions=output:1 48 | sh ovs-ofctl add-flow s1 dl_type=0x806,nw_proto=1,actions=flood 49 | ``` 50 | 自定义第二层的流匹配规则,根据协议和mac地址进行匹配。 51 | 52 | 执行 53 | ``` 54 | sh ovs-ofctl add-flow s1 priority=500, dl_type=0x800, nw_src=10.0.0.0/24, nw_dst=10.0.0.0/24, actions=normal 55 | sh ovs-ofctl add-flow s1 priority=800, ip,nw_src=10.0.0.3,actions=mod_nw_tos:184,normal 56 | 57 | sh ovs-ofctl add-flow s1 arp,nw_dst=10.0.0.1,actions=output:1 58 | sh ovs-ofctl add-flow s1 arp,nw_dst=10.0.0.2,actions=output:2 59 | sh ovs-ofctl add-flow s1 arp,nw_dst=10.0.0.3,actions=output:3 60 | ``` 61 | 自定义第三层的流匹配规则,根据协议和IP地址进行匹配。 62 | 63 | 执行 64 | ``` 65 | h3 python -m SimpleHTTPServer 80 & 66 | sh ovs-ofctl add-flow s1 arp,actions=normal 67 | sh ovs-ofctl add-flow s1 priority=500,dl_type=0x800,nw_proto=6,tp_dst=80,actions=output:3 68 | sh ovs-ofctl add-flow s1 priority=800,ip,nw_src=10.0.0.3,actions=normal 69 | h1 curl h3 70 | h2 curl h3 71 | ``` 72 | 自定义第四层的流匹配规则。 73 | -------------------------------------------------------------------------------- /advanced/interaction.md: -------------------------------------------------------------------------------- 1 | ## 交换机与控制器交互 2 | 我们可以启动一个简单的控制器,默认没有任何流表项,仅仅作为一台带学习功能的交换机。控制器默认监听端口是 6633。 3 | 4 | 以下控制器与交换机之间的消息交互过程,可以通过 wireshark,配置 of 过滤器观察到交换机跟控制器之间的交互消息。 5 | 参见下面的表格。 6 | 7 | | 消息 | 类型 | 描述 | 8 | | -- | -- | -- | 9 | | Hello | Controller->Switch | 跟着 TCP 握手,控制器发送它的版本号到交换机。| 10 | | Hello | Switch->Controller | 交换机回复它支持的版本。| 11 | | Features Request | Controller->Switch | 控制器询问可用端口。| 12 | | Set Config | Controller->Switch | 控制器让交换机发送流超时消息。| 13 | | Features Reply | Switch->Controller | 交换机回复端口、端口速度、支持的表和行动。| 14 | 15 | 16 | 同样,我们可以用 wireshark 观察到当第一次有 ping 包从 h2 发到 h3 时,控制器如何自动添加相应的表项到交换机。wireshark 相应的过滤器为 `of && (of.type != 3) && (of.type != 2)`。 17 | 18 | 相关的消息过程参考下面的表格。 19 | | 消息 | 类型 | 描述 | 20 | | -- | -- | -- | 21 | | Packet-In | Switch->Controller | 网包到达交换机,没有发生匹配,发送到控制器。| 22 | | Packet-Out | Controller->Switch | 从交换机端口发出包。| 23 | | Flow-Mod | Controller->Switch | 添加指定流。| 24 | | Flow-Expired | Switch->Controller | 流超时。| 25 | 26 | -------------------------------------------------------------------------------- /advanced/nox.md: -------------------------------------------------------------------------------- 1 | ## 使用 NOX 2 | 3 | 首先确定没有其他控制器在运行(占据 6633 端口) 4 | ``` 5 | sudo killall controller 6 | ``` 7 | 启动 Mininet 8 | ``` 9 | sudo mn --topo single,3 --mac --switch ovsk --controller remote 10 | ``` 11 | 然后启动 NOX,默认路径为 ~/noxcore/build/src,重新打开一个 ssh 终端执行 12 | ``` 13 | ./nox_core -v -i ptcp: pytutorial 14 | ``` 15 | 会自动打开运行 tutorial 应用的 NOX,打印出详细的调试信息,并监听 6633 端口。 16 | 17 | 直到打印出类似如下信息,说明交换机已经成功连接到 NOX。 18 | ``` 19 | 00039|nox|DBG:Registering switch with DPID = 1 20 | ``` 21 | 通过互 ping 测试,各个主机连通,此时 switch 等同于一个 hub。 22 | 然后通过修改 ~/noxcore/src/nox/coreapps/tutorial/pytutorial.py 中代码,让 NOX 工作成一个带学习功能的交换机。相关命令参考 ofinclude 代码,以及 NOX 对各个包的解析代码目录:~/noxcore/src/nox/lib/packet/。 23 | 24 | 通过编写 NOX 程序,我们可以让交换机的行为更加智能化、复杂化。为了测试我们编写的 NOX 程序,我们可以使用 cbench 来进行测试。 25 | 26 | -------------------------------------------------------------------------------- /basic_usage/README.md: -------------------------------------------------------------------------------- 1 | # 基本使用 2 | 通过几个简单例子来介绍 Mininet 的基本功能。 3 | -------------------------------------------------------------------------------- /basic_usage/information.md: -------------------------------------------------------------------------------- 1 | ## 查看信息 2 | 3 | 查看全部节点: 4 | ``` 5 | mininet> nodes 6 | available nodes are: 7 | c0 h2 h3 s1 8 | ``` 9 | 查看链路信息: 10 | ``` 11 | mininet> net 12 | s1 <-> h2-eth0 h3-eth0 13 | ``` 14 | 输出各节点的信息: 15 | ``` 16 | mininet> dump 17 | c0: IP=127.0.0.1 intfs= pid=1679 18 | s1: IP=None intfs=s1-eth1,s1-eth2 pid=1682 19 | h2: IP=10.0.0.2 intfs=h2-eth0 pid=1680 20 | h3: IP=10.0.0.3 intfs=h3-eth0 pid=1681 21 | ``` 22 | -------------------------------------------------------------------------------- /basic_usage/network.md: -------------------------------------------------------------------------------- 1 | ## 创建网络 2 | 3 | Mininet 的操作十分简单,启动一个小型测试网络只需要下面几个步骤。 4 | 登录到虚拟机命令行界面,打开 wireshark,使其后台运行, 命令为 `sudo wireshark &`。 5 | 6 | 启动 Mininet,命令为`sudo mn`,则默认创建如下图所示的网络拓扑。 7 | 8 | ![默认网络拓扑](../_images/basic_topo.png) 9 | 10 | 经过短暂的等待即可进入以 `mininet>` 引导的命令行界面。 11 | 好了,从现在开始,我们就拥有了一个 1 台控制节点(controller)、一台交换(switch)、两台主机(host)的网络,并且用 wireshark 进行观测。 12 | -------------------------------------------------------------------------------- /basic_usage/node.md: -------------------------------------------------------------------------------- 1 | ## 对节点进行单独操作 2 | 如果想要对某个节点的虚拟机单独进行命令操作,也十分简单,命令格式为 `node cmd`。 3 | 4 | 例如查看交换机 s1 上的网络信息,我们只需要在执行的 ifconfig 命令前加上 s1 主机标志即可,即 `s1 ifconfig`,同样,如果我们想用 ping 3 个包的方法来测试 h2 跟 h3 之间连通情况,只需要执行 `h2 ping -c 3 h3` 即可。得到的结果为 5 | ``` 6 | mininet> h2 ping -c 3 h3 7 | PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data. 8 | 64 bytes from 10.0.0.3: icmp_seq=1 ttl=64 time=7.19 ms 9 | 64 bytes from 10.0.0.3: icmp_seq=2 ttl=64 time=0.239 ms 10 | 64 bytes from 10.0.0.3: icmp_seq=3 ttl=64 time=0.136 ms 11 | — 10.0.0.3 ping statistics — 12 | 3 packets transmitted, 3 received, 0% packet loss, time 2006ms 13 | rtt min/avg/max/mdev = 0.136/2.523/7.194/3.303 ms 14 | ``` 15 | 在本操作执行后,可以通过 wireshark 记录查看到创建新的流表项的过程,这也是造成第一个 ping 得到的结果偏大的原因。 16 | 17 | 更简单的全网络互 ping 测试命令是 `pingall`,会自动所有主机节点逐对进行 ping 连通测试。 18 | 19 | -------------------------------------------------------------------------------- /code_structure/README.md: -------------------------------------------------------------------------------- 1 | # 代码结构 2 | 介绍源代码的基本结构。 3 | -------------------------------------------------------------------------------- /code_structure/core.md: -------------------------------------------------------------------------------- 1 | ## 核心代码 2 | 核心代码基本都在 mininet/ 子目录下。 3 | 注:最新的 2.1.0 版本,核心 Python 代码仅为 4675 行。 4 | ``` 5 | $ find mininet -name "*.py" | xargs cat | wc -l 6 | ``` 7 | -------------------------------------------------------------------------------- /code_structure/docs.md: -------------------------------------------------------------------------------- 1 | ## 说明文件 2 | * CONTRIBUTORS:作者信息 3 | * README.md:主说明文件 4 | * doc/doxygen.cfg:执行doxygen生成文档时的配置文件。 5 | -------------------------------------------------------------------------------- /code_structure/install.md: -------------------------------------------------------------------------------- 1 | ## 安装相关 2 | * INSTALL:安装说明 3 | * setup.py:安装 Python 包时候的配置文件,被 Makefile 中调用。 4 | * debian/:生成 deb 安装包时的配置文件。 5 | -------------------------------------------------------------------------------- /code_structure/logic.md: -------------------------------------------------------------------------------- 1 | ## 整体逻辑功能 2 | 整体上来看,Mininet 作为一个基于 Python 的网络模拟工具,可以分为两大部分:Python库和运行文件。 3 | 4 | 前者提供对网络中元素进行抽象和实现,例如定义主机类来表示网络中的一台主机。后者则基于这些库来完成各种自定义的模拟过程。 5 | 一个典型的场景如下图所示。 6 | 7 | ![典型场景](../_images/typical_scenario.png) 8 | -------------------------------------------------------------------------------- /code_structure/others.md: -------------------------------------------------------------------------------- 1 | ## 其它 2 | * LICENSE 3 | * custom/ 目录下可以放一些用户自定义的 Python 文件,比如自定义的拓扑类等。 4 | * test/ 目录下是一些测试的例子。 5 | 6 | ### util/ 7 | 目录下是一些辅助文件,包括安装脚本、文档辅助生成等,重要的文件包括: 8 | * m 9 | bash 脚本提供用户直接在 host 执行命令的接口。例如 10 | ``` 11 | m host cmd args… 12 | ``` 13 | m 通过调用 mnexec 来实现对 Mininet 中的元素执行相应的命令。 14 | * mnexec 15 | C 程序,通过参数绑定到某个名字空间,并执行给定的命令。 16 | -------------------------------------------------------------------------------- /code_structure/runtime.md: -------------------------------------------------------------------------------- 1 | ## 运行相关 2 | * `bin/mn` 3 | 主运行文件,安装后执行 mn 即调用的本程序,是 Python 程序。 4 | * `mnexec.c` 执行一些快速命令,比如关闭文件描述符等,是 C 程序,编译后生成二进制文件 mnexec 被 Python 库调用。 5 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasy/mininet_book/c63d989b2d44dd88da9f3c42dc261f1e025c21bd/cover.jpg -------------------------------------------------------------------------------- /cover_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yeasy/mininet_book/c63d989b2d44dd88da9f3c42dc261f1e025c21bd/cover_small.jpg -------------------------------------------------------------------------------- /introduction/README.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | 3 | Stanford 大学 Nick McKeown 教授领导的研究小组基于 Linux Container 架构,开发出了这套进程虚拟化的平台。在 Mininet 的帮助下,你可以轻易的在自己的笔记本上测试一个软件定义网络 (Software-Defined Networks),对基于 Openflow、Open vSwitch 的各种协议等进行开发验证,或者验证自己的想法。 4 | 5 | 最令人振奋的是,所有的代码几乎可以无缝迁移到真实的硬件环境中。在实验室里,一行命令就可以创建一个支持 SDN 的任意拓扑的网络结构,并可以灵活的进行相关测试,验证了设计的正确后,又可以轻 松部署到真实的硬件环境中。目前 Mininet 已经作为官方的演示平台对各个版本的 Openflow 协议进行演示和测试。 6 | 7 | Mininet 项目的首页在 [http://www.openflowswitch.org/foswiki/bin/view/OpenFlow/Mininet](http://www.openflowswitch.org/foswiki/bin/view/OpenFlow/Mininet),目前主要维护者为 Bob Lantz 跟 Brandon Heller。讨论组地址为 [mininet-discuss@lists.stanford.edu](mininet-discuss@lists.stanford.edu)。 8 | -------------------------------------------------------------------------------- /introduction/feature.md: -------------------------------------------------------------------------------- 1 | ## 主要特性 2 | 3 | Mininet 作为一个轻量级软定义网络研发和测试平台,其主要特性包括: 4 | * 支持 Openflow、OpenvSwitch 等软定义网络部件 5 | * 方便多人协同开发 6 | * 支持系统级的还原测试支持复杂拓扑、自定义拓扑 7 | * 提供 Python API 8 | * 很好的硬件移植性(Linux 兼容),结果有更好的说服力 9 | * 高扩展性,支持超过 4096 台主机的网络结构 10 | -------------------------------------------------------------------------------- /introduction/install.md: -------------------------------------------------------------------------------- 1 | ## 安装 2 | 3 | ### 使用镜像 4 | #### 下载 5 | 官方网站已经提供了配置好相关环境的基于 Debian Lenny 的虚拟机镜像,下载地址为[http://openflowswitch.org/downloads/OpenFlowTutorial-081910.vmware.zip](http://openflowswitch.org/downloads/OpenFlowTutorial-081910.vmware.zip), 压缩包大小为 700 MB 左右,解压后大小为 2.1 GB 左右。虚拟机镜像格式为 Vmware 的 vmdk,可以直接使用 vmware workstation 或者 Virtualbox 等软件打开。如果使用 QEMU 和 KVM 则需要先进行格式转换。 6 | 7 | 如果使用 Virtualbox 进行加载,需要注意 8 | 尽量使用最新版本,host 操作系统需要支持 PAE,并在 Virtualbox 中打开 PAE 支持。 9 | 10 | #### 使用 11 | 默认用户名密码均为 openflow,建议通过本地利用 ssh 登录到虚拟机上使用(可以设置自动登录并将 X 重定向到本地),比较方便操作。 12 | 13 | 注意事项: 14 | 建议将 guest 主机采用 bridge 方式联网,以获取 host 可见的独立 IP;也可采用为 guest 配置两块网卡方式,一块采用 NAT(一般来说,guest 上看到为 eth0,IP 地址为 `10.0.2.*`,网关为 `10.0.2.2`),一块采用 host-only(guest 上的 eth1,IP 地址为`192.168.56.*`),但 host-only 的网卡可能无法自动 dhcp 到地址,需要手动配置(`ifconfig eth1 ip/mask`)将 host 机 .ssh 目录下 id_rsa.pub 复制到 guest 机的 .ssh 目录下,并写入 authorized_keys,可实现自动登陆认证。 15 | 16 | 17 | ### 本地安装 18 | 大部分发行版中已经带有该软件包,直接通过命令安装即可。例如,在 Ubuntu 系统中,执行 19 | 20 | ``` 21 | $ sudo aptitude install -y mininet 22 | ``` 23 | 24 | 也可以通过源代码安装。 25 | ``` 26 | $ git clone https://github.com/mininet/mininet.git 27 | $ cd mininet 28 | ``` 29 | 参考 INSTALL 文件中针对不同操作系统的安装步骤。 30 | -------------------------------------------------------------------------------- /module_link/README.md: -------------------------------------------------------------------------------- 1 | # mininet.link 模块 2 | 描述链路相关的接口和连接。包括 Intf 类、Link 类、TCIntf 类和 TCLink 类。 3 | -------------------------------------------------------------------------------- /module_link/intf.md: -------------------------------------------------------------------------------- 1 | ## mininet.link.Intf 2 | 表示基本的网络接口,比如 h1-eth0 表示 host 1 上的 eth0 接口。 3 | 属性包括所在的节点,名称,所接的 link,mac/ip 信息等。 4 | 构造的时候会传入节点、端口等属性,并绑定接口到对应的节点的端口上。 5 | ``` 6 | def __init__( self, name, node=None, port=None, link=None, **params ): 7 | """name: interface name (e.g. h1-eth0) 8 | node: owning node (where this intf most likely lives) 9 | link: parent link if we're part of a link 10 | other arguments are passed to config()""" 11 | self.node = node 12 | self.name = name 13 | self.link = link 14 | self.mac, self.ip, self.prefixLen = None, None, None 15 | # Add to node (and move ourselves if necessary ) 16 | node.addIntf( self, port=port ) 17 | # Save params for future reference 18 | self.params = params 19 | self.config( **params ) 20 | ``` 21 | 所支持的方法包括配置 mac/ip 等配置方法,大都是通过 ifconfig 命令在对应节点上调用cmd方法来实现。 22 | 此外,还提供了 config() 方法来一次性配置所有的属性。 23 | -------------------------------------------------------------------------------- /module_link/link.md: -------------------------------------------------------------------------------- 1 | ## mininet.link.Link 2 | 表示基本的一条链路,最基本的链路在 mininet 中其实就是一对 veth 接口对。 3 | ``` 4 | def __init__( self, node1, node2, port1=None, port2=None, 5 | intfName1=None, intfName2=None, 6 | intf=Intf, cls1=None, cls2=None, params1=None, 7 | params2=None ): 8 | """Create veth link to another node, making two new interfaces. 9 | node1: first node 10 | node2: second node 11 | port1: node1 port number (optional) 12 | port2: node2 port number (optional) 13 | intf: default interface class/constructor 14 | cls1, cls2: optional interface-specific constructors 15 | intfName1: node1 interface name (optional) 16 | intfName2: node2 interface name (optional) 17 | params1: parameters for interface 1 18 | params2: parameters for interface 2""" 19 | # This is a bit awkward; it seems that having everything in 20 | # params would be more orthogonal, but being able to specify 21 | # in-line arguments is more convenient! 22 | if port1 is None: 23 | port1 = node1.newPort() 24 | if port2 is None: 25 | port2 = node2.newPort() 26 | if not intfName1: 27 | intfName1 = self.intfName( node1, port1 ) 28 | if not intfName2: 29 | intfName2 = self.intfName( node2, port2 ) 30 | 31 | self.makeIntfPair( intfName1, intfName2 ) 32 | 33 | if not cls1: 34 | cls1 = intf 35 | if not cls2: 36 | cls2 = intf 37 | if not params1: 38 | params1 = {} 39 | if not params2: 40 | params2 = {} 41 | 42 | intf1 = cls1( name=intfName1, node=node1, port=port1, 43 | link=self, **params1 ) 44 | intf2 = cls2( name=intfName2, node=node2, port=port2, 45 | link=self, **params2 ) 46 | 47 | # All we are is dust in the wind, and our two interfaces 48 | self.intf1, self.intf2 = intf1, intf2 49 | ``` 50 | 创建链路时,需要在两个节点上分别生成两个端口,利用节点和端口,获取对应的两个网络接口的名称,例如 s1-eth0 和 h1-eth0,然后调用 makeIntfPair() 方法,最终调用 util.py 中的 makeIntfPair() 方法,调用系统中的 `ip link` 命令来创造一对 veth pair。 51 | ``` 52 | def makeIntfPair( intf1, intf2 ): 53 | """Make a veth pair connecting intf1 and intf2. 54 | intf1: string, interface 55 | intf2: string, interface 56 | returns: success boolean""" 57 | # Delete any old interfaces with the same names 58 | quietRun( 'ip link del ' + intf1 ) 59 | quietRun( 'ip link del ' + intf2 ) 60 | # Create new pair 61 | cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2 62 | return quietRun( cmd ) 63 | ``` 64 | -------------------------------------------------------------------------------- /module_link/tcintf.md: -------------------------------------------------------------------------------- 1 | ## mininet.link.TCIntf 2 | 被 TC(Linux 下的 traffic control 的工具)自定义的接口,可以配置包括带宽、延迟、丢包率、最大队列长度等参数。 3 | -------------------------------------------------------------------------------- /module_link/tclink.md: -------------------------------------------------------------------------------- 1 | ## mininet.link.TCLink 2 | 表示一对对称的 TC 接口连接到一起。 3 | -------------------------------------------------------------------------------- /module_net/README.md: -------------------------------------------------------------------------------- 1 | # mininet.net 模块 2 | 主要包括 Mininet 和 MininetWithControlNet 两个类。 3 | -------------------------------------------------------------------------------- /module_net/mininet.md: -------------------------------------------------------------------------------- 1 | ## mininet.net.Mininet 2 | 模拟一个 Mininet 中的网络,包括拓扑、交换机、主机、控制器、链路、接口等。 3 | 其中最主要的部分是 build() 函数,依次执行:根据拓扑创建网络,配置网络名字空间,配置主机的 IP、MAC 等信息,检查是否启动 xterm,是否配置自动静态 arp 等。 4 | 5 | ### __init__ 6 | ``` 7 | inNamespace=False, 8 | autoSetMacs=False, autoStaticArp=False, autoPinCpus=False, 9 | listenPort=None ): 10 | """Create Mininet object. 11 | topo: Topo (topology) object or None 12 | switch: default Switch class 13 | host: default Host class/constructor 14 | controller: default Controller class/constructor 15 | link: default Link class/constructor 16 | intf: default Intf class/constructor 17 | ipBase: base IP address for hosts, 18 | build: build now from topo? 19 | xterms: if build now, spawn xterms? 20 | cleanup: if build now, cleanup before creating? 21 | inNamespace: spawn switches and controller in net namespaces? 22 | autoSetMacs: set MAC addrs automatically like IP addresses? 23 | autoStaticArp: set all-pairs static MAC addrs? 24 | autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)? 25 | listenPort: base listening port to open; will be incremented for 26 | each additional switch in the net if inNamespace=False""" 27 | self.topo = topo 28 | self.switch = switch 29 | self.host = host 30 | self.controller = controller 31 | self.link = link 32 | self.intf = intf 33 | self.ipBase = ipBase 34 | self.ipBaseNum, self.prefixLen = netParse( self.ipBase ) 35 | self.nextIP = 1 # start for address allocation 36 | self.inNamespace = inNamespace 37 | self.xterms = xterms 38 | self.cleanup = cleanup 39 | self.autoSetMacs = autoSetMacs 40 | self.autoStaticArp = autoStaticArp 41 | self.autoPinCpus = autoPinCpus 42 | self.numCores = numCores() 43 | self.nextCore = 0 # next core for pinning hosts to CPUs 44 | self.listenPort = listenPort 45 | 46 | self.hosts = [] 47 | self.switches = [] 48 | self.controllers = [] 49 | 50 | self.nameToNode = {} # name to Node (Host/Switch) objects 51 | 52 | self.terms = [] # list of spawned xterm processes 53 | 54 | Mininet.init() # Initialize Mininet if necessary 55 | 56 | self.built = False 57 | if topo and build: 58 | self.build() 59 | ``` 60 | 该方法主要根据传入参数配置相关的数据结构。如果还通过参数执行了拓扑信息,则执行 build 方法,调用 buildFromTopo 方法来根据拓扑信息创建节点和相关的连接等。 61 | ### buildFromTopo 62 | ``` 63 | def buildFromTopo( self, topo=None ): 64 | """Build mininet from a topology object 65 | At the end of this function, everything should be connected 66 | and up.""" 67 | 68 | # Possibly we should clean up here and/or validate 69 | # the topo 70 | if self.cleanup: 71 | pass 72 | 73 | info( '*** Creating network\n' ) 74 | 75 | if not self.controllers and self.controller: 76 | # Add a default controller 77 | info( '*** Adding controller\n' ) 78 | classes = self.controller 79 | if type( classes ) is not list: 80 | classes = [ classes ] 81 | for i, cls in enumerate( classes ): 82 | self.addController( 'c%d' % i, cls ) 83 | 84 | info( '*** Adding hosts:\n' ) 85 | for hostName in topo.hosts(): 86 | self.addHost( hostName, **topo.nodeInfo( hostName ) ) 87 | info( hostName + ' ' ) 88 | 89 | info( '\n*** Adding switches:\n' ) 90 | for switchName in topo.switches(): 91 | self.addSwitch( switchName, **topo.nodeInfo( switchName) ) 92 | info( switchName + ' ' ) 93 | 94 | info( '\n*** Adding links:\n' ) 95 | for srcName, dstName in topo.links(sort=True): 96 | src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ] 97 | params = topo.linkInfo( srcName, dstName ) 98 | srcPort, dstPort = topo.port( srcName, dstName ) 99 | self.addLink( src, dst, srcPort, dstPort, **params ) 100 | info( '(%s, %s) ' % ( src.name, dst.name ) ) 101 | 102 | info( '\n' ) 103 | ``` 104 | -------------------------------------------------------------------------------- /module_net/mininetwithcontrolnet.md: -------------------------------------------------------------------------------- 1 | ## mininet.net.MininetWithControlNet 2 | 继承自 Mininet 类,主要用于在使用用户态 datapath 的时候模拟一个控制器网络,即连接用户态的交换机和用户态的控制器。 3 | -------------------------------------------------------------------------------- /module_node/README.md: -------------------------------------------------------------------------------- 1 | # mininet.node 模块 2 | 节点模块表示网络中的基本元素(包括主机、交换机和控制器),十分关键。 3 | 4 | 其中,每个主机默认在一个单独的名字空间中,交换机和控制器都在 root 名字空间中。 5 | -------------------------------------------------------------------------------- /module_node/base.md: -------------------------------------------------------------------------------- 1 | ## 基类 2 | 3 | 即 mininet.node.Node, 4 | 表示一个基本的虚拟网络节点,是所有的网络节点的父类。 5 | 6 | 实现上其实就是在网络名字空间中的一个shell进程,可以通过各种管道进行通信。该类是模块中其他类的根本,其它类都是直接或间接继承。 7 | 8 | 节点包括名称、是否在网络名字空间、接口、端口等可能的属性。 9 | 10 | ### __init__ 11 | ``` 12 | params: Node parameters (see config() for details)""" 13 | 14 | # Make sure class actually works 15 | self.checkSetup() 16 | 17 | self.name = name 18 | self.inNamespace = inNamespace 19 | 20 | # Stash configuration parameters for future reference 21 | self.params = params 22 | 23 | self.intfs = {} # dict of port numbers to interfaces 24 | self.ports = {} # dict of interfaces to port numbers 25 | # replace with Port objects, eventually ? 26 | self.nameToIntf = {} # dict of interface names to Intfs 27 | 28 | # Make pylint happy 29 | ( self.shell, self.execed, self.pid, self.stdin, self.stdout, 30 | self.lastPid, self.lastCmd, self.pollOut ) = ( 31 | None, None, None, None, None, None, None, None ) 32 | self.waiting = False 33 | self.readbuf = '' 34 | 35 | # Start command interpreter shell 36 | self.startShell() 37 | ``` 38 | 初始化函数主要进行参数的初始化,之后通过调用 startShell() 启动一个 shell进程(该进程默认关闭描述符,并从 tty 上分离开来),等待接受传入的命令。句柄被发送给 self.shell 上。 39 | 40 | ### addIntf 41 | ``` 42 | def addIntf( self, intf, port=None ): 43 | """Add an interface. 44 | intf: interface 45 | port: port number (optional, typically OpenFlow port number)""" 46 | if port is None: 47 | port = self.newPort() 48 | self.intfs[ port ] = intf 49 | self.ports[ intf ] = port 50 | self.nameToIntf[ intf.name ] = intf 51 | debug( '\n' ) 52 | debug( 'added intf %s:%d to node %s\n' % ( intf, port, self.name ) ) 53 | if self.inNamespace: 54 | debug( 'moving', intf, 'into namespace for', self.name, '\n' ) 55 | moveIntf( intf.name, self ) 56 | ``` 57 | 添加一个接口(比如 `` )到节点上,如果给定了 port(比如 0 ),则建立端口到接口的映射关系。这个映射关系通过 self.intfs 和 self.ports 两个字典来分别维护。 58 | 59 | ### cmd 60 | 该函数能在节点所在的进程shell上执行输入的命令。 61 | ``` 62 | def cmd( self, *args, **kwargs ): 63 | """Send a command, wait for output, and return it. 64 | cmd: string""" 65 | verbose = kwargs.get( 'verbose', False ) 66 | log = info if verbose else debug 67 | log( '*** %s : %s\n' % ( self.name, args ) ) 68 | self.sendCmd( *args, **kwargs ) 69 | return self.waitOutput( verbose ) 70 | ``` 71 | 72 | ### config 73 | 配置 MAC,IP 或 default Route 信息。 74 | ``` 75 | def config( self, mac=None, ip=None, 76 | defaultRoute=None, lo='up', **_params ): 77 | """Configure Node according to (optional) parameters: 78 | mac: MAC address for default interface 79 | ip: IP address for default interface 80 | ifconfig: arbitrary interface configuration 81 | Subclasses should override this method and call 82 | the parent class's config(**params)""" 83 | # If we were overriding this method, we would call 84 | # the superclass config method here as follows: 85 | # r = Parent.config( **_params ) 86 | r = {} 87 | self.setParam( r, 'setMAC', mac=mac ) 88 | self.setParam( r, 'setIP', ip=ip ) 89 | self.setParam( r, 'setDefaultRoute', defaultRoute=defaultRoute ) 90 | # This should be examined 91 | self.cmd( 'ifconfig lo ' + lo ) 92 | return r 93 | ``` 94 | 95 | ### connectionsTo 96 | 返回所有从自身连接到给定节点的接口,即 [ intf1, intf2... ]。 97 | ``` 98 | def connectionsTo( self, node): 99 | "Return [ intf1, intf2... ] for all intfs that connect self to node." 100 | # We could optimize this if it is important 101 | connections = [] 102 | for intf in self.intfList(): 103 | link = intf.link 104 | if link: 105 | node1, node2 = link.intf1.node, link.intf2.node 106 | if node1 == self and node2 == node: 107 | connections += [ ( intf, link.intf2 ) ] 108 | elif node1 == node and node2 == self: 109 | connections += [ ( intf, link.intf1 ) ] 110 | return connections 111 | ``` 112 | -------------------------------------------------------------------------------- /module_node/controller.md: -------------------------------------------------------------------------------- 1 | ## 控制器类 2 | ### mininet.node.Controller 3 | 控制器基类。默认的控制器是一个参考的实现,controller。 4 | 5 | 表示一个控制器节点。包括 IP 地址、端口等。 6 | 7 | 主要方法包括启动和停止一个控制器。 8 | 9 | #### __init__ 10 | ``` 11 | def __init__( self, name, inNamespace=False, command='controller', 12 | cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1", 13 | port=6633, **params ): 14 | self.command = command 15 | self.cargs = cargs 16 | self.cdir = cdir 17 | self.ip = ip 18 | self.port = port 19 | Node.__init__( self, name, inNamespace=inNamespace, 20 | ip=ip, **params ) 21 | self.cmd( 'ifconfig lo up' ) # Shouldn't be necessary 22 | self.checkListening() 23 | ``` 24 | #### checkListening 25 | 确保系统中安装了 telnet,并且在监听端口上并没有其他控制器在监听。 26 | 27 | #### start 28 | ``` 29 | def start( self ): 30 | """Start on controller. 31 | Log to /tmp/cN.log""" 32 | pathCheck( self.command ) 33 | cout = '/tmp/' + self.name + '.log' 34 | if self.cdir is not None: 35 | self.cmd( 'cd ' + self.cdir ) 36 | self.cmd( self.command + ' ' + self.cargs % self.port + 37 | ' 1>' + cout + ' 2>' + cout + '&' ) 38 | self.execed = False 39 | ``` 40 | 该方法检查控制器程序存在,并根据传入的参数来启动它。 41 | 42 | #### stop 43 | ``` 44 | def stop( self ): 45 | "Stop controller." 46 | self.cmd( 'kill %' + self.command ) 47 | self.terminate() 48 | ``` 49 | 该方法杀死控制器进程和节点的 shell 进程,并执行清理工作。 50 | 51 | ### mininet.node.NOX 52 | 表示一个 NOX 控制器(需要系统中事先安装了 NOX)。 53 | 54 | ### mininet.node.OVSController 55 | 表示一个ovs-controller(需要系统中实现安装了 ovs-controller)。 56 | 57 | ### mininet.node.RemoteController 58 | 表示一个在 Mininet 控制外的控制器,即用户自己额外运行了控制器,此处需要维护连接的相关信息。 59 | 60 | -------------------------------------------------------------------------------- /module_node/host.md: -------------------------------------------------------------------------------- 1 | ## 主机类 2 | 包括 Host 和 CPULimitedHost 两个类。 3 | 4 | ### mininet.node.Host 5 | 表示一个主机节点,目前跟 Node 类定义相同。 6 | 在主机类上执行命令可以通过 Cmd() 或者 sendCmd() 方法,前者会等待命令的输出结果,后者会直接返回,并允许使用后续的 monitor() 来进行监视跟踪。 7 | 8 | ### mininet.node.CPULimitedHost 9 | 继承自 Host 类,通过 cgroup 工具来对 CPU 进行限制。 10 | 11 | #### __init__ 12 | ``` 13 | def __init__( self, name, sched='cfs', **kwargs ): 14 | Host.__init__( self, name, **kwargs ) 15 | # Initialize class if necessary 16 | if not CPULimitedHost.inited: 17 | CPULimitedHost.init() 18 | # Create a cgroup and move shell into it 19 | self.cgroup = 'cpu,cpuacct,cpuset:/' + self.name 20 | errFail( 'cgcreate -g ' + self.cgroup ) 21 | # We don't add ourselves to a cpuset because you must 22 | # specify the cpu and memory placement first 23 | errFail( 'cgclassify -g cpu,cpuacct:/%s %s' % ( self.name, self.pid ) ) 24 | # BL: Setting the correct period/quota is tricky, particularly 25 | # for RT. RT allows very small quotas, but the overhead 26 | # seems to be high. CFS has a mininimum quota of 1 ms, but 27 | # still does better with larger period values. 28 | self.period_us = kwargs.get( 'period_us', 100000 ) 29 | self.sched = sched 30 | self.rtprio = 20 31 | ``` 32 | -------------------------------------------------------------------------------- /module_node/switch.md: -------------------------------------------------------------------------------- 1 | ## 交换机类 2 | ### mininet.node.Switch 3 | 表示一个交换机的基类。 4 | 5 | 运行在 root 名字空间。主要包括 dpid、listenport 等属性。 6 | 7 | ### mininet.node.IVSSwitch 8 | 表示一台 indigo 交换机(需要系统中已存在)。 9 | 10 | ### mininet.node.OVSLegacyKernelSwitch 11 | 传统的 openvswitch 交换机,基于 ovs-openflowd。不推荐。 12 | 13 | ### mininet.node.OVSSwitch 14 | 表示一台 openvswitch 交换机(需要系统中已经安装并配置好 openvswitch),基于 ovs-vsctl 进行操作。目前所谓的 OVSKernelSwitch 实际上就是 OVSSwitch 15 | 16 | ### mininet.node.UserSwitch 17 | 用户态的 openflow 参考交换机,即 ofdatapath。不推荐。 18 | -------------------------------------------------------------------------------- /module_others/clean.md: -------------------------------------------------------------------------------- 1 | ## mininet.clean 模块 2 | 提供对执行 Mininet 后的清理工作,主要包括 cleanup() 函数,该函数实际上调用了 sh() 函数。 3 | 4 | cleanup() 函数主要包括清除僵尸进程,临时文件,X11 tunnel,额外的内核态 datapath,ovs datapath,ip link 等。 5 | 6 | 实现过程主要是通过调用 subprocess 模块(主要用于执行外部命令和程序)中的 Popen 类中方法来对进程发送指令。 7 | ``` 8 | def sh( cmd ): 9 | "Print a command and send it to the shell" 10 | info( cmd + '\n' ) 11 | return Popen( [ '/bin/sh', '-c', cmd ], stdout=PIPE ).communicate()[ 0 ] 12 | ``` 13 | communicate() 是 Popen 对象的一个方法,该方法会阻塞父进程,直到子进程完成。 14 | 通过指定 stdout=PIPE,可以通过 stdout 获取程序的返回值。通过列表传入要执行的命令和参数。 15 | -------------------------------------------------------------------------------- /module_others/cli.md: -------------------------------------------------------------------------------- 1 | ## mininet.cli 模块 2 | 主要包括 CLI 类,该类继承自 Python 库的 Cmd 类。 3 | 4 | 提供对 CLI 的支持,创建 Mininet 的 bash,接受通过 bash 传输的 Mininet 命令,形成可以进行交互的 Mininet 命令行环境。 5 | 6 | 主要方法包括初始化之后提供一个界面,通过 Python 库的 Cmd 类的 cmdloop() 方法不断执行输入的命令。这些命令可以是指定对某个节点进行的操作或者是对 Mininet 对象本身。 7 | 8 | 对于大部分对 Mininet 对象的操作命令 xxx,会调用 Python 库的 Cmd 类的 onecmd() 方法来执行,该方法会对应调用 do_xxx 方法。 9 | 10 | 各个 do_xxx 方法分别用于执行支持的命令。例如 do_dpctl() 方法响应用户输入 dpctl 相关命令。目前包括 dpctl,dump,EOF,exit,gterm,help,intfs,iperf,iperfudp,link,net,nodes,noecho,pingall,pingallfull,pingpair,pingparifull,px,py,quit,sh,source,time,x,xterm 等。 11 | -------------------------------------------------------------------------------- /module_others/log.md: -------------------------------------------------------------------------------- 1 | ## mininet.log 模块 2 | 利用 logging 包,主要提供进行 log 相关的功能,包括三个类:MininetLogger、Singleton、StreamHandlerNoNewline。 3 | 4 | ### mininet.log.MininetLogger 5 | 自定义的 logger 类。 6 | 提供输出 log、配置 LogLevel 功能。 7 | 8 | ### mininet.log.Singleton 9 | 软件设计模式,限定所创建的类只能有一个实例。供 MininetLogger 使用。 10 | 11 | ### mininet.log.StreamHandlerNoNewline 12 | 自动添加换行和对流进行格式化,供 MininetLogger 使用。 13 | -------------------------------------------------------------------------------- /module_others/moduledeps.md: -------------------------------------------------------------------------------- 1 | ## mininet.moduledeps 模块 2 | 定义几个对 Linux 系统中内核模块进行操作的函数,包括列出模块 lsmod,移除模块 rmmod,探测模块 modprobe 和处理模块的依赖等。 3 | -------------------------------------------------------------------------------- /module_others/term.md: -------------------------------------------------------------------------------- 1 | ## mininet.term 模块 2 | 支持 term 相关的命令,例如在主机上创建一个 xterm。实现依赖于 socat 和 xterm。 3 | -------------------------------------------------------------------------------- /module_others/util.md: -------------------------------------------------------------------------------- 1 | ## mininet.util 模块 2 | 一些辅助的方法。包括如下重要的方法。 3 | 4 | * errFail 5 | 利用 errRun(利用 popen 来在 shell 中执行命令)来执行一个命令,并且如果执行不成功则抛出异常。 6 | -------------------------------------------------------------------------------- /module_topo/README.md: -------------------------------------------------------------------------------- 1 | # mininet.topo 模块 2 | 维护网络拓扑相关的信息。 3 | 4 | 除了一些固定结构的拓扑类之外,还提供了 mininet.topolib 模块,提供用户自己创建复杂拓扑相关的库,目前仅包括一个 mininet.topolib.TreeTopo 拓扑,是个树拓扑类,给定深度和广度可以自己生成相应的标准树拓扑。 5 | 6 | -------------------------------------------------------------------------------- /module_topo/lineartopo.md: -------------------------------------------------------------------------------- 1 | ## mininet.topo.LinearTopo 2 | 表示一个线行拓扑,交换机连接成一条链,每个交换机上挂载相等个数的主机。 3 | -------------------------------------------------------------------------------- /module_topo/multigraph.md: -------------------------------------------------------------------------------- 1 | ## mininet.topo.MultiGraph 2 | 表示一个图结构。 3 | 类似于networkx中的图G(V,E)的概念。主要维护节点、边信息。 4 | 在MultiGraph中,节点就是一个序号,边则通过节点和节点所对应的连接列表中元素来表示。节点和节点的连接列表的对应关系通过字典结构来维护。 5 | 6 | ### __init__ 7 | ``` 8 | def __init__( self ): 9 | self.data = {} 10 | ``` 11 | 图结构最主要的功能就是维护一个字典。Key 是节点,value 是该节点所连接的所有的其他节点的列表。 12 | ``` 13 | add_node 14 | def add_node( self, node ): 15 | "Add node to graph" 16 | self.data.setdefault( node, [] ) 17 | ``` 18 | 添加一个节点,实际上就是添加一个 key 到 data 字典中。 19 | ``` 20 | add_edge 21 | def add_edge( self, src, dest ): 22 | "Add edge to graph" 23 | src, dest = sorted( ( src, dest ) ) 24 | self.add_node( src ) 25 | self.add_node( dest ) 26 | self.data[ src ].append( dest ) 27 | ``` 28 | 添加一条边,实际上就是添加两个节点,然后将连接信息放到 data 字典中(需要注意的是一条边的信息仅被保存了一次,即放到序号较小的节点对应的 list 中)。 29 | -------------------------------------------------------------------------------- /module_topo/singleswitch.md: -------------------------------------------------------------------------------- 1 | ## mininet.topo.SingleSwitchTopo 2 | 单个交换机上挂载若干主机,主机序号按照从小到大的顺序依次挂载到交换机的各个端口上。 3 | -------------------------------------------------------------------------------- /module_topo/singleswitchreversed.md: -------------------------------------------------------------------------------- 1 | ## mininet.topo.SingleSwitchReversedTopo 2 | 单个交换机上挂载若干主机,主机序号按照从大到小的顺序依次挂载到交换机的各个端口上。 3 | -------------------------------------------------------------------------------- /module_topo/topo.md: -------------------------------------------------------------------------------- 1 | ## mininet.topo.Topo 2 | 拓扑基类,默认的拓扑图被 multigraph 类维护,此外还包括节点、连接信息等。主要的方法就是添加节点、连接等。 3 | Topo 中一个 node 实际上就是图结构中的一个节点,一个 port 是全局维护增长的源和目的 node 所对应的序号,而连接 4 | ### __init__ 5 | ``` 6 | def __init__(self, hopts=None, sopts=None, lopts=None): 7 | """Topo object: 8 | hinfo: default host options 9 | sopts: default switch options 10 | lopts: default link options""" 11 | self.g = MultiGraph() 12 | self.node_info = {} 13 | self.link_info = {} # (src, dst) tuples hash to EdgeInfo objects 14 | self.hopts = {} if hopts is None else hopts 15 | self.sopts = {} if sopts is None else sopts 16 | self.lopts = {} if lopts is None else lopts 17 | self.ports = {} # ports[src][dst] is port on src that connects to dst 18 | ``` 19 | ### addNode 20 | ``` 21 | def addNode(self, name, **opts): 22 | """Add Node to graph. 23 | name: name 24 | opts: node options 25 | returns: node name""" 26 | self.g.add_node(name) 27 | self.node_info[name] = opts 28 | return name 29 | ``` 30 | 添加节点方法被添加主机 addHost、交换机 addSwitch、控制器 addController 等方法使用。 31 | 32 | 该方法在图上添加一个节点,然后添加对应的节点信息到拓扑的参数上。 33 | ### addPort 34 | ``` 35 | def addPort(self, src, dst, sport=None, dport=None): 36 | '''Generate port mapping for new edge. 37 | @param src source switch name 38 | @param dst destination switch name 39 | ''' 40 | self.ports.setdefault(src, {}) 41 | self.ports.setdefault(dst, {}) 42 | # New port: number of outlinks + base 43 | src_base = 1 if self.isSwitch(src) else 0 44 | dst_base = 1 if self.isSwitch(dst) else 0 45 | if sport is None: 46 | sport = len(self.ports[src]) + src_base 47 | if dport is None: 48 | dport = len(self.ports[dst]) + dst_base 49 | self.ports[src][dst] = sport 50 | self.ports[dst][src] = dport 51 | ``` 52 | addPort 会同时创建源和目标节点上的端口号信息。 53 | 拓扑中所有的 port 都被 self.ports 结构维护,其中 ports[src][dst] 表示在 src 节点上的 port,该 port 所在 link 连接到 dst 节点。 54 | 添加一个 port 就是更新了这些信息。 55 | ### addLink 56 | ``` 57 | def addLink(self, node1, node2, port1=None, port2=None, 58 | **opts): 59 | """node1, node2: nodes to link together 60 | port1, port2: ports (optional) 61 | opts: link options (optional) 62 | returns: link info key""" 63 | if not opts and self.lopts: 64 | opts = self.lopts 65 | self.addPort(node1, node2, port1, port2) 66 | key = tuple(self.sorted([node1, node2])) 67 | self.link_info[key] = opts 68 | self.g.add_edge(*key) 69 | return key 70 | ``` 71 | 添加一条连接实际上就是添加对应的两个 port,并在图上添加上边。 72 | -------------------------------------------------------------------------------- /operation/README.md: -------------------------------------------------------------------------------- 1 | # 常用操作 2 | 介绍一些常用的操作命令。 3 | -------------------------------------------------------------------------------- /operation/cmd_summary.md: -------------------------------------------------------------------------------- 1 | ## 常用命令总结 2 | * `help` 默认列出所有命令文档,后面加命令名将介绍该命令用法 3 | * `dump` 打印节点信息 4 | * `gterm` 给定节点上开启 gnome-terminal。注:可能导致 Mininet 崩溃 5 | * `xterm` 给定节点上开启 xterm 6 | * `intfs` 列出所有的网络接口 7 | * `iperf` 两个节点之间进行简单的 iperf TCP测试 8 | * `iperfudp` 两个节点之间用指定带宽 udp 进行测试 9 | * `net` 显示网络链接情况 10 | * `noecho` 运行交互式窗口,关闭回应(echoing) 11 | * `pingpair` 在前两个主机之间互 ping 测试 12 | * `source` 从外部文件中读入命令 13 | * `dpctl` 在所有交换机上用 dptcl 执行相关命令,本地为 `tcp 127.0.0.1:6634` 14 | * `link` 禁用或启用两个节点之间的链路 15 | * `nodes` 列出所有的节点信息 16 | * `pingall` 所有 host 节点之间互 ping 17 | * `py` 执行 Python 表达式 18 | * `sh` 运行外部 shell 命令 19 | * `quit/exit` 退出 20 | -------------------------------------------------------------------------------- /operation/link.md: -------------------------------------------------------------------------------- 1 | ## 链路操作 2 | 在 Mininet cli 中,使用 `link` 命令,禁用或启用某条链路,格式为 3 | ``` 4 | link node1 node2 up/down 5 | ``` 6 | 7 | 例如临时禁用 s1 跟 h2 之间的链路,可以用 8 | ``` 9 | link s1 h2 down 10 | ``` 11 | -------------------------------------------------------------------------------- /operation/mac.md: -------------------------------------------------------------------------------- 1 | ## 使用友好的 MAC 编号 2 | 默认情况下,主机跟交换机启动后分配的 MAC 地址是随机的,这在某些情况下不方便查找问题。 3 | 4 | 可以使用 `--mac` 选项,这样主机跟交换机分配到的 MAC 地址跟他们的 ID 是一致的,容易通过 MAC 地址较快找到对应的节点。 5 | -------------------------------------------------------------------------------- /operation/namespace.md: -------------------------------------------------------------------------------- 1 | ## 名字空间 2 | 默认情况下,主机节点有用独立的名字空间(namespace),而控制节点跟交换节点都在根名字空间(root namespace)中。 3 | 4 | 如果想要让所有节点拥有各自的名字空间,需要添加 `--innamespace` 参数,即启动方式为 `sudo mn --innamespace`。 5 | 6 | 注意:为了方便测试,在默认情况下,所有节点使用同一进程空间,因此,在 h2 跟 h3 或者 s1 上使用 `ps` 查看进程得到的结果是一致的,都是根名字空间中的进程信息。 7 | -------------------------------------------------------------------------------- /operation/other.md: -------------------------------------------------------------------------------- 1 | ## 其他操作 2 | 执行 `sudo mn -c` 会进行清理配置操作,适合故障后恢复。 3 | 执行 `exit` 会退出 Mininet 的 CLI,同时给出运行时间统计。 4 | `py cmd` 使用 Python 来执行 cmd。 5 | 测试 Mininet 启动后立刻关闭的时间可以用 `sudo mn --test none`。 6 | -------------------------------------------------------------------------------- /operation/start_parameter.md: -------------------------------------------------------------------------------- 1 | ## 启动参数总结 2 | 3 | * `-h, --help` 打印帮助信息 4 | * `--switch=SWITCH` 交换机类型,包括 [kernel user ovsk] 5 | * `--host=HOST` 模拟主机类型,包括 [process] 6 | * `--controller=CONTROLLER` 控制器类型,包括 [nox_dump none ref remote nox_pysw] 7 | * `--topo=TOPO,arg1,arg2,...argN` 指定自带拓扑,包括 [tree reversed single linear minimal] 8 | * `-c, --clean`清理环境 9 | * `--custom=CUSTOM` 使用自定义拓扑和节点参数 10 | * `--test=TEST` 测试命令,包括 [cli build pingall pingpair iperf all iperfudp none] 11 | * `-x, --xterms` 在每个节点上打开 xterm 12 | * `--mac` 让MAC 地址跟 DP ID 相同 13 | * `--arp` 配置所有 ARP 项 14 | * `-v VERBOSITY, --verbosity=VERBOSITY [info warning critical error debug output]` 输出日志级别 15 | * `--ip=IP` 远端控制器的IP地址 16 | * `--port=PORT` 远端控制器监听端口 17 | * `--innamespace` 在独立的名字空间内 18 | * `--listenport=LISTENPORT` 被动监听的起始端口 19 | * `--nolistenport` 不使用被动监听端口 20 | * `--pre=PRE` 测试前运行的 CLI 脚本 21 | * `--post=POST` 测试后运行的 CLI 脚本 22 | 23 | -------------------------------------------------------------------------------- /operation/test.md: -------------------------------------------------------------------------------- 1 | ## 快捷测试 2 | 除了 cli 的交互方式之外,Mininet 还提供了更方便的自动执行的快捷测试方式,其格式为 `sudo mn --test cmd`,即可自动启动并执行 cmd 操作,完成后自动退出。 3 | 4 | 例如 `sudo mn --test pingpair`,可以直接对主机连通性进行测试,`sudo mn --test iperf`启动后直接进行性能测试。用这种方式很方便直接得到实验结果。 5 | -------------------------------------------------------------------------------- /operation/topology.md: -------------------------------------------------------------------------------- 1 | ## 自定义拓扑 2 | Mininet 提供了 Python API,可以用来方便的自定义拓扑结构。 3 | 4 | 在 mininet/custom 目录下给出了几个例子。例如在 topo-2sw-2host.py 文件中定义了一个 mytopo,则可以通过 `--topo` 选项来指定使用这一拓扑,命令为 `sudo mn --custom ~/mininet/custom/topo-2sw-2host.py --topo mytopo --test pingall`。 5 | 6 | 同样的,我们可以通过下面的 Python 脚本来完成对一个2 层 tree 拓扑网络的测试。 7 | 8 | ``` 9 | from mininet.net import Mininet 10 | from mininet.topolib import TreeTopo 11 | tree4 = TreeTopo(depth=2,fanout=2) 12 | net = Mininet(topo=tree4) 13 | net.start() 14 | h1, h4 = net.hosts[0], net.hosts[3] 15 | print h1.cmd('ping -c1 %s' % h4.IP()) 16 | net.stop() 17 | ``` 18 | -------------------------------------------------------------------------------- /operation/type.md: -------------------------------------------------------------------------------- 1 | ## 指定交换机跟控制器类型 2 | 通过 `--switch` 选项跟 `--controller` 选项可以分别指定采用哪种类型的交换机跟控制器。 3 | 4 | 例如使用用户态的交换机: 5 | ``` 6 | sudo mn --switch user 7 | ``` 8 | 使用 OpenvSwitch: 9 | ``` 10 | sudo mn --switch ovsk 11 | ``` 12 | 使用 NOX pyswitch: 13 | * 首先确保 NOX 运行 14 | ``` 15 | cd $NOX_CORE_DIR 16 | ./nox_core -v -i ptcp: 17 | ``` 18 | 然后 `Ctrl-c` 杀死 NOX 进程 19 | * 然后指定 NOX 交换机 20 | ``` 21 | sudo -E mn --controller nox_pysw 22 | ``` 23 | 24 | 注意:通过`-E`选项来保持预定义的环境变量(此处为 `NOX_CORE_DIR`)。 25 | -------------------------------------------------------------------------------- /operation/xterm.md: -------------------------------------------------------------------------------- 1 | ## 使用XTerm 2 | 为了能够正确使用 xterm,我们需要做些准备工作。在这里推荐利用远程方式登录到 OpenflowVM。 3 | 4 | ### 客户端 5 | 对于自带 X 的 Linux 主机,无需配置 X。 6 | 7 | 如果客户端是 Windows 主机,需要先在 Windows 机器上安装 Xserver(Xming)。下载地址:http://sourceforge.net/projects/xming/files/Xming/6.9.0.31/Xming-6-9-0-31-setup.exe。 8 | 9 | 如果你使用 secureCRT 远程登录到 OpenflowVM,需要在“会话选项->SSH2->密钥交换”下,取消 `diffie-hellman-group14` 和 `diffie-hellman` 选择;同时在“远程/X11”下,选择转发“X11数据包(F)”;点击确定。 10 | 11 | 开启 Xming,使用如下命令远程到Openflow VM即可。 12 | ``` 13 | ssh -X openflow@[Guest IP here] 14 | ``` 15 | 如果你使用 puTTY 远程登录到 OpenflowVM: 16 | 点击“puTTY->Connection->SSH->X11”,选择“X11 forwarding->Enable X11 forwaring”;开启 Xming,点击 Windows 开始按钮,在运行栏输入“cmd”,打开终端,输入 `cd ` 切换到保存 puTTY 的目录下;使用下面的命令远程登录到 openflow VM。 17 | ``` 18 | putty.exe -X openflow@[Guest IP here] 19 | ``` 20 | 21 | ### 主机端 22 | 通过使用 `-x` 参数,Mininet 在启动后会在每个节点上自动打开一个 XTerm,方便某些情况下的对多个节点分别进行操作。命令为 23 | ``` 24 | sudo mn -x 25 | ``` 26 | 在进入 mn cli之后,也可以使用 xterm node 命令指定启动某些节点上的 xterm,例如分别启用 s1 跟 h2 上的 xterm,可以用 27 | ``` 28 | xterm s1 h2 29 | ``` 30 | -------------------------------------------------------------------------------- /runtime_and_example/README.md: -------------------------------------------------------------------------------- 1 | # 运行代码和示例 2 | -------------------------------------------------------------------------------- /runtime_and_example/baresshd.md: -------------------------------------------------------------------------------- 1 | # baresshd 2 | -------------------------------------------------------------------------------- /runtime_and_example/example.md: -------------------------------------------------------------------------------- 1 | ## 示例程序 2 | Mininet 代码中带有了大量的示例程序,供大家参考和理解代码。所有的示例程序都在 example 目录下,包括 3 | ### baresshd.py: 4 | 使用 Mininet 的中层 API 来在一个 namespace 中创建主机、链路,并在主机上启动 sshd 进程,让用户可以登录。并未使用 OpenFlow。 5 | ### consoles.py: 6 | 为每一个节点都创建一些 console 窗口,并允许用户对这些节点进行操作和观测,支持图形界面。 7 | ### controllers.py: 8 | 使用一个自定义的Switch() 子类,创建一个带有多个控制器的网络。 9 | ### controllers2.py: 10 | 创建一个拥有多个控制器的网络,通过创建空的网络,添加节点和手动启动交换机实现。 11 | ### controlnet.py: 12 | 通过创建两个mininet对象来建模一个控制网络和数据网络。 13 | ### cpu.py: 14 | 在不同的 CPU 限制下测试 iperf 的带宽性能。 15 | ### emptynet.py: 16 | 演示创建一个空的网络,之后添加节点进去。 17 | ### hwintf.py: 18 | 添加一个接口 (例如一个物理接口)到一个网络中。 19 | ### limit.py: 20 | 演示如何使用 link 和 CPU 限制。 21 | ### linearbandwidth.py: 22 | 基于 Topo 创建一个拓扑子类,并进行简单的测试。 23 | ### miniedit.py: 24 | 通过一个图形界面的编辑器来创建网络。 25 | ### multiping.py: 26 | 使用 node.monitor() 来检测多个主机的输出。 27 | ### multipoll.py: 28 | 检测多个主机的输出文件。 29 | ### multitest.py: 30 | 创建一个网络,并在其上进行多个测试。 31 | ### nat.py: 32 | 将 Mininet 的网络通过 nat 连接到外部网络中。 33 | ### popen.py: 34 | 使用 host.popen() 和 pmonitor() 来检测多个主机。 35 | ### popenpoll.py: 36 | 使用 node.popen() 和 pmonitor() 检测多个主机的输出。 37 | ### scratchnet.py, scratchnetuser.py: 38 | 使用底层的 Mininet 函数来创建网络。 39 | ### simpleperf.py: 40 | 配置网络和 CPU、带宽限制等。 41 | ### sshd.py: 42 | 在每个主机里面运行一个 sshd 进程,使得用户可以通过 ssh 来访问主机。这需要将 Mininet 的数据网络连接到 root 名字空间的一个接口上。一般的,控制网络已经在 root 名字空间了,所以默认已经被连接。 43 | ### tree1024.py: 44 | 创建一个 1024 主机的网络,然后运行 CLI。根据系统资源情况,可能需要利用 sysctl 进行相关修改。 45 | ### treeping64.py: 46 | 创建一个 64 主机的树状网络,利用 ping 来检查连通性。 47 | -------------------------------------------------------------------------------- /runtime_and_example/mn.md: -------------------------------------------------------------------------------- 1 | ## mn 2 | 该脚本定义了一个 MininetRunner 类,用来表示模拟网络的主程序。 3 | 4 | 主要过程是创建一个 MininetRunner() 实例,依次解析传入参数,进行初始化后开启网络。 5 | 6 | 整体过程如下图所示。 7 | 8 | ![mn 脚本主要过程](../_images/mn.png) 9 | 10 | 其中 Mininet 类的 start() 方法是核心的启动过程,主要包括调用 build 方法来根据拓扑创建网络、控制器、交换机、主机和连接等。之后依次启动控制器和交换机进程。 11 | 在执行完 start() 之后,通过 test 参数来判断 mininet 运行的模式。 12 | ``` 13 | if test == 'none': 14 | pass 15 | elif test == 'all': 16 | mn.start() 17 | mn.ping() 18 | mn.iperf() 19 | elif test == 'cli': 20 | CLI( mn ) 21 | elif test != 'build': 22 | getattr( mn, test )() 23 | ``` 24 | 默认情况下,参数为 cli,即进入到控制台模式,允许用户自己输入对 Mininet 的操作命令。 25 | 最终执行 mininet.stop() 进行删除资源的工作。 26 | --------------------------------------------------------------------------------