在传统网络中,存在着一定的广播流量,占据了一部分的网络带宽。同时,在有环的拓扑中,如果不运行某些协议,广播数据还会引起网络风暴,使网络瘫痪。
如有以下的一个网络拓扑结构(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风暴,效果如下图所示。