class RequestForwardHeader(GenericMessage): """Ofp Request Forward Header""" #: Type OFPT_REQUESTFORWARD header = Header(message_type=Type.OFPT_REQUESTFORWARD) #: Request being forwarded request = Header()
def _test_pack_unpack(self, *args, **kwargs): """Pack the message, unpack and check whether they are the same. Call this method multiple times if you want to test more than one object. """ obj = self._msg_cls(*args, **kwargs) packed = obj.pack() header = Header() header_size = header.get_size() header.unpack(packed[:header_size]) unpacked = new_message_from_header(header) unpacked.unpack(packed[header_size:]) self.assertEqual(packed, unpacked.pack())
class TableStatus(GenericMessage): """OpenFlow TableStatus Message OFPT_TABLE_STATUS. A table config has changed in the datapath. """ #: :class:`~pyof.v0x05.common.action.ActionHeader`: OpenFlow Header header = Header(message_type=Type.OFPT_TABLE_STATUS) #: One of OFPTR_.* reason = UBInt8(enum_ref=TableReason) #: Pad to 64 bits pad = Pad(7) #: New table config table = TableDesc() def __init__(self, xid=None, reason=None, table=None): """Create a message with the optional parameters below. Args: xid (int): xid to be used on the message header. reason (int): One of OFPTR_* table (TableDesc): New table config. """ super().__init__(xid) self.reason = reason self.table = table
class BarrierReply(GenericMessage): """OpenFlow Barrier Reply Message. This message does not contain a body beyond the OpenFlow Header. """ header = Header(message_type=Type.OFPT_BARRIER_REPLY)
class QueueGetConfigReply(GenericMessage): """Class implements the response to the config request.""" #: Openflow :class:`~pyof.v0x05.common.header.Header`. header = Header(message_type=Type.OFPT_GET_CONFIG_REPLY) #: Port to be queried. Should refer to a valid physical port #: (i.e. < OFPP_MAX), or OFPP_ANY to request all configured queues. port = UBInt32(enum_ref=PortNo) #: Pad to 64-bits. pad = Pad(4) #: List of configured queues. queues = ListOfQueues() def __init__(self, xid=None, port=None, queues=None): """Create a QueueGetConfigReply with the optional parameters below. Args: xid (int): xid of OpenFlow header. port (:class:`~pyof.v0x05.common.port.PortNo`): Target port for the query. queue (:class:`~pyof.v0x05.common.queue.ListOfQueues`): List of configured queues. """ super().__init__(xid) self.port = port self.queues = [] if queues is None else queues
class SwitchFeatures(GenericMessage): """Message sent by the switch device to the controller. This message is the response for a features_request message, sent by the controller to the switch device. The 'OFPT_FEATURES_REPLY' message inherits from this class, despite the strange name. """ header = Header(message_type=Type.OFPT_FEATURES_REPLY) #: Datapath unique ID. The lower 48-bits are for a MAC address, while the upper 16-bits are #: implemented-defined. datapath_id = DPID() #: Max packets buffered at once. n_buffers = UBInt32() #: Number of tables supported by datapath. n_tables = UBInt8() #: Identify auxiliary connections auxiliary_id = UBInt8() #: Align to 64-bits. pad = Pad(2) # Features #: Bitmap of support "ofp_capabilities" capabilities = UBInt32(enum_ref=Capabilities) reserved = UBInt32() def __init__(self, xid=None, datapath_id=None, n_buffers=None, n_tables=None, auxiliary_id=None, capabilities=None, reserved=None): """Create a SwitchFeatures with the optional parameters below. Args: xid (int): xid to be used on the message header. datapath_id (int): Datapath unique ID. The lower 48-bits are for MAC address, while the upper 16-bits are implementer-defined. n_buffers (int): Max packets buffered at once. n_tables (int): Number of tables supported by datapath. auxiliary_id (int): Identify auxiliary connections. capabilities (int): bitmap of supported capabilities. reserved (int): Reserved. """ super().__init__(xid) self.datapath_id = datapath_id self.n_buffers = n_buffers self.n_tables = n_tables self.auxiliary_id = auxiliary_id self.capabilities = capabilities self.reserved = reserved
class BundleControlMsg(GenericMessage): """Message structure for OFPT_BUNDLE_CONTROL""" header = Header(message_type=Type.OFPT_BUNDLE_CONTROL) #: Identify the bundle bundle_id = UBInt32() #: OFPBCT_ * type = UBInt16() #: Bitmap of OFPBF_* flags flags = UBInt16(enum_ref=BundleFlags) #: Bundle Property list properties = ListOfBundleProperties() def __init__(self, xid=None, bundle_id=None, type=BundleControlType, flags=BundleFlags, properties=None): """Assign parameters to object attributes. Args: xid (int): :class:`~pyof.v0x05.common.header.Header`'s xid. Defaults to random. bundle_id (int): ID of the bundle flags (int): Bitmap of OFPBF_* flags """ super().__init__() self.header.xid = xid self.bundle_id = bundle_id self.type = type self.flags = flags self.properties = properties
class RequestForwardHeader(GenericStruct): """ Group/Meter request forwarding. """ header = Header(Type.OFPT_REQUESTFORWARD) request = Header() def __init__(self, header=Header(Type.OFPT_REQUESTFORWARD), request=None): """Create an instance of the header. Args: header (Header): Type OFPT_REQUESTFORWARD. request (Header): Request being forwarded. """ super().__init__() self.header = header self.request = request
class MeterMod(GenericMessage): """Meter configuration.""" header = Header(message_type=Type.OFPT_METER_MOD) #: One of OFPMC_*. command = UBInt16(enum_ref=MeterModCommand) #: Bitmap of OFPMF_* flags. flags = UBInt16(enum_ref=MeterFlags) #: Meter instance. meter_id = UBInt32() #: The band list length field in the header. bands = FixedTypeList(MeterBandHeader) def __init__(self, xid=None, command=None, flags=None, meter_id=None, bands=None): """Create a MeterMod with the optional parameters below. Args: xid (int): Headers transaction id. Defaults to random. command (MeterModCommand): One of OFPMC_*. flags (MeterFlags): One of OFPMF_*. meter_id (int): Meter instance. bands (MeterBandHeader): The bands length is inferred from the length field in the header. """ super().__init__(xid) self.command = command self.flags = flags self.meter_id = meter_id self.bands = bands
class FeaturesRequest(GenericMessage): """Features request message. This message does not contain a body in addition to the OpenFlow Header. """ header = Header(message_type=Type.OFPT_FEATURES_REQUEST)
class TableMod(GenericMessage): """Configure/Modify behavior of a flow table.""" #: class:`~pyof.v0x05.common.action.ActionHeader`: OpenFlow Header header = Header(message_type=Type.OFPT_TABLE_MOD) #: ID of the table, OFPTT_ALL indicates all tables table_id = UBInt8() #: Pad to 32 bits pad = Pad(3) #: Bitmap of OFPTC_* flags config = UBInt32() #: Table Mod Property list properties = FixedTypeList(TableModPropHeader) def __init__(self, xid=None, table_id=Table.OFPTT_ALL, config=3, properties=None): """Assing parameters to object attributes. Args: xid (int): :class:`~pyof.v0x05.common.header.Header`'s xid. Defaults to random. table_id (int): ID of the table, OFPTT_ALL indicates all tables. config (int): Bitmap of OFPTC_* flags """ super().__init__(xid) self.table_id = table_id # This is reserved for future used. The default value is the only valid # one from the Enum. self.config = config self.properties = properties
class BarrierRequest(GenericMessage): """OpenFlow Barrier Request Message. This message does not contain a body in addition to the OpenFlow Header. """ header = Header(message_type=Type.OFPT_BARRIER_REQUEST)
def unpack_message(buffer): """Unpack the whole buffer, including header pack. Args: buffer (bytes): Bytes representation of a openflow message. Returns: object: Instance of openflow message. """ hdr_size = Header().get_size() hdr_buff, msg_buff = buffer[:hdr_size], buffer[hdr_size:] header = Header() header.unpack(hdr_buff) message = new_message_from_header(header) message.unpack(msg_buff) return message
class GetAsyncRequest(GenericMessage): """Request Asynchronous messages. Query the asynchronous messages that it wants to receive (other than error messages) on a given OpenFlow channel. """ #: OpenFlow :class:`~pyof.v0x05.common.header.Header` header = Header(message_type=Type.OFPT_GET_ASYNC_REQUEST)
def __init__(self, header=Header(Type.OFPT_REQUESTFORWARD), request=None): """Create an instance of the header. Args: header (Header): Type OFPT_REQUESTFORWARD. request (Header): Request being forwarded. """ super().__init__() self.header = header self.request = request
class BundleAddMsg(GenericMessage): """ Message structure for OFPT_BUNDLE_ADD_MESSAGE. Adding a message in a bundle is done with""" header = Header(message_type=Type.OFPT_BUNDLE_ADD_MESSAGE) #: Identify the bundle bundle_id = UBInt32() #: Align to 64 bits pad = Pad() #: Bitmap of OFPBF_* flags. flags = UBInt16(enum_ref=BundleFlags) #: Message added to the bundle message = Header() """ If there is one property or more, ’message’ is followed by: Exactly (message.length + 7)/8*8 - (message.length) (between 0 and 7) bytes of all-zero bytes""" #: Bundle Property list property = ListOfBundleProperties() def __init__(self, xid=None, bundle_id=None, flags=BundleFlags, message=None, properties=None): """Assign parameters to object attributes. Args: xid (int): :class:`~pyof.v0x05.common.header.Header`'s xid. Defaults to random. bundle_id (int): ID of the bundle. A 32 bit number chosen by the controller. The bundle identifier should be a bundle that has been previously opened and not yet closed flags (int): Bitmap of OFPBF_* flags message :a OpenFlow message to be added to the bundle, it can be any OpenFlow message that the switch can support in a bundle. The field xid in the message must be identical to the field xid of the OFPT_BUNDLE_ADD_MESSAGE message. """ super().__init__(xid) self.bundle_id = bundle_id self.flags = flags self.message = message self.properties = properties
class TestHeader(unittest.TestCase): """Test the message Header.""" def setUp(self): """Setup the TestHeader Class instantiating a HELLO header.""" self.message = Header() self.message.message_type = Type.OFPT_HELLO self.message.xid = 1 self.message.length = 0 def test_size(self): """[Common/Header] - size 8.""" self.assertEqual(self.message.get_size(), 8) @unittest.expectedFailure def test_pack_empty(self): """[Common/Header] - packing empty header.""" self.assertRaises(TypeError, Header().pack()) def test_pack(self): """[Common/Header] - packing Hello.""" packed_header = b'\x04\x00\x00\x00\x00\x00\x00\x01' self.assertEqual(self.message.pack(), packed_header) def test_unpack(self): """[Common/Header] - unpacking Hello.""" filename = os.path.join(os.path.dirname(os.path.realpath('__file__')), 'raw/v0x05/ofpt_hello.dat') try: f = open(filename, 'rb') self.message.unpack(f.read(8)) self.assertEqual(self.message.length, 8) self.assertEqual(self.message.xid, 1) self.assertEqual(self.message.message_type, Type.OFPT_HELLO) self.assertEqual(self.message.version, 0x04) f.close() except FileNotFoundError: raise self.skipTest('There is no raw dump file for this test')
class PortMod(GenericMessage): """Implement messages to modify the physical port behavior.""" header = Header(message_type=Type.OFPT_PORT_MOD) port_no = UBInt32() pad = Pad(4) #: The hardware address is not configurable. This is used to #: sanity-check the request, so it must be the same as returned in an port struct. hw_addr = HWAddress() #: Pad to 64 bits. pad2 = Pad(2) #: Bitmap of OFPPC_* flags. config = UBInt32(enum_ref=PortConfig) #: Bitmap of OFPPC_* flags to be changed. mask = UBInt32(enum_ref=PortConfig) #: Port mod property list - 0 or more properties properties = FixedTypeList(pyof_class=PortModPropHeader) def __init__(self, xid=None, port_no=None, hw_addr=None, config=None, mask=None, properties=FixedTypeList(pyof_class=PortModPropHeader)): """Create a PortMod with the optional parameters below. Args: xid (int): OpenFlow xid to the header. port_no (int): Physical port number. hw_addr (HWAddress): The hardware address is not configurable. This is used to sanity-check the request, so it must be the same as returned in an ofp_phy_port struct. config (~pyof.v0x05.common.port.PortConfig): Bitmap of OFPPC_* flags mask (~pyof.v0x05.common.port.PortConfig): Bitmap of OFPPC_* flags to be changed properties (FixedTypeList(pyof_class=PortModPropHeader)): Port mod property list - 0 or more properties """ super().__init__(xid) self.port_no = port_no self.hw_addr = hw_addr self.config = config self.mask = mask self.properties = properties
class EchoRequest(GenericMessage): """OpenFlow Reply message. This message does not contain a body after the OpenFlow Header. """ header = Header(message_type=Type.OFPT_ECHO_REQUEST, length=8) data = BinaryData() def __init__(self, xid=None, data=b''): """Create a EchoRequest with the optional parameters below. Args: xid (int): xid to be used on the message header. data (bytes): arbitrary-length data field. """ super().__init__(xid) self.data = data
class QueueGetConfigRequest(GenericMessage): """Query structure for configured queues on a port.""" #: Openflow :class:`~pyof.v0x05.common.header.Header`. header = Header(message_type=Type.OFPT_GET_CONFIG_REQUEST) #: Port to be queried. Should refer to a valid physical port #: (i.e. < OFPP_MAX), or OFPP_ANY to request all configured queues. port = UBInt32(enum_ref=PortNo) pad = Pad(4) def __init__(self, xid=None, port=None): """Create a QueueGetConfigRequest with the optional parameters below. Args: xid (int): xid of OpenFlow header port (:class:`~.common.port.PortNo`): Target port for the query. """ super().__init__(xid) self.port = port
class ExperimenterHeader(GenericMessage): """OpenFlow Experimenter message. The experimenter field is a 32-bit value that uniquely identifies the experimenter. If the most significant byte is zero, the next three bytes are the experimenter’s IEEE OUI. If the most significant byte is not zero, it is a value allocated by the Open Networking Foundation. If experimenter does not have (or wish to use) their OUI, they should contact the Open Networking Foundation to obtain an unique experimenter ID. The rest of the body is uninterpreted by standard OpenFlow processing and is arbitrarily defined by the corresponding experimenter. If a switch does not understand an experimenter extension, it must send an OFPT_ERROR message with a OFPBRC_BAD_EXPERIMENTER error code and OFPET_BAD_REQUEST error type. """ header = Header(message_type=Type.OFPT_EXPERIMENTER) experimenter = UBInt32() exp_type = UBInt32() experimenter_data = BinaryData() def __init__(self, xid=None, experimenter=None, exp_type=None, experimenter_data=b''): """Create a ExperimenterHeader with the optional parameters below. Args: xid (int): xid to be used on the message header. experimenter (int): Vendor ID: MSB 0: low-order bytes are IEEE OUI. MSB != 0: defined by ONF. exp_type (int): Experimenter defined. experimenter_data (Binary): Experimenter-defined arbitrary additional data. """ super().__init__(xid) self.experimenter = experimenter self.exp_type = exp_type self.experimenter_data = experimenter_data
class RoleRequest(RoleBaseMessage): """RoleRequest Message. When the controller wants to change its role, it uses the OFPT_ROLE_REQUEST message. """ header = Header(message_type=Type.OFPT_ROLE_REQUEST) role = UBInt32(enum_ref=ControllerRole) pad = Pad(4) generation_id = UBInt64() def __init__(self, xid=None, role=None, generation_id=None): """Create a RoleRequest with the optional parameters below. Args: xid (int): OpenFlow xid to the header. role (:class:`~.controller2switch.common.ControllerRole`): Is the new role that the controller wants to assume. generation_id (int): Master Election Generation Id. """ super().__init__(xid, role, generation_id)
class RoleStatusMsg(GenericMessage): """OpenFlow Controller Role Status Message OFPT_ROLE_REQUEST. """ #: Type OFPT_ROLE_STATUS header = Header(message_type=Type.OFPT_ROLE_STATUS) #: One of OFPCR_ROLE_* role = UBInt32(enum_ref=ControllerRole) #: One of OFPCRR_*. reason = UBInt8(enum_ref=RoleReason) #: Align to 64 bits pad = Pad(3) #: Master Election Generation Id generation_id = UBInt64() #: Role Property list properties = ListOfRoleProperties() def __init__(self, xid=None, role=ControllerRole, reason=RoleReason, generation_id=None, properties=None): """Create a message with the optional parameters below. Args: xid (int): xid to be used on the message header. role (int): the new role of the controller reason (int): one of RoleReason generation_id (int): the generation ID that was included in the role request message that triggered the role change properties: a list of role properties, describing dynamic parameters of table configuration """ super().__init__() self.header.xid = xid self.role = role self.reason = reason self.generation_id = generation_id self.properties = properties if properties else []
class ErrorExperimenterMsg(GenericMessage): """OFPET_EXPERIMENTER: Error message (datapath -> controller). The experimenter field is the Experimenter ID, which takes the same form as in :class:`~.symmetric.experimenter.ExperimenterHeader """ # :class:`~.header.Header`: OpenFlow Header header = Header(message_type=Type.OFPT_ERROR) #: OFPET_EXPERIMENTER. type = UBInt16(ErrorType.OFPET_EXPERIMENTER, enum_ref=ErrorType) #: Experimenter Defined exp_type = UBInt16() #: Experimenter ID which takes the same form as in #: :class:`~.symmetric.experimenter.ExperimenterHeader`. experimenter = UBInt32() #: Variable-length data interpreted based on the type and code. No padding. data = BinaryData() def __init__(self, xid=None, exp_type=None, experimenter=None, data=b''): """Assign parameters to object attributes. Args: xid (int): To be included in the message header. exp_type (int): Experimenter defined. experimenter (int): Experimenter ID which takes the same form as in :class:`~.symmetric.experimenter.ExperimenterHeader`. data: Variable-length data interpreted based on the type and code. No padding. """ super().__init__(xid) self.exp_type = exp_type self.experimenter = experimenter self.data = data def unpack(self, buff, offset=0): """Unpack binary data into python object.""" raise exceptions.MethodNotImplemented("'Unpack' method not " "implemented on ErrorMsg class")
class ErrorMsg(GenericMessage): """OpenFlow Error Message. This message does not contain a body in addition to the OpenFlow Header. """ #: :class:`~.header.Header`: OpenFlow Header header = Header(message_type=Type.OFPT_ERROR) #: ErrorType enum item type = UBInt16(enum_ref=ErrorType) #: Error code associated with ErrorType code = UBInt16() #: Variable-length data interpreted based on the type and code. No padding. data = BinaryData() def __init__(self, xid=None, error_type=None, code=None, data=b''): """Assign parameters to object attributes. Args: xid (int): To be included in the message header. error_type (ErrorType): Error type. code (Enum): Error code. data: Its content is specified in the error code documentation. Unless specified otherwise, the data field contains at least 64 bytes of the failed request that caused the error message to be generated, if the failed request is shorter than 64 bytes it should be the full request without any padding. """ super().__init__(xid) self.type = error_type self.code = code self.data = data def unpack(self, buff, offset=0): """Unpack binary data into python object.""" super().unpack(buff, offset) code_class = ErrorType(self.error_type).get_class() self.code = code_class(self.code)
class Hello(GenericMessage): """OpenFlow Hello Message OFPT_HELLO. This message includes zero or more hello elements having variable size. Unknown element types must be ignored/skipped, to allow for future extensions. """ header = Header(Type.OFPT_HELLO) #: Hello element list #: List of elements - 0 or more elements = ListOfHelloElements() def __init__(self, xid=None, elements=None): """Create a Hello with the optional parameters below. Args: xid (int): xid to be used on the message header. elements: List of elements - 0 or more """ super().__init__(xid) self.elements = elements
class SwitchConfig(GenericMessage): """Used as base class for SET_CONFIG and GET_CONFIG_REPLY messages.""" #: OpenFlow :class:`~pyof.v0x05.common.header.Header` header = Header() flags = UBInt16(enum_ref=ConfigFlag) miss_send_len = UBInt16() def __init__(self, xid=None, flags=ConfigFlag.OFPC_FRAG_NORMAL, miss_send_len=ControllerMaxLen.OFPCML_NO_BUFFER): """Create a SwitchConfig with the optional parameters below. Args: xid (int): xid to be used on the message header. flags (ConfigFlag): OFPC_* flags. miss_send_len (int): UBInt16 max bytes of new flow that the datapath should send to the controller. """ super().__init__(xid) self.flags = flags self.miss_send_len = miss_send_len
class RoleBaseMessage(GenericMessage): """Role basic structure for RoleRequest and RoleReply messages.""" #: :class:`~pyof.v0x05.common.header.Header` #: Type OFPT_ROLE_REQUEST/OFPT_ROLE_REPLY. header = Header() #: One of NX_ROLE_*. (:class:`~.controller2switch.common.ControllerRole`) role = UBInt32(enum_ref=ControllerRole) #: Align to 64 bits. pad = Pad(4) #: Master Election Generation Id. generation_id = UBInt64() def __init__(self, xid=None, role=None, generation_id=None): """Create a RoleBaseMessage with the optional parameters below. Args: xid (int): OpenFlow xid to the header. role (:class:`~.controller2switch.common.ControllerRole`): . generation_id (int): Master Election Generation Id. """ super().__init__(xid) self.role = role self.generation_id = generation_id
class GroupMod(GenericMessage): """Group setup and teardown (controller -> datapath).""" header = Header(message_type=Type.OFPT_GROUP_MOD) #: One of OFPGC_* command = UBInt16(enum_ref=GroupModCommand) #: One of OFPGT_* group_type = UBInt8() #: Pad to 64 bits. pad = Pad(1) # :Group identifier. group_id = UBInt32() #: The length of the bucket array is inferred from the length field in the header. buckets = ListOfBuckets() def __init__(self, xid=None, command=None, group_type=None, group_id=None, buckets=None): """Create a GroupMod with the optional parameters below. Args: xid (int): Header's transaction id. Defaults to random. command (GroupModCommand): One of OFPGC_*. group_type (GroupType): One of OFPGT_*. group_id (int): Group identifier. buckets (:class:`ListOfBuckets`): The length of the bucket array is inferred from the length field in the header. """ super().__init__(xid) self.command = command self.group_type = group_type self.group_id = group_id self.buckets = buckets
class PortStatus(GenericMessage): """A physical port has changed in the datapath.""" #: :class:`~pyof.v0x05.common.action.ActionHeader`: OpenFlow Header header = Header(message_type=Type.OFPT_PORT_STATUS) #: One of OFPPR_*. reason = UBInt8(enum_ref=PortReason) #: Align to 32-bits. pad = Pad(7) #: :class:`~pyof.v0x05.common.port.Port` desc = Port() def __init__(self, xid=None, reason=None, desc=None): """Assign parameters to object attributes. Args: xid (int): Header's xid. reason (~pyof.v0x05.asynchronous.port_status.PortReason): Addition, deletion or modification. desc (~pyof.v0x05.common.port.Port): Port description. """ super().__init__(xid) self.reason = reason self.desc = desc