def header(self): """Header of the TLV Packet. The header is composed by the Type (7 bits) and Length (9 bits), summing up 16 bits. To achieve that, we need to do some bitshift operations. Returns: :class:`~pyof.foundation.basic_types.UBInt16`: Result after all operations. """ return UBInt16(((self.tlv_type & 127) << 9) | (self.length & 511))
def unpack(self, buff=None, offset=0): """Unpack *buff* into this object. This method will convert a binary data into a readable value according to the attribute format. Args: buff (bytes): Binary buffer. offset (int): Where to begin unpacking. Raises: :exc:`~.exceptions.UnpackException`: If unpack fails. """ band_type = UBInt16(enum_ref=MeterBandType) band_type.unpack(buff, offset) self.__class__ = MeterBandType(band_type.value).find_class() length = UBInt16() length.unpack(buff, offset=offset + 2) super().unpack(buff[:offset + length.value], offset)
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 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 error_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.error_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)
def __init__(self, type=InstructionType, length=None, exp_data=None): """The instruction_ids field is the list of instructions supported by this table. The elements of that list are variable in size to enable expressing experimenter instructions. Non-experimenter instructions are 4 bytes. Args: type(InstructionType): One of OFPIT_*. length(int): Length is 4 or experimenter defined. exp_data(int): Optional experimenter id + data. """ super().__init__() self.type = type self.length = UBInt16(4) if length is None else length self.exp_data = exp_data
def unpack(self, buff, offset=0): """Unpack *buff* into this object. Do nothing, since the _length is already defined and it is just a Pad. Keep buff and offset just for compability with other unpack methods. Args: buff (bytes): Buffer where data is located. offset (int): Where data stream begins. """ self.length = UBInt16() self.length.unpack(buff, offset) max_length = offset + self.length.value super().unpack(buff[:max_length], offset)
class SwitchConfig(GenericMessage): """Used as base class for SET_CONFIG and GET_CONFIG_REPLY messages.""" #: OpenFlow :class:`~pyof.v0x04.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 ActionDLAddr(ActionHeader): """Action structure for :attr:`ActionType.OFPAT_SET_DL_SRC` or _DST.""" dl_addr_type = UBInt16(enum_ref=ActionType) length = UBInt16(16) dl_addr = HWAddress() #: Pad for bit alignment. pad = Pad(6) def __init__(self, dl_addr_type=None, dl_addr=None): """The following constructor parameters are optional. Args: dl_addr_type (ActionType): :attr:`~ActionType.OFPAT_SET_DL_SRC` or :attr:`~ActionType.OFPAT_SET_DL_DST`. dl_addr (:class:`~.HWAddress`): Ethernet address. Defaults to None. """ super().__init__() self.dl_addr_type = dl_addr_type self.dl_addr = dl_addr self.allowed_types = (ActionType.OFPAT_SET_DL_SRC, ActionType.OFPAT_SET_DL_DST)
class ActionVlanVid(ActionHeader): """Action structure for :attr:`ActionType.OFPAT_SET_VLAN_VID`. .. note:: The vlan_vid field is 16 bits long, when an actual VLAN id is only 12 bits. The value 0xffff is used to indicate that no VLAN id was set """ action_type = UBInt16(ActionType.OFPAT_SET_VLAN_VID, enum_ref=ActionType) length = UBInt16(8) vlan_id = UBInt16() #: Pad for bit alignment. pad2 = Pad(2) def __init__(self, vlan_id=None): """The following constructor parameters are optional. Args: vlan_id (int): VLAN priority. """ super().__init__() self.vlan_id = vlan_id self.allowed_types = ActionType.OFPAT_SET_VLAN_VID,
def unpack(self, buffer, offset=0): """Unpack a binary message into this object's attributes. Unpack the binary value *buff* and update this object attributes based on the results. Args: buffer (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: Exception: If there is a struct unpacking error. """ prio_cfi_id = UBInt16() prio_cfi_id.unpack(buffer[offset:2]) self.pcp = prio_cfi_id.value >> 13 self.cfi = (prio_cfi_id.value & 0x1000) >> 12 self.vid = prio_cfi_id.value & 0xfff etype = UBInt16() etype.unpack(buffer[offset+2:4]) self.ether_type = etype.value
class InstructionGotoTable(GenericStruct): """Instruction structure for OFPIT_GOTO_TABLE.""" #: OFPIT_GOTO_TABLE. instruction_type = UBInt16(InstructionType.OFPIT_GOTO_TABLE, enum_ref=InstructionType) #: Length of this struct in bytes. length = UBInt16() #: Set next table in the lookup pipeline. table_id = UBInt8() #: Pad to 64 bits. pad = Pad(3) def __init__(self, length=None, table_id=None): """Instruction structure for OFPIT_GOTO_TABLE. Args: - length (int): Length of this struct in bytes. - table_id (int): set next table in the lookup pipeline. """ super().__init__() self.length = length self.table_id = table_id
class FlowMod(GenericMessage): """Modifies the flow table from the controller.""" header = Header(message_type=Type.OFPT_FLOW_MOD) match = Match() cookie = UBInt64() command = UBInt16(enum_ref=FlowModCommand) idle_timeout = UBInt16() hard_timeout = UBInt16() priority = UBInt16() buffer_id = UBInt32() out_port = UBInt16(enum_ref=Port) flags = UBInt16(enum_ref=FlowModFlags) actions = ListOfActions() def __init__(self, xid=None, match=None, cookie=0, command=None, idle_timeout=0, hard_timeout=0, priority=0, buffer_id=NO_BUFFER, out_port=Port.OFPP_NONE, flags=FlowModFlags.OFPFF_CHECK_OVERLAP, actions=None): """The constructor just assings parameters to object attributes. Args: xid (int): xid to be used on the message header. match (Match): Fields to match. cookie (int): Opaque controller-issued identifier. command (FlowModCommand): One of OFPFC_*. idle_timeout (int): Idle time before discarding (seconds). hard_timeout (int): Max time before discarding (seconds). priority (int): Priority level of flow entry. buffer_idle (int): Buffered packet to apply to (or -1). Not meaningful for OFPFC_DELETE*. out_port (Port): For OFPFC_DELETE* commands, require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. flags (FlowModFlags): One of OFPFF_*. actions (ListOfActions): The action length is inferred from the length field in the header. """ super().__init__(xid) self.match = 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
class ActionOutput(GenericStruct): """Defines the actions output. Action structure for :attr:`ActionType.OFPAT_OUTPUT`, which sends packets out :attr:`port`. When the :attr:`port` is the :attr:`.Port.OFPP_CONTROLLER`, :attr:`max_length` indicates the max number of bytes to send. A :attr:`max_length` of zero means no bytes of the packet should be sent. """ #: OFPAT_OUTPUT. action_type = UBInt16(ActionType.OFPAT_OUTPUT, enum_ref=ActionType) #: Length is 16. length = UBInt16(16) #: Output port. port = UBInt16() #: Max length to send to controller. max_length = UBInt16() #: Pad to 64 bits. pad = Pad(6) def __init__(self, action_type=None, length=None, port=None, max_length=None): """The following constructor parameters are optional. Args: port (:class:`Port` or :class:`int`): Output port. max_length (int): Max length to send to controller. """ super().__init__() self.action_type = action_type self.length = length self.port = port self.max_length = max_length
class Bucket(GenericStruct): """Bucket for use in groups.""" length = UBInt16() weight = UBInt16() watch_port = UBInt32() watch_group = UBInt32() pad = Pad(4) actions = FixedTypeList(ActionHeader) def __init__(self, length=None, weight=None, watch_port=None, watch_group=None, actions=None): """Initialize all instance variables. Args: length (int): Length the bucket in bytes, including this header and any padding to make it 64-bit aligned. weight (int): Relative weight of bucket. Only defined for select groups. watch_port (int): Port whose state affects whether this bucket is live. Only required for fast failover groups. watch_group (int): Group whose state affects whether this bucket is live. Only required for fast failover groups. actions (~pyof.v0x04.common.action.ListOfActions): The action length is inferred from the length field in the header. """ super().__init__() self.length = length self.weight = weight self.watch_port = watch_port self.watch_group = watch_group self.actions = actions
class Ethernet(GenericStruct): destination = HWAddress() source = HWAddress() type = UBInt16() data = BinaryData() def __init__(self, destination=None, source=None, type=None, data=b''): super().__init__() self.destination = destination self.source = source self.type = type self.data = data def get_hash(self): return hash(self.pack())
class PacketIn(GenericMessage): """Packet received on port (datapath -> controller).""" #: :class:`~pyof.v0x01.common.header.Header`: OpenFlow Header header = Header(message_type=Type.OFPT_PACKET_IN) buffer_id = UBInt32() total_len = UBInt16() in_port = UBInt16() reason = UBInt8(enum_ref=PacketInReason) #: Align to 32-bits. pad = Pad(1) data = BinaryData() def __init__(self, xid=None, buffer_id=NO_BUFFER, total_len=None, in_port=None, reason=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. in_port (int): Port on which frame was received. reason (~pyof.v0x01.asynchronous.packet_in.PacketInReason): The reason why the packet is being sent 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.in_port = in_port self.reason = reason self.data = data
class TableModPropVacancy(GenericStruct): """Vacancy table mod property""" #: OFPTMPT_VACANCY type = UBInt16() #: Length in bytes of this property length = UBInt16() #: Vacancy threshold when space decreases (%) vacancy_down = UBInt8() #: Vacancy threshold when space increases (%) vacancy_up = UBInt8() #: Current vacancy (%) - only in ofp_table_desc vacancy = UBInt8() #: Align to 64 bits pad = Pad(1) def __init__(self, type=TableModPropType.OFPTMPT_VACANCY, length=None, vacancy_down=None, vacancy_up=None, vacancy=None): super().__init__() self.type = type self.length = length self.vacancy_down = vacancy_down self.vacancy_up = vacancy_up self.vacancy = vacancy
class InstructionWriteMetadata(InstructionHeader): """Instruction structure for OFPIT_WRITE_METADATA.""" #: OFPIT_WRITE_METADATA type = UBInt16() #: Length is 24. len = UBInt16() #: Align to 64-bits pad = Pad(4) #: Metadata value to write metadata = UBInt64() #: Metadata write bitmask metadata_mask = UBInt64() def __init__(self, metadata=0, metadata_mask=0): """Create InstructionWriteMetadata with the optional parameters below. Args: metadata (int): Metadata value to write. metadata_mask (int): Metadata write bitmask. """ super().__init__(InstructionType.OFPIT_WRITE_METADATA) self.metadata = metadata self.metadata_mask = metadata_mask
class ActionPopMPLS(ActionHeader): """Action structure for OFPAT_POP_MPLS.""" ethertype = UBInt16() pad = Pad(2) _allowed_types = (ActionType.OFPAT_POP_MPLS, ) def __init__(self, ethertype=None): """Create an ActionPopMPLS with the optional parameters below. Args: ethertype (int): indicates the Ethertype of the payload. """ super().__init__(action_type=ActionType.OFPAT_POP_MPLS) self.ethertype = ethertype
class GroupStats(GenericStruct): """Body of reply to OFPMP_GROUP request.""" length = UBInt16() #: Align to 64 bits. pad = Pad(2) group_id = UBInt32() ref_count = UBInt32() #: Align to 64 bits. pad2 = Pad(4) packet_count = UBInt64() byte_count = UBInt64() duration_sec = UBInt32() duration_nsec = UBInt32() bucket_stats = FixedTypeList(BucketCounter) def __init__(self, length=None, group_id=None, ref_count=None, packet_count=None, byte_count=None, duration_sec=None, duration_nsec=None, bucket_stats=None): """Create a GroupStats with the optional parameters below. Args: length: Length of this entry group_id: Group identifier ref_count: Number of flows or groups that directly forward to this group. packet_count: Number of packets processed by group byte_count: Number of bytes processed by group duration_sec: Time group has been alive in seconds duration_nsec: Time group has been alive in nanoseconds bucket_stats: List of stats of group buckets """ super().__init__() self.length = length self.group_id = group_id self.ref_count = ref_count self.packet_count = packet_count self.byte_count = byte_count self.duration_sec = duration_sec self.duration_nsec = duration_nsec self.bucket_stats = bucket_stats
class QueueGetConfigRequest(GenericMessage): """Query structure for configured queues on a port.""" header = Header(message_type=Type.OFPT_QUEUE_GET_CONFIG_REQUEST) port = UBInt16(enum_ref=Port) #: Pad to 64-bits pad = Pad(2) def __init__(self, xid=None, port=None): """Create a QueueGetConfigRequest with the optional parameters below. Args: xid (int): xid of OpenFlow header port (~pyof.v0x01.common.phy_port.Port): Target port for the query """ super().__init__(xid) self.port = port
def unpack(self, buff=None, offset=0): """Unpack *buff* into this object. This method will convert a binary data into a readable value according to the attribute format. Args: buff (bytes): Binary buffer. offset (int): Where to begin unpacking. Raises: :exc:`~.exceptions.UnpackException`: If unpack fails. """ length = UBInt16() length.unpack(buff, offset) super().unpack(buff[:offset+length.value], offset)
class QueueGetConfigRequest(GenericMessage): """Query structure for configured queues on a port.""" header = Header(message_type=Type.OFPT_GET_CONFIG_REQUEST) port = UBInt16(enum_ref=Port) #: Pad to 64-bits pad = Pad(2) def __init__(self, xid=None, port=None): """The constructor just assings parameters to object attributes. Args: xid (int): xid of OpenFlow header port (Port): Target port for the query """ super().__init__(xid) self.port = port
class QueuePropMinRate(GenericStruct): """Define the minimum-rate type queue.""" prop_header = QueuePropHeader( queue_property=QueueProperties.OFPQT_MIN_RATE, length=16) rate = UBInt16() #: 64-bit alignmet. pad = Pad(6) def __init__(self, rate=None): """Create a QueuePropMinRate with the optional parameters below. Args: rate (int): In 1/10 of a percent (1000 -> 100%); >1000 -> disabled. """ super().__init__() self.rate = rate
class QueuePropMinRate(GenericStruct): """Minimum-rate queue property uses the following structure and fields.""" prop_header = QueuePropHeader(property=QueueProperties.OFPQT_MIN_RATE, length=16) #: In 1/10 of a percent; >1000 -> disabled. rate = UBInt16() #: 64-bit alignmet. pad = Pad(6) def __init__(self, rate=None): """The contructor takes the paremeters below. Args: rate (int): In 1/10 of a percent (1000 -> 100%); >1000 -> disabled. """ super().__init__() self.rate = rate
class QueuePropMaxRate(GenericStruct): """Maximum-rate queue property uses the following structure and fields.""" prop_header = QueuePropHeader( queue_property=QueueProperties.OFPQT_MAX_RATE, length=16) #: In 1/10 of a percent; >1000 -> disabled. rate = UBInt16() #: 64-bit alignmet. pad = Pad(6) def __init__(self, rate=None): """Create a QueuePropMaxRate with the optional parameters below. Args: rate (int): In 1/10 of a percent (1000 -> 100%); >1000 -> disabled. """ super().__init__() self.rate = rate
class PortStatsRequest(GenericStruct): """Body for ofp_stats_request of type OFPST_PORT.""" port_no = UBInt16() #: Align to 64-bits. pad = Pad(6) def __init__(self, port_no=None): """The constructor just assings parameters to object attributes. Args: port_no (:class:`int`, :class:`.Port`): OFPST_PORT message must request statistics either for a single port (specified in ``port_no``) or for all ports (if port_no == :attr:`.Port.OFPP_NONE`). """ super().__init__() self.port_no = port_no
class PortStatsRequest(GenericStruct): """Body for ofp_stats_request of type OFPST_PORT.""" port_no = UBInt16() #: Align to 64-bits. pad = Pad(6) def __init__(self, port_no=Port.OFPP_NONE): """Create a PortStatsRequest with the optional parameters below. Args: port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): OFPST_PORT message must request statistics either for a single port (specified in ``port_no``) or for all ports (if ``port_no`` == :attr:`.Port.OFPP_NONE`). """ super().__init__() self.port_no = port_no
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
def test_hello_elem_version_bitmap(self): """ Testing the class HelloElemVersionBitmap. Test support 2 versions (ver 1 = 0x01 and ver 1.3 = 0x04) Testing 2 version support ver 1 and ver 1.3 based on the specification's example. :return: None """ ver1 = 0x01 ver3 = 0x04 ver = ver1 << ver3 ver = ver | 2 test_val = b'\x00\x01\x00\x08\x00\x00\x00\x12' self.test_object_hello_elem_version = Hello.HelloElemVersionBitmap(UBInt16(8), UBInt32(ver).pack()) val = self.test_object_hello_elem_version.pack() self.assertEqual(test_val,val)