You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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 连通正常,同属VLAN10
    • h2 ping h4 连通正常,同属VLAN20
    • h1 ping h2、h3 ping h4 不通,跨VLAN二层隔离,和手动OVS配置效果完全一致

如果之前手动配置过OVS的VLAN,启动Mininet前先执行sudo mn -c清除所有OVS残留配置即可,不需要额外修改交换机参数。

内容的提问来源于stack exchange,提问作者COLLiDER

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 20:39:17