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
报文如果有消息体,则需要重写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