Mininet中控制器检测链路故障的方法及流表删除需求咨询
Mininet环境下控制器检测链路故障并触发流表删除方案
我来帮你梳理下Mininet环境里控制器检测链路故障的核心逻辑,还有解决你用HostTracker没达到预期效果的问题,以及实现你要的「链路故障时通知所有交换机删除对应目的主机流表」的需求。
一、控制器检测链路故障的常用机制
OpenFlow控制器主要通过两种方式感知链路故障:
- 端口状态事件(PortStatus):当交换机的物理端口down掉(比如链路被拔断),交换机会主动给控制器发送
OFPPortStatus消息,控制器监听这个事件就能快速感知故障。 - 主动链路探测(LLDP/BDDP):对于那种端口没down但链路实际不通的情况(比如光纤松动),控制器可以定期发送LLDP(链路层发现协议)报文,交换机收到后会转发到对端交换机,控制器如果在超时时间内没收到对端的LLDP回应,就判定链路故障。BDDP是更轻量的替代方案,适合低延迟检测。
二、为什么HostTracker没达到你的预期?
HostTracker的核心作用是跟踪主机的位置——它通过监听ARP报文,学习主机IP和交换机端口的映射关系,但它本身不负责链路故障检测:
- 链路断了之后,HostTracker要等主机主动发送ARP请求才会更新映射,反应非常慢;
- 它没有触发流表删除的逻辑,完全不满足你「实时通知删流表」的需求。所以用它来做故障检测确实不合适。
三、实现需求的具体方案(以Ryu控制器为例)
下面我用Ryu(最常用的OpenFlow控制器之一)给你写个完整的示例,涵盖故障检测和流表删除逻辑:
1. 监听链路故障事件
我们结合PortStatus事件和LLDP主动探测,覆盖所有故障场景:
from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER from ryu.controller.handler import set_ev_cls from ryu.ofproto import ofproto_v1_3 from ryu.lib.packet import lldp, ethernet from ryu.lib import hub from ryu.lib.packet import arp, ipv4 class LinkFailureHandler(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] def __init__(self, *args, **kwargs): super(LinkFailureHandler, self).__init__(*args, **kwargs) self.datapaths = {} # 存储所有连接的交换机 self.link_map = {} # 链路映射:(src_dpid, src_port) -> (dst_dpid, dst_port) self.host_map = {} # 主机位置映射:{host_ip: (dpid, port)} # 启动LLDP发送线程,每2秒探测一次链路 self.lldp_thread = hub.spawn(self._periodic_lldp_send) # 链路超时检查线程,每3秒检查一次 self.link_timeout_thread = hub.spawn(self._check_link_timeout) self.link_last_update = {} # 记录每条链路的最后更新时间 # 定期发送LLDP报文 def _periodic_lldp_send(self): while True: for dp in self.datapaths.values(): for port in dp.ports.values(): if port.port_no != ofproto_v1_3.OFPP_LOCAL: self._send_lldp_packet(dp, port.port_no) hub.sleep(2) # 构造并发送LLDP报文 def _send_lldp_packet(self, datapath, port_no): ofproto = datapath.ofproto parser = datapath.ofproto_parser # 构造LLDP的ChassisID和PortID chassis_id = lldp.ChassisID(subtype=lldp.ChassisID.SUB_LOCALLY_ASSIGNED, id=str(datapath.id).encode()) port_id = lldp.PortID(subtype=lldp.PortID.SUB_PORT_NUMBER, id=str(port_no).encode()) lldp_pkt = lldp.lldp(chassis_id=chassis_id, port_id=port_id, ttl=lldp.TTL(ttl=1)) eth_pkt = ethernet.ethernet(dst=lldp.LLDP_MAC_NEAREST_BRIDGE, src=datapath.ports[port_no].hw_addr, ethertype=ethernet.ETH_TYPE_LLDP) pkt = eth_pkt / lldp_pkt # 发送PacketOut消息 actions = [parser.OFPActionOutput(port_no)] out = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=ofproto.OFPP_CONTROLLER, actions=actions, data=pkt.data) datapath.send_msg(out) # 监听交换机连接事件,初始化datapaths @set_ev_cls(ofp_event.EventOFPStateChange, [MAIN_DISPATCHER, CONFIG_DISPATCHER]) def state_change_handler(self, ev): datapath = ev.datapath if ev.state == MAIN_DISPATCHER: if datapath.id not in self.datapaths: self.datapaths[datapath.id] = datapath elif ev.state == CONFIG_DISPATCHER: if datapath.id in self.datapaths: del self.datapaths[datapath.id] # 监听PortStatus事件,处理端口down/删除 @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) def port_status_handler(self, ev): msg = ev.msg dp = msg.datapath ofproto = dp.ofproto reason = msg.reason port_no = msg.desc.port_no if reason in [ofproto.OFPPR_DELETE, ofproto.OFPPR_DOWN]: key = (dp.id, port_no) if key in self.link_map: dst_dpid, dst_port = self.link_map[key] self.logger.info(f"Link failure detected: Switch {dp.id}:{port_no} <-> Switch {dst_dpid}:{dst_port}") # 删除链路映射 del self.link_map[key] del self.link_map[(dst_dpid, dst_port)] # 触发流表删除 self._delete_host_flows_on_failure(dst_dpid) # 监听PacketIn事件,处理LLDP和ARP报文 @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def packet_in_handler(self, ev): msg = ev.msg dp = msg.datapath in_port = msg.match['in_port'] pkt = ethernet.ethernet.parser(msg.data) # 处理LLDP报文,更新链路映射和超时时间 if pkt.ethertype == ethernet.ETH_TYPE_LLDP: lldp_pkt = pkt.payload src_dpid = int(lldp_pkt.chassis_id.id.decode()) src_port = int(lldp_pkt.port_id.id.decode()) # 更新双向链路映射 self.link_map[(src_dpid, src_port)] = (dp.id, in_port) self.link_map[(dp.id, in_port)] = (src_dpid, src_port) # 更新最后更新时间 self.link_last_update[(src_dpid, src_port)] = hub.time() self.link_last_update[(dp.id, in_port)] = hub.time() # 处理ARP报文,学习主机位置(替代HostTracker的功能) elif pkt.ethertype == ethernet.ETH_TYPE_ARP: arp_pkt = pkt.payload if arp_pkt.opcode == arp.ARP_REQUEST: src_ip = arp_pkt.src_ip self.host_map[src_ip] = (dp.id, in_port) self.logger.info(f"Learned host {src_ip} on Switch {dp.id}:{in_port}") # 检查链路超时,处理LLDP没回应的情况 def _check_link_timeout(self): while True: current_time = hub.time() # 遍历所有链路,检查超时(超过3秒没更新就判定故障) to_delete = [] for link_key, last_time in self.link_last_update.items(): if current_time - last_time > 3: to_delete.append(link_key) # 删除超时链路并触发流表删除 for link_key in to_delete: src_dpid, src_port = link_key dst_dpid, dst_port = self.link_map[link_key] self.logger.info(f"Link timeout detected: Switch {src_dpid}:{src_port} <-> Switch {dst_dpid}:{dst_port}") del self.link_map[link_key] del self.link_map[(dst_dpid, dst_port)] del self.link_last_update[link_key] del self.link_last_update[(dst_dpid, dst_port)] self._delete_host_flows_on_failure(dst_dpid) hub.sleep(1)
2. 实现流表删除逻辑
当检测到链路故障后,我们需要找出故障交换机侧的所有主机,然后通知其他交换机删除到这些主机的流表:
def _delete_host_flows_on_failure(self, failed_dpid): # 找出所有在故障交换机上的主机 affected_hosts = [ip for ip, (dpid, _) in self.host_map.items() if dpid == failed_dpid] if not affected_hosts: self.logger.info(f"No hosts on failed switch {failed_dpid}, skipping flow deletion") return # 遍历所有交换机,删除到这些主机的流表 for dp in self.datapaths.values(): ofproto = dp.ofproto parser = dp.ofproto_parser for host_ip in affected_hosts: # 构造流删除消息,匹配目的IP match = parser.OFPMatch(ipv4_dst=host_ip) flow_mod = parser.OFPFlowMod( datapath=dp, command=ofproto.OFPFC_DELETE, out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, match=match ) dp.send_msg(flow_mod) self.logger.info(f"Deleted flows to {host_ip} on Switch {dp.id}")
3. 额外说明
- 如果用POX控制器,思路完全一致:监听
PortStatus事件,用lldp模块做主动探测,然后通过flow_mod消息删除流表; - 你可以根据实际需求调整LLDP发送间隔和超时时间,比如把间隔改成1秒,超时改成2秒,提升检测速度;
- 主机位置学习的逻辑是简化版,你可以扩展成支持ARP响应、IPv6等场景,或者直接整合HostTracker的代码到这个应用里。
内容的提问来源于stack exchange,提问作者Carol
相关产品推荐
相关产品推荐

