如何通过Ryu控制器编程实现单交换机下主机VLAN划分
Ryu控制器实现端口VLAN划分方案
实现逻辑
要达到和手动配置OVS端口VLAN完全一致的效果,只需要在Ryu中模拟传统二层接入交换机的VLAN处理规则即可,不需要手动修改交换机端口配置:
- 入方向处理:主机发送的是不带VLAN标签的普通以太网帧,交换机收到接入端口的报文后,先为其绑定该端口对应的VLAN ID
- 转发规则:报文仅能在同VLAN的端口之间转发,跨VLAN报文直接丢弃
- 出方向处理:报文从同VLAN的目标端口发出前,剥离VLAN标签,保证终端主机可以正常识别帧内容
完整实现代码
把代码保存为vlan_app.py即可,基于OpenFlow 1.3版本实现,适配OVS默认的OpenFlow版本支持:
from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER, set_ev_cls from ryu.ofproto import ofproto_v1_3 from ryu.lib.packet import packet, ethernet class SimplePortVLAN(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] def __init__(self, *args, **kwargs): super(SimplePortVLAN, self).__init__(*args, **kwargs) # 端口VLAN映射表:key为交换机端口号,value为所属VLAN ID,和需求一一对应 self.port_vlan_map = { 1: 10, 2: 20, 3: 10, 4: 20 } # 记录MAC地址对应的转发端口,用于后续二层转发 self.mac_to_port = {} @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_init(self, ev): """交换机连接上线时初始化配置,下发默认流表将未知报文送控制器""" datapath = ev.msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser # 优先级0的默认流,所有未匹配的报文转发到控制器 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): """封装流表下发方法""" ofproto = datapath.ofproto parser = datapath.ofproto_parser inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] 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): """处理上报到控制器的报文,完成VLAN转发逻辑+流表下发""" msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser in_port = msg.match['in_port'] # 非定义VLAN的端口收到的报文直接丢弃 if in_port not in self.port_vlan_map: return in_vlan = self.port_vlan_map[in_port] pkt = packet.Packet(msg.data) eth_pkt = pkt.get_protocol(ethernet.ethernet) dst_mac = eth_pkt.dst src_mac = eth_pkt.src # 学习源MAC对应的端口和VLAN self.mac_to_port.setdefault(datapath.id, {}) self.mac_to_port[datapath.id][src_mac] = (in_port, in_vlan) # 确定目标转发端口 out_ports = [] if dst_mac in self.mac_to_port[datapath.id]: # 已知单播报文,检查目标端口VLAN是否和入端口一致,一致才转发 out_port, out_vlan = self.mac_to_port[datapath.id][dst_mac] if out_vlan == in_vlan: out_ports.append(out_port) else: # 未知单播/广播报文,泛洪到同VLAN的所有其他端口 for port, vlan in self.port_vlan_map.items(): if vlan == in_vlan and port != in_port: out_ports.append(port) # 构造转发动作,出方向统一剥离VLAN标签 actions = [] for out_port in out_ports: # 入方向打VLAN标签、出方向剥标签,和传统接入端口行为完全一致 actions += [ parser.OFPActionPushVlan(0x8100), parser.OFPActionSetField(vlan_vid=(0x1000 | in_vlan)), parser.OFPActionPopVlan(), parser.OFPActionOutput(out_port) ] # 下发流表,后续同类型报文直接由交换机转发,不需要再上报控制器 if out_ports: match = parser.OFPMatch(in_port=in_port, eth_dst=dst_mac, eth_src=src_mac) self.add_flow(datapath, 10, match, actions) # 发送当前报文 out = parser.OFPPacketOut( datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port, actions=actions, data=msg.data if msg.buffer_id == ofproto.OFP_NO_BUFFER else None ) datapath.send_msg(out)
验证步骤
- 启动Mininet拓扑,不要执行任何ovs-vsctl的VLAN相关配置命令,直接执行:
sudo mn --topo single,4 --controller remote --mac -i 10.0.0.1/24 - 另开终端启动Ryu应用:
ryu-manager vlan_app.py - 在Mininet命令行执行连通性测试:
h1 ping h3连通正常,同属VLAN10h2 ping h4连通正常,同属VLAN20h1 ping h2、h3 ping h4不通,跨VLAN二层隔离,和手动OVS配置效果完全一致
如果之前手动配置过OVS的VLAN,启动Mininet前先执行
sudo mn -c清除所有OVS残留配置即可,不需要额外修改交换机参数。
内容的提问来源于stack exchange,提问作者COLLiDER
相关产品推荐
相关产品推荐

