Ryu 控制器 struct.error 添加新的 table 流时

Ryu controller struct.error when adding a new table flow

我正在编写一个 Ryu L4 swtich 应用程序,我正在尝试执行以下操作:当识别出 TCP/UDP 数据包时,应用程序会检查本地数据库以查看数据包参数是否来自已知攻击者(源 IP、目标 IP 和目标端口)。

如果数据包与攻击者数据库中记录的数据相匹配,则向交换机添加一个流以丢弃特定数据包(此流持续 2 小时),如果数据包不匹配,则将一个流添加到转发到特定的交换机端口(此流持续 5 分钟)。

问题是,当控制器将新流发送到 switch/datapath 时,我收到以下错误:

SimpleSwitch13: Exception occurred during handler processing. Backtrace from offending handler [_packet_in_handler] servicing event [EventOFPPacketIn] follows.
Traceback (most recent call last):
File "/root/SecAPI/Code/lib/python3.5/site-packages/ryu/base/app_manager.py", line 290, in _event_loop
handler(ev)
File "/root/SecAPI/Flasks/Code/SDN/switchL3.py", line 237, in _packet_in_handler
self.add_security_flow(datapath, 1, match, actions)
File "/root/SecAPI/Flasks/Code/SDN/switchL3.py", line 109, in add_security_flow
datapath.send_msg(mod)
File "/root/SecAPI/Code/lib/python3.5/site-packages/ryu/controller/controller.py", line 423, in send_msg
msg.serialize()
File "/root/SecAPI/Code/lib/python3.5/site-packages/ryu/ofproto/ofproto_parser.py", line 270, in serialize
self._serialize_body()
File "/root/SecAPI/Code/lib/python3.5/site-packages/ryu/ofproto/ofproto_v1_3_parser.py", line 2738, in _serialize_body
self.out_group, self.flags)
File "/root/SecAPI/Code/lib/python3.5/site-packages/ryu/lib/pack_utils.py", line 25, in msg_pack_into
struct.pack_into(fmt, buf, offset, *args)
struct.error: 'H' format requires 0 <= number <= 65535

这是我的完整代码:

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
from ryu.lib.packet import ethernet
from ryu.lib.packet import ether_types
from ryu.lib.packet import ipv4
from ryu.lib.packet import tcp
from ryu.lib.packet import udp
from ryu.lib.packet import in_proto
import sqlite3

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

def __init__(self, *args, **kwargs):
    super(SimpleSwitch13, self).__init__(*args, **kwargs)
    self.mac_to_port = {}
    self.initial = True
    self.security_alert = False

@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()
    self.initial = True
    actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
                                      ofproto.OFPCML_NO_BUFFER)]
    self.add_flow(datapath, 0, match, actions)
    self.initial = False

# Adds a flow into a specific datapath, with a hard_timeout of 5 minutes.
# Meaning that a certain packet flow ceases existing after 5 minutes.
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:
        if self.initial == True:
            mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id,
                                    priority=priority, match=match,
                                    instructions=inst)
        elif self.initial == False:
            mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id,
                                    priority=priority, match=match,
                                    instructions=inst,hard_timeout=300)
    else:
        if self.initial == True:
            mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
                                    match=match, instructions=inst)

        elif self.initial == False:
            mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
                                    match=match, instructions=inst,
                                    hard_timeout=300)
    datapath.send_msg(mod)

# Adds a security flow into the controlled device, a secured flow differs from a normal
# flow in it's duration, a security flow has a duration of 2 hours.
def add_security_flow(self, datapath, priority, match, actions, buffer_id=None):
    ofproto = datapath.ofproto
    parser = datapath.ofproto_parser

    #inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
    #                                     actions)]
    inst = [parser.OFPInstructionActions(ofproto.OFPIT_CLEAR_ACTIONS, [])]

    if buffer_id:
        mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id,
                                priority=priority,match=match,command=ofproto.OFPFC_ADD,
                                instructions=inst, hard_timeout=432000)
    else:
        mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
                                match=match, instructions=inst, command=ofproto.OFPFC_ADD,
                                hard_timeout=432000)

    datapath.send_msg(mod)

# Deletes a already existing flow that matches has a given packet match.
def del_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,instruction=inst,
                                command=ofproto.OFPFC_DELETE)
    else:
        mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
                                match=match, instructions=inst,
                                command=ofproto.OFPFC_DELETE)

    datapath.send_msg(mod)

@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
    # If you hit this you might want to increase
    # the "miss_send_length" of your switch
    if ev.msg.msg_len < ev.msg.total_len:
        self.logger.debug("packet truncated: only %s of %s bytes",
                          ev.msg.msg_len, ev.msg.total_len)
    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_protocols(ethernet.ethernet)[0]

    if eth.ethertype == ether_types.ETH_TYPE_LLDP:
        # ignore lldp packet
        return
    dst = eth.dst
    src = eth.src

    dpid = datapath.id
    self.mac_to_port.setdefault(dpid, {})

    self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)

    # learn a mac address to avoid FLOOD 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 to avoid packet_in next time
    if out_port != ofproto.OFPP_FLOOD:
        #match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
        # check IP Protocol and create a match for IP
        if eth.ethertype == ether_types.ETH_TYPE_IP:
            conn = sqlite3.connect("database/sdnDatabase.db")
            cursor = conn.cursor()
            ip = pkt.get_protocol(ipv4.ipv4)
            srcip = ip.src
            dstip = ip.dst
            #match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP,ipv4_src=srcip,ipv4_dst=dstip)
            protocol = ip.proto

            # ICMP Protocol
            if protocol == in_proto.IPPROTO_ICMP:
                print("WARN - We have a ICMP packet")
                cursor.execute('select id from knownAttackers where srcaddr =  \"{0}\" and dstaddr = \"{1}\" and protocol = "icmp";'.format(srcip, dstip))
                result = cursor.fetchall()
                match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_src=srcip, ipv4_dst=dstip,
                                        ip_proto=protocol)
                if len(result) == 0:
                    self.security_alert = False
                else:
                    self.security_alert = True

            # TCP Protocol
            elif protocol == in_proto.IPPROTO_TCP:
                print("WARN - We have a TCP packet")
                t = pkt.get_protocol(tcp.tcp)
                cursor.execute('select id from knownAttackers where srcaddr =  \"{0}\" and dstaddr = \"{1}\" and dstport = \"{2}\" and protocol = "tcp";'.format(srcip, dstip, t.dst_port))
                result = cursor.fetchall()
                match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_src=srcip, ipv4_dst=dstip,
                                        ip_proto=protocol, tcp_dst=t.dst_port)
                if len(result) == 0:
                    self.security_alert = False
                else:
                    print("We have a register in the database for this specific packet: {0}".format(result))
                    self.security_alert = True

            # UDP Protocol
            elif protocol == in_proto.IPPROTO_UDP:
                print("WARN - We have a UDP packet")
                u = pkt.get_protocol(udp.udp)
                cursor.execute('select id from knownAttackers where srcaddr =  \"{0}\" and dstaddr = \"{1}\" and dstport = \"{2}\" and protocol = "udp";'.format(srcip, dstip, u.dst_port))
                result = cursor.fetchall()
                match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_src=srcip, ipv4_dst=dstip,
                                        ip_proto=protocol, udp_dst=u.dst_port)
                if len(result) == 0:
                    self.security_alert = False
                else:
                    self.security_alert = True

            else:
                self.security_alert = False
                match = 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 self.security_alert == False:
                if msg.buffer_id != ofproto.OFP_NO_BUFFER:
                    self.add_flow(datapath, 1, match, actions, msg.buffer_id)
                    return
                else:
                    self.add_flow(datapath, 1, match, actions)

            elif self.security_alert == True:
                if msg.buffer_id != ofproto.OFP_NO_BUFFER:
                    self.add_security_flow(datapath, 1, match, actions, msg.buffer_id)
                    return
                else:
                    self.add_security_flow(datapath, 1, match, actions)


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

    if self.security_alert == False:
        out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
                              in_port=in_port, actions=actions, data=data)
        datapath.send_msg(out)

上述错误出现在 add_security_flow() class 方法的末尾,当我尝试建立一个被识别为已知攻击者的 TCP 连接时,当他试图发送流时mod化 (datapath.send_msg(mod)) 到 switch/datapath.

我做错了什么?我是否缺少某种变量?

在 Ryu 控制器邮件列表中,一个名叫 IWAMOTO 的用户告诉我,我的 hard_timeout 值对于结构打包来说太大了(2 小时是 7200 秒,我不知道我的头在哪里让我找到432000 哈哈),在将 hard_timeout 缩小到 7200 秒后一切正常。

始终检查您尝试发送到数据路径的值的大小,看看它是否不超过 65535。