# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license # Copyright (C) 2014 Maxence Tury # OpenFlow is an open standard used in SDN deployments. # Based on OpenFlow v1.0.1 # Specifications can be retrieved from https://www.opennetworking.org/ # scapy.contrib.description = Openflow v1.0 # scapy.contrib.status = loads from __future__ import absolute_import import struct from scapy.compat import chb, orb, raw from scapy.config import conf from scapy.error import warning from scapy.fields import BitEnumField, BitField, ByteEnumField, ByteField, FieldLenField, FlagsField, IntEnumField, IntField, IPField, LongField, MACField, PacketField, PacketListField, ShortEnumField, ShortField, StrFixedLenField, X3BytesField, XBitField, XByteField, XIntField, XShortField # noqa: E501 from scapy.layers.l2 import Ether from scapy.layers.inet import TCP from scapy.packet import Packet, Raw, bind_bottom_up, bind_top_down from scapy.utils import binrepr from scapy.modules import six # If prereq_autocomplete is True then match prerequisites will be # automatically handled. See OFPMatch class. conf.contribs['OPENFLOW'] = {'prereq_autocomplete': True} ##################################################### # Predefined values # ##################################################### ofp_port_no = {0xfff8: "IN_PORT", 0xfff9: "TABLE", 0xfffa: "NORMAL", 0xfffb: "FLOOD", 0xfffc: "ALL", 0xfffd: "CONTROLLER", 0xfffe: "LOCAL", 0xffff: "NONE"} ofp_table = {0xff: "ALL"} ofp_queue = {0xffffffff: "ALL"} ofp_buffer = {0xffffffff: "NO_BUFFER"} ofp_max_len = {0xffff: "NO_BUFFER"} ##################################################### # Common structures # ##################################################### # The following structures will be used in different types # of OpenFlow messages: ports, matches, actions, queues. # Ports # ofp_port_config = ["PORT_DOWN", "NO_STP", "NO_RECV", "NO_RECV_STP", "NO_FLOOD", "NO_FWD", "NO_PACKET_IN"] ofp_port_state = ["LINK_DOWN"] ofp_port_state_stp = {0: "OFPPS_STP_LISTEN", 1: "OFPPS_STP_LEARN", 2: "OFPPS_STP_FORWARD", 3: "OFPPS_STP_BLOCK"} ofp_port_features = ["10MB_HD", "10MB_FD", "100MB_HD", "100MB_FD", "1GB_HD", "1GB_FD", "10GB_FD", "COPPER", "FIBER", "AUTONEG", "PAUSE", "PAUSE_ASYM"] class OFPPhyPort(Packet): name = "OFP_PHY_PORT" fields_desc = [ShortEnumField("port_no", 0, ofp_port_no), MACField("hw_addr", "0"), StrFixedLenField("port_name", "", 16), FlagsField("config", 0, 32, ofp_port_config), BitEnumField("stp_state", 0, 24, ofp_port_state), FlagsField("state", 0, 8, ofp_port_state), FlagsField("curr", 0, 32, ofp_port_features), FlagsField("advertised", 0, 32, ofp_port_features), FlagsField("supported", 0, 32, ofp_port_features), FlagsField("peer", 0, 32, ofp_port_features)] def extract_padding(self, s): return b"", s class OFPMatch(Packet): name = "OFP_MATCH" fields_desc = [FlagsField("wildcards1", None, 12, ["DL_VLAN_PCP", "NW_TOS"]), BitField("nw_dst_mask", None, 6), BitField("nw_src_mask", None, 6), FlagsField("wildcards2", None, 8, ["IN_PORT", "DL_VLAN", "DL_SRC", "DL_DST", "DL_TYPE", "NW_PROTO", "TP_SRC", "TP_DST"]), ShortEnumField("in_port", None, ofp_port_no), MACField("dl_src", None), MACField("dl_dst", None), ShortField("dl_vlan", None), ByteField("dl_vlan_pcp", None), XByteField("pad1", None), ShortField("dl_type", None), ByteField("nw_tos", None), ByteField("nw_proto", None), XShortField("pad2", None), IPField("nw_src", "0"), IPField("nw_dst", "0"), ShortField("tp_src", None), ShortField("tp_dst", None)] def extract_padding(self, s): return b"", s # with post_build we create the wildcards field bit by bit def post_build(self, p, pay): # first 10 bits of an ofp_match are always set to 0 lst_bits = "0" * 10 # when one field has not been declared, it is assumed to be wildcarded if self.wildcards1 is None: if self.nw_tos is None: lst_bits += "1" else: lst_bits += "0" if self.dl_vlan_pcp is None: lst_bits += "1" else: lst_bits += "0" else: w1 = binrepr(self.wildcards1) lst_bits += "0" * (2 - len(w1)) lst_bits += w1 # ip masks use 6 bits each if self.nw_dst_mask is None: if self.nw_dst == "0": lst_bits += "111111" # 0x100000 would be ok too (32-bit IP mask) else: lst_bits += "0" * 6 else: m1 = binrepr(self.nw_dst_mask) lst_bits += "0" * (6 - len(m1)) lst_bits += m1 if self.nw_src_mask is None: if self.nw_src == "0": lst_bits += "111111" else: lst_bits += "0" * 6 else: m2 = binrepr(self.nw_src_mask) lst_bits += "0" * (6 - len(m2)) lst_bits += m2 # wildcards2 works the same way as wildcards1 if self.wildcards2 is None: if self.tp_dst is None: lst_bits += "1" else: lst_bits += "0" if self.tp_src is None: lst_bits += "1" else: lst_bits += "0" if self.nw_proto is None: lst_bits += "1" else: lst_bits += "0" if self.dl_type is None: lst_bits += "1" else: lst_bits += "0" if self.dl_dst is None: lst_bits += "1" else: lst_bits += "0" if self.dl_src is None: lst_bits += "1" else: lst_bits += "0" if self.dl_vlan is None: lst_bits += "1" else: lst_bits += "0" if self.in_port is None: lst_bits += "1" else: lst_bits += "0" else: w2 = binrepr(self.wildcards2) lst_bits += "0" * (8 - len(w2)) lst_bits += w2 # In order to write OFPMatch compliant with the specifications, # if prereq_autocomplete has been set to True # we assume ethertype=IP or nwproto=TCP when appropriate subfields are provided. # noqa: E501 if conf.contribs['OPENFLOW']['prereq_autocomplete']: if self.dl_type is None: if self.nw_src != "0" or self.nw_dst != "0" or \ self.nw_proto is not None or self.nw_tos is not None: p = p[:22] + struct.pack("!H", 0x0800) + p[24:] lst_bits = lst_bits[:-5] + "0" + lst_bits[-4:] if self.nw_proto is None: if self.tp_src is not None or self.tp_dst is not None: p = p[:22] + struct.pack("!H", 0x0800) + p[24:] lst_bits = lst_bits[:-5] + "0" + lst_bits[-4:] p = p[:25] + struct.pack("!B", 0x06) + p[26:] lst_bits = lst_bits[:-6] + "0" + lst_bits[-5:] ins = b"".join(chb(int("".join(x), 2)) for x in zip(*[iter(lst_bits)] * 8)) # noqa: E501 p = ins + p[4:] return p + pay class _ofp_header(Packet): name = "Dummy OpenFlow Header for some lower layers" def post_build(self, p, pay): if self.len is None: tmp_len = len(p) + len(pay) p = p[:2] + struct.pack("!H", tmp_len) + p[4:] return p + pay class _ofp_header_item(Packet): name = "Dummy OpenFlow Header for items layers" def post_build(self, p, pay): if self.len is None: tmp_len = len(p) + len(pay) p = struct.pack("!H", tmp_len) + p[2:] return p + pay # Actions # class _UnknownOpenFlow(Raw): name = "Unknown OpenFlow packet" class OpenFlow(_ofp_header): name = "OpenFlow dissector" @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: version = orb(_pkt[0]) if version == 0x04: # OpenFlow 1.3 from scapy.contrib.openflow3 import OpenFlow3 return OpenFlow3.dispatch_hook(_pkt, *args, **kargs) elif version == 0x01: # OpenFlow 1.0 # port 6653 has been allocated by IANA, port 6633 should no # longer be used # OpenFlow function may be called with a None # self in OFPPacketField of_type = orb(_pkt[1]) if of_type == 1: err_type = orb(_pkt[9]) # err_type is a short int, but last byte is enough if err_type == 255: err_type = 65535 return ofp_error_cls[err_type] elif of_type == 16: mp_type = orb(_pkt[9]) if mp_type == 255: mp_type = 65535 return ofp_stats_request_cls[mp_type] elif of_type == 17: mp_type = orb(_pkt[9]) if mp_type == 255: mp_type = 65535 return ofp_stats_reply_cls[mp_type] else: return ofpt_cls[of_type] else: warning("Unknown OpenFlow packet") return _UnknownOpenFlow ofp_action_types = {0: "OFPAT_OUTPUT", 1: "OFPAT_SET_VLAN_VID", 2: "OFPAT_SET_VLAN_PCP", 3: "OFPAT_STRIP_VLAN", 4: "OFPAT_SET_DL_SRC", 5: "OFPAT_SET_DL_DST", 6: "OFPAT_SET_NW_SRC", 7: "OFPAT_SET_NW_DST", 8: "OFPAT_SET_NW_TOS", 9: "OFPAT_SET_TP_SRC", 10: "OFPAT_SET_TP_DST", 11: "OFPAT_ENQUEUE", 65535: "OFPAT_VENDOR"} class OFPATOutput(OpenFlow): name = "OFPAT_OUTPUT" fields_desc = [ShortEnumField("type", 0, ofp_action_types), ShortField("len", 8), ShortEnumField("port", 0, ofp_port_no), ShortEnumField("max_len", "NO_BUFFER", ofp_max_len)] class OFPATSetVLANVID(OpenFlow): name = "OFPAT_SET_VLAN_VID" fields_desc = [ShortEnumField("type", 1, ofp_action_types), ShortField("len", 8), ShortField("vlan_vid", 0), XShortField("pad", 0)] class OFPATSetVLANPCP(OpenFlow): name = "OFPAT_SET_VLAN_PCP" fields_desc = [ShortEnumField("type", 2, ofp_action_types), ShortField("len", 8), ByteField("vlan_pcp", 0), X3BytesField("pad", 0)] class OFPATStripVLAN(OpenFlow): name = "OFPAT_STRIP_VLAN" fields_desc = [ShortEnumField("type", 3, ofp_action_types), ShortField("len", 8), XIntField("pad", 0)] class OFPATSetDlSrc(OpenFlow): name = "OFPAT_SET_DL_SRC" fields_desc = [ShortEnumField("type", 4, ofp_action_types), ShortField("len", 16), MACField("dl_addr", "0"), XBitField("pad", 0, 48)] class OFPATSetDlDst(OpenFlow): name = "OFPAT_SET_DL_DST" fields_desc = [ShortEnumField("type", 5, ofp_action_types), ShortField("len", 16), MACField("dl_addr", "0"), XBitField("pad", 0, 48)] class OFPATSetNwSrc(OpenFlow): name = "OFPAT_SET_NW_SRC" fields_desc = [ShortEnumField("type", 6, ofp_action_types), ShortField("len", 8), IPField("nw_addr", "0")] class OFPATSetNwDst(OpenFlow): name = "OFPAT_SET_NW_DST" fields_desc = [ShortEnumField("type", 7, ofp_action_types), ShortField("len", 8), IPField("nw_addr", "0")] class OFPATSetNwToS(OpenFlow): name = "OFPAT_SET_TP_TOS" fields_desc = [ShortEnumField("type", 8, ofp_action_types), ShortField("len", 8), ByteField("nw_tos", 0), X3BytesField("pad", 0)] class OFPATSetTpSrc(OpenFlow): name = "OFPAT_SET_TP_SRC" fields_desc = [ShortEnumField("type", 9, ofp_action_types), ShortField("len", 8), ShortField("tp_port", 0), XShortField("pad", 0)] class OFPATSetTpDst(OpenFlow): name = "OFPAT_SET_TP_DST" fields_desc = [ShortEnumField("type", 10, ofp_action_types), ShortField("len", 8), ShortField("tp_port", 0), XShortField("pad", 0)] class OFPATEnqueue(OpenFlow): name = "OFPAT_ENQUEUE" fields_desc = [ShortEnumField("type", 11, ofp_action_types), ShortField("len", 16), ShortEnumField("port", 0, ofp_port_no), XBitField("pad", 0, 48), IntField("queue_id", 0)] class OFPATVendor(OpenFlow): name = "OFPAT_VENDOR" fields_desc = [ShortEnumField("type", 65535, ofp_action_types), ShortField("len", 8), IntField("vendor", 0)] ofp_action_cls = {0: OFPATOutput, 1: OFPATSetVLANVID, 2: OFPATSetVLANPCP, 3: OFPATStripVLAN, 4: OFPATSetDlSrc, 5: OFPATSetDlDst, 6: OFPATSetNwSrc, 7: OFPATSetNwDst, 8: OFPATSetNwToS, 9: OFPATSetTpSrc, 10: OFPATSetTpDst, 11: OFPATEnqueue, 65535: OFPATVendor} class OFPAT(Packet): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_action_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s # Queues # ofp_queue_property_types = {0: "OFPQT_NONE", 1: "OFPQT_MIN_RATE"} class OFPQTNone(_ofp_header): name = "OFPQT_NONE" fields_desc = [ShortEnumField("type", 0, ofp_queue_property_types), ShortField("len", 8), XIntField("pad", 0)] class OFPQTMinRate(_ofp_header): name = "OFPQT_MIN_RATE" fields_desc = [ShortEnumField("type", 1, ofp_queue_property_types), ShortField("len", 16), XIntField("pad", 0), ShortField("rate", 0), XBitField("pad2", 0, 48)] ofp_queue_property_cls = {0: OFPQTNone, 1: OFPQTMinRate} class OFPQT(Packet): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: t = struct.unpack("!H", _pkt[:2])[0] return ofp_queue_property_cls.get(t, Raw) return Raw def extract_padding(self, s): return b"", s class OFPPacketQueue(Packet): name = "OFP_PACKET_QUEUE" fields_desc = [IntField("queue_id", 0), ShortField("len", None), XShortField("pad", 0), PacketListField("properties", [], OFPQT, length_from=lambda pkt:pkt.len - 8)] # noqa: E501 def extract_padding(self, s): return b"", s def post_build(self, p, pay): if self.properties == []: p += raw(OFPQTNone()) if self.len is None: tmp_len = len(p) + len(pay) p = p[:4] + struct.pack("!H", tmp_len) + p[6:] return p + pay ##################################################### # OpenFlow 1.0 Messages # ##################################################### ofp_version = {0x01: "OpenFlow 1.0", 0x02: "OpenFlow 1.1", 0x03: "OpenFlow 1.2", 0x04: "OpenFlow 1.3", 0x05: "OpenFlow 1.4"} ofp_type = {0: "OFPT_HELLO", 1: "OFPT_ERROR", 2: "OFPT_ECHO_REQUEST", 3: "OFPT_ECHO_REPLY", 4: "OFPT_VENDOR", 5: "OFPT_FEATURES_REQUEST", 6: "OFPT_FEATURES_REPLY", 7: "OFPT_GET_CONFIG_REQUEST", 8: "OFPT_GET_CONFIG_REPLY", 9: "OFPT_SET_CONFIG", 10: "OFPT_PACKET_IN", 11: "OFPT_FLOW_REMOVED", 12: "OFPT_PORT_STATUS", 13: "OFPT_PACKET_OUT", 14: "OFPT_FLOW_MOD", 15: "OFPT_PORT_MOD", 16: "OFPT_STATS_REQUEST", 17: "OFPT_STATS_REPLY", 18: "OFPT_BARRIER_REQUEST", 19: "OFPT_BARRIER_REPLY", 20: "OFPT_QUEUE_GET_CONFIG_REQUEST", 21: "OFPT_QUEUE_GET_CONFIG_REPLY"} class OFPTHello(_ofp_header): name = "OFPT_HELLO" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 0, ofp_type), ShortField("len", None), IntField("xid", 0)] ##################################################### # OFPT_ERROR # ##################################################### # this class will be used to display some messages # sent back by the switch after an error class OFPacketField(PacketField): def getfield(self, pkt, s): try: tmp_len = s[2:4] tmp_len = struct.unpack("!H", tmp_len)[0] ofload = s[:tmp_len] remain = s[tmp_len:] return remain, OpenFlow(ofload) except Exception: return "", Raw(s) ofp_error_type = {0: "OFPET_HELLO_FAILED", 1: "OFPET_BAD_REQUEST", 2: "OFPET_BAD_ACTION", 3: "OFPET_FLOW_MOD_FAILED", 4: "OFPET_PORT_MOD_FAILED", 5: "OFPET_QUEUE_OP_FAILED"} class OFPETHelloFailed(_ofp_header): name = "OFPET_HELLO_FAILED" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 0, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPHFC_INCOMPATIBLE", 1: "OFPHFC_EPERM"}), OFPacketField("data", "", Raw)] class OFPETBadRequest(_ofp_header): name = "OFPET_BAD_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 1, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPBRC_BAD_VERSION", 1: "OFPBRC_BAD_TYPE", 2: "OFPBRC_BAD_STAT", 3: "OFPBRC_BAD_VENDOR", 4: "OFPBRC_BAD_SUBTYPE", 5: "OFPBRC_EPERM", 6: "OFPBRC_BAD_LEN", 7: "OFPBRC_BUFFER_EMPTY", 8: "OFPBRC_BUFFER_UNKNOWN"}), OFPacketField("data", "", Raw)] class OFPETBadAction(_ofp_header): name = "OFPET_BAD_ACTION" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 2, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPBAC_BAD_TYPE", 1: "OFPBAC_BAD_LEN", 2: "OFPBAC_BAD_VENDOR", 3: "OFPBAC_BAD_VENDOR_TYPE", 4: "OFPBAC_BAD_OUT_PORT", 5: "OFPBAC_BAD_ARGUMENT", 6: "OFPBAC_EPERM", 7: "OFPBAC_TOO_MANY", 8: "OFPBAC_BAD_QUEUE"}), OFPacketField("data", "", Raw)] class OFPETFlowModFailed(_ofp_header): name = "OFPET_FLOW_MOD_FAILED" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 3, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPFMFC_ALL_TABLES_FULL", 1: "OFPFMFC_OVERLAP", 2: "OFPFMFC_EPERM", 3: "OFPFMFC_BAD_EMERG_TIMEOUT", # noqa: E501 4: "OFPFMFC_BAD_COMMAND", 5: "OFPFMFC_UNSUPPORTED"}), OFPacketField("data", "", Raw)] class OFPETPortModFailed(_ofp_header): name = "OFPET_PORT_MOD_FAILED" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 4, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPPMFC_BAD_PORT", 1: "OFPPMFC_BAD_HW_ADDR"}), OFPacketField("data", "", Raw)] class OFPETQueueOpFailed(_ofp_header): name = "OFPET_QUEUE_OP_FAILED" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 5, ofp_error_type), ShortEnumField("errcode", 0, {0: "OFPQOFC_BAD_PORT", 1: "OFPQOFC_BAD_QUEUE", 2: "OFPQOFC_EPERM"}), OFPacketField("data", "", Raw)] # ofp_error_cls allows generic method OpenFlow() to choose the right class for dissection # noqa: E501 ofp_error_cls = {0: OFPETHelloFailed, 1: OFPETBadRequest, 2: OFPETBadAction, 3: OFPETFlowModFailed, 4: OFPETPortModFailed, 5: OFPETQueueOpFailed} # end of OFPT_ERRORS # class OFPTEchoRequest(_ofp_header): name = "OFPT_ECHO_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 2, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTEchoReply(_ofp_header): name = "OFPT_ECHO_REPLY" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 3, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTVendor(_ofp_header): name = "OFPT_VENDOR" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 4, ofp_type), ShortField("len", None), IntField("xid", 0), IntField("vendor", 0)] class OFPTFeaturesRequest(_ofp_header): name = "OFPT_FEATURES_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 5, ofp_type), ShortField("len", None), IntField("xid", 0)] ofp_action_types_flags = [v for v in six.itervalues(ofp_action_types) if v != 'OFPAT_VENDOR'] class OFPTFeaturesReply(_ofp_header): name = "OFPT_FEATURES_REPLY" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 6, ofp_type), ShortField("len", None), IntField("xid", 0), LongField("datapath_id", 0), IntField("n_buffers", 0), ByteField("n_tables", 1), X3BytesField("pad", 0), FlagsField("capabilities", 0, 32, ["FLOW_STATS", "TABLE_STATS", "PORT_STATS", "STP", "RESERVED", "IP_REASM", "QUEUE_STATS", "ARP_MATCH_IP"]), FlagsField("actions", 0, 32, ofp_action_types_flags), PacketListField("ports", [], OFPPhyPort, length_from=lambda pkt:pkt.len - 32)] class OFPTGetConfigRequest(_ofp_header): name = "OFPT_GET_CONFIG_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 7, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTGetConfigReply(_ofp_header): name = "OFPT_GET_CONFIG_REPLY" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 8, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("flags", 0, {0: "FRAG_NORMAL", 1: "FRAG_DROP", 2: "FRAG_REASM", 3: "FRAG_MASK"}), ShortField("miss_send_len", 0)] class OFPTSetConfig(_ofp_header): name = "OFPT_SET_CONFIG" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 9, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("flags", 0, {0: "FRAG_NORMAL", 1: "FRAG_DROP", 2: "FRAG_REASM", 3: "FRAG_MASK"}), ShortField("miss_send_len", 128)] class OFPTPacketIn(_ofp_header): name = "OFPT_PACKET_IN" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 10, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), ShortField("total_len", 0), ShortEnumField("in_port", 0, ofp_port_no), ByteEnumField("reason", 0, {0: "OFPR_NO_MATCH", 1: "OFPR_ACTION"}), XByteField("pad", 0), PacketField("data", None, Ether)] class OFPTFlowRemoved(_ofp_header): name = "OFPT_FLOW_REMOVED" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 11, ofp_type), ShortField("len", None), IntField("xid", 0), PacketField("match", OFPMatch(), OFPMatch), LongField("cookie", 0), ShortField("priority", 0), ByteEnumField("reason", 0, {0: "OFPRR_IDLE_TIMEOUT", 1: "OFPRR_HARD_TIMEOUT", 2: "OFPRR_DELETE"}), XByteField("pad1", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0), ShortField("idle_timeout", 0), XShortField("pad2", 0), LongField("packet_count", 0), LongField("byte_count", 0)] class OFPTPortStatus(_ofp_header): name = "OFPT_PORT_STATUS" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 12, ofp_type), ShortField("len", None), IntField("xid", 0), ByteEnumField("reason", 0, {0: "OFPPR_ADD", 1: "OFPPR_DELETE", 2: "OFPPR_MODIFY"}), XBitField("pad", 0, 56), PacketField("desc", OFPPhyPort(), OFPPhyPort)] class OFPTPacketOut(_ofp_header): name = "OFPT_PACKET_OUT" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 13, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), ShortEnumField("in_port", "NONE", ofp_port_no), FieldLenField("actions_len", None, fmt="H", length_of="actions"), # noqa: E501 PacketListField("actions", [], OFPAT, ofp_action_cls, length_from=lambda pkt:pkt.actions_len), # noqa: E501 PacketField("data", None, Ether)] class OFPTFlowMod(_ofp_header): name = "OFPT_FLOW_MOD" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 14, ofp_type), ShortField("len", None), IntField("xid", 0), PacketField("match", OFPMatch(), OFPMatch), LongField("cookie", 0), ShortEnumField("cmd", 0, {0: "OFPFC_ADD", 1: "OFPFC_MODIFY", 2: "OFPFC_MODIFY_STRICT", 3: "OFPFC_DELETE", 4: "OFPFC_DELETE_STRICT"}), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), ShortField("priority", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), ShortEnumField("out_port", "NONE", ofp_port_no), FlagsField("flags", 0, 16, ["SEND_FLOW_REM", "CHECK_OVERLAP", "EMERG"]), PacketListField("actions", [], OFPAT, ofp_action_cls, length_from=lambda pkt:pkt.len - 72)] class OFPTPortMod(_ofp_header): name = "OFPT_PORT_MOD" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 15, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("port_no", 0, ofp_port_no), MACField("hw_addr", "0"), FlagsField("config", 0, 32, ofp_port_config), FlagsField("mask", 0, 32, ofp_port_config), FlagsField("advertise", 0, 32, ofp_port_features), IntField("pad", 0)] ##################################################### # OFPT_STATS # ##################################################### ofp_stats_types = {0: "OFPST_DESC", 1: "OFPST_FLOW", 2: "OFPST_AGGREGATE", 3: "OFPST_TABLE", 4: "OFPST_PORT", 5: "OFPST_QUEUE", 65535: "OFPST_VENDOR"} class OFPTStatsRequestDesc(_ofp_header): name = "OFPST_STATS_REQUEST_DESC" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 0, ofp_stats_types), FlagsField("flags", 0, 16, [])] class OFPTStatsReplyDesc(_ofp_header): name = "OFPST_STATS_REPLY_DESC" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 0, ofp_stats_types), FlagsField("flags", 0, 16, []), StrFixedLenField("mfr_desc", "", 256), StrFixedLenField("hw_desc", "", 256), StrFixedLenField("sw_desc", "", 256), StrFixedLenField("serial_num", "", 32), StrFixedLenField("dp_desc", "", 256)] class OFPTStatsRequestFlow(_ofp_header): name = "OFPST_STATS_REQUEST_FLOW" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 1, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketField("match", OFPMatch(), OFPMatch), ByteEnumField("table_id", "ALL", ofp_table), ByteField("pad", 0), ShortEnumField("out_port", "NONE", ofp_port_no)] class OFPFlowStats(Packet): name = "OFP_FLOW_STATS" fields_desc = [ShortField("length", None), ByteField("table_id", 0), XByteField("pad1", 0), PacketField("match", OFPMatch(), OFPMatch), IntField("duration_sec", 0), IntField("duration_nsec", 0), ShortField("priority", 0), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), XBitField("pad2", 0, 48), LongField("cookie", 0), LongField("packet_count", 0), LongField("byte_count", 0), PacketListField("actions", [], OFPAT, ofp_action_cls, length_from=lambda pkt:pkt.length - 88)] def post_build(self, p, pay): if self.length is None: tmp_len = len(p) + len(pay) p = struct.pack("!H", tmp_len) + p[2:] return p + pay def extract_padding(self, s): return b"", s class OFPTStatsReplyFlow(_ofp_header): name = "OFPST_STATS_REPLY_FLOW" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 1, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketListField("flow_stats", [], OFPFlowStats, length_from=lambda pkt:pkt.len - 12)] # noqa: E501 class OFPTStatsRequestAggregate(OFPTStatsRequestFlow): name = "OFPST_STATS_REQUEST_AGGREGATE" stats_type = 2 class OFPTStatsReplyAggregate(_ofp_header): name = "OFPST_STATS_REPLY_AGGREGATE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 2, ofp_stats_types), FlagsField("flags", 0, 16, []), LongField("packet_count", 0), LongField("byte_count", 0), IntField("flow_count", 0), XIntField("pad", 0)] class OFPTStatsRequestTable(_ofp_header): name = "OFPST_STATS_REQUEST_TABLE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 3, ofp_stats_types), FlagsField("flags", 0, 16, [])] class OFPTableStats(Packet): def extract_padding(self, s): return b"", s name = "OFP_TABLE_STATS" fields_desc = [ByteField("table_id", 0), X3BytesField("pad", 0), StrFixedLenField("name", "", 32), FlagsField("wildcards1", 0x003, 12, ["DL_VLAN_PCP", "NW_TOS"]), BitField("nw_dst_mask", 63, 6), # 32 would be enough BitField("nw_src_mask", 63, 6), FlagsField("wildcards2", 0xff, 8, ["IN_PORT", "DL_VLAN", "DL_SRC", "DL_DST", "DL_TYPE", "NW_PROTO", "TP_SRC", "TP_DST"]), IntField("max_entries", 0), IntField("active_count", 0), LongField("lookup_count", 0), LongField("matched_count", 0)] class OFPTStatsReplyTable(_ofp_header): name = "OFPST_STATS_REPLY_TABLE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 3, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketListField("table_stats", [], OFPTableStats, length_from=lambda pkt:pkt.len - 12)] class OFPTStatsRequestPort(_ofp_header): name = "OFPST_STATS_REQUEST_PORT" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 4, ofp_stats_types), FlagsField("flags", 0, 16, []), ShortEnumField("port_no", "NONE", ofp_port_no), XBitField("pad", 0, 48)] class OFPPortStats(Packet): def extract_padding(self, s): return b"", s name = "OFP_PORT_STATS" fields_desc = [ShortEnumField("port_no", 0, ofp_port_no), XBitField("pad", 0, 48), LongField("rx_packets", 0), LongField("tx_packets", 0), LongField("rx_bytes", 0), LongField("tx_bytes", 0), LongField("rx_dropped", 0), LongField("tx_dropped", 0), LongField("rx_errors", 0), LongField("tx_errors", 0), LongField("rx_frame_err", 0), LongField("rx_over_err", 0), LongField("rx_crc_err", 0), LongField("collisions", 0)] class OFPTStatsReplyPort(_ofp_header): name = "OFPST_STATS_REPLY_TABLE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 4, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketListField("port_stats", [], OFPPortStats, length_from=lambda pkt:pkt.len - 12)] class OFPTStatsRequestQueue(_ofp_header): name = "OFPST_STATS_REQUEST_QUEUE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 5, ofp_stats_types), FlagsField("flags", 0, 16, []), ShortEnumField("port_no", "NONE", ofp_port_no), XShortField("pad", 0), IntEnumField("queue_id", "ALL", ofp_queue)] class OFPTStatsReplyQueue(_ofp_header): name = "OFPST_STATS_REPLY_QUEUE" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 5, ofp_stats_types), FlagsField("flags", 0, 16, []), ShortEnumField("port_no", "NONE", ofp_port_no), XShortField("pad", 0), IntEnumField("queue_id", "ALL", ofp_queue), LongField("tx_bytes", 0), LongField("tx_packets", 0), LongField("tx_errors", 0)] class OFPTStatsRequestVendor(_ofp_header): name = "OFPST_STATS_REQUEST_VENDOR" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 6, ofp_stats_types), FlagsField("flags", 0, 16, []), IntField("vendor", 0)] class OFPTStatsReplyVendor(_ofp_header): name = "OFPST_STATS_REPLY_VENDOR" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 6, ofp_stats_types), FlagsField("flags", 0, 16, []), IntField("vendor", 0)] # ofp_stats_request/reply_cls allows generic method OpenFlow() (end of script) # to choose the right class for dissection ofp_stats_request_cls = {0: OFPTStatsRequestDesc, 1: OFPTStatsRequestFlow, 2: OFPTStatsRequestAggregate, 3: OFPTStatsRequestTable, 4: OFPTStatsRequestPort, 5: OFPTStatsRequestQueue, 65535: OFPTStatsRequestVendor} ofp_stats_reply_cls = {0: OFPTStatsReplyDesc, 1: OFPTStatsReplyFlow, 2: OFPTStatsReplyAggregate, 3: OFPTStatsReplyTable, 4: OFPTStatsReplyPort, 5: OFPTStatsReplyQueue, 65535: OFPTStatsReplyVendor} # end of OFPT_STATS # class OFPTBarrierRequest(_ofp_header): name = "OFPT_BARRIER_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTBarrierReply(_ofp_header): name = "OFPT_BARRIER_REPLY" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0)] class OFPTQueueGetConfigRequest(_ofp_header): name = "OFPT_QUEUE_GET_CONFIG_REQUEST" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 20, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("port", 0, ofp_port_no), XShortField("pad", 0)] class OFPTQueueGetConfigReply(_ofp_header): name = "OFPT_QUEUE_GET_CONFIG_REPLY" fields_desc = [ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 21, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("port", 0, ofp_port_no), XBitField("pad", 0, 48), PacketListField("queues", [], OFPPacketQueue, length_from=lambda pkt:pkt.len - 16)] # ofpt_cls allows generic method OpenFlow() to choose the right class for dissection # noqa: E501 ofpt_cls = {0: OFPTHello, # 1: OFPTError, 2: OFPTEchoRequest, 3: OFPTEchoReply, 4: OFPTVendor, 5: OFPTFeaturesRequest, 6: OFPTFeaturesReply, 7: OFPTGetConfigRequest, 8: OFPTGetConfigReply, 9: OFPTSetConfig, 10: OFPTPacketIn, 11: OFPTFlowRemoved, 12: OFPTPortStatus, 13: OFPTPacketOut, 14: OFPTFlowMod, 15: OFPTPortMod, # 16: OFPTStatsRequest, # 17: OFPTStatsReply, 18: OFPTBarrierRequest, 19: OFPTBarrierReply, 20: OFPTQueueGetConfigRequest, 21: OFPTQueueGetConfigReply} bind_bottom_up(TCP, OpenFlow, dport=6653) bind_bottom_up(TCP, OpenFlow, sport=6653) bind_bottom_up(TCP, OpenFlow, dport=6633) bind_bottom_up(TCP, OpenFlow, sport=6633) bind_top_down(TCP, _ofp_header, sport=6653, dport=6653)