我是OpenFlow和SDN的新手。我需要帮助在 Ubuntu 或 Debian 机器上设置 Ryu OpenFlow 控制器,并了解基本的 Ryu 应用程序。
注意:这个问题已经有了答案。
这可能是我在Stack Overflow上写过的最长的文章之一。我一直在学习OpenFlow,SDN和Ryu,并希望在这里为初学者记录我的知识。如果需要,请更正/编辑我的帖子。
本简短指南假定您已经了解计算机网络和主要网络协议。本指南将帮助您从系统设置开始使用 OpenFlow。
1. 什么是OpenFlow和SDN?
请阅读SDN/OpenFlow |流量可。
延伸阅读:Scott Shenker 的《网络的未来和协议的过去》和《软件定义网络》,IEEE INFOCOM 2009。
开始之前:
基础架构层包括网络核心内的路由器和交换机。
控制层包括运行OpenFlow控制器的PC以及控制器本身。
应用程序层包括在该控制器上运行的应用程序。在Ryu中,这些应用程序是用Python编写的。
OpenFlow 是一种基础设施和控制层交互的协议。OpenFlow不提供自己的API。它是一种开源协议,由开发支持 OpenFlow 的交换机的供应商和编写控制器的开发人员(如 Ryu)使用。API 由控制器提供。
2. 在 Debian 8 上设置 Ryu OpenFlow 控制器
先决条件
您需要互联网接入。如果您在虚拟机中运行 Debian,请发出以下命令以通过 NAT 自动配置您的以太网接口:
su
dhclient eth0
启用须藤
Debian 默认不附带 sudo。您稍后将使用的一些 Ryu 应用程序需要 sudo。您可以安装 sudo 并将自己添加到 sudo'ers 列表中,如下所示:
su
apt-get install sudo # you might need to do apt-get update first!
visudo
找到显示 %sudo ALL=(ALL:ALL) ALL 的行,并在其下方添加一个条目:
yourusername ALL=(ALL:ALL) ALL
按Ctrl+X,然后按Y保存对 sudoers 文件的更改。现在,您可以以root身份注销以返回到自己的shell
exit
启用最佳屏幕分辨率(仅限 VM)
如果您在 Virtual Box中运行 Debian,默认安装不会启用对 Virtual Box 的全屏分辨率支持。在第 3 节的后面部分,您将需要一个更大的屏幕。现在启用它是一个好主意。
在虚拟机的窗口中,单击设备>插入客户机添加 CD 映像...
然后 cd 到包含文件的目录
cd /media/cdrom
由于权限问题,Debian 不会让你运行脚本。将文件复制到主目录,更改权限,然后运行它:
mkdir ~/VBOXGUEST
cp * ~/VBOXGUEST
cd ~/VBOXGUEST
chmod 755 *
sudo ./VBoxLinuxAdditions.run
重新启动
sudo shutdown -r now
安装 Git
sudo apt-get install git
安装迷你网
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
我建议您安装 OpenFlow Wireshark Dissector。您可以稍后安装 Wireshark 来分析数据包。OpenFlow Wireshark Dissector帮助Wireshark从OpenFlow数据包中获取尽可能多的信息。
mininet/util/install.sh -h
运行以下命令以检查您的迷你网安装:
sudo mn --test pingall
安装 Ryu OpenFlow 控制器
OpenFlow 控制器使用 OpenFlow 协议在控制层和基础架构层之间进行通信。此外,它是提供 API 的控制器,用于开发在应用层(控制层之上)运行的 SDN 应用程序。有许多OpenFlow控制器。Ryu OpenFlow控制器是一个使用Python脚本作为其应用程序的控制器。同样,使用 Git 安装它:
cd ~
git clone git://github.com/osrg/ryu.git
安装 Wireshark
sudo apt-get install wireshark
安装支持的 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 .
以上将自动运行位于此目录中 setup.py,并从 Python 包索引中获取缺少的 Python 模块。该脚本将自动安装所有相关模块。但是,请运行以下命令以确保以后不会丢失任何模块:
sudo pip install webob
sudo pip install eventlet
sudo pip install paramiko
sudo pip install routes
启动
使用以下命令启动 mininet 以模拟 3 台主机和一个交换机:
sudo mn --topo single,3 --mac --switch ovsk --controller remote
您将看到一个迷你网提示。此提示可用于 ping 主机、在它们之间发送数据包等。
打开另一个终端窗口以运行 Ryu。在此示例中,我们将运行一个应用程序 (simple_switch_13.py),该应用程序将模拟一个简单的第 2 层交换机,该交换机会将所有收到的数据包转发到除接收端口之外的所有端口。
cd ~/ryu
PYTHONPATH=. ./bin/ryu-manager ryu/app/simple_switch_13.py
运行此命令时,请确保您位于主目录中。
一切就绪。要 ping 主机并分析数据包传输,请转到下一部分。
3. 尝试使用Wireshark和tcpdump
在本节中,我们将使用 mininet 将数据包从一台主机发射到另一台主机,并使用 tcpdump 和 Wireshark 分析由此产生的传输。
数据包的传输方式正是我们可以在软件定义网络中控制的方式。我们通过编写在控制器上运行的不同应用程序来做到这一点。这些应用程序构成了 SDN 控制平面的应用层。
设置拓扑并运行控制应用程序
注意:在前面的部分中,您使用mininet创建了一个拓扑,并启动了一个Ryu应用程序来控制传输。如果您重新启动或退出其中任何一个,我将重复这些命令以创建拓扑并在此处启动 Ryu 应用程序:
cd ~
sudo mn --topo single,3 --mac --switch ovsk --controller remote
并在单独的终端窗口中:
cd ~/ryu
PYTHONPATH=. ./bin/ryu-manager ryu/app/simple_switch_13.py
玩转数据包
在 mininet 提示符下,发出以下命令,为您创建的拓扑中的三个主机中的每一个打开一个控制台窗口:
mininet> xterm h1 h2 h3
堆叠这些控制台,以便您可以同时看到它们!然后在 h2 和 h3 的 xterms 中,运行 tcpdump,这是一个用于打印主机看到的数据包的实用程序:
tcpdump -XX -n -i h2-eth0
tcpdump -XX -n -i h3-eth0
注意:如果您之前使用过 Wireshark,就像分别在这两个主机的 eth0 接口上捕获数据包一样。
创建拓扑时,mininet 将以下 IP 地址分配给三个主机:
h1: 10.0.0.1
h2: 10.0.0.2
h3: 10.0.0.3
从主机 1 的 shell 中,ping 主机 2 和主机 3,并在每个命令后观察对其他两个控制台的影响:
ping 10.0.0.2
ping 10.0.0.3
尝试 ping 无法访问(不存在的主机),并查看对控制台的影响:
ping 10.0.0.7
您应该在本节中观察到ICMP(ping)和ARP(谁拥有此IP地址)协议!您也可以使用 Wireshark 而不是 tcpdump 来执行上述操作。这是 tcpdump 的图形替代方案。
注意:数据包的转发方式取决于在Ryu上运行的应用程序。您可以编写一个应用程序来丢弃所有数据包。在这种情况下,您的 ping 不会对其他两个控制台产生任何影响。
4. 了解基本的第 2 层交换机应用
在本节中,我们将分析第 2 层交换机应用程序的简化版本的工作,该应用程序控制第 3 节中的数据包传输。
学习网桥(或第 2 层交换机)的工作
我之前提到过,如果您正在阅读本指南,我假设您已经了解基本的网络协议(包括第 2 层交换机、学习网桥或以太网交换机的工作!无论如何,我将在下面用几行来总结它。
"学习"网桥根据其端口存储其连接的主机的数据库。主机由其网卡的 MAC 地址标识,如下所示:ab:cd:ef:12:34:56
(十六进制)。端口仅通过其编号进行标识。例如,具有 4 个端口的交换机具有端口 1、2、3 和 4。
如果交换机在其端口 2 上收到数据包,它将查看该数据包的目标 MAC 地址(它要发送到哪个主机)。然后,它会查看其数据库,以查看它是否知道该主机连接到哪个端口。如果发现它,它将仅将该数据包转发到该特定端口。但是,如果它的数据库中还没有条目,它会将该数据包泛洪到所有端口,主机可以自己检查数据包是否发往它们。
同时,交换机会查看该数据包的源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)
进口
我不会深入研究导入语句。我们将在分析使用导入的代码时单独讨论导入。
基本应用程序框架
以下代码是一个完全完整的 Ryu 应用程序。事实上,你也可以执行它!不过它不会做任何事情:
from ryu.base import app_manager
class L2Switch(app_manager.RyuApp):
def __init__(self, *args, **kwargs):
super(L2Switch, self).__init__(*args, **kwargs)
作为类的参数,我们传递导入ryu.base.app_manager.RyuApp
(在第一行导入)。从 Ryu API 手册中,app_manager
类是 Ryu 应用程序的集中管理。它加载 Ryu 应用程序,为它们提供上下文并在 Ryu 应用程序之间路由消息。
事件OFPPacketIn Event
新方法packet_in_handler
将添加到类L2Switch
。当 Ryu 收到 OpenFlowpacket_in
消息时,将调用此值。当 Ryu 收到packet_in
消息时,将引发ofp_event.EventOFPPacketIn
事件。set_ev_cls
装饰器告诉 Ryu 何时应调用关联的函数packet_in_handler
。
set_ev_cls
装饰器的第一个参数指示使函数调用的事件。正如您所期望的那样,每次引发ofp_event.EventOFPPacketIn
事件时,都会调用此函数。
第二个参数指示您希望允许 Ryu 处理事件时交换机的状态。您可能希望在Ryu和交换机之间的握手完成之前忽略OpenFlowpacket_in
消息。使用MAIN_DISPATCHER
作为第二个参数意味着仅在协商完成后调用此函数。MAIN_DISPATCHER
表示交换机的正常状态。在初始化阶段,交换机处于HANDSHAKE_DISPATCHER
状态!
现在让我们看一下函数的主体。我们将它分为两部分。
msg = ev.msg
dp = msg.datapath
ofp = dp.ofproto
ofp_parser = dp.ofproto_parser
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)
OFPActionOutput
类与packet_out
消息一起使用,以指定要从中发送数据包的交换机端口。由于这个简化的应用程序中没有转发数据库,因此我们将数据包泛洪到所有端口,因此使用常量OFPP_FLOOD
。
OFPPacketOut
类用于生成packet_out
消息。
通过使用类datapath
send_msg
方法,您可以将 OpenFlow 消息对象发送到 actions 变量中定义的端口。我再说一遍,在这种情况下,构建的操作使目标包括所有端口。
事件
您在上面的代码中反复看到术语事件。在事件驱动编程中,程序的流程由事件控制,事件由系统接收的消息引发(例如 当 Ryu 从(启用 OpenFlow)开关接收到packet_in
消息时,会引发EventOFPPacketIn
)。我们之前讨论过OpenFlow是控制器(Ryu,PC)和基础设施(或交换机)通信的协议。像这样的消息packet_in
正是使用OpenFlow协议两者之间的通信的样子!
后续步骤
您可能希望继续构建自己的 Ryu 应用程序。学习Ryu API(或Python语言,如果你还不熟悉它)可能是一个很好的起点。祝你好运!
您可能会发现使用Ryu控制器有用的东西是Ryuretic。Ryuretic是一个模块化的,基于SDN的网络应用程序开发框架。它允许网络运营商直接使用 OSI 模型各个级别的数据包标头字段,包括 L2、L3、L4 和填充层协议。用户只需选择匹配字段并选择提供的操作即可更新 OpenFlow 开关。
Ryuretic 后端将所有事件作为 pkt(字典对象)呈现给用户,并且通过提供感兴趣的标头字段来检索 pkt 的内容(例如,pkt['srcmac']、pkt['dstmac']、pkt['ethtype']、pkt['inport']、pkt['srcip'] 等)。使用 pkt 中的信息,用户可以选择要匹配的字段以及在找到匹配项时要执行的操作(fwd、丢弃、重定向、镜像、制作)。
要安装 Ryuretic,只需将 [files] (https://github.com/Ryuretic/RyureticLabs/tree/master/ryu/ryu/app/Ryuretic) 复制到目录/ryu/ryu/app/Ryuretic。如果您安装了 Ryu,那么您已经拥有/ryu/ryu/app 目录。您只需要创建 Ryuretic 目录并将文件复制到那里。
Ryuretic Labs提供了使用Ryuretic在SDN上实现安全功能的设置说明和一些用例。它还提供了一个 Mininet 测试平台,用于在 SDN-Hub 提供的 VM 上测试网络应用程序。