我是 OpenFlow 和 SDN 的新手。我需要帮助在 Ubuntu 或 Debian 机器上设置 Ryu OpenFlow 控制器并了解基本的 Ryu 应用程序。
注意:这个问题已经有了答案。
这可能是我在 Stack Overflow 上写的最长的帖子之一。我一直在学习 OpenFlow、SDN 和 Ryu,并想在这里为初学者记录我的知识。如果需要,请更正/编辑我的帖子。
本简短指南假定您已经了解计算机网络和主要网络协议。本指南将帮助您从系统设置开始使用 OpenFlow。
进一步阅读:Scott Shenker 和软件定义网络的网络的未来和协议的过去,IEEE INFOCOM 2009。
在你开始之前:
基础设施层包括网络核心内的路由器和交换机。
控制层包括运行 OpenFlow 控制器的 PC 以及控制器本身。
应用层包括在该控制器之上运行的应用程序。在 Ryu 中,这些应用程序是用 Python 编写的。
OpenFlow 是一种协议,基础设施和控制层使用该协议进行交互。OpenFlow 本身不提供 API。它是一种开源协议,由开发支持 OpenFlow 的交换机的供应商和编写控制器的开发人员(如 Ryu)使用。API 由控制器提供。
先决条件
你需要上网。如果您在虚拟机中运行 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
运行以下命令来检查您的 mininet 安装:
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
您将看到一个 mininet 提示。此提示可用于 ping 主机、在它们之间发送数据包等。
打开另一个终端窗口来运行 Ryu。在此示例中,我们将运行一个应用程序 (simple_switch_13.py),该应用程序将模拟一个简单的第 2 层交换机,它将所有接收到的数据包转发到除接收到的端口之外的所有端口。
cd ~/ryu
PYTHONPATH=. ./bin/ryu-manager ryu/app/simple_switch_13.py
当你运行它时,确保你在你的主目录中。
你都准备好了。要 ping 主机并分析数据包传输,请移至下一部分。
在本节中,我们将使用 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 中,对主机 2 和主机 3 执行 ping 操作,并在执行每个命令后观察其他两个控制台上的效果:
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 操作不会对其他两个控制台产生影响。
在本节中,我们将分析第 3 节中控制数据包传输的第 2 层交换机应用程序的简化版本的工作原理。
学习桥(或第 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
import(在第一行导入)。从 Ryu API 手册中,app_manager
类是 Ryu 应用程序的中心管理。它加载 Ryu 应用程序,为它们提供上下文并在 Ryu 应用程序之间路由消息。
EventOFPPacketIn 事件
一个新方法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 处理事件时开关的状态。可能,您想packet_in
在 Ryu 和交换机之间的握手完成之前忽略 OpenFlow 消息。用作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 变量中定义的端口。我重复一遍,在这种情况下,构建的动作使得目的地包括所有端口。
活动
您在上面的代码中反复看到了术语事件。在事件驱动编程中,程序的流程由事件控制,这些事件由系统接收到的消息EventOFPPacketIn
引发(例如,当Ryu从(启用 OpenFlow 的)交换机接收到packet_in
消息时引发)。我们之前讨论过 OpenFlow 是一种协议,控制器(Ryu、PC)和基础设施(或交换机)使用该协议进行通信。消息之类的正是两者之间使用 OpenFlow 协议进行通信的样子! packet_in
下一步
您可能希望继续构建自己的 Ryu 应用程序。学习 Ryu API(或 Python 语言,如果您还不熟悉的话)可能是一个很好的起点。祝你好运!
你可能会发现使用 Ryu 控制器有用的东西是 Ryuretic。Ryuretic 是一个模块化的、基于 SDN 的网络应用程序开发框架。它允许网络运营商在 OSI 模型的各个级别(包括 L2、L3、L4 和 shim 层协议)上直接处理数据包头字段。用户只需选择匹配字段并选择提供的操作来更新 OpenFlow 交换机。
Ryuretic 后端将所有事件作为 pkt(字典对象)呈现给用户,并通过提供感兴趣的头字段(例如,pkt['srcmac']、pkt['dstmac']、 pkt['ethtype'], pkt['inport'], pkt['srcip'], etc.) 使用来自 pkt 的信息,用户可以选择要匹配的字段和操作(fwd、drop、redirect、mirror , craft) 找到匹配项时获取。
要安装 Ryuretic,只需将 [文件] ( 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 上测试您的网络应用程序。