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

在RYU控制器中通过ping检测节点可用性的实现方法问询

在RYU控制器中实现节点Ping检测的方案

核心思路

RYU本身没有封装现成的ICMP Ping API,你可以通过两种方式实现节点在线检测:要么调用系统ping命令快速验证,要么直接构造ICMP包通过RYU的数据包收发机制处理(更贴合SDN原生场景)。

方案一:调用系统Ping命令(快速实现)

适合快速验证需求,在RYU应用中启动独立线程,执行系统ping命令并解析返回结果判断节点状态:

import subprocess
import threading
from ryu.base.app_manager import RyuApp
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

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

    def __init__(self, *args, **kwargs):
        super(NodeMonitor, self).__init__(*args, **kwargs)
        self.node_status = {}  # 存储节点状态:key为IP,value为True(在线)/False(离线)
        # 启动定时检测线程
        self.monitor_thread = threading.Thread(target=self._ping_nodes)
        self.monitor_thread.daemon = True
        self.monitor_thread.start()

    def _ping_nodes(self):
        import time
        while True:
            # 替换为你需要检测的节点IP列表
            target_nodes = ["192.168.1.100", "192.168.1.101"]
            for node_ip in target_nodes:
                # 执行ping命令:发送2个包,超时1秒
                result = subprocess.run(
                    ["ping", "-c", "2", "-W", "1", node_ip],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    text=True
                )
                # 根据返回码判断状态(0为成功,即在线)
                self.node_status[node_ip] = (result.returncode == 0)
                self.logger.info(f"Node {node_ip} status: {'Online' if self.node_status[node_ip] else 'Offline'}")
            time.sleep(10)  # 每10秒检测一次

    # 以下是你原有控制器的逻辑(示例)
    @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

        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)

方案二:构造ICMP包(SDN原生方式)

更适合SDN环境,直接通过RYU构造ICMP Echo Request包发送到节点,监听ICMP Echo Reply来标记在线状态:

import struct
import threading
from ryu.base.app_manager import RyuApp
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER, DEAD_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, ipv4, icmp

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

    def __init__(self, *args, **kwargs):
        super(NodeMonitor, self).__init__(*args, **kwargs)
        self.node_status = {}
        self.datapaths = {}  # 存储在线交换机的datapath实例
        self.monitor_thread = threading.Thread(target=self._send_icmp_requests)
        self.monitor_thread.daemon = True
        self.monitor_thread.start()

    @set_ev_cls(ofp_event.EventOFPStateChange, [MAIN_DISPATCHER, DEAD_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 == DEAD_DISPATCHER:
            if datapath.id in self.datapaths:
                del self.datapaths[datapath.id]

    def _send_icmp_requests(self):
        import time
        while True:
            target_nodes = ["192.168.1.100", "192.168.1.101"]
            controller_ip = "192.168.1.200"  # 替换为你的控制器IP
            # 无在线交换机时等待重试
            if not self.datapaths:
                time.sleep(5)
                continue
            # 取第一个在线交换机作为发包出口(多交换机场景需根据拓扑选择对应出口)
            datapath = next(iter(self.datapaths.values()))
            ofproto = datapath.ofproto
            parser = datapath.ofproto_parser

            for node_ip in target_nodes:
                # 初始标记为离线,收到回复后更新状态
                self.node_status[node_ip] = False
                # 构造ICMP Echo Request包
                icmp_pkt = icmp.icmp(
                    type_=icmp.ICMP_ECHO_REQUEST,
                    code=0,
                    csum=0,
                    id=0x1234,
                    seq=0x0001
                )
                ipv4_pkt = ipv4.ipv4(
                    version=4,
                    header_length=5,
                    tos=0,
                    total_length=20 + 8,  # IPv4头(20字节) + ICMP头(8字节)
                    identification=0,
                    flags=0,
                    offset=0,
                    ttl=64,
                    proto=ipv4.inet.IPPROTO_ICMP,
                    csum=0,
                    src=controller_ip,
                    dst=node_ip
                )
                eth_pkt = ethernet.ethernet(
                    dst="ff:ff:ff:ff:ff:ff",  # 已知节点MAC可替换,减少广播
                    src=datapath.address,
                    ethertype=ethernet.ETH_TYPE_IP
                )
                pkt = packet.Packet()
                pkt.add_protocol(eth_pkt)
                pkt.add_protocol(ipv4_pkt)
                pkt.add_protocol(icmp_pkt)
                pkt.serialize()

                # 构造PacketOut消息发送
                actions = [parser.OFPActionOutput(ofproto.OFPP_FLOOD)]
                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)
                self.logger.info(f"Sent ICMP request to {node_ip}")
            time.sleep(10)
            # 打印当前节点状态
            for ip, status in self.node_status.items():
                self.logger.info(f"Node {ip} status: {'Online' if status else 'Offline'}")

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        msg = ev.msg
        pkt = packet.Packet(msg.data)
        icmp_pkt = pkt.get_protocol(icmp.icmp)

        # 监听ICMP Echo Reply包
        if icmp_pkt and icmp_pkt.type == icmp.ICMP_ECHO_REPLY:
            ipv4_pkt = pkt.get_protocol(ipv4.ipv4)
            src_ip = ipv4_pkt.src
            self.node_status[src_ip] = True
            self.logger.info(f"Received ICMP reply from {src_ip}, marked as online")

    # 以下是你原有控制器的逻辑(示例)
    @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

        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)

注意事项

  • 方案一跨平台需调整ping参数:Windows下用-n代替-c指定发包数量。
  • 方案二需确保控制器IP能被节点访问,且交换机流表允许ICMP包转发到控制器;已知节点MAC时替换广播MAC,可降低网络负载。
  • 可根据需求调整检测间隔,避免过于频繁导致网络压力过大。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 04:10:03