《软件定义网络:基于OpenFlow的SDN》一一3.2 已有的实现方案-阿里云开发者社区

开发者社区> 华章出版社> 正文
登录阅读全文

《软件定义网络:基于OpenFlow的SDN》一一3.2 已有的实现方案

简介:

本节书摘来自华章计算机《软件定义网络:基于OpenFlow的SDN》一书中的第3章,第3.2节,作者:Siamak Azodolmolky,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

3.2 已有的实现方案

目前存在不同的OpenFlow(以及SDN)控制器实现方案,我们将把它们作为现有开源项目的组成部分,放在第8章中详细介绍。本章内容主要集中在NOX、POX、NodeFlow、Floodlight(派生自Beacon)和OpenDaylight方面,通过这些实现方案介绍若干OpenFlow控制器,以及在开发网络应用时各种可选的编程语言。
3.2.1 NOX和POX
第一个OpenFlow控制器是用C++编写的NOX(www.noxrepo.org),它同时还提供了用于Python开发的API。在早期人们探索OpenFlow和SDN领域的时候,NOX一直是很多研究和开发项目的基础。NOX的开发遵循两条不同的路线:
经典NOX(NOX-Classic)。
NOX,也称为新NOX。
前者是广为人知的开发路线,它包括了Python和C++,以及一组网络应用,不过,这种开发路线将被弃用,对NOX-Classic已不再有进一步的开发计划。新的NOX只支持C++,与经典NOX相比,它的网络应用也更少,但是运行速度更快,代码更简洁。POX是一个只支持Python的NOX版本,可以把它看作是一个通用的、开源的、用Python编写的OpenFlow控制器,也是一个能快速开发网络应用和构建网络应用原型的平台。POX的主要目标是研究领域,由于大多数研究项目在本质上生命周期都不长,POX的开发人员关注的是提供合适的接口,而非维护一个稳定的API。NOX(和POX)目前由GitHub的Git源代码仓库(Git source code repository)管理。复制(Cloning)Git repository是获取NOX和POX的最佳途径。POX软件可分为两个分支(branch):active分支和released分支,active分支是正处于活跃开发阶段的软件,released分支是在开发的某个阶段,被选作新的版本发布的软件。最新发布的分支仍有可能继续改进,不过只提供以补丁形式的修订,而新增的功能总是包含在active分支中。可以通过以下命令获取最新版本的NOX和POX软件:
image

在第2章中,我们利用Mininet仿真环境搭建了OpenFlow实验平台,在这一节,我们将从一个实现简单以太网集线器功能的网络应用开始,读者可以尝试修改这个应用,把它变成一个学习型的以太网二层交换机。在这个应用中,交换机将查看每一个数据包,学习源–端口映射关系,然后建立源MAC地址和端口之间的关联。如果数据包的目的端口已经跟某个端口建立起了关联,则该数据包将被发送到那个端口,否则,数据包将被洪泛到交换机的所有端口去。第一步是启动你的OpenFlow虚拟机,然后,需要把POX下载到你的虚拟机中:
image

3.2.2 运行一个POX应用
有了POX 控制器后,可以尝试通过以下命令运行POX中的一个基本的集线器样例:
image

这个命令行告诉POX启用详细日志记录,并且启动of_tutorial组件,该组件承担以太网集线器的功能,后面你将用到这个组件。现在你可以使用以下命令行开始Mininet的OpenFlow实验:
image

交换机需要花费点时间建立连接,通常情况下,当OpenFlow交换机失去与控制器的连接时,它试图向控制器发起连接的时间间隔就会变长,最长为15秒,该定时值的设置取决于具体的实现方案,可以由用户定义。由于OpenFlow交换机还没有连接上,延时可以是0~15秒的任何值。如果认为这个等待时间太长,可以使用“--max-backoff”参数把交换机的最大等待时间设置为N秒,直到应用程序指示OpenFlow交换机已经连接上,这时,POX 将显示类似以下的信息:
image

上面信息的第一行来自于POX中处理OpenFlow连接的部分,第二行来自于tutorial组件本身。
现在,我们用ping命令来检测主机之间是否连通,查看所有主机是否都能看到完全一样的流量:这是集线器的特点。为此,我们在每台主机上创建xterm,观察每台主机上的流量显示,在Mininet命令行界面,启动三个xterm:
image

调整每个xterm,使它们都能立即出现在屏幕上,对于笔记本电脑,可能需要减少显示高度以适应狭窄的屏幕。在主机h2、h3的xterm上,运行tcpdump,这是一个在主机上显示输出数据包的工具,分别输入命令
image

这时,ping数据包被上传到控制器,然后又被洪泛到所有的接口(发送它的那个接口除外)。在两个运行tcpdump的xterm上,应能看到由ping命令所产生的同样的ARP和ICMP数据包。这反映了集线器的工作原理:它向每个网络端口发送所有的数据包。现在来看一下,在对不存在的主机操作而没有得到响应时会发生什么情况。从主机h1的xterm上输入:
image

在运行tcpdump的xterm上,你将看到有三个没有得到回复的ARP请求,如果之后你的程序停止运行,那么三个没有得到应答的ARP请求是一个提醒,告诉你可能有意外丢弃的数据包,这时,你可以关闭xterm。
为了把集线器的行为模式改为交换机的学习模式,必须把学习型交换机的功能加到of_tutorial.py中。进入SSH终端,按Ctrl + C组合键停止tutorial的集线器控制器。需要修改文件pox/misc/of_tutorial.py的内容。用你常用的编辑器打开pox/misc/of_tutorial.py,当前代码从packet_in消息的句柄中调用act_like_hub()函数,该函数规定交换机的行为,你需要将它切换为act_like_switch()函数,后者包含了学习型交换机的代码框架。注意每次修改和保存该文件时,一定要重新启动POX,然后再用ping命令检验一下,看交换机和控制器两者的行为方式符合下面哪种功能定位:

  1. 集线器。
  2. 基于控制器的学习型以太网交换机。
  3. 支持流加速的学习型交换机。
    对于第二种和第三种情况,在初始的ARP请求后,只有ping命令的目的主机有tcpdump流量显示,而其他非目的主机不会有tcpdump流量显示。Python是一个动态的解释型语言,没有独立的编译步骤,只需更新你的代码并返回即可。Python有一个内置的散列表,称作字典;还有向量,称作列表(list)。对于学习型交换机,通常所需要的操作如下:

初始化一个字典:
image

在字典中加入一个元素:

![image](https://yqfile.alicdn.com/29005540f781616d40d4eb957734193747386455.png)

检查字典的成员关系:

![image](https://yqfile.alicdn.com/0f52235873a8537edba32ba317ef5dd4e050523e.png)

在POX中显示输出调试信息:

![image](https://yqfile.alicdn.com/8d9c78dc6644b2b33b97b2ad81aadbc1d78f163e.png)

在POX中显示输出错误信息:
image

显示输出一个对象的所有成员变量和函数:

![image](https://yqfile.alicdn.com/0e0b752bafb44e8755e2c6857e8af841152b2cfe.png)

注释代码行:

![image](https://yqfile.alicdn.com/f455105610d0f9ea4870500c5233238ab0d74a66.png)

读者可以从以下网址发现更多的Python资源。
Python中的内置函数清单:
http://docs.python.org/2/library/functions.html
Python官方指南:
http://docs.python.org/2/tutorial/
除了前面提到的函数,读者还需要了解有关POX API的一些详细信息,这对于开发学习型交换机是很有帮助的。此外,POX网站的相关栏目中还包含了其他一些可用的文档。
在POX中发送OpenFlow消息:
image

当开启一个与交换机的连接时,就启动了一个ConnectionUp事件,示例代码创建了一个新的Tutorial对象,它包含了一个对相关联的Connection对象的引用,可用于之后向交换机发送命令(即OpenFlow消息):
image

这是一个与ofp_packet_out和ofp_flow_mod一起使用的操作,它定义了一个用于发送数据包的交换机端口,该命令还可以使用特殊的端口数,例如OFPP_FLOOD,它将向除了接收端口以外的所有端口发送数据包。下面的例子建立了一个输出操作,用于向所有端口发送数据包:
image

这个类的对象描述了用来进行匹配的数据包的首部字段和输入端口,所有的字段都是可选的,没有定义的项可视为通配符,能够匹配任何值。下面是有关ofp_match对象的几个需要关注的字段:
dl_src:数据链路层(MAC)源地址
dl_dst:数据链路层(MAC)目的地址
in_port:交换机的数据包输入端口
举例:创建一条匹配规则,用于匹配从端口3输入的数据包:
image

消息ofp_packet_out 指示交换机发送一个数据包,该数据包可以是由控制器构造的,也可以是由交换机接收、经缓存后转发给控制器的(这时用buffer_id引用它)。需要关注的字段有:
buffer_id:发送缓存区的ID,如果是发送一个构造的数据包,就不需要设置该字段。
data:希望交换机发送的原始数据字节。如果是发送一个缓存的数据包,就不需要设置该字段。
actions:打算使用的操作列表(对于该tutorial,只包含一个操作,就是ofp_action_output操作)。
in_port:如果是通过buffer_id发送数据包,这里设为数据包初始到达的端口号,否则,该字段值设为OFPP_NONE。
举例:of_tutorial的send_packet()方法:

image

该段代码指示交换机安装一个流表记录,流表记录与输入数据包的某些字段相匹配,并对所匹配的数据包执行一系列操作。这些操作与之前提到的ofp_packet_out的操作一样(这里再强调一遍,对于tutorial,所有需要的操作就是简单的ofp_action_output操作)。由ofp_match对象对匹配进行描述,需要关注的字段有:
idle_timeout:流记录被清除之前的空闲秒数,默认情况下不设空闲超时值。
hard_timeout:流记录被清除之前的秒数,默认情况下不设超时值。
actions:施加于所匹配数据包上的一系列操作(如ofp_action_output)。
priority:当使用非精确匹配(通配符)模式时,对于多重匹配情况,该字段用于定义优先级,值越大、优先级越高。对于精确匹配模式,或者没有出现多重匹配记录时,该字段不重要。
buffer_id:缓存区的buffer_id字段,直接对该缓存进行规定的操作(action)。若没有可以不定义。
in_port:若使用了buffer_id,则该字段就是相关联的输入端口。
match:是一个ofp_match类的对象,默认匹配所有情况,所以,有可能需要设置其中的某些字段。
举例:创建flow_mod,将来自端口3的数据包从端口4发送出去:

image

更多关于OpenFlow中定义的常量信息,可参阅有关OpenFlow的主要类型、枚举类型和结构体定义文件openflow.h,该文档路径为: ~/openflow/include/openflow/openflow.h。也可以参考POX的OpenFlow库,该文档路径为pox/openflow/libopenflow_01.py。当然,还有OpenFlow 1.0规范。
POX的数据包库用于解析数据包,使其每个协议字段对Python可见。这个库也可以用来构造要发送的数据包。用于解析数据包的库位于pox/lib/packet/。
对于每个协议,有一个相应的解析文件。在第一个练习中,读者只需要访问以太网的源地址和目的地址字段,为了提取数据包的源地址,可以使用下面的点记法:
image

以太网的源地址和目的地址字段保存在pox.lib.addresses.EthAddr对象中,可以很容易地将其转变为常用的字符串形式(使用str(addr)),返回类似于 "01:ea:be:02:05:01"形式的值,或者从常用的字符串形式创建地址(EthAddr("01:ea:be:02:05:01"))。若要查看所解析数据包对象的所有成员,可以使用:

对于ARP数据包,你可以看到如下内容:
image
image

很多都是所有Python对象的普通字段,可以忽略,但是比起使用函数文档而言,这种方法算是一条捷径。
3.2.3 NodeFlow
NodeFlow是一个极度简化的OpenFlow控制器,由Cisco Systems CTO办公室的技术主管Gary Berger开发(http://garyberger.net/?p=537),这是一个用JavaScript编写的一个极简的OpenFlow控制器,用于Node.js(www.nodejs.org)。Node.js是一个服务器端的软件系统,用于编写可扩展的因特网应用(如HTTP服务器)。可以把它看作一个用JavaScript编写的包含了Google V8 JavaScript引擎、libuv平台的抽象层以及一个核心库的编译包。由于Node.js采用事件驱动的非阻塞I/O模型,因此,对于跨分布式设备运行的数据密集型实时应用而言,它兼具轻量和高效的优点,堪称完美。该程序用JavaScript编写,运行在服务器端,通过使用事件驱动的异步I/O模式,使得开销最小化、可扩展性最大化。所以,和大多数JavaScript程序不同,它不在Web浏览器中执行,而是作为一个服务器端的JavaScript应用程序运行。NodeFlow实际上是一个非常简单的程序,其工作很大程度上需要依赖于一个被称为OFLIB-NODE的协议解释器(由Zoltan LaJos Kis编写)。NodeFlow是一个实验系统,可以从GitHub上获取(git://github.com/gaberger/NodeFLow),同时还可以获取一个派生的库 OFLIB-NODE(git://github.com/gaberger/oflib-node)。NodeFlow之美体现在它运行上的简洁性,以及它对OpenFlow控制器的理解能以不到500行的代码表现出来。借助JavaScript和高性能的Google V8 JavaScript引擎,就能够在网络基础设施上进行各种SDN特性的实验,而且无须为了事件驱动编程去搞定所有那些程式化的代码。
NodeFlow服务器(即OpenFlow控制器)通过一个对net.createServer的简单调用完成实例化。地址和监听端口可以通过一个启动脚本来配置:
image

接下来的步骤是创建一个唯一的会话ID,通过这个ID,控制器就可以跟踪与每一个交换机的连接。事件监听器维持相应的socket,每当从socket通道接收到数据时,就启动事件处理主循环。利用stream库缓存数据,通过msgs对象返回已解码的OpenFlow消息,将msgs对象传递给函数_ProcessMessage以便后续处理:

image
image

最后一部分是事件处理程序,利用Node.js的EventEmitters触发回调,这些事件处理程序等待特定的事件发生,然后触发事件处理。NodeFlow处理两种特定的事件:OFPT_PACKET_IN,是OpenFlow的PACKET_IN事件中需要监听的主要事件;以及SENDPACKET,仅需要解码并发送OpenFlow消息。
image

基于NodeFlow的简单的网络应用可以是一个学习型的交换机(参见下面的do_l2_learning函数)。学习型交换机只查找源MAC地址,如果该源地址不在学习表中,则它将把该地址插入到转发表中的相应源端口处。

image
image

完整的NodeFlow服务器称为server.js,可以从NodeFlow Git repository下载。为了运行NodeFlow控制器,需要执行Node.js,并将NodeFlow服务器(即server.js)传递给Node.js的二进制代码(如Windows中的node.exe):image

3.2.4 Floodlight
Floodlight是一个基于Java的OpenFlow控制器,它以Beacon实现方案为基础,既支持物理的OpenFlow交换机,也支持虚拟的OpenFlow交换机。Beacon也是用Java实现的一个跨平台的、模块化的OpenFlow控制器,支持基于事件的和线程的操作。Beacon是由斯坦福大学的David Erickson创建的跨平台的OpenFlow控制器,之前注册在GPL v2之下,Floodlight派生于Beacon,持有Apache许可证。Floodlight 被重新设计后,不再采用OSGI框架,所以,其开发、运行及维护都不需要有OSGI经验。此外,目前的Floodlight社群成员包括一些Big Switch Networks的开发人员,他们在测试、修复bug、建设附加的工具和插件方面都很活跃。Floodlight控制器的目标是成为众多网络应用的支持平台,网络应用非常重要,因为它们能够针对实际中的联网问题提供解决方案。Floodlight提供的网络应用包括:
虚拟网络过滤器(Virtual Networking Filter):能够识别进入网络的与现有流不匹配的数据包,该应用程序能够判断源或者目的节点是否在同一个虚拟网络中,如果是,则通知控制器完成流的创建。这个过滤器实际上是基于第二层(MAC层)的一个网络虚拟化,使用户得以在一个二层域中创建多个逻辑上的第二层网络。
静态流创建工具(Static Flow Pusher):可以在流的第一个数据包进入网络之前就创建一个流。该功能通过Floodlight的REST API实现,允许用户以人工方式向OpenFlow网络中插入流。
线路创建工具(Circuit Pusher):能够创建一个流,并部署通向数据包目的节点的沿途路径上的交换机。这个源节点和目的节点之间的双向线路是一个永久的流记录,存在于两个设备节点间路由上的所有交换机中。
防火墙模块:与传统的物理网络中的防火墙作用一样,防火墙模块为软件定义网络中的设备提供保护。通过访问控制列表(Access Control List,ACL)规则来控制是否建立到达某个特定目的节点的流,防火墙应用作为Floodlight的一个模块而实现,负责在网络中的OpenFlow交换机上执行ACL规则。对数据包的监控通过packet-in消息完成。
对于使用Neutron的OpenStack,Floodlight可以作为一个网络插件运行。Neutron插件借助Floodlight提供的REST API实现了一个网络云服务Networking-as-a-Service,NaaS)模型,该解决方案具有两个组件:一个是Floodlight(实现了Neutron API)中的虚拟网络过滤器(VirtualNetworkFilter)模块;另一个是Neutron RestProxy插件,它负责将Floodlight连接到Neutron。一旦将Floodlight集成到OpenStack中,网络工程师就能够动态地部署网络资源以及其他虚拟的和物理的计算机资源,从而提高整体的灵活性和性能。
更多有关Floodlight的细节和指南,参看有关FloodLight OpenFlowHub的网页,网站链接为http://www.projectfloodlight.org/floodlight/

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: