# Copyright (C) 2011, 2012 Nippon Telegraph and Telephone Corporation. # Copyright (C) 2011, 2012 Isaku Yamahata # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """ Decoder/Encoder implementations of OpenFlow 1.0. """ import struct import base64 import six import netaddr from os_ken.ofproto.ofproto_parser import StringifyMixin, MsgBase from os_ken.lib import addrconv from os_ken.lib import ip from os_ken.lib import mac from os_ken.lib.packet import packet from os_ken.lib.pack_utils import msg_pack_into from os_ken.ofproto import nx_match from os_ken.ofproto import ofproto_common from os_ken.ofproto import ofproto_parser from os_ken.ofproto import ofproto_v1_0 as ofproto from os_ken.ofproto import nx_actions from os_ken import utils import logging LOG = logging.getLogger('os_ken.ofproto.ofproto_v1_0_parser') _MSG_PARSERS = {} 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) # OFP_MSG_REPLY = { # OFPFeaturesRequest: OFPSwitchFeatures, # OFPBarrierRequest: OFPBarrierReply, # OFPQueueGetConfigRequest: OFPQueueGetConfigReply, # # # ofp_stats_request -> ofp_stats_reply # OFPDescStatsRequest: OFPDescStatsReply, # OFPFlowStatsRequest: OFPFlowStatsReply, # OFPAggregateStatsRequest: OFPAggregateStatsReply, # OFPTableStatsRequest: OFPTableStatsReply, # OFPPortStatsRequest: OFPPortStatsReply, # OFPQueueStatsRequest: OFPQueueStatsReply, # OFPVendorStatsRequest: OFPVendorStatsReply, # } 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 # # common structures # class OFPPhyPort(ofproto_parser.namedtuple('OFPPhyPort', ( 'port_no', 'hw_addr', 'name', 'config', 'state', 'curr', 'advertised', 'supported', 'peer'))): """ Description of a port ========== ========================================================= Attribute Description ========== ========================================================= port_no Port number and it uniquely identifies a port within a switch. hw_addr MAC address for the port. name Null-terminated string containing a human-readable name for the interface. config Bitmap of port configration flags. | OFPPC_PORT_DOWN | OFPPC_NO_STP | OFPPC_NO_RECV | OFPPC_NO_RECV_STP | OFPPC_NO_FLOOD | OFPPC_NO_FWD | OFPPC_NO_PACKET_IN state Bitmap of port state flags. | OFPPS_LINK_DOWN | OFPPS_STP_LISTEN | OFPPS_STP_LEARN | OFPPS_STP_FORWARD | OFPPS_STP_BLOCK | OFPPS_STP_MASK curr Current features. advertised Features being advertised by the port. supported Features supported by the port. peer Features advertised by peer. ========== ========================================================= """ _TYPE = { 'ascii': [ 'hw_addr', ], 'utf-8': [ # OF spec is unclear about the encoding of name. # we assumes UTF-8, which is used by OVS. 'name', ] } @classmethod def parser(cls, buf, offset): port = struct.unpack_from(ofproto.OFP_PHY_PORT_PACK_STR, buf, offset) port = list(port) i = cls._fields.index('hw_addr') port[i] = addrconv.mac.bin_to_text(port[i]) i = cls._fields.index('name') port[i] = port[i].rstrip(b'\0') return cls(*port) class OFPMatch(StringifyMixin): """ Flow Match Structure This class is implementation of the flow match structure having compose/query API. ================ ================================================== Attribute Description ================ ================================================== wildcards Wildcard fields. (match fields) For the available match fields, please refer to the following. ================ ================================================== ================ =============== ================================== Argument Value Description ================ =============== ================================== in_port Integer 16bit Switch input port. dl_src MAC address Ethernet source address. dl_dst MAC address Ethernet destination address. dl_vlan Integer 16bit Input VLAN id. dl_vlan_pcp Integer 8bit Input VLAN priority. dl_type Integer 16bit Ethernet frame type. nw_tos Integer 8bit IP ToS (actually DSCP field, 6 bits). nw_proto Integer 8bit IP protocol or lower 8 bits of ARP opcode. nw_src IPv4 address IP source address. nw_dst IPv4 address IP destination address. tp_src Integer 16bit TCP/UDP source port. tp_dst Integer 16bit TCP/UDP destination port. nw_src_mask Integer 8bit IP source address mask specified as IPv4 address prefix. nw_dst_mask Integer 8bit IP destination address mask specified as IPv4 address prefix. ================ =============== ================================== Example:: >>> # compose >>> match = parser.OFPMatch( ... in_port=1, ... dl_type=0x0800, ... dl_src='aa:bb:cc:dd:ee:ff', ... nw_src='192.168.0.1') >>> # query >>> if 'nw_src' in match: ... print match['nw_src'] ... '192.168.0.1' """ def __init__(self, wildcards=None, in_port=None, dl_src=None, dl_dst=None, dl_vlan=None, dl_vlan_pcp=None, dl_type=None, nw_tos=None, nw_proto=None, nw_src=None, nw_dst=None, tp_src=None, tp_dst=None, nw_src_mask=32, nw_dst_mask=32): super(OFPMatch, self).__init__() wc = ofproto.OFPFW_ALL if in_port is None: self.in_port = 0 else: wc &= ~ofproto.OFPFW_IN_PORT self.in_port = in_port if dl_src is None: self.dl_src = mac.DONTCARE else: wc &= ~ofproto.OFPFW_DL_SRC if (isinstance(dl_src, (six.text_type, str)) and netaddr.valid_mac(dl_src)): dl_src = addrconv.mac.text_to_bin(dl_src) if dl_src == 0: self.dl_src = mac.DONTCARE else: self.dl_src = dl_src if dl_dst is None: self.dl_dst = mac.DONTCARE else: wc &= ~ofproto.OFPFW_DL_DST if (isinstance(dl_dst, (six.text_type, str)) and netaddr.valid_mac(dl_dst)): dl_dst = addrconv.mac.text_to_bin(dl_dst) if dl_dst == 0: self.dl_dst = mac.DONTCARE else: self.dl_dst = dl_dst if dl_vlan is None: self.dl_vlan = 0 else: wc &= ~ofproto.OFPFW_DL_VLAN self.dl_vlan = dl_vlan if dl_vlan_pcp is None: self.dl_vlan_pcp = 0 else: wc &= ~ofproto.OFPFW_DL_VLAN_PCP self.dl_vlan_pcp = dl_vlan_pcp if dl_type is None: self.dl_type = 0 else: wc &= ~ofproto.OFPFW_DL_TYPE self.dl_type = dl_type if nw_tos is None: self.nw_tos = 0 else: wc &= ~ofproto.OFPFW_NW_TOS self.nw_tos = nw_tos if nw_proto is None: self.nw_proto = 0 else: wc &= ~ofproto.OFPFW_NW_PROTO self.nw_proto = nw_proto if nw_src is None: self.nw_src = 0 else: wc &= (32 - nw_src_mask) << ofproto.OFPFW_NW_SRC_SHIFT \ | ~ofproto.OFPFW_NW_SRC_MASK if not isinstance(nw_src, int): nw_src = ip.ipv4_to_int(nw_src) self.nw_src = nw_src if nw_dst is None: self.nw_dst = 0 else: wc &= (32 - nw_dst_mask) << ofproto.OFPFW_NW_DST_SHIFT \ | ~ofproto.OFPFW_NW_DST_MASK if not isinstance(nw_dst, int): nw_dst = ip.ipv4_to_int(nw_dst) self.nw_dst = nw_dst if tp_src is None: self.tp_src = 0 else: wc &= ~ofproto.OFPFW_TP_SRC self.tp_src = tp_src if tp_dst is None: self.tp_dst = 0 else: wc &= ~ofproto.OFPFW_TP_DST self.tp_dst = tp_dst if wildcards is None: self.wildcards = wc else: self.wildcards = wildcards def __getitem__(self, name): if not isinstance(name, str): raise KeyError(name) elif name == 'nw_src_mask': _m = 32 - ((self.wildcards & ofproto.OFPFW_NW_SRC_MASK) >> ofproto.OFPFW_NW_SRC_SHIFT) return 0 if _m < 0 else _m elif name == 'nw_dst_mask': _m = 32 - ((self.wildcards & ofproto.OFPFW_NW_DST_MASK) >> ofproto.OFPFW_NW_DST_SHIFT) return 0 if _m < 0 else _m elif name == 'wildcards': return self.wildcards wc = getattr(ofproto, 'OFPFW_' + name.upper(), 0) if ~self.wildcards & wc: value = getattr(self, name) if name in ['dl_src', 'dl_dst']: value = addrconv.mac.bin_to_text(value) elif name in ['nw_src', 'nw_dst']: value = ip.ipv4_to_str(value) return value else: raise KeyError(name) def __contains__(self, name): wc = getattr(ofproto, 'OFPFW_' + name.upper(), 0) return ~self.wildcards & wc def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_MATCH_PACK_STR, buf, offset, self.wildcards, self.in_port, self.dl_src, self.dl_dst, self.dl_vlan, self.dl_vlan_pcp, self.dl_type, self.nw_tos, self.nw_proto, self.nw_src, self.nw_dst, self.tp_src, self.tp_dst) @classmethod def parse(cls, buf, offset): match = struct.unpack_from(ofproto.OFP_MATCH_PACK_STR, buf, offset) return cls(*match) def to_jsondict(self): fields = {} # copy values to avoid original values conversion for k, v in self.__dict__.items(): if k in ['dl_src', 'dl_dst']: fields[k] = addrconv.mac.bin_to_text(v) elif k in ['nw_src', 'nw_dst']: fields[k] = ip.ipv4_to_str(v) else: fields[k] = v return {self.__class__.__name__: fields} @classmethod def from_jsondict(cls, dict_): return cls(**dict_) class OFPActionHeader(StringifyMixin): _base_attributes = ['type', 'len'] def __init__(self, type_, len_): self.type = type_ self.len = len_ def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_ACTION_HEADER_PACK_STR, buf, offset, self.type, self.len) 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. ================ ====================================================== Note:: The reason of this magic number (0xffe5) is because there is no good constant in of1.0. The same value as OFPCML_MAX of of1.2 and of1.3 is used. """ def __init__(self, port, max_len=0xffe5): 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) assert type_ == ofproto.OFPAT_OUTPUT assert len_ == ofproto.OFP_ACTION_OUTPUT_SIZE 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) @OFPAction.register_action_type(ofproto.OFPAT_SET_VLAN_VID, ofproto.OFP_ACTION_VLAN_VID_SIZE) class OFPActionVlanVid(OFPAction): """ Set the 802.1q VLAN id action This action indicates the 802.1q VLAN id to be set. ================ ====================================================== Attribute Description ================ ====================================================== vlan_vid VLAN id. ================ ====================================================== """ def __init__(self, vlan_vid): super(OFPActionVlanVid, self).__init__() self.vlan_vid = vlan_vid @classmethod def parser(cls, buf, offset): type_, len_, vlan_vid = struct.unpack_from( ofproto.OFP_ACTION_VLAN_VID_PACK_STR, buf, offset) assert type_ == ofproto.OFPAT_SET_VLAN_VID assert len_ == ofproto.OFP_ACTION_VLAN_VID_SIZE return cls(vlan_vid) def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_ACTION_VLAN_VID_PACK_STR, buf, offset, self.type, self.len, self.vlan_vid) @OFPAction.register_action_type(ofproto.OFPAT_SET_VLAN_PCP, ofproto.OFP_ACTION_VLAN_PCP_SIZE) class OFPActionVlanPcp(OFPAction): """ Set the 802.1q priority action This action indicates the 802.1q priority to be set. ================ ====================================================== Attribute Description ================ ====================================================== vlan_pcp VLAN priority. ================ ====================================================== """ def __init__(self, vlan_pcp): super(OFPActionVlanPcp, self).__init__() self.vlan_pcp = vlan_pcp @classmethod def parser(cls, buf, offset): type_, len_, vlan_pcp = struct.unpack_from( ofproto.OFP_ACTION_VLAN_PCP_PACK_STR, buf, offset) assert type_ == ofproto.OFPAT_SET_VLAN_PCP assert len_ == ofproto.OFP_ACTION_VLAN_PCP_SIZE return cls(vlan_pcp) def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_ACTION_VLAN_PCP_PACK_STR, buf, offset, self.type, self.len, self.vlan_pcp) @OFPAction.register_action_type(ofproto.OFPAT_STRIP_VLAN, ofproto.OFP_ACTION_HEADER_SIZE) class OFPActionStripVlan(OFPAction): """ Strip the 802.1q header action This action indicates the 802.1q priority to be striped. """ def __init__(self): super(OFPActionStripVlan, self).__init__() @classmethod def parser(cls, buf, offset): type_, len_ = struct.unpack_from( ofproto.OFP_ACTION_HEADER_PACK_STR, buf, offset) assert type_ == ofproto.OFPAT_STRIP_VLAN assert len_ == ofproto.OFP_ACTION_HEADER_SIZE return cls() class OFPActionDlAddr(OFPAction): def __init__(self, dl_addr): super(OFPActionDlAddr, self).__init__() if (isinstance(dl_addr, (six.text_type, str)) and netaddr.valid_mac(dl_addr)): dl_addr = addrconv.mac.text_to_bin(dl_addr) self.dl_addr = dl_addr @classmethod def parser(cls, buf, offset): type_, len_, dl_addr = struct.unpack_from( ofproto.OFP_ACTION_DL_ADDR_PACK_STR, buf, offset) assert type_ in (ofproto.OFPAT_SET_DL_SRC, ofproto.OFPAT_SET_DL_DST) assert len_ == ofproto.OFP_ACTION_DL_ADDR_SIZE return cls(dl_addr) def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_ACTION_DL_ADDR_PACK_STR, buf, offset, self.type, self.len, self.dl_addr) def to_jsondict(self): body = {"dl_addr": addrconv.mac.bin_to_text(self.dl_addr)} return {self.__class__.__name__: body} @classmethod def from_jsondict(cls, dict_): return cls(**dict_) @OFPAction.register_action_type(ofproto.OFPAT_SET_DL_SRC, ofproto.OFP_ACTION_DL_ADDR_SIZE) class OFPActionSetDlSrc(OFPActionDlAddr): """ Set the ethernet source address action This action indicates the ethernet source address to be set. ================ ====================================================== Attribute Description ================ ====================================================== dl_addr Ethernet address. ================ ====================================================== """ def __init__(self, dl_addr): super(OFPActionSetDlSrc, self).__init__(dl_addr) @OFPAction.register_action_type(ofproto.OFPAT_SET_DL_DST, ofproto.OFP_ACTION_DL_ADDR_SIZE) class OFPActionSetDlDst(OFPActionDlAddr): """ Set the ethernet destination address action This action indicates the ethernet destination address to be set. ================ ====================================================== Attribute Description ================ ====================================================== dl_addr Ethernet address. ================ ====================================================== """ def __init__(self, dl_addr): super(OFPActionSetDlDst, self).__init__(dl_addr) class OFPActionNwAddr(OFPAction): def __init__(self, nw_addr): super(OFPActionNwAddr, self).__init__() if not isinstance(nw_addr, int): nw_addr = ip.ipv4_to_int(nw_addr) self.nw_addr = nw_addr @classmethod def parser(cls, buf, offset): type_, len_, nw_addr = struct.unpack_from( ofproto.OFP_ACTION_NW_ADDR_PACK_STR, buf, offset) assert type_ in (ofproto.OFPAT_SET_NW_SRC, ofproto.OFPAT_SET_NW_DST) assert len_ == ofproto.OFP_ACTION_NW_ADDR_SIZE return cls(nw_addr) def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_ACTION_NW_ADDR_PACK_STR, buf, offset, self.type, self.len, self.nw_addr) def to_jsondict(self): body = {"nw_addr": ip.ipv4_to_str(self.nw_addr)} return {self.__class__.__name__: body} @classmethod def from_jsondict(cls, dict_): return cls(**dict_) @OFPAction.register_action_type(ofproto.OFPAT_SET_NW_SRC, ofproto.OFP_ACTION_NW_ADDR_SIZE) class OFPActionSetNwSrc(OFPActionNwAddr): """ Set the IP source address action This action indicates the IP source address to be set. ================ ====================================================== Attribute Description ================ ====================================================== nw_addr IP address. ================ ====================================================== """ def __init__(self, nw_addr): super(OFPActionSetNwSrc, self).__init__(nw_addr) @OFPAction.register_action_type(ofproto.OFPAT_SET_NW_DST, ofproto.OFP_ACTION_NW_ADDR_SIZE) class OFPActionSetNwDst(OFPActionNwAddr): """ Set the IP destination address action This action indicates the IP destination address to be set. ================ ====================================================== Attribute Description ================ ====================================================== nw_addr IP address. ================ ====================================================== """ def __init__(self, nw_addr): super(OFPActionSetNwDst, self).__init__(nw_addr) @OFPAction.register_action_type(ofproto.OFPAT_SET_NW_TOS, ofproto.OFP_ACTION_NW_TOS_SIZE) class OFPActionSetNwTos(OFPAction): """ Set the IP ToS action This action indicates the IP ToS (DSCP field, 6 bits) to be set. ================ ====================================================== Attribute Description ================ ====================================================== tos IP ToS (DSCP field, 6 bits). ================ ====================================================== """ def __init__(self, tos): super(OFPActionSetNwTos, self).__init__() self.tos = tos @classmethod def parser(cls, buf, offset): type_, len_, tos = struct.unpack_from( ofproto.OFP_ACTION_NW_TOS_PACK_STR, buf, offset) assert type_ == ofproto.OFPAT_SET_NW_TOS assert len_ == ofproto.OFP_ACTION_NW_TOS_SIZE return cls(tos) def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_ACTION_NW_TOS_PACK_STR, buf, offset, self.type, self.len, self.tos) class OFPActionTpPort(OFPAction): def __init__(self, tp): super(OFPActionTpPort, self).__init__() self.tp = tp @classmethod def parser(cls, buf, offset): type_, len_, tp = struct.unpack_from( ofproto.OFP_ACTION_TP_PORT_PACK_STR, buf, offset) assert type_ in (ofproto.OFPAT_SET_TP_SRC, ofproto.OFPAT_SET_TP_DST) assert len_ == ofproto.OFP_ACTION_TP_PORT_SIZE return cls(tp) def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_ACTION_TP_PORT_PACK_STR, buf, offset, self.type, self.len, self.tp) @OFPAction.register_action_type(ofproto.OFPAT_SET_TP_SRC, ofproto.OFP_ACTION_TP_PORT_SIZE) class OFPActionSetTpSrc(OFPActionTpPort): """ Set the TCP/UDP source port action This action indicates the TCP/UDP source port to be set. ================ ====================================================== Attribute Description ================ ====================================================== tp TCP/UDP port. ================ ====================================================== """ def __init__(self, tp): super(OFPActionSetTpSrc, self).__init__(tp) @OFPAction.register_action_type(ofproto.OFPAT_SET_TP_DST, ofproto.OFP_ACTION_TP_PORT_SIZE) class OFPActionSetTpDst(OFPActionTpPort): """ Set the TCP/UDP destination port action This action indicates the TCP/UDP destination port to be set. ================ ====================================================== Attribute Description ================ ====================================================== tp TCP/UDP port. ================ ====================================================== """ def __init__(self, tp): super(OFPActionSetTpDst, self).__init__(tp) @OFPAction.register_action_type(ofproto.OFPAT_ENQUEUE, ofproto.OFP_ACTION_ENQUEUE_SIZE) class OFPActionEnqueue(OFPAction): """ Output to queue action This action indicates send packets to given queue on port. ================ ====================================================== Attribute Description ================ ====================================================== port Port that queue belongs. queue_id Where to enqueue the packets. ================ ====================================================== """ def __init__(self, port, queue_id): super(OFPActionEnqueue, self).__init__() self.port = port self.queue_id = queue_id @classmethod def parser(cls, buf, offset): type_, len_, port, queue_id = struct.unpack_from( ofproto.OFP_ACTION_ENQUEUE_PACK_STR, buf, offset) assert type_ == ofproto.OFPAT_ENQUEUE assert len_ == ofproto.OFP_ACTION_ENQUEUE_SIZE return cls(port, queue_id) def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_ACTION_ENQUEUE_PACK_STR, buf, offset, self.type, self.len, self.port, self.queue_id) @OFPAction.register_action_type(ofproto.OFPAT_VENDOR, 0) class OFPActionVendor(OFPAction): """ Vendor action This action is an extensible action for the vendor. """ _ACTION_VENDORS = {} @staticmethod def register_action_vendor(vendor): def _register_action_vendor(cls): cls.cls_vendor = vendor OFPActionVendor._ACTION_VENDORS[cls.cls_vendor] = cls return cls return _register_action_vendor def __init__(self, vendor=None): super(OFPActionVendor, self).__init__() self.type = ofproto.OFPAT_VENDOR self.len = None if vendor is None: self.vendor = self.cls_vendor else: self.vendor = vendor @classmethod def parser(cls, buf, offset): type_, len_, vendor = struct.unpack_from( ofproto.OFP_ACTION_VENDOR_HEADER_PACK_STR, buf, offset) data = buf[(offset + ofproto.OFP_ACTION_VENDOR_HEADER_SIZE ): offset + len_] if vendor == ofproto_common.NX_EXPERIMENTER_ID: obj = NXAction.parse(data) # noqa else: cls_ = cls._ACTION_VENDORS.get(vendor, None) if cls_ is None: obj = OFPActionVendorUnknown(vendor, data) else: obj = cls_.parser(buf, offset) obj.len = len_ return obj def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_ACTION_VENDOR_HEADER_PACK_STR, buf, offset, self.type, self.len, self.vendor) # OpenFlow1.2 or later compatible OFPActionExperimenter = OFPActionVendor class OFPActionVendorUnknown(OFPActionVendor): def __init__(self, vendor, data=None, type_=None, len_=None): super(OFPActionVendorUnknown, self).__init__(vendor=vendor) self.data = data def serialize(self, buf, offset): # fixup data = self.data if data is None: data = bytearray() self.len = (utils.round_up(len(data), 8) + ofproto.OFP_ACTION_VENDOR_HEADER_SIZE) super(OFPActionVendorUnknown, self).serialize(buf, offset) msg_pack_into('!%ds' % len(self.data), buf, offset + ofproto.OFP_ACTION_VENDOR_HEADER_SIZE, self.data) @OFPActionVendor.register_action_vendor(ofproto_common.NX_EXPERIMENTER_ID) class NXActionHeader(OFPActionVendor): _NX_ACTION_SUBTYPES = {} @staticmethod def register_nx_action_subtype(subtype, len_): def _register_nx_action_subtype(cls): cls.cls_action_len = len_ cls.cls_subtype = subtype NXActionHeader._NX_ACTION_SUBTYPES[cls.cls_subtype] = cls return cls return _register_nx_action_subtype def __init__(self): super(NXActionHeader, self).__init__() self.subtype = self.cls_subtype def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_ACTION_HEADER_PACK_STR, buf, offset, self.type, self.len) @classmethod def parser(cls, buf, offset): type_, len_, vendor, subtype = struct.unpack_from( ofproto.NX_ACTION_HEADER_PACK_STR, buf, offset) cls_ = cls._NX_ACTION_SUBTYPES.get(subtype) return cls_.parser(buf, offset) class OFPDescStats(ofproto_parser.namedtuple('OFPDescStats', ( 'mfr_desc', 'hw_desc', 'sw_desc', 'serial_num', 'dp_desc'))): _TYPE = { 'ascii': [ 'mfr_desc', 'hw_desc', 'sw_desc', 'serial_num', 'dp_desc', ] } @classmethod def parser(cls, buf, offset): desc = struct.unpack_from(ofproto.OFP_DESC_STATS_PACK_STR, buf, offset) desc = list(desc) desc = [x.rstrip(b'\0') for x in desc] stats = cls(*desc) stats.length = ofproto.OFP_DESC_STATS_SIZE return stats class OFPFlowStats(StringifyMixin): def __init__(self): super(OFPFlowStats, self).__init__() self.length = None self.table_id = None self.match = None self.duration_sec = None self.duration_nsec = None self.priority = None self.idle_timeout = None self.hard_timeout = None self.cookie = None self.packet_count = None self.byte_count = None self.actions = None @classmethod def parser(cls, buf, offset): flow_stats = cls() flow_stats.length, flow_stats.table_id = struct.unpack_from( ofproto.OFP_FLOW_STATS_0_PACK_STR, buf, offset) offset += ofproto.OFP_FLOW_STATS_0_SIZE flow_stats.match = OFPMatch.parse(buf, offset) offset += ofproto.OFP_MATCH_SIZE (flow_stats.duration_sec, flow_stats.duration_nsec, flow_stats.priority, flow_stats.idle_timeout, flow_stats.hard_timeout, flow_stats.cookie, flow_stats.packet_count, flow_stats.byte_count) = struct.unpack_from( ofproto.OFP_FLOW_STATS_1_PACK_STR, buf, offset) offset += ofproto.OFP_FLOW_STATS_1_SIZE flow_stats.actions = [] length = ofproto.OFP_FLOW_STATS_SIZE while length < flow_stats.length: action = OFPAction.parser(buf, offset) flow_stats.actions.append(action) offset += action.len length += action.len return flow_stats class OFPAggregateStats(ofproto_parser.namedtuple('OFPAggregateStats', ( 'packet_count', 'byte_count', 'flow_count'))): @classmethod def parser(cls, buf, offset): agg = struct.unpack_from( ofproto.OFP_AGGREGATE_STATS_REPLY_PACK_STR, buf, offset) stats = cls(*agg) stats.length = ofproto.OFP_AGGREGATE_STATS_REPLY_SIZE return stats class OFPTableStats(ofproto_parser.namedtuple('OFPTableStats', ( 'table_id', 'name', 'wildcards', 'max_entries', 'active_count', 'lookup_count', 'matched_count'))): _TYPE = { 'utf-8': [ # OF spec is unclear about the encoding of name. # we assumes UTF-8. 'name', ] } @classmethod def parser(cls, buf, offset): tbl = struct.unpack_from(ofproto.OFP_TABLE_STATS_PACK_STR, buf, offset) tbl = list(tbl) i = cls._fields.index('name') tbl[i] = tbl[i].rstrip(b'\0') stats = cls(*tbl) stats.length = ofproto.OFP_TABLE_STATS_SIZE return stats class OFPPortStats(ofproto_parser.namedtuple('OFPPortStats', ( 'port_no', 'rx_packets', 'tx_packets', 'rx_bytes', 'tx_bytes', 'rx_dropped', 'tx_dropped', 'rx_errors', 'tx_errors', 'rx_frame_err', 'rx_over_err', 'rx_crc_err', 'collisions'))): @classmethod def parser(cls, buf, offset): port = struct.unpack_from(ofproto.OFP_PORT_STATS_PACK_STR, buf, offset) stats = cls(*port) stats.length = ofproto.OFP_PORT_STATS_SIZE return stats class OFPQueueStats(ofproto_parser.namedtuple('OFPQueueStats', ( 'port_no', 'queue_id', 'tx_bytes', 'tx_packets', 'tx_errors'))): @classmethod def parser(cls, buf, offset): queue = struct.unpack_from(ofproto.OFP_QUEUE_STATS_PACK_STR, buf, offset) stats = cls(*queue) stats.length = ofproto.OFP_QUEUE_STATS_SIZE return stats class OFPVendorStats(ofproto_parser.namedtuple('OFPVendorStats', ('specific_data'))): @classmethod def parser(cls, buf, offset): stats = cls(buf[offset:]) stats.length = len(stats.specific_data) return stats class NXFlowStats(StringifyMixin): def __init__(self): super(NXFlowStats, self).__init__() self.length = None self.table_id = None self.duration_sec = None self.duration_nsec = None self.priority = None self.idle_timeout = None self.hard_timeout = None self.match_len = None self.idle_age = None self.hard_age = None self.cookie = None self.packet_count = None self.byte_count = None @classmethod def parser(cls, buf, offset): original_offset = offset nxflow_stats = cls() (nxflow_stats.length, nxflow_stats.table_id, nxflow_stats.duration_sec, nxflow_stats.duration_nsec, nxflow_stats.priority, nxflow_stats.idle_timeout, nxflow_stats.hard_timeout, nxflow_stats.match_len, nxflow_stats.idle_age, nxflow_stats.hard_age, nxflow_stats.cookie, nxflow_stats.packet_count, nxflow_stats.byte_count) = struct.unpack_from( ofproto.NX_FLOW_STATS_PACK_STR, buf, offset) offset += ofproto.NX_FLOW_STATS_SIZE fields = [] match_len = nxflow_stats.match_len match_len -= 4 while match_len > 0: field = nx_match.MFField.parser(buf, offset) offset += field.length match_len -= field.length fields.append(field) nxflow_stats.fields = fields actions = [] total_len = original_offset + nxflow_stats.length match_len = nxflow_stats.match_len offset += utils.round_up(match_len, 8) - match_len while offset < total_len: action = OFPAction.parser(buf, offset) actions.append(action) offset += action.len nxflow_stats.actions = actions return nxflow_stats class NXAggregateStats(ofproto_parser.namedtuple('NXAggregateStats', ( 'packet_count', 'byte_count', 'flow_count'))): @classmethod def parser(cls, buf, offset): agg = struct.unpack_from( ofproto.NX_AGGREGATE_STATS_REPLY_PACK_STR, buf, offset) stats = cls(*agg) stats.length = ofproto.NX_AGGREGATE_STATS_REPLY_SIZE return stats class OFPQueuePropHeader(StringifyMixin): _QUEUE_PROPERTIES = {} @staticmethod def register_queue_property(prop_type, prop_len): def _register_queue_propery(cls): cls.cls_prop_type = prop_type cls.cls_prop_len = prop_len OFPQueuePropHeader._QUEUE_PROPERTIES[prop_type] = cls return cls return _register_queue_propery def __init__(self): self.property = self.cls_prop_type self.len = self.cls_prop_len @classmethod def parser(cls, buf, offset): property_, len_ = struct.unpack_from( ofproto.OFP_QUEUE_PROP_HEADER_PACK_STR, buf, offset) prop_cls = cls._QUEUE_PROPERTIES[property_] assert property_ == prop_cls.cls_prop_type assert len_ == prop_cls.cls_prop_len offset += ofproto.OFP_QUEUE_PROP_HEADER_SIZE return prop_cls.parser(buf, offset) @OFPQueuePropHeader.register_queue_property( ofproto.OFPQT_NONE, ofproto.OFP_QUEUE_PROP_HEADER_SIZE) class OFPQueuePropNone(OFPQueuePropHeader): def __init__(self): super(OFPQueuePropNone, self).__init__() @classmethod def parser(cls, buf, offset): return cls() @OFPQueuePropHeader.register_queue_property( ofproto.OFPQT_MIN_RATE, ofproto.OFP_QUEUE_PROP_MIN_RATE_SIZE) class OFPQueuePropMinRate(OFPQueuePropHeader): def __init__(self, rate): super(OFPQueuePropMinRate, self).__init__() self.rate = rate @classmethod def parser(cls, buf, offset): (rate,) = struct.unpack_from( ofproto.OFP_QUEUE_PROP_MIN_RATE_PACK_STR, buf, offset) return cls(rate) class OFPPacketQueue(StringifyMixin): """ Description of a queue ========== ========================================================= Attribute Description ========== ========================================================= queue_id ID for the specific queue. len Length in bytes of this queue desc. properties List of ``OFPQueueProp*`` instance. ========== ========================================================= """ def __init__(self, queue_id, len_): self.queue_id = queue_id self.len = len_ self.properties = None @classmethod def parser(cls, buf, offset): queue_id, len_ = struct.unpack_from( ofproto.OFP_PACKET_QUEUE_PQCK_STR, buf, offset) packet_queue = cls(queue_id, len_) packet_queue.properties = [] cur_len = ofproto.OFP_PACKET_QUEUE_SIZE offset += ofproto.OFP_PACKET_QUEUE_SIZE while (cur_len + ofproto.OFP_QUEUE_PROP_HEADER_SIZE <= packet_queue.len): prop = OFPQueuePropHeader.parser(buf, offset) packet_queue.properties.append(prop) cur_len += prop.len offset += prop.len return packet_queue # # Symmetric messages # parser + serializer # @_register_parser @_set_msg_type(ofproto.OFPT_HELLO) class OFPHello(MsgBase): """ Hello message When connection is started, the hello message is exchanged between a switch and a controller. This message is handled by the OSKen framework, so the OSKen application do not need to process this typically. """ def __init__(self, datapath): super(OFPHello, self).__init__(datapath) @_register_parser @_set_msg_type(ofproto.OFPT_ERROR) class OFPErrorMsg(MsgBase): """ Error message The switch notifies controller of problems by this message. ========== ========================================================= Attribute Description ========== ========================================================= type High level type of error code Details depending on the type data Variable length data depending on the type and code ========== ========================================================= ``type`` attribute corresponds to ``type_`` parameter of __init__. Types and codes are defined in ``os_ken.ofproto.ofproto``. =========================== =========== Type Code =========================== =========== OFPET_HELLO_FAILED OFPHFC_* OFPET_BAD_REQUEST OFPBRC_* OFPET_BAD_ACTION OFPBAC_* OFPET_FLOW_MOD_FAILED OFPFMFC_* OFPET_PORT_MOD_FAILED OFPPMFC_* OFPET_QUEUE_OP_FAILED OFPQOFC_* =========================== =========== Example:: @set_ev_cls(ofp_event.EventOFPErrorMsg, [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER]) def error_msg_handler(self, ev): msg = ev.msg self.logger.debug('OFPErrorMsg received: type=0x%02x code=0x%02x ' 'message=%s', msg.type, msg.code, utils.hex_array(msg.data)) """ def __init__(self, datapath, type_=None, code=None, data=None): super(OFPErrorMsg, self).__init__(datapath) self.type = type_ self.code = code if isinstance(data, six.string_types): data = data.encode('ascii') self.data = data @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): msg = super(OFPErrorMsg, cls).parser(datapath, version, msg_type, msg_len, xid, buf) msg.type, msg.code = struct.unpack_from( ofproto.OFP_ERROR_MSG_PACK_STR, msg.buf, ofproto.OFP_HEADER_SIZE) msg.data = msg.buf[ofproto.OFP_ERROR_MSG_SIZE:] return msg def _serialize_body(self): assert self.data is not None msg_pack_into(ofproto.OFP_ERROR_MSG_PACK_STR, self.buf, ofproto.OFP_HEADER_SIZE, self.type, self.code) self.buf += self.data @_register_parser @_set_msg_type(ofproto.OFPT_ECHO_REQUEST) class OFPEchoRequest(MsgBase): """ Echo request message This message is handled by the OSKen framework, so the OSKen application do not need to process this typically. ========== ========================================================= Attribute Description ========== ========================================================= data An arbitrary length data. ========== ========================================================= Example:: def send_echo_request(self, datapath, data): ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPEchoRequest(datapath, data) datapath.send_msg(req) """ def __init__(self, datapath, data=None): super(OFPEchoRequest, self).__init__(datapath) self.data = data @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): msg = super(OFPEchoRequest, cls).parser(datapath, version, msg_type, msg_len, xid, buf) msg.data = msg.buf[ofproto.OFP_HEADER_SIZE:] return msg def _serialize_body(self): if self.data is not None: self.buf += self.data @_register_parser @_set_msg_type(ofproto.OFPT_ECHO_REPLY) class OFPEchoReply(MsgBase): """ Echo reply message This message is handled by the OSKen framework, so the OSKen application do not need to process this typically. ========== ========================================================= Attribute Description ========== ========================================================= data An arbitrary length data. ========== ========================================================= Example:: @set_ev_cls(ofp_event.EventOFPEchoReply, [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER]) def echo_reply_handler(self, ev): self.logger.debug('OFPEchoReply received: data=%s', utils.hex_array(ev.msg.data)) """ def __init__(self, datapath, data=None): super(OFPEchoReply, self).__init__(datapath) self.data = data @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): msg = super(OFPEchoReply, cls).parser(datapath, version, msg_type, msg_len, xid, buf) msg.data = msg.buf[ofproto.OFP_HEADER_SIZE:] return msg def _serialize_body(self): assert self.data is not None self.buf += self.data @_register_parser @_set_msg_type(ofproto.OFPT_VENDOR) class OFPVendor(MsgBase): """ Vendor message The controller send this message to send the vendor-specific information to a switch. """ _VENDORS = {} @staticmethod def register_vendor(id_): def _register_vendor(cls): OFPVendor._VENDORS[id_] = cls return cls return _register_vendor def __init__(self, datapath): super(OFPVendor, self).__init__(datapath) self.data = None self.vendor = None @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): msg = super(OFPVendor, cls).parser(datapath, version, msg_type, msg_len, xid, buf) (msg.vendor,) = struct.unpack_from( ofproto.OFP_VENDOR_HEADER_PACK_STR, msg.buf, ofproto.OFP_HEADER_SIZE) cls_ = cls._VENDORS.get(msg.vendor) if cls_: msg.data = cls_.parser(datapath, msg.buf, 0) else: msg.data = msg.buf[ofproto.OFP_VENDOR_HEADER_SIZE:] return msg def serialize_header(self): msg_pack_into(ofproto.OFP_VENDOR_HEADER_PACK_STR, self.buf, ofproto.OFP_HEADER_SIZE, self.vendor) def _serialize_body(self): assert self.data is not None self.serialize_header() self.buf += self.data @OFPVendor.register_vendor(ofproto_common.NX_EXPERIMENTER_ID) class NiciraHeader(OFPVendor): _NX_SUBTYPES = {} @staticmethod def register_nx_subtype(subtype): def _register_nx_subtype(cls): cls.cls_subtype = subtype NiciraHeader._NX_SUBTYPES[cls.cls_subtype] = cls return cls return _register_nx_subtype def __init__(self, datapath, subtype): super(NiciraHeader, self).__init__(datapath) self.vendor = ofproto_common.NX_EXPERIMENTER_ID self.subtype = subtype def serialize_header(self): super(NiciraHeader, self).serialize_header() msg_pack_into(ofproto.NICIRA_HEADER_PACK_STR, self.buf, ofproto.OFP_HEADER_SIZE, self.vendor, self.subtype) @classmethod def parser(cls, datapath, buf, offset): vendor, subtype = struct.unpack_from( ofproto.NICIRA_HEADER_PACK_STR, buf, offset + ofproto.OFP_HEADER_SIZE) cls_ = cls._NX_SUBTYPES.get(subtype) return cls_.parser(datapath, buf, offset + ofproto.NICIRA_HEADER_SIZE) class NXTSetFlowFormat(NiciraHeader): def __init__(self, datapath, flow_format): super(NXTSetFlowFormat, self).__init__( datapath, ofproto.NXT_SET_FLOW_FORMAT) self.format = flow_format def _serialize_body(self): self.serialize_header() msg_pack_into(ofproto.NX_SET_FLOW_FORMAT_PACK_STR, self.buf, ofproto.NICIRA_HEADER_SIZE, self.format) class NXTFlowMod(NiciraHeader): def __init__(self, datapath, cookie, command, idle_timeout=0, hard_timeout=0, priority=ofproto.OFP_DEFAULT_PRIORITY, buffer_id=0xffffffff, out_port=ofproto.OFPP_NONE, flags=0, rule=None, actions=None): # the argument, rule, is positioned at the one before the last due # to the layout struct nxt_flow_mod. # Although rule must be given, default argument to rule, None, # is given to allow other default value of argument before rule. assert rule is not None if actions is None: actions = [] super(NXTFlowMod, self).__init__(datapath, ofproto.NXT_FLOW_MOD) self.cookie = cookie self.command = command self.idle_timeout = idle_timeout self.hard_timeout = hard_timeout self.priority = priority self.buffer_id = buffer_id self.out_port = out_port self.flags = flags self.rule = rule self.actions = actions def _serialize_body(self): self.serialize_header() offset = ofproto.NX_FLOW_MOD_SIZE match_len = nx_match.serialize_nxm_match(self.rule, self.buf, offset) offset += nx_match.round_up(match_len) msg_pack_into(ofproto.NX_FLOW_MOD_PACK_STR, self.buf, ofproto.NICIRA_HEADER_SIZE, self.cookie, self.command, self.idle_timeout, self.hard_timeout, self.priority, self.buffer_id, self.out_port, self.flags, match_len) if self.actions is not None: for a in self.actions: a.serialize(self.buf, offset) offset += a.len class NXTRoleRequest(NiciraHeader): def __init__(self, datapath, role): super(NXTRoleRequest, self).__init__( datapath, ofproto.NXT_ROLE_REQUEST) self.role = role def _serialize_body(self): self.serialize_header() msg_pack_into(ofproto.NX_ROLE_PACK_STR, self.buf, ofproto.NICIRA_HEADER_SIZE, self.role) @NiciraHeader.register_nx_subtype(ofproto.NXT_ROLE_REPLY) class NXTRoleReply(NiciraHeader): def __init__(self, datapath, role): super(NXTRoleReply, self).__init__( datapath, ofproto.NXT_ROLE_REPLY) self.role = role @classmethod def parser(cls, datapath, buf, offset): (role,) = struct.unpack_from( ofproto.NX_ROLE_PACK_STR, buf, offset) return cls(datapath, role) class NXTFlowModTableId(NiciraHeader): def __init__(self, datapath, set_): super(NXTFlowModTableId, self).__init__( datapath, ofproto.NXT_FLOW_MOD_TABLE_ID) self.set = set_ def _serialize_body(self): self.serialize_header() msg_pack_into(ofproto.NX_FLOW_MOD_TABLE_ID_PACK_STR, self.buf, ofproto.NICIRA_HEADER_SIZE, self.set) @NiciraHeader.register_nx_subtype(ofproto.NXT_FLOW_REMOVED) class NXTFlowRemoved(NiciraHeader): def __init__(self, datapath, cookie, priority, reason, duration_sec, duration_nsec, idle_timeout, match_len, packet_count, byte_count, match): super(NXTFlowRemoved, self).__init__( datapath, ofproto.NXT_FLOW_REMOVED) self.cookie = cookie self.priority = priority self.reason = reason self.duration_sec = duration_sec self.duration_nsec = duration_nsec self.idle_timeout = idle_timeout self.match_len = match_len self.packet_count = packet_count self.byte_count = byte_count self.match = match @classmethod def parser(cls, datapath, buf, offset): (cookie, priority, reason, duration_sec, duration_nsec, idle_timeout, match_len, packet_count, byte_count) = struct.unpack_from( ofproto.NX_FLOW_REMOVED_PACK_STR, buf, offset) offset += (ofproto.NX_FLOW_REMOVED_SIZE - ofproto.NICIRA_HEADER_SIZE) match = nx_match.NXMatch.parser(buf, offset, match_len) return cls(datapath, cookie, priority, reason, duration_sec, duration_nsec, idle_timeout, match_len, packet_count, byte_count, match) class NXTSetPacketInFormat(NiciraHeader): def __init__(self, datapath, packet_in_format): super(NXTSetPacketInFormat, self).__init__( datapath, ofproto.NXT_SET_PACKET_IN_FORMAT) self.format = packet_in_format def _serialize_body(self): self.serialize_header() msg_pack_into(ofproto.NX_SET_PACKET_IN_FORMAT_PACK_STR, self.buf, ofproto.NICIRA_HEADER_SIZE, self.format) @NiciraHeader.register_nx_subtype(ofproto.NXT_PACKET_IN) class NXTPacketIn(NiciraHeader): def __init__(self, datapath, buffer_id, total_len, reason, table_id, cookie, match_len, match, frame): super(NXTPacketIn, self).__init__( datapath, ofproto.NXT_PACKET_IN) self.buffer_id = buffer_id self.total_len = total_len self.reason = reason self.table_id = table_id self.cookie = cookie self.match_len = match_len self.match = match self.frame = frame @classmethod def parser(cls, datapath, buf, offset): (buffer_id, total_len, reason, table_id, cookie, match_len) = struct.unpack_from( ofproto.NX_PACKET_IN_PACK_STR, buf, offset) offset += (ofproto.NX_PACKET_IN_SIZE - ofproto.NICIRA_HEADER_SIZE) match = nx_match.NXMatch.parser(buf, offset, match_len) offset += (match_len + 7) // 8 * 8 frame = buf[offset:] if total_len < len(frame): frame = frame[:total_len] return cls(datapath, buffer_id, total_len, reason, table_id, cookie, match_len, match, frame) class NXTFlowAge(NiciraHeader): def __init__(self, datapath): super(NXTFlowAge, self).__init__( datapath, ofproto.NXT_FLOW_AGE) def _serialize_body(self): self.serialize_header() class NXTSetAsyncConfig(NiciraHeader): def __init__(self, datapath, packet_in_mask, port_status_mask, flow_removed_mask): super(NXTSetAsyncConfig, self).__init__( datapath, ofproto.NXT_SET_ASYNC_CONFIG) self.packet_in_mask = packet_in_mask self.port_status_mask = port_status_mask self.flow_removed_mask = flow_removed_mask def _serialize_body(self): self.serialize_header() msg_pack_into(ofproto.NX_ASYNC_CONFIG_PACK_STR, self.buf, ofproto.NICIRA_HEADER_SIZE, self.packet_in_mask[0], self.packet_in_mask[1], self.port_status_mask[0], self.port_status_mask[1], self.flow_removed_mask[0], self.flow_removed_mask[1]) class NXTSetControllerId(NiciraHeader): def __init__(self, datapath, controller_id): super(NXTSetControllerId, self).__init__( datapath, ofproto.NXT_SET_CONTROLLER_ID) self.controller_id = controller_id def _serialize_body(self): self.serialize_header() msg_pack_into(ofproto.NX_CONTROLLER_ID_PACK_STR, self.buf, ofproto.NICIRA_HEADER_SIZE, self.controller_id) # # asymmetric message (datapath -> controller) # parser only # @_register_parser @_set_msg_type(ofproto.OFPT_FEATURES_REPLY) class OFPSwitchFeatures(MsgBase): """ Features reply message The switch responds with a features reply message to a features request. This message is handled by the OSKen framework, so the OSKen application do not need to process this typically. ================ ====================================================== Attribute Description ================ ====================================================== datapath_id Datapath unique ID. n_buffers Max packets buffered at once. n_tables Number of tables supported by datapath. capabilities Bitmap of capabilities flag. | OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | OFPC_STP | OFPC_RESERVED | OFPC_IP_REASM | OFPC_QUEUE_STATS | OFPC_ARP_MATCH_IP actions Bitmap of supported OFPAT_*. ports List of ``OFPPhyPort`` instances. ================ ====================================================== Example:: @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): msg = ev.msg self.logger.debug('OFPSwitchFeatures received: ' 'datapath_id=0x%016x n_buffers=%d ' 'n_tables=%d capabilities=0x%08x ports=%s', msg.datapath_id, msg.n_buffers, msg.n_tables, msg.capabilities, msg.ports) """ def __init__(self, datapath, datapath_id=None, n_buffers=None, n_tables=None, capabilities=None, actions=None, ports=None): super(OFPSwitchFeatures, self).__init__(datapath) self.datapath_id = datapath_id self.n_buffers = n_buffers self.n_tables = n_tables self.capabilities = capabilities self.actions = actions self.ports = ports @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): msg = super(OFPSwitchFeatures, cls).parser(datapath, version, msg_type, msg_len, xid, buf) (msg.datapath_id, msg.n_buffers, msg.n_tables, msg.capabilities, msg.actions) = struct.unpack_from( ofproto.OFP_SWITCH_FEATURES_PACK_STR, msg.buf, ofproto.OFP_HEADER_SIZE) msg.ports = {} n_ports = ((msg_len - ofproto.OFP_SWITCH_FEATURES_SIZE) // ofproto.OFP_PHY_PORT_SIZE) offset = ofproto.OFP_SWITCH_FEATURES_SIZE for _i in range(n_ports): port = OFPPhyPort.parser(msg.buf, offset) # print 'port = %s' % str(port) msg.ports[port.port_no] = port offset += ofproto.OFP_PHY_PORT_SIZE return msg @_register_parser @_set_msg_type(ofproto.OFPT_PORT_STATUS) class OFPPortStatus(MsgBase): """ Port status message The switch notifies controller of change of ports. ================ ====================================================== Attribute Description ================ ====================================================== reason One of the following values. | OFPPR_ADD | OFPPR_DELETE | OFPPR_MODIFY desc instance of ``OFPPhyPort`` ================ ====================================================== Example:: @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) def port_status_handler(self, ev): msg = ev.msg dp = msg.datapath ofp = dp.ofproto if msg.reason == ofp.OFPPR_ADD: reason = 'ADD' elif msg.reason == ofp.OFPPR_DELETE: reason = 'DELETE' elif msg.reason == ofp.OFPPR_MODIFY: reason = 'MODIFY' else: reason = 'unknown' self.logger.debug('OFPPortStatus received: reason=%s desc=%s', reason, msg.desc) """ def __init__(self, datapath, reason=None, desc=None): super(OFPPortStatus, self).__init__(datapath) self.reason = reason self.desc = desc @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): msg = super(OFPPortStatus, cls).parser(datapath, version, msg_type, msg_len, xid, buf) msg.reason = struct.unpack_from( ofproto.OFP_PORT_STATUS_PACK_STR, msg.buf, ofproto.OFP_HEADER_SIZE)[0] msg.desc = OFPPhyPort.parser(msg.buf, ofproto.OFP_PORT_STATUS_DESC_OFFSET) return msg @_register_parser @_set_msg_type(ofproto.OFPT_PACKET_IN) class OFPPacketIn(MsgBase): """ Packet-In message The switch sends the packet that received to the controller by this message. ============= ========================================================= Attribute Description ============= ========================================================= buffer_id ID assigned by datapath. total_len Full length of frame. in_port Port on which frame was received. reason Reason packet is being sent. | OFPR_NO_MATCH | OFPR_ACTION | OFPR_INVALID_TTL data Ethernet frame. ============= ========================================================= Example:: @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def packet_in_handler(self, ev): msg = ev.msg dp = msg.datapath ofp = dp.ofproto if msg.reason == ofp.OFPR_NO_MATCH: reason = 'NO MATCH' elif msg.reason == ofp.OFPR_ACTION: reason = 'ACTION' elif msg.reason == ofp.OFPR_INVALID_TTL: reason = 'INVALID TTL' else: reason = 'unknown' self.logger.debug('OFPPacketIn received: ' 'buffer_id=%x total_len=%d in_port=%d, ' 'reason=%s data=%s', msg.buffer_id, msg.total_len, msg.in_port, reason, utils.hex_array(msg.data)) """ def __init__(self, datapath, buffer_id=None, total_len=None, in_port=None, reason=None, data=None): super(OFPPacketIn, self).__init__(datapath) self.buffer_id = buffer_id self.total_len = total_len self.in_port = in_port self.reason = reason self.data = data @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): msg = super(OFPPacketIn, cls).parser(datapath, version, msg_type, msg_len, xid, buf) (msg.buffer_id, msg.total_len, msg.in_port, msg.reason) = struct.unpack_from( ofproto.OFP_PACKET_IN_PACK_STR, msg.buf, ofproto.OFP_HEADER_SIZE) msg.data = msg.buf[ofproto.OFP_PACKET_IN_SIZE:] if msg.total_len < len(msg.data): # discard padding for 8-byte alignment of OFP packet msg.data = msg.data[:msg.total_len] return msg @_register_parser @_set_msg_type(ofproto.OFPT_GET_CONFIG_REPLY) class OFPGetConfigReply(MsgBase): """ Get config reply message The switch responds to a configuration request with a get config reply message. ============= ========================================================= Attribute Description ============= ========================================================= flags One of the following configuration flags. | OFPC_FRAG_NORMAL | OFPC_FRAG_DROP | OFPC_FRAG_REASM | OFPC_FRAG_MASK miss_send_len Max bytes of new flow that datapath should send to the controller. ============= ========================================================= Example:: @set_ev_cls(ofp_event.EventOFPGetConfigReply, MAIN_DISPATCHER) def get_config_reply_handler(self, ev): msg = ev.msg dp = msg.datapath ofp = dp.ofproto if msg.flags == ofp.OFPC_FRAG_NORMAL: flags = 'NORMAL' elif msg.flags == ofp.OFPC_FRAG_DROP: flags = 'DROP' elif msg.flags == ofp.OFPC_FRAG_REASM: flags = 'REASM' elif msg.flags == ofp.OFPC_FRAG_MASK: flags = 'MASK' else: flags = 'unknown' self.logger.debug('OFPGetConfigReply received: ' 'flags=%s miss_send_len=%d', flags, msg.miss_send_len) """ def __init__(self, datapath): super(OFPGetConfigReply, self).__init__(datapath) @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): msg = super(OFPGetConfigReply, cls).parser(datapath, version, msg_type, msg_len, xid, buf) (msg.flags, msg.miss_send_len) = struct.unpack_from( ofproto.OFP_SWITCH_CONFIG_PACK_STR, msg.buf, ofproto.OFP_HEADER_SIZE) return msg @_register_parser @_set_msg_type(ofproto.OFPT_BARRIER_REPLY) class OFPBarrierReply(MsgBase): """ Barrier reply message The switch responds with this message to a barrier request. Example:: @set_ev_cls(ofp_event.EventOFPBarrierReply, MAIN_DISPATCHER) def barrier_reply_handler(self, ev): self.logger.debug('OFPBarrierReply received') """ def __init__(self, datapath): super(OFPBarrierReply, self).__init__(datapath) @_register_parser @_set_msg_type(ofproto.OFPT_FLOW_REMOVED) class OFPFlowRemoved(MsgBase): """ Flow removed message When flow entries time out or are deleted, the switch notifies controller with this message. ================ ====================================================== Attribute Description ================ ====================================================== match Instance of ``OFPMatch``. cookie Opaque controller-issued identifier. priority Priority level of flow entry. reason One of the following values. | OFPRR_IDLE_TIMEOUT | OFPRR_HARD_TIMEOUT | OFPRR_DELETE duration_sec Time flow was alive in seconds. duration_nsec Time flow was alive in nanoseconds beyond duration_sec. idle_timeout Idle timeout from original flow mod. packet_count Number of packets that was associated with the flow. byte_count Number of bytes that was associated with the flow. ================ ====================================================== Example:: @set_ev_cls(ofp_event.EventOFPFlowRemoved, MAIN_DISPATCHER) def flow_removed_handler(self, ev): msg = ev.msg dp = msg.datapath ofp = dp.ofproto if msg.reason == ofp.OFPRR_IDLE_TIMEOUT: reason = 'IDLE TIMEOUT' elif msg.reason == ofp.OFPRR_HARD_TIMEOUT: reason = 'HARD TIMEOUT' elif msg.reason == ofp.OFPRR_DELETE: reason = 'DELETE' elif msg.reason == ofp.OFPRR_GROUP_DELETE: reason = 'GROUP DELETE' else: reason = 'unknown' self.logger.debug('OFPFlowRemoved received: ' 'match=%s cookie=%d priority=%d reason=%s ' 'duration_sec=%d duration_nsec=%d ' 'idle_timeout=%d packet_count=%d byte_count=%d', msg.match, msg.cookie, msg.priority, reason, msg.duration_sec, msg.duration_nsec, msg.idle_timeout, msg.packet_count, msg.byte_count) """ def __init__(self, datapath): super(OFPFlowRemoved, self).__init__(datapath) @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): msg = super(OFPFlowRemoved, cls).parser(datapath, version, msg_type, msg_len, xid, buf) msg.match = OFPMatch.parse(msg.buf, ofproto.OFP_HEADER_SIZE) (msg.cookie, msg.priority, msg.reason, msg.duration_sec, msg.duration_nsec, msg.idle_timeout, msg.packet_count, msg.byte_count) = struct.unpack_from( ofproto.OFP_FLOW_REMOVED_PACK_STR0, msg.buf, ofproto.OFP_HEADER_SIZE + ofproto.OFP_MATCH_SIZE) return msg @_register_parser @_set_msg_type(ofproto.OFPT_QUEUE_GET_CONFIG_REPLY) class OFPQueueGetConfigReply(MsgBase): """ Queue configuration reply message The switch responds with this message to a queue configuration request. ================ ====================================================== Attribute Description ================ ====================================================== port Port to be queried. queues List of ``OFPPacketQueue`` instance. ================ ====================================================== Example:: @set_ev_cls(ofp_event.EventOFPQueueGetConfigReply, MAIN_DISPATCHER) def queue_get_config_reply_handler(self, ev): msg = ev.msg self.logger.debug('OFPQueueGetConfigReply received: ' 'port=%s queues=%s', msg.port, msg.queues) """ def __init__(self, datapath): super(OFPQueueGetConfigReply, self).__init__(datapath) @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): msg = super(OFPQueueGetConfigReply, cls).parser( datapath, version, msg_type, msg_len, xid, buf) offset = ofproto.OFP_HEADER_SIZE (msg.port,) = struct.unpack_from( ofproto.OFP_QUEUE_GET_CONFIG_REPLY_PACK_STR, msg.buf, offset) msg.queues = [] offset = ofproto.OFP_QUEUE_GET_CONFIG_REPLY_SIZE while offset + ofproto.OFP_PACKET_QUEUE_SIZE <= msg_len: queue = OFPPacketQueue.parser(msg.buf, offset) msg.queues.append(queue) offset += queue.len return msg def _set_stats_type(stats_type, stats_body_cls): def _set_cls_stats_type(cls): cls.cls_stats_type = stats_type cls.cls_stats_body_cls = stats_body_cls return cls return _set_cls_stats_type @_register_parser @_set_msg_type(ofproto.OFPT_STATS_REPLY) class OFPStatsReply(MsgBase): _STATS_MSG_TYPES = {} @staticmethod def register_stats_type(body_single_struct=False): def _register_stats_type(cls): assert cls.cls_stats_type is not None assert cls.cls_stats_type not in OFPStatsReply._STATS_MSG_TYPES assert cls.cls_stats_body_cls is not None cls.cls_body_single_struct = body_single_struct OFPStatsReply._STATS_MSG_TYPES[cls.cls_stats_type] = cls return cls return _register_stats_type def __init__(self, datapath): super(OFPStatsReply, self).__init__(datapath) self.type = None self.flags = None self.body = None @classmethod def parser_stats_body(cls, buf, msg_len, offset): body_cls = cls.cls_stats_body_cls body = [] while offset < msg_len: entry = body_cls.parser(buf, offset) body.append(entry) offset += entry.length if cls.cls_body_single_struct: return body[0] return body @classmethod def parser_stats(cls, datapath, version, msg_type, msg_len, xid, buf): # call MsgBase::parser, not OFPStatsReply::parser msg = MsgBase.parser.__func__( cls, datapath, version, msg_type, msg_len, xid, buf) msg.body = msg.parser_stats_body(msg.buf, msg.msg_len, ofproto.OFP_STATS_MSG_SIZE) return msg @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): type_, flags = struct.unpack_from(ofproto.OFP_STATS_MSG_PACK_STR, six.binary_type(buf), ofproto.OFP_HEADER_SIZE) stats_type_cls = cls._STATS_MSG_TYPES.get(type_) msg = stats_type_cls.parser_stats( datapath, version, msg_type, msg_len, xid, buf) msg.type = type_ msg.flags = flags return msg @OFPStatsReply.register_stats_type(body_single_struct=True) @_set_stats_type(ofproto.OFPST_DESC, OFPDescStats) @_set_msg_type(ofproto.OFPT_STATS_REPLY) class OFPDescStatsReply(OFPStatsReply): """ Description statistics reply message The switch responds with a stats reply that include this message to a description statistics request. ================ ====================================================== Attribute Description ================ ====================================================== mfr_desc Manufacturer description. hw_desc Hardware description. sw_desc Software description. serial_num Serial number. dp_desc Human readable description of datapath. ================ ====================================================== Example:: @set_ev_cls(ofp_event.EventOFPDescStatsReply, MAIN_DISPATCHER) def desc_stats_reply_handler(self, ev): msg = ev.msg ofp = msg.datapath.ofproto body = ev.msg.body self.logger.debug('DescStats: mfr_desc=%s hw_desc=%s sw_desc=%s ' 'serial_num=%s dp_desc=%s', body.mfr_desc, body.hw_desc, body.sw_desc, body.serial_num, body.dp_desc) """ def __init__(self, datapath): super(OFPDescStatsReply, self).__init__(datapath) @OFPStatsReply.register_stats_type() @_set_stats_type(ofproto.OFPST_FLOW, OFPFlowStats) @_set_msg_type(ofproto.OFPT_STATS_REPLY) class OFPFlowStatsReply(OFPStatsReply): """ Individual flow statistics reply message The switch responds with a stats reply that include this message to an individual flow statistics request. ================ ====================================================== Attribute Description ================ ====================================================== table_id ID of table flow came from. match Instance of ``OFPMatch``. duration_sec Time flow has been alive in seconds. duration_nsec Time flow has been alive in nanoseconds beyond duration_sec. priority Priority of the entry. Only meaningful when this is not an exact-match entry. idle_timeout Number of seconds idle before expiration. hard_timeout Number of seconds before expiration. cookie Opaque controller-issued identifier. packet_count Number of packets in flow. byte_count Number of bytes in flow. actions List of ``OFPAction*`` instance ================ ====================================================== Example:: @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) def flow_stats_reply_handler(self, ev): msg = ev.msg ofp = msg.datapath.ofproto body = ev.msg.body flows = [] for stat in body: flows.append('table_id=%s match=%s ' 'duration_sec=%d duration_nsec=%d ' 'priority=%d ' 'idle_timeout=%d hard_timeout=%d ' 'cookie=%d packet_count=%d byte_count=%d ' 'actions=%s' % (stat.table_id, stat.match, stat.duration_sec, stat.duration_nsec, stat.priority, stat.idle_timeout, stat.hard_timeout, stat.cookie, stat.packet_count, stat.byte_count, stat.actions)) self.logger.debug('FlowStats: %s', flows) """ def __init__(self, datapath): super(OFPFlowStatsReply, self).__init__(datapath) @OFPStatsReply.register_stats_type() @_set_stats_type(ofproto.OFPST_AGGREGATE, OFPAggregateStats) @_set_msg_type(ofproto.OFPT_STATS_REPLY) class OFPAggregateStatsReply(OFPStatsReply): """ Aggregate flow statistics reply message The switch responds with a stats reply that include this message to an aggregate flow statistics request. ================ ====================================================== Attribute Description ================ ====================================================== packet_count Number of packets in flows. byte_count Number of bytes in flows. flow_count Number of flows. ================ ====================================================== Example:: @set_ev_cls(ofp_event.EventOFPAggregateStatsReply, MAIN_DISPATCHER) def aggregate_stats_reply_handler(self, ev): msg = ev.msg ofp = msg.datapath.ofproto body = ev.msg.body self.logger.debug('AggregateStats: packet_count=%d byte_count=%d ' 'flow_count=%d', body.packet_count, body.byte_count, body.flow_count) """ def __init__(self, datapath): super(OFPAggregateStatsReply, self).__init__(datapath) @OFPStatsReply.register_stats_type() @_set_stats_type(ofproto.OFPST_TABLE, OFPTableStats) @_set_msg_type(ofproto.OFPT_STATS_REPLY) class OFPTableStatsReply(OFPStatsReply): """ Table statistics reply message The switch responds with a stats reply that include this message to a table statistics request. ================ ====================================================== Attribute Description ================ ====================================================== table_id ID of table. name table name. wildcards Bitmap of OFPFW_* wildcards that are supported by the table. max_entries Max number of entries supported active_count Number of active entries lookup_count Number of packets looked up in table matched_count Number of packets that hit table ================ ====================================================== Example:: @set_ev_cls(ofp_event.EventOFPTableStatsReply, MAIN_DISPATCHER) def stats_reply_handler(self, ev): msg = ev.msg ofp = msg.datapath.ofproto body = ev.msg.body tables = [] for stat in body: tables.append('table_id=%d name=%s wildcards=0x%02x ' 'max_entries=%d active_count=%d ' 'lookup_count=%d matched_count=%d' % (stat.table_id, stat.name, stat.wildcards, stat.max_entries, stat.active_count, stat.lookup_count, stat.matched_count)) self.logger.debug('TableStats: %s', tables) """ def __init__(self, datapath): super(OFPTableStatsReply, self).__init__(datapath) @OFPStatsReply.register_stats_type() @_set_stats_type(ofproto.OFPST_PORT, OFPPortStats) @_set_msg_type(ofproto.OFPT_STATS_REPLY) class OFPPortStatsReply(OFPStatsReply): """ Port statistics reply message The switch responds with a stats reply that include this message to a port statistics request. ================ ====================================================== Attribute Description ================ ====================================================== port_no Port number. rx_packets Number of received packets. tx_packets Number of transmitted packets. rx_bytes Number of received bytes. tx_bytes Number of transmitted bytes. rx_dropped Number of packets dropped by RX. tx_dropped Number of packets dropped by TX. rx_errors Number of receive errors. tx_errors Number of transmit errors. rx_frame_err Number of frame alignment errors. rx_over_err Number of packet with RX overrun. rx_crc_err Number of CRC errors. collisions Number of collisions. ================ ====================================================== Example:: @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) def port_stats_reply_handler(self, ev): msg = ev.msg ofp = msg.datapath.ofproto body = ev.msg.body ports = [] for stat in body: ports.append('port_no=%d ' 'rx_packets=%d tx_packets=%d ' 'rx_bytes=%d tx_bytes=%d ' 'rx_dropped=%d tx_dropped=%d ' 'rx_errors=%d tx_errors=%d ' 'rx_frame_err=%d rx_over_err=%d rx_crc_err=%d ' 'collisions=%d' % (stat.port_no, stat.rx_packets, stat.tx_packets, stat.rx_bytes, stat.tx_bytes, stat.rx_dropped, stat.tx_dropped, stat.rx_errors, stat.tx_errors, stat.rx_frame_err, stat.rx_over_err, stat.rx_crc_err, stat.collisions)) self.logger.debug('PortStats: %s', ports) """ def __init__(self, datapath): super(OFPPortStatsReply, self).__init__(datapath) @OFPStatsReply.register_stats_type() @_set_stats_type(ofproto.OFPST_QUEUE, OFPQueueStats) @_set_msg_type(ofproto.OFPT_STATS_REPLY) class OFPQueueStatsReply(OFPStatsReply): """ Queue statistics reply message The switch responds with a stats reply that include this message to an aggregate flow statistics request. ================ ====================================================== Attribute Description ================ ====================================================== port_no Port number. queue_id ID of queue. tx_bytes Number of transmitted bytes. tx_packets Number of transmitted packets. tx_errors Number of packets dropped due to overrun. ================ ====================================================== Example:: @set_ev_cls(ofp_event.EventOFPQueueStatsReply, MAIN_DISPATCHER) def stats_reply_handler(self, ev): msg = ev.msg ofp = msg.datapath.ofproto body = ev.msg.body queues = [] for stat in body: queues.append('port_no=%d queue_id=%d ' 'tx_bytes=%d tx_packets=%d tx_errors=%d ' % (stat.port_no, stat.queue_id, stat.tx_bytes, stat.tx_packets, stat.tx_errors)) self.logger.debug('QueueStats: %s', queues) """ def __init__(self, datapath): super(OFPQueueStatsReply, self).__init__(datapath) @OFPStatsReply.register_stats_type() @_set_stats_type(ofproto.OFPST_VENDOR, OFPVendorStats) @_set_msg_type(ofproto.OFPT_STATS_REPLY) class OFPVendorStatsReply(OFPStatsReply): """ Vendor statistics reply message The switch responds with a stats reply that include this message to an vendor statistics request. """ _STATS_VENDORS = {} @staticmethod def register_stats_vendor(vendor): def _register_stats_vendor(cls): cls.cls_vendor = vendor OFPVendorStatsReply._STATS_VENDORS[cls.cls_vendor] = cls return cls return _register_stats_vendor def __init__(self, datapath): super(OFPVendorStatsReply, self).__init__(datapath) @classmethod def parser_stats(cls, datapath, version, msg_type, msg_len, xid, buf): (type_,) = struct.unpack_from( ofproto.OFP_VENDOR_STATS_MSG_PACK_STR, six.binary_type(buf), ofproto.OFP_STATS_MSG_SIZE) cls_ = cls._STATS_VENDORS.get(type_) if cls_ is None: msg = MsgBase.parser.__func__( cls, datapath, version, msg_type, msg_len, xid, buf) body_cls = cls.cls_stats_body_cls body = body_cls.parser(buf, ofproto.OFP_STATS_MSG_SIZE) msg.body = body return msg return cls_.parser( datapath, version, msg_type, msg_len, xid, buf, ofproto.OFP_VENDOR_STATS_MSG_SIZE) @OFPVendorStatsReply.register_stats_vendor(ofproto_common.NX_EXPERIMENTER_ID) class NXStatsReply(OFPStatsReply): _NX_STATS_TYPES = {} @staticmethod def register_nx_stats_type(body_single_struct=False): def _register_nx_stats_type(cls): assert cls.cls_stats_type is not None assert cls.cls_stats_type not in \ NXStatsReply._NX_STATS_TYPES assert cls.cls_stats_body_cls is not None cls.cls_body_single_struct = body_single_struct NXStatsReply._NX_STATS_TYPES[cls.cls_stats_type] = cls return cls return _register_nx_stats_type @classmethod def parser_stats_body(cls, buf, msg_len, offset): body_cls = cls.cls_stats_body_cls body = [] while offset < msg_len: entry = body_cls.parser(buf, offset) body.append(entry) offset += entry.length if cls.cls_body_single_struct: return body[0] return body @classmethod def parser_stats(cls, datapath, version, msg_type, msg_len, xid, buf, offset): msg = MsgBase.parser.__func__( cls, datapath, version, msg_type, msg_len, xid, buf) msg.body = msg.parser_stats_body(msg.buf, msg.msg_len, offset) return msg @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf, offset): (type_,) = struct.unpack_from( ofproto.NX_STATS_MSG_PACK_STR, six.binary_type(buf), offset) offset += ofproto.NX_STATS_MSG0_SIZE cls_ = cls._NX_STATS_TYPES.get(type_) msg = cls_.parser_stats( datapath, version, msg_type, msg_len, xid, buf, offset) return msg @NXStatsReply.register_nx_stats_type() @_set_stats_type(ofproto.NXST_FLOW, NXFlowStats) class NXFlowStatsReply(NXStatsReply): def __init__(self, datapath): super(NXFlowStatsReply, self).__init__(datapath) @NXStatsReply.register_nx_stats_type() @_set_stats_type(ofproto.NXST_AGGREGATE, NXAggregateStats) class NXAggregateStatsReply(NXStatsReply): def __init__(self, datapath): super(NXAggregateStatsReply, self).__init__(datapath) # # controller-to-switch message # serializer only # @_set_msg_reply(OFPSwitchFeatures) @_set_msg_type(ofproto.OFPT_FEATURES_REQUEST) class OFPFeaturesRequest(MsgBase): """ Features request message The controller sends a feature request to the switch upon session establishment. This message is handled by the OSKen framework, so the OSKen application do not need to process this typically. Example:: def send_features_request(self, datapath): ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPFeaturesRequest(datapath) datapath.send_msg(req) """ def __init__(self, datapath): super(OFPFeaturesRequest, self).__init__(datapath) @_set_msg_type(ofproto.OFPT_GET_CONFIG_REQUEST) class OFPGetConfigRequest(MsgBase): """ Get config request message The controller sends a get config request to query configuration parameters in the switch. Example:: def send_get_config_request(self, datapath): ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPGetConfigRequest(datapath) datapath.send_msg(req) """ def __init__(self, datapath): super(OFPGetConfigRequest, self).__init__(datapath) @_set_msg_type(ofproto.OFPT_SET_CONFIG) class OFPSetConfig(MsgBase): """ Set config request message The controller sends a set config request message to set configuraion parameters. ============= ========================================================= Attribute Description ============= ========================================================= flags One of the following configuration flags. | OFPC_FRAG_NORMAL | OFPC_FRAG_DROP | OFPC_FRAG_REASM | OFPC_FRAG_MASK miss_send_len Max bytes of new flow that datapath should send to the controller. ============= ========================================================= Example:: def send_set_config(self, datapath): ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPSetConfig(datapath, ofp.OFPC_FRAG_NORMAL, 256) datapath.send_msg(req) """ def __init__(self, datapath, flags=None, miss_send_len=None): super(OFPSetConfig, self).__init__(datapath) self.flags = flags self.miss_send_len = miss_send_len def _serialize_body(self): assert self.flags is not None assert self.miss_send_len is not None msg_pack_into(ofproto.OFP_SWITCH_CONFIG_PACK_STR, self.buf, ofproto.OFP_HEADER_SIZE, self.flags, self.miss_send_len) @_set_msg_type(ofproto.OFPT_PACKET_OUT) class OFPPacketOut(MsgBase): """ Packet-Out message The controller uses this message to send a packet out throught the switch. ================ ====================================================== Attribute Description ================ ====================================================== buffer_id ID assigned by datapath (0xffffffff if none). in_port Packet's input port (OFPP_NONE if none). actions ist of ``OFPAction*`` instance. data Packet data of a binary type value or an instances of packet.Packet. ================ ====================================================== Example:: def send_packet_out(self, datapath): ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser buffer_id = 0xffffffff in_port = ofp.OFPP_NONE actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD, 0)] req = ofp_parser.OFPPacketOut(datapath, buffer_id, in_port, actions) datapath.send_msg(req) """ def __init__(self, datapath, buffer_id=None, in_port=None, actions=None, data=None): super(OFPPacketOut, self).__init__(datapath) self.buffer_id = buffer_id self.in_port = in_port self._actions_len = None self.actions = actions self.data = data def _serialize_body(self): assert self.buffer_id is not None assert self.in_port is not None assert self.actions is not None self._actions_len = 0 offset = ofproto.OFP_PACKET_OUT_SIZE for a in self.actions: a.serialize(self.buf, offset) offset += a.len self._actions_len += a.len if self.data is not None: assert self.buffer_id == 0xffffffff if isinstance(self.data, packet.Packet): self.data.serialize() self.buf += self.data.data else: self.buf += self.data msg_pack_into(ofproto.OFP_PACKET_OUT_PACK_STR, self.buf, ofproto.OFP_HEADER_SIZE, self.buffer_id, self.in_port, self._actions_len) @classmethod def from_jsondict(cls, dict_, decode_string=base64.b64decode, **additional_args): if isinstance(dict_['data'], dict): data = dict_.pop('data') ins = super(OFPPacketOut, cls).from_jsondict(dict_, decode_string, **additional_args) ins.data = packet.Packet.from_jsondict(data['Packet']) dict_['data'] = data else: ins = super(OFPPacketOut, cls).from_jsondict(dict_, decode_string, **additional_args) return ins @_register_parser @_set_msg_type(ofproto.OFPT_FLOW_MOD) class OFPFlowMod(MsgBase): """ Modify Flow entry message The controller sends this message to modify the flow table. ================ ====================================================== Attribute Description ================ ====================================================== match Instance of ``OFPMatch``. cookie Opaque controller-issued identifier. command One of the following values. | OFPFC_ADD | OFPFC_MODIFY | OFPFC_MODIFY_STRICT | OFPFC_DELETE | OFPFC_DELETE_STRICT idle_timeout Idle time before discarding (seconds). hard_timeout Max time before discarding (seconds). priority Priority level of flow entry. buffer_id Buffered packet to apply to (or 0xffffffff). Not meaningful for OFPFC_DELETE*. out_port For OFPFC_DELETE* commands, require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. flags One of the following values. | OFPFF_SEND_FLOW_REM | OFPFF_CHECK_OVERLAP | OFPFF_EMERG actions List of ``OFPAction*`` instance. ================ ====================================================== Example:: def send_flow_mod(self, datapath): ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser match = ofp_parser.OFPMatch(in_port=1) cookie = 0 command = ofp.OFPFC_ADD idle_timeout = hard_timeout = 0 priority = 32768 buffer_id = 0xffffffff out_port = ofproto.OFPP_NONE flags = 0 actions = [ofp_parser.OFPActionOutput(ofp.OFPP_NORMAL, 0)] req = ofp_parser.OFPFlowMod( datapath, match, cookie, command, idle_timeout, hard_timeout, priority, buffer_id, out_port, flags, actions) datapath.send_msg(req) """ def __init__(self, datapath, match=None, cookie=0, command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, priority=ofproto.OFP_DEFAULT_PRIORITY, buffer_id=0xffffffff, out_port=ofproto.OFPP_NONE, flags=0, actions=None): super(OFPFlowMod, self).__init__(datapath) self.match = OFPMatch() if match is None else match self.cookie = cookie self.command = command self.idle_timeout = idle_timeout self.hard_timeout = hard_timeout self.priority = priority self.buffer_id = buffer_id self.out_port = out_port self.flags = flags self.actions = [] if actions is None else actions def _serialize_body(self): offset = ofproto.OFP_HEADER_SIZE self.match.serialize(self.buf, offset) offset += ofproto.OFP_MATCH_SIZE msg_pack_into(ofproto.OFP_FLOW_MOD_PACK_STR0, self.buf, offset, self.cookie, self.command, self.idle_timeout, self.hard_timeout, self.priority, self.buffer_id, self.out_port, self.flags) offset = ofproto.OFP_FLOW_MOD_SIZE if self.actions is not None: for a in self.actions: a.serialize(self.buf, offset) offset += a.len @classmethod def parser(cls, datapath, version, msg_type, msg_len, xid, buf): msg = super(OFPFlowMod, cls).parser( datapath, version, msg_type, msg_len, xid, buf) offset = ofproto.OFP_HEADER_SIZE msg.match = OFPMatch.parse(msg.buf, offset) offset += ofproto.OFP_MATCH_SIZE (msg.cookie, msg.command, msg.idle_timeout, msg.hard_timeout, msg.priority, msg.buffer_id, msg.out_port, msg.flags) = struct.unpack_from( ofproto.OFP_FLOW_MOD_PACK_STR0, msg.buf, offset) offset = ofproto.OFP_FLOW_MOD_SIZE actions = [] while offset < msg_len: a = OFPAction.parser(buf, offset) actions.append(a) offset += a.len msg.actions = actions return msg @_set_msg_type(ofproto.OFPT_PORT_MOD) class OFPPortMod(MsgBase): """ Port modification message The controller send this message to modify the behavior of the port. ================ ====================================================== Attribute Description ================ ====================================================== port_no Port number to modify. hw_addr The hardware address that must be the same as hw_addr of ``OFPPhyPort`` of ``OFPSwitchFeatures``. config Bitmap of configuration flags. | OFPPC_PORT_DOWN | OFPPC_NO_STP | OFPPC_NO_RECV | OFPPC_NO_RECV_STP | OFPPC_NO_FLOOD | OFPPC_NO_FWD | OFPPC_NO_PACKET_IN mask Bitmap of configuration flags above to be changed advertise Bitmap of the following flags. | OFPPF_10MB_HD | OFPPF_10MB_FD | OFPPF_100MB_HD | OFPPF_100MB_FD | OFPPF_1GB_HD | OFPPF_1GB_FD | OFPPF_10GB_FD | OFPPF_COPPER | OFPPF_FIBER | OFPPF_AUTONEG | OFPPF_PAUSE | OFPPF_PAUSE_ASYM ================ ====================================================== Example:: def send_port_mod(self, datapath): ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser port_no = 3 hw_addr = 'fa:c8:e8:76:1d:7e' config = 0 mask = (ofp.OFPPC_PORT_DOWN | ofp.OFPPC_NO_RECV | ofp.OFPPC_NO_FWD | ofp.OFPPC_NO_PACKET_IN) advertise = (ofp.OFPPF_10MB_HD | ofp.OFPPF_100MB_FD | ofp.OFPPF_1GB_FD | ofp.OFPPF_COPPER | ofp.OFPPF_AUTONEG | ofp.OFPPF_PAUSE | ofp.OFPPF_PAUSE_ASYM) req = ofp_parser.OFPPortMod(datapath, port_no, hw_addr, config, mask, advertise) datapath.send_msg(req) """ _TYPE = { 'ascii': [ 'hw_addr', ] } def __init__(self, datapath, port_no=0, hw_addr='00:00:00:00:00:00', config=0, mask=0, advertise=0): super(OFPPortMod, self).__init__(datapath) self.port_no = port_no self.hw_addr = hw_addr self.config = config self.mask = mask self.advertise = advertise def _serialize_body(self): msg_pack_into(ofproto.OFP_PORT_MOD_PACK_STR, self.buf, ofproto.OFP_HEADER_SIZE, self.port_no, addrconv.mac.text_to_bin(self.hw_addr), self.config, self.mask, self.advertise) @_set_msg_reply(OFPBarrierReply) @_set_msg_type(ofproto.OFPT_BARRIER_REQUEST) class OFPBarrierRequest(MsgBase): """ Barrier request message The controller sends this message to ensure message dependencies have been met or receive notifications for completed operations. Example:: def send_barrier_request(self, datapath): ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPBarrierRequest(datapath) datapath.send_msg(req) """ def __init__(self, datapath): super(OFPBarrierRequest, self).__init__(datapath) @_set_msg_reply(OFPQueueGetConfigReply) @_set_msg_type(ofproto.OFPT_QUEUE_GET_CONFIG_REQUEST) class OFPQueueGetConfigRequest(MsgBase): """ Queue configuration request message ================ ====================================================== Attribute Description ================ ====================================================== port Port to be queried. Should refer to a valid physical port (i.e. < OFPP_MAX). ================ ====================================================== Example:: def send_queue_get_config_request(self, datapath): ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPQueueGetConfigRequest(datapath, ofp.OFPP_NONE) datapath.send_msg(req) """ def __init__(self, datapath, port): super(OFPQueueGetConfigRequest, self).__init__(datapath) self.port = port def _serialize_body(self): msg_pack_into(ofproto.OFP_QUEUE_GET_CONFIG_REQUEST_PACK_STR, self.buf, ofproto.OFP_HEADER_SIZE, self.port) class OFPStatsRequest(MsgBase): def __init__(self, datapath, flags): assert flags == 0 # none yet defined super(OFPStatsRequest, self).__init__(datapath) self.type = self.__class__.cls_stats_type self.flags = flags def _serialize_stats_body(self): pass def _serialize_body(self): msg_pack_into(ofproto.OFP_STATS_MSG_PACK_STR, self.buf, ofproto.OFP_HEADER_SIZE, self.type, self.flags) self._serialize_stats_body() @_set_msg_reply(OFPDescStatsReply) @_set_stats_type(ofproto.OFPST_DESC, OFPDescStats) @_set_msg_type(ofproto.OFPT_STATS_REQUEST) class OFPDescStatsRequest(OFPStatsRequest): """ Description statistics request message The controller uses this message to query description of the switch. ================ ====================================================== Attribute Description ================ ====================================================== flags Zero (none yet defined in the spec). ================ ====================================================== Example:: def send_desc_stats_request(self, datapath): ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPDescStatsRequest(datapath) datapath.send_msg(req) """ def __init__(self, datapath, flags): super(OFPDescStatsRequest, self).__init__(datapath, flags) class OFPFlowStatsRequestBase(OFPStatsRequest): def __init__(self, datapath, flags, match, table_id, out_port): super(OFPFlowStatsRequestBase, self).__init__(datapath, flags) self.match = match self.table_id = table_id self.out_port = out_port def _serialize_stats_body(self): offset = ofproto.OFP_STATS_MSG_SIZE self.match.serialize(self.buf, offset) offset += ofproto.OFP_MATCH_SIZE msg_pack_into(ofproto.OFP_FLOW_STATS_REQUEST_ID_PORT_STR, self.buf, offset, self.table_id, self.out_port) @_set_msg_reply(OFPFlowStatsReply) @_set_stats_type(ofproto.OFPST_FLOW, OFPFlowStats) @_set_msg_type(ofproto.OFPT_STATS_REQUEST) class OFPFlowStatsRequest(OFPFlowStatsRequestBase): """ Individual flow statistics request message The controller uses this message to query individual flow statistics. ================ ====================================================== Attribute Description ================ ====================================================== flags Zero (none yet defined in the spec). match Instance of ``OFPMatch``. table_id ID of table to read (from ofp_table_stats), 0xff for all tables or 0xfe for emergency. out_port Require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. ================ ====================================================== Example:: def send_flow_stats_request(self, datapath): ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser match = ofp_parser.OFPMatch(in_port=1) table_id = 0xff out_port = ofp.OFPP_NONE req = ofp_parser.OFPFlowStatsRequest( datapath, 0, match, table_id, out_port) datapath.send_msg(req) """ def __init__(self, datapath, flags, match, table_id, out_port): super(OFPFlowStatsRequest, self).__init__( datapath, flags, match, table_id, out_port) @_set_msg_reply(OFPAggregateStatsReply) @_set_stats_type(ofproto.OFPST_AGGREGATE, OFPAggregateStats) @_set_msg_type(ofproto.OFPT_STATS_REQUEST) class OFPAggregateStatsRequest(OFPFlowStatsRequestBase): """ Aggregate flow statistics request message The controller uses this message to query aggregate flow statictics. ================ ====================================================== Attribute Description ================ ====================================================== flags Zero (none yet defined in the spec). match Fields to match. table_id ID of table to read (from ofp_table_stats) 0xff for all tables or 0xfe for emergency. out_port Require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. ================ ====================================================== Example:: def send_aggregate_stats_request(self, datapath): ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser cookie = cookie_mask = 0 match = ofp_parser.OFPMatch(in_port=1) req = ofp_parser.OFPAggregateStatsRequest( datapath, 0, match, 0xff, ofp.OFPP_NONE) datapath.send_msg(req) """ def __init__(self, datapath, flags, match, table_id, out_port): super(OFPAggregateStatsRequest, self).__init__( datapath, flags, match, table_id, out_port) @_set_msg_reply(OFPTableStatsReply) @_set_stats_type(ofproto.OFPST_TABLE, OFPTableStats) @_set_msg_type(ofproto.OFPT_STATS_REQUEST) class OFPTableStatsRequest(OFPStatsRequest): """ Table statistics request message The controller uses this message to query flow table statictics. ================ ====================================================== Attribute Description ================ ====================================================== flags Zero (none yet defined in the spec). ================ ====================================================== Example:: def send_table_stats_request(self, datapath): ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPTableStatsRequest(datapath) datapath.send_msg(req) """ def __init__(self, datapath, flags): super(OFPTableStatsRequest, self).__init__(datapath, flags) @_set_msg_reply(OFPPortStatsReply) @_set_stats_type(ofproto.OFPST_PORT, OFPPortStats) @_set_msg_type(ofproto.OFPT_STATS_REQUEST) class OFPPortStatsRequest(OFPStatsRequest): """ Port statistics request message The controller uses this message to query information about ports statistics. ================ ====================================================== Attribute Description ================ ====================================================== flags Zero (none yet defined in the spec). port_no Port number to read (OFPP_NONE to all ports). ================ ====================================================== Example:: def send_port_stats_request(self, datapath): ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPPortStatsRequest(datapath, 0, ofp.OFPP_ANY) datapath.send_msg(req) """ def __init__(self, datapath, flags, port_no): super(OFPPortStatsRequest, self).__init__(datapath, flags) self.port_no = port_no def _serialize_stats_body(self): msg_pack_into(ofproto.OFP_PORT_STATS_REQUEST_PACK_STR, self.buf, ofproto.OFP_STATS_MSG_SIZE, self.port_no) @_set_msg_reply(OFPQueueStatsReply) @_set_stats_type(ofproto.OFPST_QUEUE, OFPQueueStats) @_set_msg_type(ofproto.OFPT_STATS_REQUEST) class OFPQueueStatsRequest(OFPStatsRequest): """ Queue statistics request message The controller uses this message to query queue statictics. ================ ====================================================== Attribute Description ================ ====================================================== flags Zero (none yet defined in the spec) port_no Port number to read (All ports if OFPT_ALL). queue_id ID of queue to read (All queues if OFPQ_ALL). ================ ====================================================== Example:: def send_queue_stats_request(self, datapath): ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser req = ofp_parser.OFPQueueStatsRequest(datapath, 0, ofp.OFPT_ALL, ofp.OFPQ_ALL) datapath.send_msg(req) """ def __init__(self, datapath, flags, port_no, queue_id): super(OFPQueueStatsRequest, self).__init__(datapath, flags) self.port_no = port_no self.queue_id = queue_id def _serialize_stats_body(self): msg_pack_into(ofproto.OFP_QUEUE_STATS_REQUEST_PACK_STR, self.buf, ofproto.OFP_STATS_MSG_SIZE, self.port_no, self.queue_id) @_set_msg_reply(OFPVendorStatsReply) @_set_stats_type(ofproto.OFPST_VENDOR, OFPVendorStats) @_set_msg_type(ofproto.OFPT_STATS_REQUEST) class OFPVendorStatsRequest(OFPStatsRequest): """ Vendor statistics request message The controller uses this message to query vendor-specific information of a switch. """ def __init__(self, datapath, flags, vendor, specific_data=None): super(OFPVendorStatsRequest, self).__init__(datapath, flags) self.vendor = vendor self.specific_data = specific_data def _serialize_vendor_stats(self): self.buf += self.specific_data def _serialize_stats_body(self): msg_pack_into(ofproto.OFP_VENDOR_STATS_MSG_PACK_STR, self.buf, ofproto.OFP_STATS_MSG_SIZE, self.vendor) self._serialize_vendor_stats() class NXStatsRequest(OFPVendorStatsRequest): def __init__(self, datapath, flags, subtype): super(NXStatsRequest, self).__init__(datapath, flags, ofproto_common.NX_EXPERIMENTER_ID) self.subtype = subtype def _serialize_vendor_stats_body(self): pass def _serialize_vendor_stats(self): msg_pack_into(ofproto.NX_STATS_MSG_PACK_STR, self.buf, ofproto.OFP_VENDOR_STATS_MSG_SIZE, self.subtype) self._serialize_vendor_stats_body() class NXFlowStatsRequest(NXStatsRequest): def __init__(self, datapath, flags, out_port, table_id, rule=None): super(NXFlowStatsRequest, self).__init__(datapath, flags, ofproto.NXST_FLOW) self.out_port = out_port self.table_id = table_id self.rule = rule self.match_len = 0 def _serialize_vendor_stats_body(self): if self.rule is not None: offset = ofproto.NX_STATS_MSG_SIZE + \ ofproto.NX_FLOW_STATS_REQUEST_SIZE self.match_len = nx_match.serialize_nxm_match( self.rule, self.buf, offset) msg_pack_into( ofproto.NX_FLOW_STATS_REQUEST_PACK_STR, self.buf, ofproto.NX_STATS_MSG_SIZE, self.out_port, self.match_len, self.table_id) class NXAggregateStatsRequest(NXStatsRequest): def __init__(self, datapath, flags, out_port, table_id, rule=None): super(NXAggregateStatsRequest, self).__init__( datapath, flags, ofproto.NXST_AGGREGATE) self.out_port = out_port self.table_id = table_id self.rule = rule self.match_len = 0 def _serialize_vendor_stats_body(self): if self.rule is not None: offset = ofproto.NX_STATS_MSG_SIZE + \ ofproto.NX_AGGREGATE_STATS_REQUEST_SIZE self.match_len = nx_match.serialize_nxm_match( self.rule, self.buf, offset) msg_pack_into( ofproto.NX_AGGREGATE_STATS_REQUEST_PACK_STR, self.buf, ofproto.NX_STATS_MSG_SIZE, self.out_port, self.match_len, self.table_id) nx_actions.generate( 'os_ken.ofproto.ofproto_v1_0', 'os_ken.ofproto.ofproto_v1_0_parser' )