这篇教程目标读者是那些使用 POX 平台 http://www.noxrepo.org/pox/about-pox/ 进行 SDN 应用开发的初学者. 熟悉 Python 的基础知识对学习本文是有帮助的, 但并不是必须的.
快速入门
下载 SDN Hub Tutorial VM http://sdnhub.org/tutorials/sdn-tutorial-vm-64-bit/ 并在 Virtualbox 或 VMware Player 中运行 VM. 关于 SDN Hub Tutorial VM 请参考: http://sdnhub.cn/all-in-one-vm.html
运行在终端窗口中运行如下的 Mininet 命令, 实现 1 个交换机和 3 个主机的网络拓扑, 交换机试图连接本机的运行在 6633 端口 (localhost 的 6633) 的控制器, 但是此时, 控制器还未启动, 所以是无法连接成功的:
#mn --topo single,3 --mac -controller remote --switch ovsk
由于 POX 目前仅支持 OpenFlow 1.0 , 而不支持 OpenFlow 1.3, 最好在命令行设置 OVS 所使用的 OpenFlow 的版本. 命令如下:
#mn --topo single,3 --mac -controller remote --switch ovsk,protocols=OpenFlow10
如果上面的命令还是出现 "WARNING:openflow.of_01:Bad OpenFlow version (0x00) on connection [None 120]" 之类的错误, 则使用如下的命令直接设置 openvswitch 使用的 OpenFlow 版本号:
- #ovs-vsctl get bridge s1 protocols
- ["OpenFlow10,"OpenFlow13]
- #ovs-vsctl set bridge s1 protocols=OpenFlow10
- #ovs-vsctl get bridge s1
- ["OpenFlow10]
接下来, 启动 POX 控制器, 假设 POX 的主目录被安装在 / home/ubuntu/pox, 下面的命令将启动 POX 控制器:
- #cd /home/ubuntu/pox
- #./pox.py log.level --DEBUG forwarding.tutorial_l2_hub
上面的命令以 DEBUG 模式运行 POX 控制器, 可以额外打印 POX 控制器和交换机之间的报文交换信息.
POX 0.1.0 (betta) / Copyright 2011-2013 James McCauley, et al.
DEBUG:core:POX 0.1.0 (betta) going up...
- DEBUG:core:Running on CPython (2.7.3/Sep 26 2013 16:35:25)
- DEBUG:core:Platform is Linux-3.5.0-30-generic-x86_64-with-Ubuntu-12.10-quantal
INFO:core:POX 0.1.0 (betta) is up.
- DEBUG:openflow.of_01:Listening on 0.0.0.0:6633
- INFO:openflow.of_01:[00-00-00-00-00-01 1] connected
接下来, mininet 测试创建的拓扑中主机间的连通性:
mininet> h1 ping h2
- PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
- 64 bytes from 10.0.0.2: icmp_req=1 ttl=64 time=49.1 ms
- 64 bytes from 10.0.0.2: icmp_req=2 ttl=64 time=46.3 ms
- 64 bytes from 10.0.0.2: icmp_req=3 ttl=64 time=29.1 ms
- 64 bytes from 10.0.0.2: icmp_req=4 ttl=64 time=42.9 ms
- 64 bytes from 10.0.0.2: icmp_req=5 ttl=64 time=26.2 ms
理解代码
上面运行的 hub 应用的代码保存在 / home/ubuntu/pox/pox/forwarding/tutorial_l2_hub.py 中, 完整代码如下:
- # Copyright (C) 2014 SDN Hub
- #
- # Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.
- # You may not use this file except in compliance with this License.
- # You may obtain a copy of the License at
- #
- # http://www.gnu.org/licenses/gpl-3.0.txt
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- # implied.
- from pox.core import core
- import pox.openflow.libopenflow_01 as of
- # Object spawned for each switch
- class L2Hub (object):
- def __init__ (self, connection):
- # Keep track of the connection to the switch so that we can
- # send it messages!
- self.connection = connection
- # This binds all our event listener
- connection.addListeners(self)
- # Handles packet in messages from the switch.
- def _handle_PacketIn (self, event):
- packet = event.parsed # This is the parsed packet data.
- packet_in = event.ofp # The actual ofp_packet_in message.
- msg = of.ofp_packet_out()
- msg.buffer_id = event.ofp.buffer_id
- msg.in_port = packet_in.in_port
- # Add an action to send to the specified port
- action = of.ofp_action_output(port = of.OFPP_FLOOD)
- msg.actions.append(action)
- # Send message to switch
- self.connection.send(msg)
- def launch ():
- def start_switch (event):
- L2Hub(event.connection)
- core.openflow.addListenerByName("ConnectionUp", start_switch)
当在命令行启动程序时, launch()函数被 POX 自动调用执行, 在这个函数内部完成应用程序注册事件监听器和创建应用程序对应 class 的对象. 如上所示的 launch()函数中, 当有新的交换机连接到控制器时, 实例化一个新的 L2Hub 对象.
1. 监听事件
有两种常用的方法为一个应用程序向控制器注册事件.
(1)针对特定的事件(事件可以是被 OpenFlow handler 模块或象拓扑发现之类的特定模块所抛出的), 注册回调函数 .
可以在 launch()函数中在一个类初始化时完成:
core.openflow.addListenerByName("EVENTNAME", CALLBACK_FUNC, PRIORITY)
(2)注册对象到 OpenFlow handler 模块或类似拓扑发现之类的特定模块.
典型在一个类初始化时, 调用 addListener(self). 一旦添加了这样的代码, 控制器将查找一个名称为 _handle_EVENTNAME(self, event)的函数. 这个方法被自动的注册为一个事件句柄(event hadler).
2. 数据包解析
POX 提供了几个基本组件完成常见包的解析, 通过 "packet_in" 事件接收到的以太网包可以使用下面的代码:
- packet = event.parsed
- src_mac = packet.src
- dst_mac = packet.dst
- if packet.type == ethernet.IP_TYPE:
- ipv4_packet = event.parsed.find("ipv4")
- # Do more processing of the IPv4 packet
- src_ip = ipv4_packet.srcip
- src_ip = ipv4_packet.dstip
然后, 使用 from_packet 函数创建流规则的匹配:
match = of.ofp_match.from_packet(packet)
3. 向交换机发送报文
在 POX 中向交换机发送 packet_out,flow_mod 和其它的 OpenFlow 协议报文是很简单的. 具体步骤:
(1)构建相应的报文对象, 在报文中填入字段值.
(2)使用 connection 对象向交换机发送报文: self.connect.send(). 下面是一个例子:
- msg = of.ofp_packet_out() # Create packet out message
- msg.buffer_id = event.ofp.buffer_id # Use the incoming packet as the data for the packet out
- msg.in_port = packet_in.in_port # Set the in_port so that the switch knows
- msg.match = of.ofp_match.from_packet(packet)
- # Add an action to send to the specified port
- action = of.ofp_action_output(port = of.OFPP_FLOOD)
- msg.actions.append(action)
- # Send message to switch
- self.connection.send(msg)
4. 管理多个应用程序
POX 支持多个应用程序同时工作. 被控制器接收的事件的回调函数具有优先级 (在函数 addListenerByName() 中指定优先级).
更多的内容
从交换机的报文 (发送到控制器) 中产生的中断:
FlowRemoved, FeaturesReceived, ConnectionUp, FeaturesReceived, RawStatsReply, PortStatus, PacketIn, BarrierIn, SwitchDescReceived, FlowStatsReceived, QueueStatsReceived, AggregateFlowStatsReceived, TableStatsReceived, PortStatsReceived
可被 pox/lib 解析的网络协议数据包:
arp, dhcp, dns, eapol, eap, ethernet, icmp, igmp, ipv4, llc, lldp, mpls, rip, tcp, udp, vlan
也可以使用命令'grep class pox/pox/openflow/libopenflow_01.py'查看应用程序可以发送给交换机的报文列表.
更多示例应用
二层 MAC 学习型交换机(L2 MAC Learning Switch): 将 pox/pox/misc/of_tutorial.py 转变为一个二层交换机
无状态负载均衡器 (Stateless load-balancer): pox/pox/forwarding/tutorial_stateless_lb.py 实现了一个负载均衡器(处理进入的 HTTP 请求), 它以循环的方式将 HTTP 请求送给一组服务器. 在代码中, 服务器的 IP 地址是静态设置的. 当流量到达负载均衡器的虚拟 IP 时, 负载均衡器以轮询(或称为循环) 的方式, 在重写双向 IP 报头后将请求转发给不同的真正的 HTTP 服务器. Mininet 中网络拓扑的配置在这里 http://sdnhub.cn/index.php/useful-mininet-setups / 可以找到.
本文固定链接: http://sdnhub.cn/index.php/pox-beginner-tutorial/ | 软件定义网络 SDN
来源: http://sdnhub.cn/index.php/pox-beginner-tutorial/