%# -*- coding: utf-8-unix -*- \section{基于OpenFlow的SDN交换机} \label{sec:c:sdn-ob:s:sdn} \subsection{实验目的} \label{subsec:c:sdn-ob:s:sdn_object} 本实验的主要目的是让学生了解SDN的概念及架构原理,掌握OpenFlow协议格式、内容、工作流程及对应功能作用。掌握SDN控制器北向API使用方法,可通过API编程获取和设置交换机数据。 \subsection{实验内容} \label{subsec:c:sdn-ob:s:sdn_content} 使用可编程网络平台及SDN交换机示例框架代码,完成OpenFlow协议的部分功能逻辑。验证并分析SDN交换机的工作原理与处理流程。主要完成以下内容: \begin{enumerate} \item \textbf{阅读协议:}阅读OpenFlow 1.3版本协议白皮书, 掌握其协议内容及对应功能; \item \textbf{设计实现协议功能:}基于SDN交换机示例框架代码, 选择开发OpenFlow协议的部分功能,完成逻辑设计与实现; \item \textbf{验证交换机功能:}运行自己的SDN交换机,与控制器建立连接, 在控制器查询其工作状态与相关协议功能展示。 如交换机基本信息描述、交换机流表状态、 交换机端口状态及计数统计、网络拓扑展示效果; \item \textbf{检验流表:}验证SDN交换机连接主机间的通信功能, 并观察流表内容及变化情况; \item \textbf{REST API应用:}使用实验提供的北向REST API接口查询控制器, 获取或设置交换机的不同功能数据; \item \textbf{定义流表:}使用REST API接口修改流表内容, 观察主机的通信情况是否受到影响。 \end{enumerate} \subsection{实验原理、方法和手段} \label{subsec:c:sdn-ob:s:sdn_principle} \subsubsection{SDN交换机工作原理} SDN是一种新型网络创新架构,用于解决传统网络架构中对于网络虚拟化、各类自动化部署以及新型网络协议的问题。 SDN将传统的紧耦合网络设备拆分成了应用、控制和转发三层,其中应用和控制层面能够控制网络中数据的流向以及协议控制,而底层的网络设备被抽象为了可自定义的转发逻辑实体,可被上层应用定义为不同类型的转发设备。 在应用层,用户可通过通用、简单的API获取网络信息,并可修改转发设备的工作逻辑,达到动态改变网络架构与功能的目的。 \subsection{SDN交换机分组处理流程} \begin{enumerate} \item \textbf{启动协议通道:} SDN交换机运行OpenFlow协议代理程序与上层控制器建立通信连接。 交换机解析处理OpenFlow协议, 根据协议要求完成协议功能的相关数据收集和配置功能, 构造相应的OpenFlow协议应答控制器; \item \textbf{建立连接:}OpenFlow协议代理运行时, 交换机首先和控制器建立连接, 并发送OFP\_HELLO消息用以确认双方都支持的OpenFlow协议版本信息; \item \textbf{查询或设置数据:} 控制器通过OpenFlow的具体消息协议查询或设置交换机其他相关功能的数据, 如OFPT\_HELLO、OFPT\_ECHO\_REQUEST、OFPT\_PACKET\_IN、OFPMP\_DESC、 OFPT\_PACKET\_OUT、OFPT\_FLOW\_MOD、OFPT\_PORT\_MOD、OFPMP\_FLOW、OFPMP\_ PORT\_STATS等; \item \textbf{处理转发分组:}当交换机接收到一个数据转发分组时, 提取分组数据中的相关字段组成查表关键字,然后在本地进行流表匹配。 如果匹配成功,则根据流表的动作处理该分组,如从其他端口转发出去; 如果匹配不成功,则会执行默认动作,将分组打包送到控制器; \item \textbf{PACKET\_IN分组上传:}交换机查表不命中或是接收到链路探测报文, 则将该数据封装在OpenFlow的PACKET\_IN消息中,上传给控制器; \item \textbf{PACKET\_OUT分组下发:}通过PACKET\_IN上传的分组, 经过控制器分析处理后,会将其封装在OpenFlow协议的PACKET\_OUT消息中, 并携带输出端口信息下发到交换机。交换机接收到该消息数据后, 根据输出信息指示,将该分组从指定端口输出。 通过PACKET\_OUT发送的分组数据还包括链路探测协议报文; \item \textbf{FLOW\_MOD消息处理:} 控制器通过下发FLOW\_MOD消息给交换机的流表设置转发表项内容, FLOW\_MOD消息中包括分组特征元组信息和动作执行列表。 特征元组通常包括常用的MAC信息、帧类型和五元组信息等, 动作通常包括输出端口或丢弃信息等。 \end{enumerate} \subsection{实验条件} \label{subsec:c:sdn-ob:s:sdn_requirement} \begin{itemize} \item 可编程网络平台一个,测试主机两台, SDN控制器(安装SDN控制器软件Floodlight)一台。控制器主机没有两个网口时,中间加一个普通交换机; \item 测试主机与控制器主机的网络配置及连接拓扑如图\ref{fig:c:sdn-ob_sdn-topo}所示; \item 串口线一根,网线三根; \item SDN交换机框架开发源代码、流表配置Python脚本。 \end{itemize} \begin{figure}[!ht] \centering \includegraphics[width=9cm]{sdn-topo} \caption{OpenFlow交换机实验拓扑图} \label{fig:c:sdn-ob_sdn-topo} \end{figure} \subsection{实验步骤} \label{subsec:c:sdn-ob:s:sdn_procedure} \subsubsection{编译运行SDN交换机,验证交换机基本功能;} 请参考附录\ref{app:openbox}:《FAST平台介绍与操作说明手册》完成。 \subsubsection{基于SDN交换机源码,选择实现自己的协议功能;} \begin{enumerate} \item 修改源代码 \begin{code}[c] enum ofperr handle_openflow_callback(struct ofp_buffer *ofpbuf,int len) { int oftype = ofpbuf->header.type; LOG_DBG("ofpbuf->header.type=%d\n",ofpbuf->header.type); switch(oftype) { case OFPT_HELLO: /*用户不实现该类消息处理,则直接break,函数返回CONTINUE, *由系统库完成后续处理;*/ break; case OFPT_PACKET_OUT: /*若用户想实现该消息的处理,则在此处完成对消息的功能逻辑, *然后直接返回HANDLE,表示已经处理,库函数直接返回*/ //TO DO 用户在些添加处理逻辑,完成该消息功能 return HANDLE; //可再增加其他OpenFlow协议的消息类型 } return CONTINUE; } \end{code} 用户可以根据自己的需求选择实现OpenFlow协议的部分消息功能,一旦处理了该消息,一定要返回消息处理标识HANDLE,否则直接break即可。 \item 编译源代码 \begin{code}[console] root@HNXS-FAST:/home/hnxs/sdn# make gcc –o user_openflow –l reg –l ua –l rule –l ofp -lpthread root@HNXS-FAST:/home/hnxs/sdn# ls main_openflow.c Makefile user_openflow root@HNXS-FAST:/home/hnxs/sdn# \end{code} \end{enumerate} \subsubsection{运行修改后的交换机,再次验证SDN交换机的功能;} \begin{enumerate} \item 运行SDN交换机,交换机正常工作后输出如下: \begin{code}[console] root@HNXS-FAST:/home/hnxs/sdn# ./ user_openflow -4 192.168.1.3 -i obx0,obx1,obx2,obx3 \end{code} \begin{code}[text] fastU->REG Version:20180827,OpenBox HW Version:2030200722 port_name:eth0,port:0 port_name:obx0,port:0 port_name:obx1,port:1 port_name:obx2,port:2 port_name:obx3,port:3 fastU->librule version:20181015,Default Action:0x40000080 xofp)Connect to SDN Controller [ 192.168.1.3:6653 ] OK! 0_nms_of13)DELETE ALL RULE! obx0(0x29008) Start... obx2(0x2e100) Start... obx3(0x30958) Start... obx1(0x2b890) Start... \end{code} \item 查看控制器界面基本信息,在浏览器打开控制器WEB网站http://192.168.1.3/ui/index.html,控制界面如图\ref{fig:c:sdn-ob_sdn-main}所示: \begin{figure}[ht!] \centering \includegraphics[width=11cm]{sdn-main} \caption{SDN主界面信息} \label{fig:c:sdn-ob_sdn-main} \end{figure} \item 查看连接交换机信息,如图\ref{fig:c:sdn-ob_sdn-switch}所示: \begin{figure}[H] \centering \includegraphics[width=11cm]{sdn-switch} \caption{SDN交换机信息} \label{fig:c:sdn-ob_sdn-switch} \end{figure} \item 查看连接测试主机信息,SDN主机及连接信息如图\ref{fig:c:sdn-ob_sdn-host}所示: \begin{figure}[htp!] \centering \includegraphics[width=12cm]{sdn-host} \caption{SDN主机信息} \label{fig:c:sdn-ob_sdn-host} \end{figure} \item 查看端口状态与计数信息与流表信息,端口与流表信息如图\ref{fig:c:sdn-ob_port-flowtable}所示。交换机流表详细信息查询方法见下一小节描述。 \begin{figure}[htp!] \centering \includegraphics[width=12cm]{port-flowtable} \caption{端口状态与流表计数信息} \label{fig:c:sdn-ob_port-flowtable} \end{figure} \item 查看网络拓扑信息,网络拓扑信息如图\ref{fig:c:sdn-ob_sdn-test-topo}所示: \begin{figure}[htp!] \centering \includegraphics[width=10cm]{sdn-test-topo} \caption{SDN网络拓扑信息} \label{fig:c:sdn-ob_sdn-test-topo} \end{figure} \end{enumerate} \subsubsection{在测试主机ping的前后,分别观察流表变化;} \begin{enumerate} \item ping之前的流表信息为空,如图\ref{fig:c:sdn-ob_empty-flowtable}所示: \begin{figure}[htp!] \centering \includegraphics[width=10cm]{empty-flowtable} \caption{空流表信息} \label{fig:c:sdn-ob_empty-flowtable} \end{figure} \item ping之后的流表信息,如图\ref{fig:c:sdn-ob_flowtable-ping}所示: \begin{figure}[htp!] \centering \includegraphics[width=10cm]{flowtable-ping} \caption{ping通后流表信息} \label{fig:c:sdn-ob_flowtable-ping} \end{figure} ping通之后在SDN交换机的流表里多了4条转发流表,分别是两个方向的ARP应答报文转发表和两个方向的IP报文转发表(具体内容在后面的REST API数据中查验)。由此可以说明:控制器向SDN交换机注入了四条转发流表,允许两边的主机相互ping通。 \end{enumerate} \subsubsection{使用REST API接口查询交换机的相关功能数据;} \begin{enumerate} \item 查询交换机基本信息 使用curl命令和对应的REST API地址查询交换机的对应信息。在命令行终端输入如下命令,即可查询返回交换机的基本信息数据内容。 \begin{code}[console] # curl http://192.168.1.3:8080/wm/core/switch/all/desc/json \end{code} \begin{code}[json] { "desc": { "version": "OF_13", "manufacturer_description": "HuNan XinShi NetWork", "hardware_description": "OpenBox HW 2017", "software_description": "OpenBox Driver 1.0.0", "serial_number": "None", "datapath_description": "None" } } \end{code} \item 查询端口计数信息 \begin{code}[console] # curl http://192.168.1.3:8080/wm/core/switch/all/port/json \end{code} \begin{code}[json] { "port_reply": [{ "version": "OF_13", "port": [{ "port_number": "local", "receive_packets": "10957", "transmit_packets": "1180", "receive_bytes": "592738", "transmit_bytes": "143898", "receive_dropped": "0", "transmit_dropped": "0", "receive_errors": "0", "transmit_errors": "0", "receive_frame_errors": "0", "receive_overrun_errors": "0", "receive_CRC_errors": "0", "collisions": "0", "duration_sec": "4294966827", "duration_nsec": "573987" }, ] }] } \end{code} 端口计数统计信息返回的内容中只摘取显示了一个端口的数据显示,其他端口数据内容已经省略。 \item 查询交换机流表信息 \begin{code}[console] # curl http://192.168.1.3:8080/wm/core/switch/all/flow/json \end{code} \begin{code}[json] { "flows": [{ "version": "OF_13", "cookie": "9007199271518208", "table_id": "0x0", "packet_count": "162", "byte_count": "571400", "duration_sec": "0", "duration_nsec": "0", "priority": "1", "idle_timeout_s": "5", "hard_timeout_s": "0", "flags": [], "match": { "in_port": "3", "eth_dst": "b8:27:eb:d8:83:20", "eth_src": "b8:27:eb:c1:d1:39", "eth_type": "0x800", "ipv4_src": "192.168.2.119", "ipv4_dst": "192.168.2.111" }, "instructions": { "instruction_apply_actions": { "actions": "output=1" } } }, ] } \end{code} 交换机流表信息返回的内容中只摘取显示了一条流表的数据显示,其他流表数据内容已经省略。 \end{enumerate} \subsubsection{修改Python脚本,手动配置流表,观察测试主机的通信变化;} \begin{enumerate} \item 修改流表脚本,使其转发端口错误; 修改openflow\_flow-test.py,主要修改其流表定义内容,其他位置代码无需修改。流表定义内容如下: \begin{code}[python] flowbe0 = { 'switch':"00:00:00:0a:00:00:08:01", "table":"0", "name":"flow-0", "cookie":"60", "priority":"1", "active":"true", "eth_dst":" b8:27:eb:c1:d1:39", #dmac "eth_src":" b8:27:eb:d8:83:20", #smac "eth_type":"0x800", #type "in_port":"1", #inport "ipv4_src":"192.168.2.111", #sip "ipv4_dst":"192.168.2.119", #dip "actions":"output=2" #执行动作,输出端口为2 } flowbe1 = { 'switch':"00:00:00:0a:00:00:08:01", "table":"0", "name":"flow-1", "cookie":"61", "priority":"1", "active":"true", "eth_dst":"b8:27:eb:d8:83:20", #dmac "eth_src":"b8:27:eb:c1:d1:39", #smac "eth_type":"0x800", #type "in_port":"3", #inport "ipv4_src":"192.168.2.119", #sip "ipv4_dst":"192.168.2.111", #dip "actions":"output=2" #执行动作,输出端口为2 } \end{code} \item 重启SDN控制器,并停止测试主机的ping操作; \item 配置用户流表,脚本执行命令如下: \begin{code}[console] # python openflow_flow-test.py (200, 'OK', '{"status" : "Entry pushed"}') (200, 'OK', '{"status" : "Entry pushed"}') \end{code} 脚本执行成功,两条流表添加成功,在测试主机两边进行ping测试,观察是否能够ping通? \item 修改流表脚本,使其转发端口正确,再次验证。 \begin{enumerate} \item 两种动作分别修改为1进3出和3进1出。添加流表后,再重新ping测试,观察是否能够ping通? \item 由此说明:对于SDN交换机, 可以通过用户修改流表的方式来定义某一条具体流的转发行为。 \end{enumerate} \end{enumerate} \subsection{思考题} \label{subsec:c:sdn-ob:s:sdn_rethink} \begin{enumerate} \item WEB界面上显示的转发流表是如何生成的?分析其生成流程; \item 测试主机第一次ping通的数据处理流程与第二次ping通的数据处理流程有何不同? \item 控制器界面的拓扑图是如何生成的?需要哪些数据? \end{enumerate} \subsection{注意事项及有关说明} \label{subsec:c:sdn-ob:s:sdn_notice} \begin{enumerate} \item SDN控制器为JAVA实现,有时候WEB存在缓存,导致数据显示不及时,在流表更新显示(两次实验对比)时,先清空历史缓存数据后再显示; \item Python脚本实现了一个http协议的客户端脚本程序,通过http协议及REST API的路径请求或设置SDN控制器WEB的对应功能模块,获取或设置该模块的返回数据或输入参数,传输数据类型为json格式。 \end{enumerate} \subsection{考核方法} \label{subsec:c:sdn-ob:s:sdn_criterion} 完成本次实验,需要提交一份实验报告、一份程序源代码和一份程序输出日志。 \begin{enumerate} \item (20分)在规定时间内完成实验,并提交实验成果; \item (40分)根据实现OpenFlow消息类型的多少计分,简单类型5分,复杂类型10分; \item (10分)通过脚本设置可以实现两个测试主机ping通和不能ping通; \item (20分)通过网上搜索学习, 使用其他多个REST API查询到了交换机更多的数据内容; \item (10分)实验报告与源代码内容完整、格式规范。 \end{enumerate}