这可能是我在Stack Overflow上写的最长的帖子之一。我一直在学习OpenFlow,SDN和Ryu,并希望在这里为初学者记录我的知识。如果需要,请更正/编辑我的帖子。
本简短指南假设您已经掌握了计算机网络和主要网络协议的知识。本指南将帮助您从系统设置开始使用OpenFlow。
1.什么是OpenFlow和SDN?
请阅读
SDN / OpenFlow | Flowgrammable
。
进一步阅读:
网络的未来,以及Scott Shenker和软件定义网络协议的过去,IEEE INFOCOM 2009
。
在你开始之前:
Infrastructure层包括网络核心内的路由器和交换机。
控制层包括运行OpenFlow控制器的PC以及控制器本身。
Application层包括在该控制器之上运行的应用程序。在Ryu中,这些应用程序是用Python编写的。
OpenFlow是基础结构和控制层交互的协议。 OpenFlow不提供自身的API。它是一种开源协议,供开发支持OpenFlow的交换机的供应商和编写控制器的开发人员(如Ryu)使用。 API由控制器提供。
2.在Debian 8上设置Ryu OpenFlow控制器
的
先决条件
</强>
你需要上网。如果您在虚拟机中运行Debian,请发出以下命令以通过NAT自动配置以太网接口:
su
dhclient eth0
</code>
的
启用sudo
</强>
Debian默认不带sudo。您稍后将使用的一些Ryu应用程序需要sudo。您可以安装sudo并将自己添加到sudo’ers列表,如下所示:
su
apt-get install sudo # you might need to do apt-get update first!
nano /etc/sudoers
</code>
找到%sudo ALL =(ALL:ALL)ALL的行并在其下方添加一个条目:
yourusername ALL=(ALL:ALL) ALL
</code>
按CTRL + X,然后按Y将更改保存到sudoers文件。现在您可以以root身份注销以返回到您自己的shell
exit
</code>
的
启用最佳屏幕分辨率(仅限VM)
</强>
如果您在Virtual Box中运行Debian,则默认安装不会启用Virtual Box的全屏分辨率支持。稍后您将需要在第3节中使用更大的屏幕。现在启用它是个好主意。
在虚拟机的窗口中,单击设备&gt;插入访客添加CD图像…
然后cd到包含文件的目录
cd /media/cdrom
</code>
由于权限问题,Debian不允许您运行脚本。将文件复制到主目录,更改权限,然后运行它:
mkdir ~/VBOXGUEST
cp ~/VBOXGUEST
cd ~/VBOXGUEST
chmod 755
sudo ./VBoxLinuxAdditions.run
</code>
重启
sudo shutdown -r now
</code>
的
安装Git
</强>
sudo apt-get install git
</code>
的
安装Mininet
</强>
Mininet允许您虚拟模拟笔记本电脑/ PC上的各种网络接口。使用Git安装它:
cd ~ # if you are in some other directory
git clone git://github.com/mininet/mininet
cd mininet
git tag # this will list available versions
git checkout -b 2.2.1 2.2.1 # replace 2.2.1 with the version you wish to install
cd ..
mininet/util/install.sh -a # default installation, includes all components, recommended
</code>
我建议你安装OpenFlow Wireshark Dissector。您可以稍后安装Wireshark来分析数据包。 OpenFlow Wireshark Dissector帮助Wireshark从OpenFlow数据包中获取尽可能多的信息。
mininet/util/install.sh -h
</code>
运行以下命令以检查mininet安装:
sudo mn —test pingall
</code>
的
安装Ryu OpenFlow控制器
</强>
OpenFlow控制器使用OpenFlow协议在控制层和Infrastructure层之间进行通信。此外,它是控制器,它提供API来开发在应用层(在控制层之上)运行的SDN应用程序。有许多OpenFlow控制器。 Ryu OpenFlow控制器是一个使用Python脚本作为其应用程序的控制器。再次,使用Git安装它:
cd ~
git clone git://github.com/osrg/ryu.git
</code>
的
安装Wireshark
</强>
sudo apt-get install wireshark
</code>
的
安装支持的Python模块
</强>
Debian 8.3默认安装了Python 2.7和3.4。但是,您需要安装Ryu应用程序(Python脚本)使用的一些Python模块。你可以安装使用pip的Python模块:
cd ~/ryu
sudo apt-get install python-dev python-pip python-setuptools
sudo pip install .
</code>
以上将自动运行位于此目录中的setup.py并从Python包索引中获取缺少的Python模块。该脚本将自动安装所有相关模块。但是,请运行以下命令以确保您以后不会丢失任何模块:
sudo pip install webob
sudo pip install eventlet
sudo pip install paramiko
sudo pip install routes
</code>
的
启动
</强>
使用以下命令启动mininet以模拟3个主机和交换机:
sudo mn —topo single,3 —mac —switch ovsk —controller remote
</code>
你会看到一个mininet提示。此提示可用于ping主机,在它们之间发送数据包等。
打开另一个终端窗口来运行Ryu。在此示例中,我们将运行一个应用程序(simple_switch_13.py),该应用程序将模拟一个简单的第2层交换机,该交换机将所有收到的数据包转发到除收到的所有端口之外的所有端口。
cd ~/ryu
PYTHONPATH=. ./bin/ryu-manager ryu/app/simple_switch_13.py
</code>
运行时,请确保您在主目录中。
你们都准备好了。要ping主机并分析数据包传输,请转到下一部分。
3.尝试使用Wireshark和tcpdump
在本节中,我们将使用mininet从一个主机向另一个主机发送数据包,并使用tcpdump和Wireshark分析生成的传输。
数据包传输的方式正是我们可以在软件定义网络中控制的。我们通过编写在控制器上运行的不同应用程序来完成此操作。这些应用程序构成SDN控制平面的应用层。
的
设置拓扑并运行控制应用程序
</强>
注意:在前面的部分中,您使用mininet创建了拓扑,并启动了一个Ryu应用程序来控制传输。如果您重新启动或退出其中任何一个,我重复命令以创建拓扑并在此处启动Ryu应用程序:
cd ~
sudo mn —topo single,3 —mac —switch ovsk —controller remote
</code>
并在一个单独的终端窗口中:
cd ~/ryu
PYTHONPATH=. ./bin/ryu-manager ryu/app/simple_switch_13.py
</code>
的
玩数据包
</强>
在mininet提示符下,发出以下命令为您创建的拓扑中的每个主机打开一个控制台窗口:
mininet> xterm h1 h2 h3
</code>
堆叠这些控制台,以便您可以同时看到它们!然后在h2和h3的xterms中,运行tcpdump,一个实用程序来打印主机看到的数据包:
tcpdump -XX -n -i h2-eth0
tcpdump -XX -n -i h3-eth0
</code>
注意:如果您之前使用过Wireshark,则分别在这两台主机的eth0接口上捕获数据包。
创建拓扑时,mininet为以下三个主机分配了以下IP地址:
h1: 10.0.0.1
h2: 10.0.0.2
h3: 10.0.0.3
</code>
从主机1的shell,ping主机2和主机3,并观察每个命令后对其他两个控制台的影响:
ping 10.0.0.2
ping 10.0.0.3
</code>
尝试ping无法访问(不存在的主机),并查看对控制台的影响:
ping 10.0.0.7
</code>
您应该已经在本节中观察到ICMP(ping)和ARP(具有此IP地址的人)协议!您也可以使用Wireshark而不是tcpdump来完成上述操作。这是tcpdump的图形替代品。
注意:数据包转发的方式取决于在Ryu上运行的应用程序。您可以编写一个应用程序来删除所有数据包。在这种情况下,你的ping对其他两个控制台没有任何影响。
4.了解基本的第2层交换机应用程序
在本节中,我们将分析第3节中控制数据包传输的第2层交换机应用程序的简化版本的工作原理。
的
学习桥(或第2层交换机)的工作
</强>
我之前提到过,如果您正在阅读本指南,我假设您已经掌握了基本网络协议的知识(包括第2层交换机,学习桥或以太网交换机的工作!)我将在以下内容中进行总结。以下几行无论如何。
“学习”桥存储与其连接的主机的数据库,对应它的端口。主机由其网卡的MAC地址标识,如下所示:
ab
ef:12:34:56
(它是十六进制的)。端口仅通过其编号来识别。例如,具有4个端口的交换机具有端口1,2,3和4。
如果交换机在其端口2上收到数据包,它将查看该数据包的目标MAC地址(它指向的主机)。然后它查看它的数据库,看它是否知道该主机连接到哪个端口。如果发现它,它只将该数据包转发到该特定端口。但是如果它的数据库中还没有条目,它会将该数据包泛洪到ALL端口,主机可以检查数据包是否发往它们。
同时,开关看着
资源
该数据包的MAC地址,它立即知道主机X位于端口2.它将该条目存储在该数据库中。所以现在您知道,如果目标主机回复源主机,交换机将不必泛洪回复数据包!
的
Ryu API Python代码简介
</强>
不要直接进入simple_switch_13.py,让我们选择一个没有“学习”能力的非常简单的程序。目前,没有转发数据库。以下程序只是一个简单的第2层交换机,它将收到的数据包发送到所有端口(泛洪数据包):
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
class L2Switch(app_manager.RyuApp):
def __init__(self, *args, **kwargs):
super(L2Switch, self).__init__(*args, **kwargs)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
dp = msg.datapath
ofp = dp.ofproto
ofp_parser = dp.ofproto_parser
actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
out = ofp_parser.OFPPacketOut(
datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,
actions=actions)
dp.send_msg(out)
</code>
的
进口
</强>
我还不会深入研究import语句。我们将在分析使用它们的代码时单独讨论导入。
的
基本应用程序框架
</强>
以下代码是完全完整的Ryu应用程序。实际上你也可以执行它!它不会做任何事情:
from ryu.base import app_manager
class L2Switch(app_manager.RyuApp):
def __init__(self, *args, **kwargs):
super(L2Switch, self).__init__(*args, **kwargs)
</code>
作为班级的论据,我们通过了
ryu.base.app_manager.RyuApp
导入(在第一行导入)。来自Ryu API手册,
app_manager
class是Ryu应用程序的中央管理。它加载Ryu应用程序,为它们提供上下文并在Ryu应用程序之间路由消息。
的
EventOFPPacketIn事件
</强>
一种新方法
packet_in_handler
被添加到
L2Switch
类。当Ryu收到OpenFlow时会调用此方法
packet_in
信息。当Ryu收到一个
packet_in
消息,a
ofp_event.EventOFPPacketIn
事件被提出。该
set_ev_cls
装饰员告诉Ryu相关功能,
packet_in_handler
应该叫。
的第一个论点
set_ev_cls
decorator表示一个使函数调用的事件。正如您所期望的那样,每次都是
ofp_event.EventOFPPacketIn
引发事件,调用此函数。
第二个参数表示当您想允许Ryu处理事件时的开关状态。可能你想忽略OpenFlow
packet_in
在Ryu和交换机完成握手之前的消息。运用
MAIN_DISPATCHER
因为第二个参数意味着只有在协商完成后才会调用此函数。
MAIN_DISPATCHER
表示开关的正常状态。在初始化阶段,开关进入
HANDSHAKE_DISPATCHER
州!
现在让我们看看函数的主体。我们将其分为两部分。
msg = ev.msg
dp = msg.datapath
ofp = dp.ofproto
ofp_parser = dp.ofproto_parser
</code>
ev.msg
是包含接收数据包的数据结构。
msg.dp
是数据结构中的一个对象,表示数据路径(交换机)。
dp.ofproto
和
dp.ofproto_parser
是表示Ryu和交换机协商的OpenFlow协议的对象。
actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
out = ofp_parser.OFPPacketOut(
datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,
actions=actions)
dp.send_msg(out)
</code>
OFPActionOutput
class与a一起使用
packet_out
用于指定要将数据包发送出去的交换机端口的消息。由于在这个简化的应用程序中没有转发数据库,我们将数据包泛洪到所有端口,因此常量
OFPP_FLOOD
用来。
OFPPacketOut
class用于构建一个
packet_out
信息。
通过使用
datapath
类的
send_msg
方法,您可以将OpenFlow消息对象发送到actions变量中定义的端口。我再说一遍,在这种情况下,构建动作使目的地包括所有端口。
的
活动
</强>
您在上面的代码中反复看到了术语事件。在事件驱动编程中,程序的流程由事件控制,事件由系统接收的消息引发(例如,
EventOFPPacketIn
是的
packet_in
收到消息
的
通过
</强>
刘某
的
从
</强>
(启用OpenFlow)开关)。我们之前讨论过OpenFlow是一种协议,控制器(Ryu,PC)和基础设施(或交换机)使用该协议进行通信。
的
消息如
</强>
packet_in
的
正是这两者之间的通信看起来像使用OpenFlow协议!
</强>
的
下一步
</强>
您可能希望继续构建自己的Ryu应用。学习Ryu API(或Python语言,如果您还不熟悉它)可能是一个很好的起点。祝好运!