3-3 基于RYU的流量风暴事件原理与响应策略

在传统网络中,存在着一定的广播流量,占据了一部分的网络带宽。同时,在有环的拓扑中,如果不运行某些协议,广播数据还会引起网络风暴,使网络瘫痪。
如有以下的一个网络拓扑结构(3_2_topoplus.py)

from mininet.net import Mininet
from mininet.node import OVSSwitch, Host
from mininet.cli import CLI
from mininet.link import Link
from mininet.node import RemoteController
#import networkx as nx
#import matplotlib.pyplot as plt

def create_network():
    net = Mininet()
        # 创建单个OVS交换机
    switch1 = net.addSwitch('s1', cls=OVSSwitch,protocols='OpenFlow13')
    switch2 = net.addSwitch('s2', cls=OVSSwitch,protocols='OpenFlow13')
    
    
    # 创建2个主机
    host1 = net.addHost('h1', cls=Host, ip='192.168.0.1/24', defaultRoute='via 192.168.0.254')
    host2 = net.addHost('h2', cls=Host, ip='192.168.0.2/24', defaultRoute='via 192.168.0.254')
    host3 = net.addHost('h3', cls=Host, ip='192.168.0.3/24', defaultRoute='via 192.168.0.254')
    host4 = net.addHost('h4', cls=Host, ip='192.168.0.4/24', defaultRoute='via 192.168.0.254')

   
    
    # 连接主机到交换机
    net.addLink(host1, switch1)
    net.addLink(host2, switch1)
    net.addLink(host3, switch2)
    net.addLink(host4, switch2)
#交换机连接交换机
    net.addLink(switch1, switch2)
    net.addLink(switch1, switch2)#环路
     
    
    # 指定控制器的IP地址和端口
    #可以先使用ss -tlnp | grep ryu-manager查看ryu运行后的监听端口
    controller_ip = '127.0.0.1'
    controller_port = 6633

# 创建Mininet网络,并指定控制器和OpenFlow协议版本
  
    net.addController('controller', controller=RemoteController, ip=controller_ip, port=controller_port,protocols='OpenFlow13')
  
    # 启动网络
    net.start()
      
    # 打开命令行界面
    CLI(net)
    
    # 关闭网络
    net.stop()

if __name__ == '__main__':
    create_network()

结合上一篇文章中已经实现的控制器应用程序,我们进行测试,并观察产生的数据交互信息,因产生广播风暴(瞬间达到千万级别的包)导致资源崩溃,无法ping通
在这里插入图片描述
在软件定义网络中,要解决以上安全问题,我们可以在控制器应用程序中进行编程实现。下文将实现在控制器RYU应用程序上开发ARP代理功能模块,以解决目前的网络广播风暴安全隐患。

进入/etc/sysctl.conf修改linux内核配置,关闭icmpv6功能
root@zmq-virtual-machine:/etc# vi sysctl.conf
root@zmq-virtual-machine:/etc# nano sysctl.conf
root@zmq-virtual-machine:/etc# nano sysctl.conf
在这里插入图片描述
在文件的末尾添加以下行来禁用 ICMPv6:
复制
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6=1
这将禁用所有接口(all)和默认接口(default)的 IPv6 功能。
然后保存并重新加载配置文件并应用更改:
在这里插入图片描述
以上关闭后最后,重启一下linux系统会重启ipv6
在这里插入图片描述
经过以上操作后,如果生成的host中仍然还有ipv6因为我们在构建网络中比有IP协议的支撑,即使关掉HOST的IPv6 后仍然有ovs生成的虚拟交换机发送相关信息(即使禁用所有设备的ipv6相关你握手包,也还会有IPv4的包,如果都没有握手包,IP协议将因无法正常协商而出现异常)
因此,需要使用编程的办法在ryu控制器APP上进行数据包剔除,把相关的促发信息改为ARP(ping的先行数据包)才进行响应,包括下发流表和mac地址学习等。

解决问题: 前面的packet_in数据包是没有ip地址的?只有ARP才开始有?
1 在packet_in_handler函数中剔除ICMP

from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import set_ev_cls
from ryu.controller.handler import  MAIN_DISPATCHER
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.ofproto import  ofproto_v1_3
from ryu.lib.packet import  packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import ipv6
from ryu.lib.packet import icmp
from ryu.lib.packet import icmpv6


class L2Switch(app_manager.RyuApp):
 def __init__(self, *args, **kwargs):
     super(L2Switch, self).__init__(*args, **kwargs)
     self.mac_port_table={}

 @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
 def switch_features_handler(self, ev):
     datapath = ev.msg.datapath
     ofproto = datapath.ofproto

     #msg = ev.msg    #features阶段的数据包没有封装承载其他的协议,因此没有data字段 
     parser=datapath.ofproto_parser
     #pkt = packet.Packet(msg.data)#features阶段的数据包没有封装承载其他的协议,因此没有data字段 
     #icmp_pkt = pkt.get_protocol(icmp.icmp)#features阶段的数据包没有封装承载其他的协议,因此没有data字段 
     #icmp6_pkt = pkt.get_protocol(icmpv6.icmpv6)#features阶段的数据包没有封装承载其他的协议,因此没有data字段 
     #if not icmp_pkt and not icmp6_pkt:  #无需判断
     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_hander(self,ev):
     msg = ev.msg
     dp = msg.datapath
     ofp = dp.ofproto
     ofp_parser = dp.ofproto_parser
     in_port = msg.match['in_port']

     dpid=dp.id
     pkt = packet.Packet(msg.data)
     icmp_pkt = pkt.get_protocol(icmp.icmp)
     icmp6_pkt = pkt.get_protocol(icmpv6.icmpv6)
     if not icmp_pkt  and not icmp6_pkt:
         self.mac_port_table.setdefault(dpid, {})
         pkt = packet.Packet(msg.data)
         eth_pkt = pkt.get_protocols(ethernet.ethernet)[0]

         dst = eth_pkt.dst
         print(dst)
         src = eth_pkt.src
         self.mac_port_table[dpid][src] = in_port
         if dst in self.mac_port_table[dpid]:
             out_port = self.mac_port_table[dpid][dst]
         else:
             out_port = ofp.OFPP_FLOOD
         actions = [ofp_parser.OFPActionOutput(out_port)]

         if out_port != ofp.OFPP_FLOOD:
             match = ofp_parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
             # verify if we have a valid buffer_id, if yes avoid to send both
             # flow_mod & packet_out
             if msg.buffer_id != ofp.OFP_NO_BUFFER:
                 self.add_flow(dp, 1, match, actions, msg.buffer_id)
                 print("1")
                 return
             else:
                 self.add_flow(dp, 1, match, actions)
                 print("2")

         data = None
         if msg.buffer_id == ofp.OFP_NO_BUFFER:
             data = msg.data

         out = ofp_parser.OFPPacketOut(datapath=dp, buffer_id=msg.buffer_id,
                                       in_port=in_port, actions=actions, data=data)
         dp.send_msg(out)

在这里插入图片描述

本代码运行后,因为剔除了icmp,因此不会因为因为icmp直接产生风暴风险,但是在mininet中执行ping后将产生ARP风暴,效果如下图所示。

在这里插入图片描述

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于RYU的POX算法的示例文件: ```python 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_0 from ryu.lib.packet import packet, ethernet from ryu.lib.packet.ethernet import ETH_TYPE_IP from ryu.lib.packet.ipv4 import ipv4 class SimpleSwitch(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION] def __init__(self, *args, **kwargs): super(SimpleSwitch, self).__init__(*args, **kwargs) self.mac_to_port = {} @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def packet_in_handler(self, ev): msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser in_port = msg.in_port pkt = packet.Packet(msg.data) eth = pkt.get_protocol(ethernet.ethernet) if eth.ethertype == ETH_TYPE_IP: ip = pkt.get_protocol(ipv4.ipv4) src_ip = ip.src dst_ip = ip.dst # Add flow entry to forward the packet to the correct port actions = [parser.OFPActionOutput(self.mac_to_port[dst_ip])] match = parser.OFPMatch(dl_type=ETH_TYPE_IP, nw_src=src_ip, nw_dst=dst_ip) self.add_flow(datapath, 1, match, actions) # Send packet out the correct port out = parser.OFPPacketOut( datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port, actions=actions, data=msg.data ) datapath.send_msg(out) 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.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): datapath = ev.msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser # Install default flow entry to send all packets to controller match = parser.OFPMatch() actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] self.add_flow(datapath, 0, match, actions) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def _packet_in_handler(self, ev): msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser in_port = msg.match['in_port'] pkt = packet.Packet(msg.data) eth = pkt.get_protocol(ethernet.ethernet) dst = eth.dst src = eth.src dpid = datapath.id self.mac_to_port.setdefault(dpid, {}) # Learn the MAC address to avoid flooding next time self.mac_to_port[dpid][src] = in_port if dst in self.mac_to_port[dpid]: out_port = self.mac_to_port[dpid][dst] else: out_port = ofproto.OFPP_FLOOD actions = [parser.OFPActionOutput(out_port)] # Install a flow entry to forward the packet to the correct port if out_port != ofproto.OFPP_FLOOD: match = parser.OFPMatch(in_port=in_port, dl_dst=dst) self.add_flow(datapath, 1, match, actions) # Send packet out the correct port out = parser.OFPPacketOut( datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port, actions=actions, data=msg.data ) datapath.send_msg(out) ``` 此文件实现了一个简单的交换机应用程序,其目的是学习MAC地址并使用流表转发数据包。它还添加了一个特定于IP地址的流表,以便可以根据源IP地址和目标IP地址转发数据包。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值