学习SDN开发工具

发布于:2022-11-11 ⋅ 阅读:(573) ⋅ 点赞:(0)


申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址
全文共计12677字,阅读大概需要5分钟
欢迎关注我的个人公众号:不懂开发的程序猿

学习SDN开发工具

OpenFlow 的调试工具

  • SSH终端:连接到 OpenFlowTutorial。
  • xterm终端:连接到虚拟网络中的主机,将在顶部标有主机名。
  • OpenFlow 控制器:位于 OpenFlow 接口之上。该控制器与 OpenFlow 交换机结合用作以太网学习交换机。将运行它并查看正在发送的消息。
  • OpenFlow 交换机:位于 OpenFlow 接口下方。OpenFlow。Open vSwitch 是另一种基于内核的软件交换机
  • ovs-ofctl:发送快速 OpenFlow 消息的命令行实用程序,对于查看交换机端口和流统计信息或手动插入流条目很有用。
  • Wireshark:用于查看数据包的通用图形实用程序。OpenFlow 参考分布包括一个 Wireshark 解析器,它以一种方便可读的方式解析发送到 OpenFlow 默认端口 (6633) 的 OpenFlow 消息。
  • iperf:用于测试单个 TCP 连接速度的通用命令行实用程序。
  • Mininet:网络仿真平台。Mininet 在单个真实或虚拟机上创建虚拟 OpenFlow 网络——控制器、交换机、主机和链接。
  • cbench:用于测试 OpenFlow 控制器的流设置速率的实用程序。

启动网络

网络拓扑:3 台主机和一个交换机,还有一个 OpenFlow 控制器,

在这里插入图片描述

在 VM 中创建此网络,在 SSH 终端中输入:

sudo mn --topo single,3 --mac --switch ovsk --controller remote

在这里插入图片描述

这告诉 Mininet 启动一个 3 主机、单(基于 openvSwitch)交换机的拓扑结构,将每个主机的 MAC 地址设置为其 IP,并指向一个默认为 localhost 的远程控制器。

这是 Mininet 刚刚所做的:

1)创建了 3 个虚拟主机,每个都有一个单独的 IP 地址。

2)在内核中创建了一个具有 3 个端口的 OpenFlow 软件交换机。

3)使用虚拟以太网电缆将每个虚拟主机连接到交换机。

4)将每个主机的 MAC 地址设置为其 IP。

5)配置OpenFlow交换机连接远程控制器。

ovs-ofctl用法

ovs-ofctl是 Open vSwitch 附带的实用程序,可实现对单个交换机的流表的可见性和控制。通过查看流状态和流计数器,它对于调试特别有用。大多数 OpenFlow 交换机可以使用被动侦听端口启动,可以从该端口轮询交换机,而无需向控制器添加调试代码。

创建第二个 SSH 窗口,运行:

sudo ovs-ofctl show s1

在这里插入图片描述

输入:sudo ovs-ofctl dump-flows s1

查看流表

在这里插入图片描述

由于我们还没有启动任何控制器,流表应该是空的。

ping测试

现在,回到 mininet 控制台并尝试从 h1 ping h2。在 Mininet 控制台中:

h1 ping -c3 h2

在这里插入图片描述

请注意,在 Mininet 控制台中运行命令时,主机 h2 的名称会自动替换为其 IP 地址 (10.0.0.2)。

正如之前看到的,交换机流表是空的。除此之外,没有控制器连接到交换机,因此交换机不知道如何处理传入的流量,从而导致 ping 失败。

将使用ovs-ofctl手动安装必要的流程。在的 SSH 终端中:

ovs-ofctl add-flow s1 in_port=1,actions=output:2

ovs-ofctl add-flow s1 in_port=2,actions=output:1

这会将来自端口 1 的数据包转发到端口 2,反之亦然。通过检查流表来验证

ovs-ofctl dump-flows s1

在这里插入图片描述

再次运行 ping 命令。在的 mininet 控制台中:

h1 ping -c3 h2

在这里插入图片描述

再次检查流表并查看每个流条目的统计信息。根据 ping 流量,可以看到已经ping通了

启动 Wireshark

VM 映像包括预安装的 OpenFlow Wireshark。Wireshark 对于查看 OpenFlow 协议消息以及一般调试非常有用。

打开Wireshark命令:

sudo wireshark &

如果出错就尝试:

sudo -E wireshark &

单击菜单栏中的Capture->Interfaces。通过在靠近顶部的过滤器框中键入“of”,为 OpenFlow 控制流量设置过滤器:

在 Wireshark 中启动 Controller 并查看启动消息

第一步 mininet 并清理

exit
mn -c

现在,在 Wireshark 解析器监听的情况下,启动 OpenFlow 参考控制器。在的 SSH 终端中输入

sudo controller ptcp:6633

将启动一个简单的控制器,该控制器充当学习开关,而无需安装任何流条目。

现在重新启动拓扑

sudo mn --topo single,3 --mac --switch ovsk --controller remote

在这里插入图片描述

这些消息包括:

信息 类型 描述
Hello 控制器->开关 在 TCP 握手之后,控制器将其版本号发送到交换机。
Hello 开关->控制器 交换机回复其支持的版本号
功能请求 控制器->开关 控制器要求查看哪些端口可用
设置配置 控制器->开关 在这种情况下,控制器要求交换机发送流到期
特色回复 开关->控制器 交换机回复端口列表、端口速度以及支持的表和操作
端口状态 开关->控制器 使交换机能够通知该控制器端口速度或连接的变化

由于使用 Mininet 时所有消息都是通过本地主机发送的,因此当有很多模拟开关时,确定消息的发送者可能会令人困惑。但是,这不会成为问题,因为我们只有一个开关。控制器位于标准 OpenFlow 端口 (6633),而交换机位于其他用户级端口。

查看用于 Ping 的 OpenFlow 消息

现在,将查看响应数据包而生成的消息。

建议清理两台主机上的 ARP 缓存,否则可能看不到一些 ARP 请求/回复,因为将使用缓存:

h1 ip -s -s neigh flush all

h2 ip -s -s neigh flush all

在 Mininet 控制台中执行 ping:

h1 ping -c1 h2

在这里插入图片描述

在 Wireshark 窗口中,应该会看到许多新的消息类型:

在这里插入图片描述

信息 类型 描述
分组输入 开关->控制器 收到一个数据包,但它与交换机流表中的任何条目都不匹配,导致数据包被发送到控制器
出包 控制器->开关 控制器从一个或多个交换机端口发送数据包
Flow-Mod 控制器->开关 指示交换机将特定流添加到其流表中
流量过期 开关->控制器 流在一段时间不活动后超时

首先,会看到一个 ARP 请求错过了流表,它会生成一个广播 Packet-Out 消息。接下来,ARP响应回来了;由于控制器现在知道这两个 MAC 地址,它可以使用 Flow-Mod 消息将流下推到交换机。然后交换机会推送 ICMP 数据包的流。后续的 ping 请求直接通过数据路径,不会产生额外的消息;由于连接 h1 和 h2 的流已经推送到交换机,因此没有控制器参与。

带 iperf 的基准控制器

iperf是一个命令行工具,用于检查两台计算机之间的速度。

在 mininet 控制台中运行:

iperf

在这里插入图片描述

这个 Mininet 命令在一个虚拟主机上运行一个 iperf TCP 服务器,然后在第二个虚拟主机上运行一个 iperf 客户端。一旦连接,它们就会在彼此之间发送数据包并报告结果。

现在在Mininet控制台中:

exit

使用用户空间开关启动同一个 Mininet:

mn --topo single,3 --controller remote --switch user

使用参考控制器再运行一个 iperf 测试:

iperf

在这里插入图片描述

区别:使用用户空间交换机,数据包必须从用户空间跨越到内核空间,然后在每一跳上返回,而不是在通过交换机时留在内核中。用户空间开关更容易修改(无需处理内核oops),但模拟速度较慢。

路由器练习

本练习中,将创建一个静态的第 3 层转发器/交换机。它不完全是路由器,因为它不会减少 IP TTL 并在每个跃点重新计算校验和(因此 traceroute 不起作用)。

IP 路由器与其他类型的数据包交换设备的区别在于,路由器将检查 IP 协议报头作为交换过程的一部分。它通常删除接收消息的链路层报头,修改 IP 报头,并替换链路层报头以进行重传。

为了简化练习,路由器将是完全静态的。由于没有 BGP 或 OSPF,将无需发送或接收路由表更新。

每个网络节点都有一个已配置的子网。如果数据包的目的地是该子网中的主机,则该节点充当交换机,将数据包原封不动地转发到已知端口或广播。如果数据包的目的地是路由器知道下一跳的某个 IP 地址,则应该修改第 2 层目的地并将数据包转发到正确的端口。

这个练习将表明,使用支持 OpenFlow 的转发设备,网络实际上是无层的;可以混合使用交换机、路由器和更高层的功能。

创建拓扑

在这里插入图片描述

代码如图

在这里插入图片描述

首先,将系统给出的示例代码复制到一个新文件中:

cp ~/mininet/custom/topo-2sw-2host.py mytopo.py

要运行自定义拓扑,将自定义文件传递给 Mininet 并传入自定义拓扑:

sudo mn --custom mytopo.py --topo mytopo --mac

然后,在 Mininet 控制台中,运行:

pingall

现在,修改的拓扑文件以匹配图片并在 Mininet 控制台中使用 pingall 验证完整的主机连接。

设置主机

在每个虚拟主机上设置 IP 配置,以强制每个虚拟主机向网关发送位于其配置子网之外的目标 IP。

需要为每个主机配置子网、IP、网关和网络掩码。

这似乎很明显,但无论如何我们都会警告:不要尝试为属于交换机 s1 和 s2 的接口分配 IP 地址。如果需要处理“到”或“来自”交换机的流量,请使用 OpenFlow。

我们可以直接从使用 mininet 创建的自定义拓扑中执行此操作。编辑的“mytopo.py”以包含上述信息。

有用的代码:

host1 = self.addHost( 'h1', ip="10.0.1.100/24", defaultRoute = "via 10.0.1.1" )

路由器通常必须响应 ARP 请求。将看到以太网广播,这些广播将(至少最初)转发到控制器。

控制器应该构建 ARP 回复并将它们转发到适当的端口。

需要的结构:

* arp缓存

* 路由表(创建一个静态分配的所有信息的结构)

* ip 到端口字典

* 消息队列(当路由器等待 ARP 回复时)

示例路由表:

(带有网络前缀的ip、主机ip、接口名称、接口地址、交换机端口)

[‘10.0.1.100/24’, ‘10.0.1.100’, ‘s1-eth1’, ‘10.0.1.1’, 1],

[‘10.0.2.100/24’, ‘10.0.2.100’, ‘s1-eth2’, ‘10.0.2.1’, 3],

[‘10.0.3.100/24’、‘10.0.3.100’、‘s1-eth3’、‘10.0.3.1’、2]

ARP结构包含:

- hwdst(目的地的硬件地址 - 我们要求的)

- hwsrc(源的硬件地址)

- protodst(目的地的IP地址)

- protosrc(源IP地址)

- 操作码(arp 包的类型[REQUEST, REPLY, REV_REQUEST, REV_REPLY])

静态路由

处理完 ARP 后,将需要处理静态配置的路由。由于我们知道每个端口连接的是什么,我们可以匹配 IP 地址(或前缀,在远程子网的情况下)并转发出适当的端口。

我们需要通过将其转发到正确的子网来处理通过路由器的所有 ipv4 流量。数据包中唯一的变化应该是源和目标 MAC 地址。

如果路由器不知道目的地的 MAC 地址,它将必须为该主机进行 arp。

ICMP

此外,的控制器可能会收到对路由器的 ICMP 回显 (ping) 请求,它应该对此做出响应。

最后,不可达子网的数据包应该用 ICMP 网络不可达消息进行响应。

注意:控制器默认只接受 128 字节的 packet_ins。如果的消息被截断,这可能是原因。作为一种解决方法,在调用结束时也添加另一个组件作为控制器。它应该是这样的:

测试路由器

如果路由器正常工作:

尝试从 10.0.1.2 发送到未知地址范围(如 10.99.0.1)应该会产生ICMP 目标不可达消息。

发送到已知地址范围内的主机的数据包应将其 MAC dst 字段更改为下一跳路由器的 MAC dst 字段。

路由器应该可以 ping 通,并且应该生成 ICMP 回显回复以响应 ICMP 回显请求。

现在运行 iperf 来测试 tcp 和 udp 流量。在 mininet 中,在主机 1 和主机 3 中打开 xterm

mininet>xterm h1 h3

主机 3 将是 iperf 服务器,因此在它自己的终端中运行命令

$ iperf -s

主机 1 将是客户端,因此在它自己的终端中运行命令

$ iperf -c (iperf 服务器的ip地址),我这里是10.0.0.2

在这里插入图片描述

还可以通过在两个命令的末尾添加 -u 选项来测试 udp 流量

在这里插入图片描述

请注意此测试的结果。在代码的当前状态下,路由器通过 packet_ins 将所有流量发送到控制器,控制器做出路由决策并将 packet_out 发送到路由器。下一步是安装 flow mods,这应该会产生更好的 iperf 性能。

了解 Mininet API

在此介绍的过程中,已经暴露了一些Python类的包含Mininet的API,包括类如Topo,Mininet,Host,Switch,Link和它们的子类。将这些类划分为级别(或层)很方便,因为通常高级 API 是使用低级 API 构建的。

Mininet 的 API 构建在三个主要级别:

  • 低级别的API:低级API包括基节点和链接的类的(例如Host,Switch和Link和它们的亚类),其实际上可以单独实例化并用于创建网络,但它是一个有点笨拙。

  • 中级 API:中级 API 添加了Mininet对象,作为节点和链接的容器。它提供了许多方法(例如addHost()、addSwitch()、 和addLink())用于向网络添加节点和链接,以及网络配置、启动和关闭(特别是start()和 )stop()。

  • 高级 API:高级 API 添加了拓扑模板抽象Topo类,该类提供了创建可重用、参数化拓扑模板的能力。这些模板可以传递给mn命令(通过–custom选项)并从命令行使用。

了解每个 API 级别很有价值。一般来说,当想直接控制节点和交换机时,使用低级 API。当想要启动或停止网络时,通常使用中级 API(特别是Mininet类)。

当开始考虑创建完整网络时,事情就会变得有趣。可以使用任何 API 级别(如示例中所示)创建完整网络,但通常会想要选择中级 API(例如Mininet.add*())或高级 API ( Topo.add*()) 来创建网络。

以下是使用每个 API 级别创建网络的示例:

低级 API:节点和链接

h1 = Host( 'h1' )                                                   

h2 = Host( 'h2' )                                                  

s1 = OVSSwitch( 's1', inNamespace=False )                                       

c0 = Controller( 'c0', inNamespace=False )                                      

Link( h1, s1 )                                                    

Link( h2, s1 )                                                    

h1.setIP( '10.1/8' )                                                 

h2.setIP( '10.2/8' )                                                 

c0.start()                                                      

s1.start( [ c0 ] )                                                  

print( h1.cmd( 'ping -c1', h2.IP() ) )

s1.stop()                                                      

c0.stop()

中级 API:网络对象

net = Mininet()

h1 = net.addHost( 'h1' )

h2 = net.addHost( 'h2' )

s1 = net.addSwitch( 's1' )

c0 = net.addController( 'c0' )

net.addLink( h1, s1 )

net.addLink( h2, s1 )

net.start()

print( h1.cmd( 'ping -c1', h2.IP() ) )

CLI( net )

net.stop()

高级 API:拓扑模板

class SingleSwitchTopo( Topo ):                                                

  "Single Switch Topology"                                                 

  def build( self, count=1 ):                                           

    hosts = [ self.addHost( 'h%d' % i )                                         

        for i in range( 1, count + 1 ) ]                                        

     s1 = self.addSwitch( 's1' )                                             

    for h in hosts:                                                   

      self.addLink( h, s1 )                                              

 

net = Mininet( topo=SingleSwitchTopo( 3 ) )                                       

net.start()                                                       

CLI( net )                                                         

net.stop()  

如所见,中级 API 对于此示例实际上是最简单和最简洁的,因为它不需要创建拓扑类。低级和中级 API 灵活且功能强大,但与高级TopoAPI 及其拓扑模板相比,重用可能不太方便。

另请注意,在 2.2.0 之前的 Mininet 版本中,高层Topo不支持节点之间的多个链接,但低层 API 支持。目前Topo也不关心哪些开关由哪些控制器控制(可以使用自定义Switch子类来执行此操作,如上所述。)使用中级和低级 API,可以根据需要手动启动开关,将适当的控制器列表传递给每个交换机。

Mininet API 文档

Mininet 包括每个模块和 API 调用的 Python 文档字符串。这些可以从 Python 的常规help()机制中访问。例如,

    python

    >>> from mininet.node import Host

    >>> help(Host.IP)

   Help on method IP in module mininet.node:

    

    IP(self, intf=None) unbound mininet.node.Host method

       Return IP address of a node or specific interface.

 

同样的文档也可以在位于http://api.mininet.org的 Mininet 网站上找到 。

可能希望使用doxypy以下方法自己生成 HTML(和 PDF)文档:

sudo apt-get install doxypy

cd ~/mininet

make doc

cd doc

python -m SimpleHTTPServer

此时,可以将 Web 浏览器指向运行 Mininet 的主机的端口 8000,并浏览每个 Mininet 类的文档。

衡量绩效

这些是推荐的,尽管可以自由使用熟悉的任何工具。

带宽 ( bwm-ng, ethstats)

延迟(使用ping)

队列(tc包含在 中的使用monitor.py)

TCPCWND统计信息(tcp_probe,也许我们应该将其添加到monitor.py)

CPU 使用率(全局:top,或 per-container cpuacct)

OpenFlow 和自定义路由

Mininet 最强大和最有用的功能之一是它使用 软件定义网络。使用OpenFlow协议和相关工具,可以对交换机进行编程,以对进入它们的数据包执行几乎任何想做的事情。OpenFlow 使像 Mininet 这样的模拟器更加有用,因为网络系统设计,包括使用 OpenFlow 的自定义数据包转发,可以很容易地转移到硬件 OpenFlow 交换机以进行线速操作。可以在以下位置找到使用 Mininet 和 OpenFlow 创建简单学习交换机的教程:

开放流控制器

如果在mn未指定控制器的情况下运行该命令,它将根据可用的控制器选择一个默认控制器,例如Controller或OVSController。

这相当于:

$ sudo mn --controller default 

该控制器实现了一个简单的以太网学习交换机,最多支持 16 个单独的交换机。

如果Mininet()在脚本中调用构造函数而未指定控制器类,默认情况下它将使用 Controller()该类来创建斯坦福/OpenFlow 参考控制器的实例controller。就像ovs-controller,它把的开关变成了简单的学习开关,但是如果已经controller使用 Mininet 的 install.sh -f脚本安装,补丁版本controller应该支持大量的开关(理论上最多 4096,但可能会更早地最大化的计算资源.) 也可以mn通过指定来选择参考控制器–controller ref。

如果想使用自己的控制器,可以很容易地创建一个自定义的子类Controller()并将其传递给 Mininet。可以在 中看到一个示例mininet.controller.NOX(),它使用一组作为选项传入的模块来调用 NOX classic。

这是创建和使用自定义 POXController子类的简单示例:

#!/usr/bin/python                                           from mininet.net import Mininet                                    

from mininet.node import Controller                                  

from mininet.topo import SingleSwitchTopo                               

from mininet.log import setLogLevel                                  
import os                                                                                 
class POXBridge( Controller ):                                     

  "Custom Controller class to invoke POX forwarding.l2_learning"                   

  def start( self ):                                        

    "Start POX learning switch"                                  

    self.pox = '%s/pox/pox.py' % os.environ[ 'HOME' ]                       

    self.cmd( self.pox, 'forwarding.l2_learning &' )                        

  def stop( self ):                                         

    "Stop POX"                                          

    self.cmd( 'kill %' + self.pox )                                

                                                   

controllers = { 'poxbridge': POXBridge }                                
                                                   

if __name__ == '__main__':                                      

  setLogLevel( 'info' )                                       

  net = Mininet( topo=SingleSwitchTopo( 2 ), controller=POXBridge )                 

  net.start()                                            

  net.pingAll()                                           

  net.stop()   

请注意,上述脚本的编写使其也可以用作自定义参数,以mn用于不同的拓扑和测试以及 Mininet CLI:

$ sudo mn --custom poxbridge.py --controller poxbridge --topo tree,2,2 --test pingall -v output

*** Ping: testing ping reachability

h1 -> h2 h3 h4

h2 -> h1 h3 h4

h3 -> h1 h2 h4

h4 -> h1 h2 h3

*** Results: 0% dropped (0/12 lost)

如果查看 中的NOX类的实现mininet/node.py,会注意到它实际上可以接受选项以允许启动不同的模块,具体取决于从构造函数或mn命令行传递给它的参数。

外部 OpenFlow 控制器

自定义Controller()子类是自动启动和关闭控制器的最方便的方法。它很容易创建start()和stop()方法,以便 Mininet 会根据需要自动启动和停止的控制器。

但是,可能会发现将 Mininet 连接到已经在其他地方运行的现有控制器很有用,例如在的 LAN、另一个 VM 或的笔记本电脑上的某个地方。

本RemoteController类作为可运行控制网络上任何地方的控制器的代理,但是必须启动和手动关闭或Mininet直接控制的其他一些机制之外。

可以使用RemoteController从Mininet:

from functools import partial

net = Mininet( topo=topo, controller=partial( RemoteController, ip='127.0.0.1', port=6633 ) )

或者

net = Mininet( topo=topo, controller=lambda name: RemoteController( name, ip='127.0.0.1' ) )

甚至

net = Mininet( topo=topo, controller=None)

net.addController( 'c0', controller=RemoteController, ip='127.0.0.1', port=6633 )

请注意,在这种情况下controller(如hostand switch)是一个构造函数,而不是一个对象(但有关其他信息,请参见下文!)可以使用partialor 内联创建自定义构造函数lambda,或者可以传入自己的函数(必须采用name参数并返回控制器对象)或类(例如 . 的子类RemoteController)

还可以创建多个控制器并创建一个自定义Switch()子类,根据需要连接到不同的控制器:

c0 = Controller( 'c0' ) # local controller

c1 = RemoteController( 'c1', ip='127.0.0.2' ) # external controller

cmap = { 's1': c0, 's2': c1, 's3': c1 }

 

class MultiSwitch( OVSSwitch ):

  "Custom Switch() subclass that connects to different controllers"

  def start( self, controllers ):

    return OVSSwitch.start( self, [ cmap[ self.name ] ] )

还可以mn从命令行指定外部控制器:

$ sudo mn --controller remote,ip=192.168.51.101

通过传入控制器对象来滥用 API

在 Mininet 2.2.0 及更高版本中,可以选择传入一个Controller 对象而不是构造函数(甚至是一个对象列表。)这是添加的,因为尽管 API 明确指定需要构造函数,但人们仍然这样做.

这允许执行以下操作:

net = Mininet( topo, controller=RemoteController( 'c0', ip='127.0.0.1' ) )

并获得想要的行为。仍然允许构造函数。

多路径路由

重要的是要记住以太网网桥(也称为学习交换机)会淹没在其 MAC 表中丢失的数据包。它们还会泛洪广播,如 ARP 和 DHCP 请求。这意味着,如果的网络中存在环路或多条路径,它将无法与默认ovs-controller和controller 控制器、NOXpyswitch和 POX 一起使用l2_learning,它们都充当学习交换机/以太网桥接器。

尽管这个问题很明显,但它已成为一个常见问题。

更新(和更复杂)的 OpenFlow 控制器确实支持多路径路由 - 请查阅控制器文档以确定是否需要任何特殊配置。

如果正在构建类似树的拓扑结构,可能希望查看 RipLPOX,这是一个使用 POX 实现的基本数据中心控制器。可以将其用作自己的自定义多路径路由的起点。

Controller()为了方便起见,可能还希望实现自定义子类来调用 RipLPOX。

–end–

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到