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

Mininet自定义拓扑下Pingall及主机间Ping失败求助

自定义Mininet拓扑与Ryu控制器连通性故障排查

故障现象

  • 执行pingall或h1 ping h2操作时,100%丢包,无网络连通性
  • Ryu控制器日志显示:控制器启动正常,交换机成功连接;ping操作时收到Packet-in、ARP请求包,且发送了ARP回复,但Mininet终端无响应

相关代码

自定义Mininet拓扑代码

from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import RemoteController, OVSSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel

class CustomTopo(Topo):
    def build(self):
        s1 = self.addSwitch('s1', protocols='OpenFlow13')
        h1 = self.addHost('h1', ip='10.0.0.1/24', mac='00:00:00:00:00:01')
        h2 = self.addHost('h2', ip='10.0.0.2/24', mac='00:00:00:00:00:02')
        self.addLink(h1, s1)
        self.addLink(h2, s1)

def run():
    topo = CustomTopo()
    net = Mininet(topo=topo,
                  controller=lambda name: RemoteController(name, ip='127.0.0.1', port=6653),
                  switch=OVSSwitch,
                  autoSetMacs=True)
    net.start()
    CLI(net)
    net.stop()

if __name__ == '__main__':
    setLogLevel('info')
    run()

Ryu控制器路由代码

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, ethernet, ipv4, arp
from ryu.lib.packet import ether_types

class SimpleRouter(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(SimpleRouter, self).__init__(*args, **kwargs)
        self.arp_table = {
            '10.0.0.1': '00:00:00:00:00:01',
            '10.0.0.2': '00:00:00:00:00:02',
        }

    @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
    def switch_features_handler(self, ev):
        datapath = ev.msg.datapath
        parser = datapath.ofproto_parser
        ofproto = datapath.ofproto
        match = parser.OFPMatch()
        actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)]
        inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)]
        mod = parser.OFPFlowMod(datapath=datapath, priority=0, match=match, instructions=inst)
        datapath.send_msg(mod)

    def add_flow(self, datapath, priority, match, actions):
        parser = datapath.ofproto_parser
        ofproto = datapath.ofproto
        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):
        msg = ev.msg
        datapath = msg.datapath
        parser = datapath.ofproto_parser
        in_port = msg.match['in_port']
        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocol(ethernet.ethernet)

        if eth.ethertype == ether_types.ETH_TYPE_ARP:
            self.handle_arp(datapath, pkt, in_port)
        elif eth.ethertype == ether_types.ETH_TYPE_IP:
            ip_pkt = pkt.get_protocol(ipv4.ipv4)
            dst_ip = ip_pkt.dst
            if dst_ip in self.arp_table:
                out_port = 2 if in_port == 1 else 1
                actions = [parser.OFPActionOutput(out_port)]
                match = parser.OFPMatch(
                    eth_type=ether_types.ETH_TYPE_IP,
                    ipv4_dst=dst_ip
                )
                self.add_flow(datapath, 1, match, actions)
                datapath.send_packet_out(
                    buffer_id=msg.buffer_id,
                    in_port=in_port,
                    actions=actions,
                    data=msg.data
                )

    def handle_arp(self, datapath, pkt, port):
        arp_pkt = pkt.get_protocol(arp.arp)
        dst_ip = arp_pkt.dst_ip
        if dst_ip not in self.arp_table:
            return
        src_mac = self.arp_table[dst_ip]
        dst_mac = arp_pkt.src_mac
        src_ip = dst_ip
        dst_ip = arp_pkt.src_ip

        e = ethernet.ethernet(dst=dst_mac, src=src_mac, ethertype=ether_types.ETH_TYPE_ARP)
        a = arp.arp(opcode=arp.ARP_REPLY,
                    src_mac=src_mac, src_ip=src_ip,
                    dst_mac=dst_mac, dst_ip=dst_ip)
        p = packet.Packet()
        p.add_protocol(e)
        p.add_protocol(a)
        p.serialize()
        actions = [datapath.ofproto_parser.OFPActionOutput(port)]
        datapath.send_packet_out(
            buffer_id=0xffffffff,
            in_port=datapath.ofproto.OFPP_CONTROLLER,
            actions=actions,
            data=p.data
        )

故障原因

  1. MAC地址不匹配:Mininet拓扑中开启了autoSetMacs=True,会自动覆盖手动设置的主机MAC地址,导致Ryu控制器静态ARP表中的MAC与实际主机MAC不一致,ARP回复无法被主机正确识别。
  2. IP包转发未修正以太网MAC:Ryu控制器转发IP包时,未修改以太网帧的目的MAC为目标主机的正确MAC,即使数据包被转发,目标主机可能因MAC不匹配丢弃数据包。

修复方案

1. 修正Mininet拓扑MAC配置

关闭autoSetMacs,确保手动设置的MAC地址生效:

def run():
    topo = CustomTopo()
    net = Mininet(topo=topo,
                  controller=lambda name: RemoteController(name, ip='127.0.0.1', port=6653),
                  switch=OVSSwitch,
                  autoSetMacs=False)  # 禁用自动MAC分配
    net.start()
    CLI(net)
    net.stop()

2. 修正Ryu控制器IP包转发逻辑

转发IP包时,重新构造以太网帧,设置正确的目的MAC:

@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
    msg = ev.msg
    datapath = msg.datapath
    parser = datapath.ofproto_parser
    ofproto = datapath.ofproto
    in_port = msg.match['in_port']
    pkt = packet.Packet(msg.data)
    eth = pkt.get_protocol(ethernet.ethernet)

    if eth.ethertype == ether_types.ETH_TYPE_ARP:
        self.handle_arp(datapath, pkt, in_port)
    elif eth.ethertype == ether_types.ETH_TYPE_IP:
        ip_pkt = pkt.get_protocol(ipv4.ipv4)
        dst_ip = ip_pkt.dst
        src_ip = ip_pkt.src
        if dst_ip in self.arp_table and src_ip in self.arp_table:
            out_port = 2 if in_port == 1 else 1
            # 获取源和目的主机的正确MAC
            src_mac = self.arp_table[src_ip]
            dst_mac = self.arp_table[dst_ip]
            # 重新构造以太网帧和IP包
            new_eth = ethernet.ethernet(dst=dst_mac, src=src_mac, ethertype=ether_types.ETH_TYPE_IP)
            new_pkt = packet.Packet()
            new_pkt.add_protocol(new_eth)
            new_pkt.add_protocol(ip_pkt)
            new_pkt.serialize()
            # 设置转发动作和流表项
            actions = [parser.OFPActionOutput(out_port)]
            match = parser.OFPMatch(
                eth_type=ether_types.ETH_TYPE_IP,
                ipv4_dst=dst_ip
            )
            self.add_flow(datapath, 1, match, actions)
            # 发送转发数据包
            datapath.send_packet_out(
                buffer_id=msg.buffer_id,
                in_port=in_port,
                actions=actions,
                data=new_pkt.data if msg.buffer_id == ofproto.OFP_NO_BUFFER else None
            )

验证修复

  1. 重启Ryu控制器和Mininet拓扑
  2. 在Mininet终端执行pingall,应显示h1与h2连通
  3. 查看Ryu日志,确认ARP回复正常发送,IP包转发流表项正确添加

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 01:37:04