def unpack_message(buffer): """Unpack the whole buffer, including header pack.""" 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
def test_unpack(self): """Assert the known dump is unpacked correctly.""" unpacked_header = Header() unpacked_header.unpack(dump[:8]) packetin.update_header_length() self.assertEqual(packetin.header, unpacked_header) unpacked_packetin = PacketIn() unpacked_packetin.unpack(dump[8:]) unpacked_packetin.header = unpacked_header self.assertEqual(packetin, unpacked_packetin)
def test_unpack(self): """Assert the known dump is unpacked correctly.""" unpacked_header = Header() unpacked_header.unpack(DUMP[:8]) PACKETIN.update_header_length() self.assertEqual(PACKETIN.header, unpacked_header) unpacked_packetin = PacketIn() unpacked_packetin.unpack(DUMP[8:]) unpacked_packetin.header = unpacked_header self.assertEqual(PACKETIN, unpacked_packetin)
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 QueueGetConfigReply(GenericMessage): """Class implements the response to the config request.""" #: :class:`~.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): """The constructor just assings parameters to object attributes. Args: xid (int): xid of OpenFlow header. port (:class:`~.common.port.PortNo`): Target port for the query. queue (:class:`~.common.queue.ListOfQueues`): List of configured queues. """ super().__init__(xid) self.port = port self.queues = [] if queues is None else queues
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 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 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 a 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 a 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() def __init__(self, xid=None, experimenter=None, exp_type=None): """The constructor takes the 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. """ super().__init__(xid) self.experimenter = experimenter self.exp_type = exp_type
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)
class MultipartReply(GenericMessage): """Reply datapath state. While the system is running, the controller may reply state from the datapath using the OFPT_MULTIPART_Reply message. """ #: :class:`~.common.header.Header` header = Header(message_type=Type.OFPT_PORT_MOD) #: One of the OFPMP_* constants. multipart_type = UBInt16(enum_ref=MultipartTypes) #: OFPMPF_REPLY_* flags. flags = UBInt16(enum_ref=MultipartReplyFlags) #: Padding pad = Pad(4) #: Body of the reply body = BinaryData() def __init__(self, xid=None, multipart_type=None, flags=None, body=b''): """The constructor just assings parameters to object attributes. Args: xid (int): xid to the header. multipart_type (int): One of the OFPMP_* constants. flags (int): OFPMPF_REPLY_* flags. body (bytes): Body of the reply. """ super().__init__(xid) self.multipart_type = multipart_type self.flags = flags self.body = body
class RoleBaseMessage(GenericMessage): """Role basic structure for RoleRequest and RoleReply messages.""" #: :class:`~pyof.v0x04.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 MeterMod(GenericMessage): """Meter configuration.""" header = Header(message_type=Type.OFPT_METER_MOD) command = UBInt16(enum_ref=MeterModCommand) flags = UBInt16(enum_ref=MeterFlags) meter_id = UBInt32() bands = FixedTypeList(MeterBandHeader) def __init__(self, xid=None, command=None, flags=None, meter_id=None, bands=None): """Instance attributes assignment. 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 GroupMod(GenericMessage): """Group setup and teardown (controller -> datapath).""" header = Header(message_type=Type.OFPT_GROUP_MOD) command = UBInt16(enum_ref=GroupModCommand) group_type = UBInt8() #: Pad to 64 bits. pad = Pad(1) group_id = UBInt32() 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 PortMod(GenericMessage): """Implement messages to modify the physical port behavior.""" header = Header(message_type=Type.OFPT_PORT_MOD) port_no = UBInt32() pad = Pad(4) hw_addr = HWAddress() pad2 = Pad(2) config = UBInt32(enum_ref=PortConfig) mask = UBInt32(enum_ref=PortConfig) advertise = UBInt32(enum_ref=PortFeatures) #: Pad to 64-bits. pad3 = Pad(4) def __init__(self, xid=None, port_no=None, hw_addr=None, config=None, mask=None, advertise=None): """The constructor just assings parameters to object attributes. 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 (PortConfig): Bitmap of OFPPC_* flags mask (PortConfig): Bitmap of OFPPC_* flags to be changed advertise (PortFeatures): Bitmap of OFPPF_*. Zero all bits to prevent any action taking place. """ super().__init__(xid) self.port_no = port_no self.hw_addr = hw_addr self.config = config self.mask = mask self.advertise = advertise
class AsyncConfig(GenericMessage): """Asynchronous message configuration base class. Common structure for SetAsync and GetAsyncReply messages. AsyncConfig contains three 2-element arrays. Each array controls whether the controller receives asynchronous messages with a specific :class:`~pyof.v0x04.common.header.Type`. Within each array, element 0 specifies messages of interest when the controller has a OFPCR_ROLE_EQUAL or OFPCR_ROLE_MASTER role; element 1, when the controller has a OFPCR_ROLE_SLAVE role. Each array element is a bit-mask in which a 0-bit disables receiving a message sent with the reason code corresponding to the bit index and a 1-bit enables receiving it. """ #: OpenFlow :class:`~pyof.v0x04.common.header.Header` #: OFPT_GET_ASYNC_REPLY or OFPT_SET_ASYNC. header = Header() packet_in_mask1 = UBInt32(enum_ref=PacketInReason) packet_in_mask2 = UBInt32(enum_ref=PacketInReason) port_status_mask1 = UBInt32(enum_ref=PortReason) port_status_mask2 = UBInt32(enum_ref=PortReason) flow_removed_mask1 = UBInt32(enum_ref=FlowRemovedReason) flow_removed_mask2 = UBInt32(enum_ref=FlowRemovedReason) def __init__(self, xid=None, packet_in_mask1=None, packet_in_mask2=None, port_status_mask1=None, port_status_mask2=None, flow_removed_mask1=None, flow_removed_mask2=None): """Create a AsyncConfig with the optional parameters below. Args: xid (int): xid to be used on the message header. packet_in_mask1 (~pyof.v0x04.asynchronous.packet_in.PacketInReason): A instance of PacketInReason packet_in_mask2 (~pyof.v0x04.asynchronous.packet_in.PacketInReason): A instance of PacketInReason port_status_mask1 (~pyof.v0x04.asynchronous.port_status.PortReason): A instance of PortReason port_status_mask2 (~pyof.v0x04.asynchronous.port_status.PortReason): A instance of PortReason flow_removed_mask1 (~pyof.v0x04.asynchronous.flow_removed.FlowRemoved): A instance of FlowRemoved. flow_removed_mask2 (~pyof.v0x04.asynchronous.flow_removed.FlowRemoved): A instance of FlowRemoved. """ super().__init__(xid) self.packet_in_mask1 = packet_in_mask1 self.packet_in_mask2 = packet_in_mask2 self.port_status_mask1 = port_status_mask1 self.port_status_mask2 = port_status_mask2 self.flow_removed_mask1 = flow_removed_mask1 self.flow_removed_mask2 = flow_removed_mask2
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.v0x04.common.header.Header` header = Header(message_type=Type.OFPT_GET_ASYNC_REQUEST)
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/v0x04/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 PacketIn(GenericMessage): """Packet received on port (datapath -> controller).""" #: :class:`~.header.Header`: OpenFlow Header header = Header(message_type=Type.OFPT_PACKET_IN) #: ID assigned by datapath. buffer_id = UBInt32() #: Full length of frame. total_len = UBInt16() #: Reason packet is being sent (one of OFPR_*), reason = UBInt8(enum_ref=PacketInReason) #: ID of the table that was looked up. table_id = UBInt8() #: Cookie of the flow entry that was looked up. cookie = UBInt64() #: Packet metadata. Variable size. match = Match() #: Align to 64 bit + 16 bit pad = Pad(2) #: Ethernet frame whose length is inferred from header.length. #: The padding bytes preceding the Ethernet frame ensure that the IP #: header (if any) following the Ethernet header is 32-bit aligned. data = BinaryData() def __init__(self, xid=None, buffer_id=None, total_len=None, reason=None, table_id=None, cookie=None, match=None, data=b''): """Assign parameters to object attributes. Args: xid (int): Header's xid. buffer_id (int): ID assigned by datapath. total_len (int): Full length of frame. reason (PacketInReason): The reason why the packet is being sent table_id (int): ID of the table that was looked up cookie (int): Cookie of the flow entry that was looked up match (:class:`~.common.flow_match.Match`): Packet metadata. Variable size. data (bytes): Ethernet frame, halfway through 32-bit word, so the IP header is 32-bit aligned. The amount of data is inferred from the length field in the header. Because of padding, offsetof(struct ofp_packet_in, data) == sizeof(struct ofp_packet_in) - 2. """ super().__init__(xid) self.buffer_id = buffer_id self.total_len = total_len self.reason = reason self.table_id = table_id self.cookie = cookie self.match = match self.data = data
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_id = UBInt64() n_buffers = UBInt32() n_tables = UBInt8() auxiliary_id = UBInt8() #: Align to 64-bits. pad = Pad(2) # Features 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): """The constructor just assings parameters to object attributes. 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 GetConfigRequest(GenericMessage): """Get Config Request message.""" header = Header(message_type=Type.OFPT_GET_CONFIG_REQUEST) def __init__(self, xid=None): """The constructor just assings parameters to object attributes. This message does not contain a body beyond the OpenFlow Header. Args: xid (int): xid to be used on the message header. """ super().__init__(xid)
class Hello(GenericMessage): """OpenFlow Hello Message. This message does not contain a body beyond the OpenFlow Header. """ header = Header(message_type=Type.OFPT_HELLO, length=8) def __init__(self, xid=None): """The constructor takes the parameters below. Args: xid (int): xid to be used on the message header. """ super().__init__(xid)
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) def __init__(self, xid=None): """The constructor just assings parameters to object attributes. Args: xid (int): Header's xid. """ super().__init__(xid)
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 __init__(self, xid=None): """The constructor just assings parameters to object attributes. Args: xid (int): xid to be used on the message header. """ super().__init__(xid)
class EchoReply(GenericMessage): """OpenFlow Reply message. This message does not contain a body beyond the OpenFlow Header. """ header = Header(message_type=Type.OFPT_ECHO_REPLY, length=8) data = BinaryData() def __init__(self, xid=None, data=None): """The constructor takes the 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 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.""" #: :class:`~.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): """The constructor just assings parameters to object attributes. Args: xid (int): xid of OpenFlow header port (:class:`~.common.port.PortNo`): Target port for the query. """ super().__init__(xid) self.port = port
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:`~.header.Header` header = Header(message_type=Type.OFPT_GET_ASYNC_REQUEST) def __init__(self, xid=None): """The constructor just assings parameters to object attributes. This message does not contain a body beyond the OpenFlow Header. Args: xid (int): xid to be used on the message header. """ super().__init__(xid)
class TableMod(GenericMessage): """Configure/Modify behavior of a flow table.""" header = Header(message_type=Type.OFPT_TABLE_MOD) table_id = UBInt8() #: Pad to 32 bits pad = Pad(3) config = UBInt32() def __init__(self, xid=None, table_id=None, config=None): """Assing parameters to object attributes. Args: xid (int): :class:`.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 self.config = config
class SwitchConfig(GenericMessage): """Used as base class for SET_CONFIG and GET_CONFIG_REPLY messages.""" #: OpenFlow :class:`~common.header.Header` header = Header() flags = UBInt16(enum_ref=ConfigFlags) miss_send_len = UBInt16() def __init__(self, xid=None, flags=None, miss_send_len=None): """The constructor just assings parameters to object attributes. Args: xid (int): xid to be used on the message header. flags (ConfigFlags): 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 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(message_type=Type.OFPT_HELLO) #: Hello element list 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
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