- 文集信息
- 目录大纲
- 最新文档
- 知识宇宙
文集详情
文集导读
OpenFlow
OpenFlow 协议详解与实践
1. OpenFlow 协议概述
OpenFlow 是一种网络交换协议,它允许软件定义网络(SDN)控制器直接访问和控制网络设备的转发平面。 传统的网络设备,如交换机和路由器,其控制平面和数据平面紧密耦合。 OpenFlow 将这两个平面分离,使得控制平面可以在一个集中的控制器上实现,而数据平面则由网络设备执行。 这种分离带来了极大的灵活性和可编程性,使得网络管理和创新变得更加容易。
1.1 核心概念
-
Flow Table (流表): OpenFlow 交换机的核心组件,包含一系列流条目(Flow Entry)。每个流条目定义了如何处理特定类型的网络流量。
-
Flow Entry (流条目): 由匹配规则(Match Fields)、指令集(Instructions)和统计信息(Counters)组成。
-
Match Fields: 用于匹配特定类型的网络流量,例如源/目的 MAC 地址、IP 地址、端口号等。
-
Instructions: 定义了匹配流量后要执行的操作,例如转发到特定端口、修改 VLAN ID、丢弃等。
-
Counters: 用于记录匹配流量的统计信息,例如匹配的包数量和字节数量。
-
-
Secure Channel (安全通道): OpenFlow 交换机与控制器之间的安全通信通道,通常使用 TLS 协议。
-
OpenFlow Controller (OpenFlow 控制器): 负责管理和配置 OpenFlow 交换机的流表,并收集网络状态信息。
1.2 工作原理
-
当 OpenFlow 交换机收到一个数据包时,它首先会在流表中查找匹配的流条目。
-
如果找到匹配的流条目,交换机将按照流条目中定义的指令处理该数据包。
-
如果没有找到匹配的流条目,交换机可以将该数据包发送到控制器进行处理(Packet-In 消息),或者按照默认行为(例如丢弃)处理。
-
控制器收到 Packet-In 消息后,可以根据网络策略决定如何处理该数据包,并向交换机下发新的流条目,以便后续流量可以直接在交换机上处理。
1.3 协议版本
OpenFlow 协议有多个版本,常见的版本包括 OpenFlow 1.0、1.3 和 1.5。 不同版本之间存在一些差异,例如支持的匹配字段、指令集和消息类型。
2. OpenFlow 协议详解
2.1 流表结构
流表由多个流条目组成,每个流条目包含以下字段:
-
Priority (优先级): 用于确定流条目的匹配顺序。 优先级较高的流条目会优先匹配。
-
Match Fields (匹配字段): 用于匹配特定类型的网络流量。 常见的匹配字段包括:
-
in_port: 入口端口 -
eth_dst: 目的 MAC 地址 -
eth_src: 源 MAC 地址 -
eth_type: 以太网类型 -
vlan_id: VLAN ID -
ip_src: 源 IP 地址 -
ip_dst: 目的 IP 地址 -
ip_proto: IP 协议 -
tcp_src: TCP 源端口 -
tcp_dst: TCP 目的端口 -
udp_src: UDP 源端口 -
udp_dst: UDP 目的端口
-
-
Instructions (指令): 定义了匹配流量后要执行的操作。 常见的指令包括:
-
GOTO_TABLE: 跳转到另一个流表 -
WRITE_METADATA: 写入元数据 -
APPLY_ACTIONS: 应用动作 -
CLEAR_ACTIONS: 清除动作 -
WRITE_ACTIONS: 写入动作 -
METER: 应用 Meter
-
-
Actions (动作): 用于修改数据包或执行转发操作。 常见的动作包括:
-
OUTPUT: 转发到指定端口 -
SET_FIELD: 修改数据包头部字段 -
PUSH_VLAN: 添加 VLAN 标签 -
POP_VLAN: 移除 VLAN 标签 -
GROUP: 转发到组表
-
-
Counters (计数器): 用于记录匹配流量的统计信息。
2.2 OpenFlow 消息类型
OpenFlow 协议定义了多种消息类型,用于控制器和交换机之间的通信。 主要消息类型包括:
-
Controller-to-Switch Messages (控制器到交换机消息):
-
Features Request/Reply: 用于查询交换机的能力。 -
Configuration Messages: 用于配置交换机的参数。 -
Modify State Messages: 用于添加、修改或删除流条目。 -
Packet-Out: 用于从控制器发送数据包到交换机。 -
Barrier Request/Reply: 用于确保消息的顺序执行。 -
Role Request/Reply: 用于设置控制器的角色。
-
-
Switch-to-Controller Messages (交换机到控制器消息):
-
Packet-In: 用于将未匹配的数据包发送到控制器。 -
Flow Removed: 用于通知控制器流条目被移除。 -
Port Status: 用于通知控制器端口状态的变化。 -
Error: 用于通知控制器发生的错误。
-
2.3 协议流程示例
以下是一个简单的 OpenFlow 协议流程示例:
3. OpenFlow 代码实践 (Python + Mininet + Ryu)
本节将使用 Python 编程语言,结合 Mininet 网络仿真器和 Ryu OpenFlow 控制器,演示如何编写 OpenFlow 程序。
3.1 环境搭建
-
安装 Mininet:
sudo apt-get update sudo apt-get install mininet -
安装 Ryu:
sudo apt-get install python3-pip sudo pip3 install ryu
3.2 编写 Ryu 控制器
以下是一个简单的 Ryu 控制器,用于实现一个 Hub 功能:
from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER from ryu.controller.handler import set_ev_cls from ryu.ofproto import ofproto_v1_3 from ryu.lib.packet import packet from ryu.lib.packet import ethernet class SimpleSwitch13(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] def __init__(self, *args, **kwargs): super(SimpleSwitch13, self).__init__(*args, **kwargs) self.mac_to_port = {} @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): datapath = ev.msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser # install the table-miss flow entry. # # We specify NO BUFFER to drop the packet regarding table-miss # unless you have reasons to send the packet to the controller. match = parser.OFPMatch() actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] self.add_flow(datapath, 0, match, actions) def add_flow(self, datapath, priority, match, actions, buffer_id=None): ofproto = datapath.ofproto parser = datapath.ofproto_parser inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] if buffer_id: mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, priority=priority, match=match, instructions=inst) else: mod = parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst) datapath.send_msg(mod) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def _packet_in_handler(self, ev): # If you hit this you might want to increase # the "miss_send_length" of the switch if ev.msg.msg_len < ev.msg.total_len: self.logger.debug("packet truncated: only %s of %s bytes", ev.msg.msg_len, ev.msg.total_len) msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser in_port = msg.match['in_port'] pkt = packet.Packet(msg.data) eth = pkt.get_protocols(ethernet.ethernet)[0] dst = eth.dst src = eth.src dpid = datapath.id self.mac_to_port.setdefault(dpid, {}) self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) # learn a mac address to avoid FLOOD next time. self.mac_to_port[dpid][src] = in_port if dst in self.mac_to_port[dpid]: out_port = self.mac_to_port[dpid][dst] else: out_port = ofproto.OFPP_FLOOD actions = [parser.OFPActionOutput(out_port)] # install a flow to avoid packet_in next time if out_port != ofproto.OFPP_FLOOD: match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) # verify if we have a valid buffer_id, if yes avoid to send both # flow_mod & packet_out if msg.buffer_id != ofproto.OFP_NO_BUFFER: self.add_flow(datapath, 1, match, actions, msg.buffer_id) return else: self.add_flow(datapath, 1, match, actions) data = None if msg.buffer_id == ofproto.OFP_NO_BUFFER: data = msg.data out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port, actions=actions, data=data) datapath.send_msg(out)
将上述代码保存为 simple_switch_13.py。
3.3 运行 Mininet 拓扑
创建一个简单的 Mininet 拓扑:
from mininet.net import Mininet from mininet.node import Controller, RemoteController from mininet.cli import CLI from mininet.log import setLogLevel, info def create_topo(): net = Mininet(topo=None, build=False, ipBase='10.0.0.0/8') info('*** Adding controller\n') c0 = net.addController(name='c0', controller=RemoteController, ip='127.0.0.1', port=6633) info('*** Adding switches\n') s1 = net.addSwitch('s1', cls=OVSKernelSwitch) info('*** Adding hosts\n') h1 = net.addHost('h1', cls=Host, ip='10.0.0.1/8', defaultRoute=None) h2 = net.addHost('h2', cls=Host, ip='10.0.0.2/8', defaultRoute=None) info('*** Creating links\n') net.addLink(h1, s1) net.addLink(h2, s1) return net if __name__ == '__main__': setLogLevel('info') net = create_topo() net.start() CLI(net) net.stop()
将上述代码保存为 topo.py。
3.4 运行程序
-
启动 Ryu 控制器:
ryu-manager simple_switch_13.py -
启动 Mininet 拓扑:
sudo python3 topo.py -
在 Mininet CLI 中,尝试从
h1pingh2:mininet> h1 ping h2
如果一切正常,h1 应该能够成功 ping 通 h2。 这个简单的例子展示了如何使用 OpenFlow 和 Ryu 来控制网络流量。
4. 总结
OpenFlow 协议是 SDN 的核心组成部分,它通过将控制平面和数据平面分离,实现了网络的灵活性和可编程性。 通过使用 OpenFlow,网络管理员可以更加方便地管理和配置网络,并实现各种创新的网络应用。 本文详细介绍了 OpenFlow 协议的原理、结构和消息类型,并提供了一个使用 Python、Mininet 和 Ryu 的 OpenFlow 实践示例。 掌握 OpenFlow 协议对于理解和应用 SDN 技术至关重要。
目录大纲
最新文档
知识宇宙
正在加载知识图谱...