SDN功能实现(三)--- 实现自定义action(2)修改Ryu源码

一:OpenFlow协议实现

OpenFlow协议解析部分代码大部分在ofproto目录下,少部分在controller目录下。以下内容将首先介绍ofproto目录下的源码内容,再介绍controller目录下的ofp_event文件。

(一)__init__

def get_ofp_modules():
    """get modules pair for the constants and parser of OF-wire of
    a given OF version.
    """
    return ofproto_protocol._versions

def get_ofp_module(ofp_version):
    """get modules pair for the constants and parser of OF-wire of
    a given OF version.
    """
    return get_ofp_modules()[ofp_version]

该文件定义了两个功能类似的函数get_ofp_module()和get_ofp_modules(),前者用于取得协议版本对应的协议定义文件和协议解析模块,后者则取出整个字典。对应的字典在ofproto_protocol模块中定义。

(二)ofproto_protocol中_versions存放协议和协议解析模块

_versions = {
    ofproto_v1_0.OFP_VERSION: (ofproto_v1_0, ofproto_v1_0_parser),
    ofproto_v1_2.OFP_VERSION: (ofproto_v1_2, ofproto_v1_2_parser),
    ofproto_v1_3.OFP_VERSION: (ofproto_v1_3, ofproto_v1_3_parser),
    ofproto_v1_4.OFP_VERSION: (ofproto_v1_4, ofproto_v1_4_parser),
    ofproto_v1_5.OFP_VERSION: (ofproto_v1_5, ofproto_v1_5_parser),
}

除此之外,该文件还定义了Datapath的父类ProtocolDesc,此类基本上只完成了与协议版本相关的内容。该类最重要的两个成员是self.ofproto和self.ofproto_parser,其值指明所本次通信所使用的OpenFlow协议的版本以及对应的解析模块

(三)ofproto_common

ofproto_common文件比较简单,主要定义了OpenFlow需要使用的公共属性,如监听端口,报头长度,报头封装格式等内容。

(四)ofproto_parser

ofproto_parser文件定义了所有版本都需要的解析相关的公共属性。如定义了最重要的基类MsgBase(StringifyMixin)。

StringifyMixin类的定义在lib.stringify文件,有兴趣的读者可自行查看。MsgBase基类定义了最基础的属性信息,具体如下所示:

class MsgBase(StringifyMixin):
    """
    This is a base class for OpenFlow message classes.

    An instance of this class has at least the following attributes.

    ========= ==============================
    Attribute Description
    ========= ==============================
    datapath  A ryu.controller.controller.Datapath instance for this message
    version   OpenFlow protocol version
    msg_type  Type of OpenFlow message
    msg_len   Length of the message
    xid       Transaction id
    buf       Raw data
    ========= ==============================
    """

    @create_list_of_base_attributes
    def __init__(self, datapath):
        super(MsgBase, self).__init__()
        self.datapath = datapath
        self.version = None
        self.msg_type = None
        self.msg_len = None
        self.xid = None
        self.buf = None

    def set_headers(self, version, msg_type, msg_len, xid):
        assert msg_type == self.cls_msg_type

        self.version = version
        self.msg_type = msg_type
        self.msg_len = msg_len
        self.xid = xid

    def set_xid(self, xid):
        assert self.xid is None
        self.xid = xid

    def set_buf(self, buf):
        self.buf = buffer(buf)

    def __str__(self):
        def hexify(x):
            return hex(x) if isinstance(x, six.integer_types) else x
        buf = 'version=%s,msg_type=%s,msg_len=%s,xid=%s,' %\
              (hexify(self.version), hexify(self.msg_type),
               hexify(self.msg_len), hexify(self.xid))
        return buf + StringifyMixin.__str__(self)

    @classmethod
    def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
        msg_ = cls(datapath)
        msg_.set_headers(version, msg_type, msg_len, xid)
        msg_.set_buf(buf)
        return msg_

    def _serialize_pre(self):
        self.version = self.datapath.ofproto.OFP_VERSION
        self.msg_type = self.cls_msg_type
        self.buf = bytearray(self.datapath.ofproto.OFP_HEADER_SIZE)

    def _serialize_header(self):
        # buffer length is determined after trailing data is formated.
        assert self.version is not None
        assert self.msg_type is not None
        assert self.buf is not None
        assert len(self.buf) >= self.datapath.ofproto.OFP_HEADER_SIZE

        self.msg_len = len(self.buf)
        if self.xid is None:
            self.xid = 0

        struct.pack_into(self.datapath.ofproto.OFP_HEADER_PACK_STR,
                         self.buf, 0,
                         self.version, self.msg_type, self.msg_len, self.xid)

    def _serialize_body(self):
        pass

    def serialize(self):
        self._serialize_pre()
        self._serialize_body()
        self._serialize_header()

该类定义了基础的parser函数和serialize函数。基础的parser函数基本什么都没有做,仅返回一个赋值后的消息体。

其中serialize函数分为3部分,self._serialize_pre(), self._serialize_body()和self._serialize_header()。本质上完成了header的序列化。关于body的序列化,将在对应的派生类中得到重写。

(五)ofproto_v1_3---静态配置

此类文件最重要的一个目的是定义了所有需要的静态内容,包括某字段的所有选项以及消息封装的格式以及长度。与OpenFlow消息内容相关的有协议的类型,动作的类型,port的类型等。

此外对应每一个报文,都需要定义其封装的格式,以及封装的长度。Ryu采用了Python的Struct库去完成数据的解封装工作,关于Struct的介绍将在后续内容介绍。

下面列出最常用的内容:--- openflow动作类型和格式

# enum ofp_action_type
OFPAT_OUTPUT = 0                # Output to switch port.
OFPAT_COPY_TTL_OUT = 11         # Copy TTL "outwards" -- from
                                # next-to-outermost to outermost
OFPAT_COPY_TTL_IN = 12          # Copy TTL "inwards" -- from outermost to
                                # next-to-outermost
OFPAT_SET_MPLS_TTL = 15         # MPLS TTL.
OFPAT_DEC_MPLS_TTL = 16         # Decrement MPLS TTL
OFPAT_PUSH_VLAN = 17            # Push a new VLAN tag
OFPAT_POP_VLAN = 18             # Pop the outer VLAN tag
OFPAT_PUSH_MPLS = 19            # Push a new MPLS tag
OFPAT_POP_MPLS = 20             # Pop the outer MPLS tag
OFPAT_SET_QUEUE = 21            # Set queue id when outputting to a port
OFPAT_GROUP = 22                # Apply group
OFPAT_SET_NW_TTL = 23           # IP TTL.
OFPAT_DEC_NW_TTL = 24           # Decrement IP TTL.
OFPAT_SET_FIELD = 25            # Set a header field using OXM TLV format.
OFPAT_PUSH_PBB = 26             # Push a new PBB service tag (I-TAG)
OFPAT_POP_PBB = 27              # Pop the outer PBB service tag (I-TAG)
OFPAT_EXPERIMENTER = 0xffff

# struct ofp_action_header
OFP_ACTION_HEADER_PACK_STR = '!HH4x'
OFP_ACTION_HEADER_SIZE = 8
assert calcsize(OFP_ACTION_HEADER_PACK_STR) == OFP_ACTION_HEADER_SIZE

# struct ofp_action_output
OFP_ACTION_OUTPUT_PACK_STR = '!HHIH6x'
OFP_ACTION_OUTPUT_SIZE = 16
assert calcsize(OFP_ACTION_OUTPUT_PACK_STR) == OFP_ACTION_OUTPUT_SIZE

# enum ofp_controller_max_len
OFPCML_MAX = 0xffe5         # maximum max_len value which can be used to
                            # request a specific byte length.
OFPCML_NO_BUFFER = 0xffff   # indicates that no buffering should be
                            # applied and the whole packet is to be
                            # sent to the controller.

# struct ofp_action_group
OFP_ACTION_GROUP_PACK_STR = '!HHI'
OFP_ACTION_GROUP_SIZE = 8
assert calcsize(OFP_ACTION_GROUP_PACK_STR) == OFP_ACTION_GROUP_SIZE

# struct ofp_action_set_queue
OFP_ACTION_SET_QUEUE_PACK_STR = '!HHI'
OFP_ACTION_SET_QUEUE_SIZE = 8
assert calcsize(OFP_ACTION_SET_QUEUE_PACK_STR) == OFP_ACTION_SET_QUEUE_SIZE

# struct ofp_action_mpls_ttl
OFP_ACTION_MPLS_TTL_PACK_STR = '!HHB3x'
OFP_ACTION_MPLS_TTL_SIZE = 8
assert calcsize(OFP_ACTION_MPLS_TTL_PACK_STR) == OFP_ACTION_MPLS_TTL_SIZE

# struct ofp_action_nw_ttl
OFP_ACTION_NW_TTL_PACK_STR = '!HHB3x'
OFP_ACTION_NW_TTL_SIZE = 8
assert calcsize(OFP_ACTION_NW_TTL_PACK_STR) == OFP_ACTION_NW_TTL_SIZE

# struct ofp_action_push
OFP_ACTION_PUSH_PACK_STR = '!HHH2x'
OFP_ACTION_PUSH_SIZE = 8
assert calcsize(OFP_ACTION_PUSH_PACK_STR) == OFP_ACTION_PUSH_SIZE

# struct ofp_action_pop_mpls
OFP_ACTION_POP_MPLS_PACK_STR = '!HHH2x'
OFP_ACTION_POP_MPLS_SIZE = 8
assert calcsize(OFP_ACTION_POP_MPLS_PACK_STR) == OFP_ACTION_POP_MPLS_SIZE

# struct ofp_action_set_field
OFP_ACTION_SET_FIELD_PACK_STR = '!HH4x'
OFP_ACTION_SET_FIELD_SIZE = 8
assert calcsize(OFP_ACTION_SET_FIELD_PACK_STR) == OFP_ACTION_SET_FIELD_SIZE

# struct ofp_action_experimenter_header
OFP_ACTION_EXPERIMENTER_HEADER_PACK_STR = '!HHI'
OFP_ACTION_EXPERIMENTER_HEADER_SIZE = 8
assert (calcsize(OFP_ACTION_EXPERIMENTER_HEADER_PACK_STR) ==
        OFP_ACTION_EXPERIMENTER_HEADER_SIZE)

# ofp_switch_features
OFP_SWITCH_FEATURES_PACK_STR = '!QIBB2xII'
OFP_SWITCH_FEATURES_SIZE = 32
assert (calcsize(OFP_SWITCH_FEATURES_PACK_STR) + OFP_HEADER_SIZE ==
        OFP_SWITCH_FEATURES_SIZE)

其中例如:OFP_HEADER_PACK_STR = '!BBHI'的意思是将header按照8|8|16|32的长度封装成对应的数值。在Python中分别对应的是1个字节的integer、一个字节的integer、2个字节的integer、4个字节的integer。

calcsize函数用于计算对应的format的长度。

重点:

!:大端存储
c: char
B: 一个字节长度,unsigned char.
H:两个字节,16位
I: 4个字节,int型
Q: 64bits
x: padding
3x:3个字节的padding
5s: 5字节的字符串

(六)ofproto_v1_3_parser---报文解析

本模块用于定义报文的解析等动态内容。模块中定义了与OpenFlow协议对应的Common_struct及message type对应的类。

每一个message对应的类都是有MsgBase派生的,其继承了父类的parser函数和serialize函数。

若报文无消息体,如Hello报文,则无需重写parser和serialize函数。

本模块定义了几个重要的全局函数:_set_msg_type,_register_parser,msg_parser和_set_msg_reply。其作用介绍如下:

_set_msg_type: 
  完成类与ofproto模块中定义的报文名字的映射,原因在于ofproto模块定义的名字并不是类名,而解析时需要使用ofproto中的名字。 _register_parser:
  完成对应的类与类中的parser函数的映射,当解析函数从ofproto模块的名字映射到类之后,若需要解析,则需从类对应到对应的解析函数。parser函数是一个类函数,所以在使用时必须传入对应的类的对象作为参数。 msg_parser:
  从_MSG_PARSERS中获取对msg_type的parser,并返回解析之后的内容。 _set_msg_reply:
  完成该类与对应的回应报文的映射。
    
def _set_msg_type(msg_type):
        '''Annotate corresponding OFP message type'''
        def _set_cls_msg_type(cls):
            cls.cls_msg_type = msg_type
            return cls
        return _set_cls_msg_type
    
    
    def _register_parser(cls):
        '''class decorator to register msg parser'''
        assert cls.cls_msg_type is not None
        assert cls.cls_msg_type not in _MSG_PARSERS
        _MSG_PARSERS[cls.cls_msg_type] = cls.parser
        return cls
    
    
    @ofproto_parser.register_msg_parser(ofproto.OFP_VERSION)
    def msg_parser(datapath, version, msg_type, msg_len, xid, buf):
        parser = _MSG_PARSERS.get(msg_type)
        return parser(datapath, version, msg_type, msg_len, xid, buf)
    
    
    def _set_msg_reply(msg_reply):
        '''Annotate OFP reply message class'''
        def _set_cls_msg_reply(cls):
            cls.cls_msg_reply = msg_reply
            return cls
        return _set_cls_msg_reply
View Code

报文如果有消息体,则需要重写parser函数或者serialize函数,具体根据报文内容而不一样。

主要列出动作解析---重点

class OFPAction(OFPActionHeader):
    _ACTION_TYPES = {}

    @staticmethod
    def register_action_type(type_, len_):
        def _register_action_type(cls):
            cls.cls_action_type = type_
            cls.cls_action_len = len_
            OFPAction._ACTION_TYPES[cls.cls_action_type] = cls
            return cls
        return _register_action_type

    def __init__(self):
        cls = self.__class__
        super(OFPAction, self).__init__(cls.cls_action_type,
                                        cls.cls_action_len)

    @classmethod
    def parser(cls, buf, offset):
        type_, len_ = struct.unpack_from(
            ofproto.OFP_ACTION_HEADER_PACK_STR, buf, offset)
        cls_ = cls._ACTION_TYPES.get(type_)
        assert cls_ is not None
        return cls_.parser(buf, offset)


@OFPAction.register_action_type(ofproto.OFPAT_OUTPUT,
                                ofproto.OFP_ACTION_OUTPUT_SIZE)
class OFPActionOutput(OFPAction):
    """
    Output action

    This action indicates output a packet to the switch port.

    ================ ======================================================
    Attribute        Description
    ================ ======================================================
    port             Output port
    max_len          Max length to send to controller
    ================ ======================================================
    """

    def __init__(self, port, max_len=ofproto.OFPCML_MAX,
                 type_=None, len_=None):
        super(OFPActionOutput, self).__init__()
        self.port = port
        self.max_len = max_len

    @classmethod
    def parser(cls, buf, offset):
        type_, len_, port, max_len = struct.unpack_from(
            ofproto.OFP_ACTION_OUTPUT_PACK_STR, buf, offset)
        return cls(port, max_len)

    def serialize(self, buf, offset):
        msg_pack_into(ofproto.OFP_ACTION_OUTPUT_PACK_STR, buf,
                      offset, self.type, self.len, self.port, self.max_len)

OFPActionOutput是动作output解析,每个动作对应OFPActionXXX,继承于父类OFPAction,并且该类由父类静态方法装饰,用于注册该动作(重点)

@OFPAction.register_action_type(ofproto.OFPAT_OUTPUT,
                                ofproto.OFP_ACTION_OUTPUT_SIZE)
class OFPAction(OFPActionHeader):
    _ACTION_TYPES = {}  

    @staticmethod
    def register_action_type(type_, len_):
        def _register_action_type(cls):
            cls.cls_action_type = type_
            cls.cls_action_len = len_
            OFPAction._ACTION_TYPES[cls.cls_action_type] = cls
            return cls
        return _register_action_type

其中,_ACTION_TYPES字典存放了动作序号:动作解析类

{
0: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionOutput'>, 
11: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionCopyTtlOut'>, 
12: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionCopyTtlIn'>, 
15: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionSetMplsTtl'>, 
16: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionDecMplsTtl'>, 
17: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionPushVlan'>, 
18: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionPopVlan'>, 
19: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionPushMpls'>, 
20: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionPopMpls'>, 
21: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionSetQueue'>, 
22: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionGroup'>, 
23: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionSetNwTtl'>, 
24: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionDecNwTtl'>, 
25: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionSetField'>, 
26: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionPushPbb'>, 
27: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionPopPbb'>, 
65535: <class 'ryu.ofproto.ofproto_v1_3_parser.OFPActionExperimenter'>}

每个动作解析类,需要含有parser进行解析数据和serialize进行序列化操作

二:修改Ryu源码,实现openflow1.3自定义action

(一)定义action,修改ofproto_v1_3.py

# enum ofp_action_type
OFPAT_OUTPUT = 0                # Output to switch port.
OFPAT_COPY_TTL_OUT = 11         # Copy TTL "outwards" -- from
                                # next-to-outermost to outermost
OFPAT_COPY_TTL_IN = 12          # Copy TTL "inwards" -- from outermost to
                                # next-to-outermost
OFPAT_SET_MPLS_TTL = 15         # MPLS TTL.
OFPAT_DEC_MPLS_TTL = 16         # Decrement MPLS TTL
OFPAT_PUSH_VLAN = 17            # Push a new VLAN tag
OFPAT_POP_VLAN = 18             # Pop the outer VLAN tag
OFPAT_PUSH_MPLS = 19            # Push a new MPLS tag
OFPAT_POP_MPLS = 20             # Pop the outer MPLS tag
OFPAT_SET_QUEUE = 21            # Set queue id when outputting to a port
OFPAT_GROUP = 22                # Apply group
OFPAT_SET_NW_TTL = 23           # IP TTL.
OFPAT_DEC_NW_TTL = 24           # Decrement IP TTL.
OFPAT_SET_FIELD = 25            # Set a header field using OXM TLV format.
OFPAT_PUSH_PBB = 26             # Push a new PBB service tag (I-TAG)
OFPAT_POP_PBB = 27              # Pop the outer PBB service tag (I-TAG)
OFPAT_PROB_DROP = 29            #probdrop packet
OFPAT_EXPERIMENTER = 0xffff

# struct ofp_action_header
OFP_ACTION_HEADER_PACK_STR = '!HH4x'
OFP_ACTION_HEADER_SIZE = 8
assert calcsize(OFP_ACTION_HEADER_PACK_STR) == OFP_ACTION_HEADER_SIZE

# struct ofp_action_output
OFP_ACTION_OUTPUT_PACK_STR = '!HHIH6x'
OFP_ACTION_OUTPUT_SIZE = 16
assert calcsize(OFP_ACTION_OUTPUT_PACK_STR) == OFP_ACTION_OUTPUT_SIZE

# enum ofp_controller_max_len
OFPCML_MAX = 0xffe5         # maximum max_len value which can be used to
                            # request a specific byte length.
OFPCML_NO_BUFFER = 0xffff   # indicates that no buffering should be
                            # applied and the whole packet is to be
                            # sent to the controller.

# struct ofp_action_group
OFP_ACTION_GROUP_PACK_STR = '!HHI'
OFP_ACTION_GROUP_SIZE = 8
assert calcsize(OFP_ACTION_GROUP_PACK_STR) == OFP_ACTION_GROUP_SIZE

# struct ofp_action_set_queue
OFP_ACTION_SET_QUEUE_PACK_STR = '!HHI'
OFP_ACTION_SET_QUEUE_SIZE = 8
assert calcsize(OFP_ACTION_SET_QUEUE_PACK_STR) == OFP_ACTION_SET_QUEUE_SIZE

# struct ofp_action_mpls_ttl
OFP_ACTION_MPLS_TTL_PACK_STR = '!HHB3x'
OFP_ACTION_MPLS_TTL_SIZE = 8
assert calcsize(OFP_ACTION_MPLS_TTL_PACK_STR) == OFP_ACTION_MPLS_TTL_SIZE

# struct ofp_action_nw_ttl
OFP_ACTION_NW_TTL_PACK_STR = '!HHB3x'
OFP_ACTION_NW_TTL_SIZE = 8
assert calcsize(OFP_ACTION_NW_TTL_PACK_STR) == OFP_ACTION_NW_TTL_SIZE

# struct ofp_action_push
OFP_ACTION_PUSH_PACK_STR = '!HHH2x'
OFP_ACTION_PUSH_SIZE = 8
assert calcsize(OFP_ACTION_PUSH_PACK_STR) == OFP_ACTION_PUSH_SIZE

# struct ofp_action_pop_mpls
OFP_ACTION_POP_MPLS_PACK_STR = '!HHH2x'
OFP_ACTION_POP_MPLS_SIZE = 8
assert calcsize(OFP_ACTION_POP_MPLS_PACK_STR) == OFP_ACTION_POP_MPLS_SIZE

# struct ofp_action_prob_drop
OFP_ACTION_PROB_DROP_PACK_STR = '!HHI'
OFP_ACTION_PROB_DROP_SIZE = 8
assert calcsize(OFP_ACTION_PROB_DROP_PACK_STR) == OFP_ACTION_PROB_DROP_SIZE

1.OFPAT_PROB_DROP = 29            #probdrop packet

此动作,与之前OVS中添加的动作一致,且序号29与OVS动作定义中注释中序号保持一致。

2.声明数据格式,我们需要传参,固定传参中由type和len各占2字节用H表示,我们需要的参数为一位32为数据,占4字节,用I表示,故!HHI为格式,共占8字节

# struct ofp_action_prob_drop
OFP_ACTION_PROB_DROP_PACK_STR = '!HHI'
OFP_ACTION_PROB_DROP_SIZE = 8
assert calcsize(OFP_ACTION_PROB_DROP_PACK_STR) == OFP_ACTION_PROB_DROP_SIZE    #共占8字节,进行校验

(二)实现动作解析,修改ofproto_v1_3_parser.py

@OFPAction.register_action_type(ofproto.OFPAT_POP_PBB,
                                ofproto.OFP_ACTION_HEADER_SIZE)
class OFPActionPopPbb(OFPAction):
    """
    Pop PBB action

    This action pops the outermost PBB service instance header from
    the packet.
    """

    def __init__(self, type_=None, len_=None):
        super(OFPActionPopPbb, self).__init__()

    @classmethod
    def parser(cls, buf, offset):
        (type_, len_) = struct.unpack_from(
            ofproto.OFP_ACTION_HEADER_PACK_STR, buf, offset)
        return cls()

@OFPAction.register_action_type(ofproto.OFPAT_PROB_DROP,
                                ofproto.OFP_ACTION_PROB_DROP_SIZE)
class OFPActionProbdrop(OFPAction):
    """
    Prob drop action

    This action Randomly discarded the packet.
    """

    def __init__(self, probd, type_=None, len_=None):
        super(OFPActionProbdrop, self).__init__()
        self.probd = probd

    @classmethod
    def parser(cls, buf, offset):
        (type_, len_, probd) = struct.unpack_from(
            ofproto.OFP_ACTION_PROB_DROP_PACK_STR, buf, offset)
        return cls(probd)

    def serialize(self, buf, offset):
        msg_pack_into(ofproto.OFP_ACTION_PROB_DROP_PACK_STR, buf, offset,
                      self.type, self.len, self.probd)

1.我们需要进行传参处理

    def __init__(self, probd, type_=None, len_=None):
        super(OFPActionProbdrop, self).__init__()
        self.probd = probd

2.进行报文解析

    @classmethod
    def parser(cls, buf, offset):
        (type_, len_, probd) = struct.unpack_from(
            ofproto.OFP_ACTION_PROB_DROP_PACK_STR, buf, offset)
        return cls(probd)

3.进行序列化处理,按照HHI进行序列化即可

    def serialize(self, buf, offset):
        msg_pack_into(ofproto.OFP_ACTION_PROB_DROP_PACK_STR, buf, offset,
                      self.type, self.len, self.probd)

三:重新进行源码安装Ryu

进入ryu主目录,目录下含有setup.py安装文件

sudo python3 setup.py install

补充:处理Couldn't find index page for 'pbr' (maybe misspelled?)错误!!见:pip安装virtualenvwrapper报错的解决办法

sudo pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pbr

四:编写app文件,进行测试

(一)编写py文件

from ryu.base import app_manager
from ryu.ofproto import ofproto_v1_3,ofproto_v1_3_parser
from ryu.ofproto import nx_actions
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.lib.packet import packet
from ryu.lib.packet import ethernet

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

    def __init__(self,*args,**kwargs):
        super(otheraction,self).__init__(*args,**kwargs)
        self.Dst_Src_Table={}

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

        match = ofp_parser.OFPMatch()
        actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,ofproto.OFPCML_NO_BUFFER)]

        self.add_flow(datapath,0,match,actions,"default flow entry")

    def add_flow(self,datapath,priority,match,actions,remind_content):
        ofproto = datapath.ofproto
        ofp_parser = datapath.ofproto_parser

        inst = [ofp_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
                                             actions)]

        mod = ofp_parser.OFPFlowMod(datapath=datapath,priority=priority,
                                    match=match,instructions=inst);
        print("install to datapath,"+remind_content)
        datapath.send_msg(mod);

    @set_ev_cls(ofp_event.EventOFPPacketIn,MAIN_DISPATCHER)
    def packet_in_handler(self,ev):        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
        ofp_parser = datapath.ofproto_parser

        in_port = msg.match['in_port']

        match = ofp_parser.OFPMatch(in_port=in_port)
        if(datapath.id == 1 and in_port == 1):  #我们只对第一个交换机,s1,添加概率丢包动作
            actions = [ofp_parser.OFPActionProbDrop(1000000000),ofp_parser.OFPActionOutput(2)]
        else:  #对于其他交换机,泛洪处理即可
            actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_FLOOD)]

        self.add_flow(datapath,1,match,actions,"hub flow entry")

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

        datapath.send_msg(out)

(二)实验测试

1.开启ryu控制器

2.设置网络拓扑

3.查看流表

存在Ryu控制器下发的流表,和动作probdrop

4.进行链路测试

测试成功!!!

posted @ 2020-02-11 21:02  山上有风景  阅读(412)  评论(1编辑  收藏  举报