python-openflow-2019.2/0000755000175100001630000000000013577157243015575 5ustar runnerdocker00000000000000python-openflow-2019.2/setup.py0000644000175100001630000001015113577157232017303 0ustar runnerdocker00000000000000"""Setup script. Run "python3 setup --help-commands" to list all available commands and their descriptions. """ from abc import abstractmethod # Disabling checks due to https://github.com/PyCQA/pylint/issues/73 # pylint: disable=import-error,no-name-in-module from distutils.command.clean import clean # pylint: enable=import-error,no-name-in-module from subprocess import CalledProcessError, call, check_call from setuptools import Command, find_packages, setup from pyof import __version__ class SimpleCommand(Command): """Make Command implementation simpler.""" user_options = [] def __init__(self, *args, **kwargs): """Store arguments so it's possible to call other commands later.""" super().__init__(*args, **kwargs) self.__args = args self.__kwargs = kwargs @abstractmethod def run(self): """Run when command is invoked. Use *call* instead of *check_call* to ignore failures. """ pass def run_command(self, command_class): """Run another command with same __init__ arguments.""" command_class(*self.__args, **self.__kwargs).run() def initialize_options(self): """Set defa ult values for options.""" pass def finalize_options(self): """Post-process options.""" pass class Cleaner(clean): """Custom clean command to tidy up the project root.""" description = 'clean build, dist, pyc and egg from package and docs' def run(self): """Clean build, dist, pyc and egg from package and docs.""" super().run() call('rm -vrf ./build ./dist ./*.pyc ./*.egg-info', shell=True) call('find . -name __pycache__ -type d | xargs rm -rf', shell=True) call('test -d docs && make -C docs/ clean', shell=True) class TestCoverage(SimpleCommand): """Display test coverage.""" description = 'run unit tests and display code coverage' def run(self): """Run unittest quietly and display coverage report.""" cmd = 'coverage3 run setup.py test && coverage3 report' check_call(cmd, shell=True) class DocTest(SimpleCommand): """Run documentation tests.""" description = 'run documentation tests' def run(self): """Run doctests using Sphinx Makefile.""" cmd = 'make -C docs/ default doctest' check_call(cmd, shell=True) class CITest(SimpleCommand): """Run all CI tests.""" description = 'run all CI tests: unit and doc tests, linter' def run(self): """Run unit tests with coverage, doc tests and linter.""" for command in TestCoverage, DocTest, Linter: self.run_command(command) class Linter(SimpleCommand): """Lint Python source code.""" description = 'lint Python source code' def run(self): """Run yala.""" print('Yala is running. It may take several seconds...') try: check_call('yala pyof setup.py', shell=True) print('No linter error found.') except CalledProcessError: print('Linter check failed. Fix the error(s) above and try again.') exit(-1) setup(name='python-openflow', version=__version__, description='Library to parse and generate OpenFlow messages', url='http://github.com/kytos/python-openflow', author='Kytos Team', author_email='devel@lists.kytos.io', license='MIT', test_suite='tests', include_package_data=True, setup_requires=['pytest-runner'], tests_require=['pytest'], extras_require={'dev': ['pip-tools >= 2.0', 'coverage', 'pytest', 'yala', 'tox']}, packages=find_packages(exclude=['tests']), cmdclass={ 'ci': CITest, 'clean': Cleaner, 'coverage': TestCoverage, 'doctest': DocTest, 'lint': Linter }, zip_safe=False, classifiers=[ 'License :: OSI Approved :: MIT License', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python :: 3.6', 'Topic :: System :: Networking', 'Topic :: Software Development :: Libraries' ]) python-openflow-2019.2/PKG-INFO0000644000175100001630000000103313577157243016667 0ustar runnerdocker00000000000000Metadata-Version: 2.1 Name: python-openflow Version: 2019.2 Summary: Library to parse and generate OpenFlow messages Home-page: http://github.com/kytos/python-openflow Author: Kytos Team Author-email: devel@lists.kytos.io License: MIT Description: UNKNOWN Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: System :: Networking Classifier: Topic :: Software Development :: Libraries Provides-Extra: dev python-openflow-2019.2/pyof/0000755000175100001630000000000013577157243016552 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/__init__.py0000644000175100001630000000033413577157232020661 0ustar runnerdocker00000000000000"""The ofx parser package. A package to parse OpenFlow messages. This package is a library that parses and creates OpenFlow Messages. It contains all implemented versions of OpenFlow protocol """ __version__ = '2019.2' python-openflow-2019.2/pyof/utils.py0000644000175100001630000000337313577157232020270 0ustar runnerdocker00000000000000"""General Unpack utils for python-openflow. This package was moved from kytos/of_core for the purpose of creating a generic method to perform package unpack independent of the OpenFlow version. """ from pyof import v0x01, v0x04 from pyof.foundation.exceptions import UnpackException from pyof.v0x01.common import utils as u_v0x01 # pylint: disable=unused-import from pyof.v0x04.common import utils as u_v0x04 # pylint: disable=unused-import PYOF_VERSION_LIBS = {0x01: v0x01, 0x04: v0x04} def validate_packet(packet): """Check if packet is valid OF packet. Raises: UnpackException: If the packet is invalid. """ if not isinstance(packet, bytes): raise UnpackException('invalid packet') packet_length = len(packet) if packet_length < 8 or packet_length > 2**16: raise UnpackException('invalid packet') if packet_length != int.from_bytes(packet[2:4], byteorder='big'): raise UnpackException('invalid packet') version = packet[0] if version == 0 or version >= 128: raise UnpackException('invalid packet') def unpack(packet): """Unpack the OpenFlow Packet and returns a message. Args: packet: buffer with the openflow packet. Returns: GenericMessage: Message unpacked based on openflow packet. Raises: UnpackException: if the packet can't be unpacked. """ validate_packet(packet) version = packet[0] try: pyof_lib = PYOF_VERSION_LIBS[version] except KeyError: raise UnpackException('Version not supported') try: message = pyof_lib.common.utils.unpack_message(packet) return message except (UnpackException, ValueError) as exception: raise UnpackException(exception) python-openflow-2019.2/pyof/v0x04/0000755000175100001630000000000013577157243017433 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/v0x04/__init__.py0000644000175100001630000000007213577157232021541 0ustar runnerdocker00000000000000"""The ofx parser package - spec version 0x04 (1.3.0).""" python-openflow-2019.2/pyof/v0x04/symmetric/0000755000175100001630000000000013577157243021447 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/v0x04/symmetric/experimenter.py0000644000175100001630000000346113577157232024532 0ustar runnerdocker00000000000000"""Defines Experimenter message.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData, UBInt32 from pyof.v0x04.common.header import Header, Type __all__ = ('ExperimenterHeader',) # Classes 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() data = BinaryData() def __init__(self, xid=None, experimenter=None, exp_type=None, 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. """ super().__init__(xid) self.experimenter = experimenter self.exp_type = exp_type self.data = data python-openflow-2019.2/pyof/v0x04/symmetric/__init__.py0000644000175100001630000000003213577157232023551 0ustar runnerdocker00000000000000"""Symmetric Messages.""" python-openflow-2019.2/pyof/v0x04/symmetric/hello.py0000644000175100001630000000675513577157232023137 0ustar runnerdocker00000000000000"""Defines Hello message.""" # System imports from enum import IntEnum from pyof.foundation.base import GenericMessage, GenericStruct from pyof.foundation.basic_types import BinaryData, FixedTypeList, UBInt16 from pyof.foundation.exceptions import PackException from pyof.v0x04.common.header import Header, Type # Third-party imports __all__ = ('Hello', 'HelloElemHeader', 'HelloElemType', 'ListOfHelloElements') # Enums class HelloElemType(IntEnum): """Hello element types.""" #: Bitmap of version supported. OFPHET_VERSIONBITMAP = 1 # Classes class HelloElemHeader(GenericStruct): """Common header for all Hello Elements.""" element_type = UBInt16() length = UBInt16() content = BinaryData() def __init__(self, element_type=None, length=None, content=b''): """Create a HelloElemHeader with the optional parameters below. Args: element_type: One of OFPHET_*. length: Length in bytes of the element, including this header, excluding padding. """ super().__init__() self.element_type = element_type self.length = length self.content = content def pack(self, value=None): """Update the length and pack the massege into binary data. Returns: bytes: A binary data that represents the Message. Raises: Exception: If there are validation errors. """ if value is None: self.update_length() return super().pack() elif isinstance(value, type(self)): return value.pack() else: msg = "{} is not an instance of {}".format(value, type(self).__name__) raise PackException(msg) def update_length(self): """Update length attribute.""" self.length = self.get_size() 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=offset+2) super().unpack(buff[:offset+length.value], offset) class ListOfHelloElements(FixedTypeList): """List of Hello elements. Represented by instances of HelloElemHeader and used on Hello objects. """ def __init__(self, items=None): """Create a ListOfHelloElements with the optional parameters below. Args: items (HelloElemHeader): Instance or a list of instances. """ super().__init__(pyof_class=HelloElemHeader, items=items) 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 python-openflow-2019.2/pyof/v0x04/symmetric/echo_request.py0000644000175100001630000000150313577157232024504 0ustar runnerdocker00000000000000"""Defines Echo Request message during the handshake.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData from pyof.v0x04.common.header import Header, Type __all__ = ('EchoRequest',) # Classes 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 python-openflow-2019.2/pyof/v0x04/symmetric/echo_reply.py0000644000175100001630000000147213577157232024154 0ustar runnerdocker00000000000000"""Defines Echo Reply message during the handshake.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData from pyof.v0x04.common.header import Header, Type __all__ = ('EchoReply',) # Classes 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=b''): """Create a EchoReply 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 python-openflow-2019.2/pyof/v0x04/asynchronous/0000755000175100001630000000000013577157243022166 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/v0x04/asynchronous/__init__.py0000644000175100001630000000003513577157232024273 0ustar runnerdocker00000000000000"""Asynchronous messages.""" python-openflow-2019.2/pyof/v0x04/asynchronous/packet_in.py0000644000175100001630000000673613577157232024507 0ustar runnerdocker00000000000000"""For packets received by the datapath and sent to the controller.""" # System imports from enum import IntEnum from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import ( BinaryData, Pad, UBInt8, UBInt16, UBInt32, UBInt64) from pyof.v0x04.common.flow_match import Match, OxmOfbMatchField from pyof.v0x04.common.header import Header, Type # Third-party imports __all__ = ('PacketIn', 'PacketInReason') # Enums class PacketInReason(IntEnum): """Reason why this packet is being sent to the controller.""" #: matching flow (table-miss flow entry). OFPR_NO_MATCH = 0 #: Action explicitly output to controller. OFPR_ACTION = 1 #: Packet has invalid TTL. OFPR_INVALID_TTL = 2 # Classes class PacketIn(GenericMessage): """Packet received on port (datapath -> controller).""" #: :class:`~pyof.v0x04.common.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 (~pyof.v0x04.asynchronous.packet_in.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:`~pyof.v0x04.common.flow_match.Match`): Packet metadata with 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 @property def in_port(self): """Retrieve the 'in_port' that generated the PacketIn. This method will look for the OXM_TLV with type OFPXMT_OFB_IN_PORT on the `oxm_match_fields` field from `match` field and return its value, if the OXM exists. Returns: The integer number of the 'in_port' that generated the PacketIn if it exists. Otherwise return None. """ in_port = self.match.get_field(OxmOfbMatchField.OFPXMT_OFB_IN_PORT) return int.from_bytes(in_port, 'big') python-openflow-2019.2/pyof/v0x04/asynchronous/error_msg.py0000644000175100001630000004112413577157232024537 0ustar runnerdocker00000000000000"""Defines an Error Message.""" # System imports from enum import IntEnum from pyof.foundation import exceptions from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData, UBInt16, UBInt32 from pyof.v0x04.common.header import Header, Type # Third-party imports __all__ = ('BadActionCode', 'BadInstructionCode', 'BadMatchCode', 'ErrorType', 'FlowModFailedCode', 'GroupModFailedCode', 'HelloFailedCode', 'MeterModFailedCode', 'PortModFailedCode', 'QueueOpFailedCode', 'RoleRequestFailedCode', 'SwitchConfigFailedCode', 'TableFeaturesFailedCode', 'TableModFailedCode', 'GenericFailedCode') # Enums class GenericFailedCode(IntEnum): """Error_msg 'code' values for OFPET_BAD_ACTION. 'data' contains at least the first 64 bytes of the failed request. """ #: Unknown error GENERIC_ERROR = 0 class BadActionCode(IntEnum): """Error_msg 'code' values for OFPET_BAD_ACTION. 'data' contains at least the first 64 bytes of the failed request. """ #: Unknown action type. OFPBAC_BAD_TYPE = 0 #: Length problem in actions. OFPBAC_BAD_LEN = 1 #: Unknown experimenter id specified. OFPBAC_BAD_EXPERIMENTER = 2 #: Unknown action for experimenter id. OFPBAC_BAD_EXP_TYPE = 3 #: Problem validating output port. OFPBAC_BAD_OUT_PORT = 4 #: Bad action argument. OFPBAC_BAD_ARGUMENT = 5 #: Permissions error. OFPBAC_EPERM = 6 #: Can’t handle this many actions. OFPBAC_TOO_MANY = 7 #: Problem validating output queue. OFPBAC_BAD_QUEUE = 8 #: Invalid group id in forward action. OFPBAC_BAD_OUT_GROUP = 9 #: Action can’t apply for this match, or Set-Field missing prerequisite. OFPBAC_MATCH_INCONSISTENT = 10 #: Action order is unsupported for the action list in an Apply-Actions #: instruction OFPBAC_UNSUPPORTED_ORDER = 11 #: Actions uses an unsupported tag/encap. OFPBAC_BAD_TAG = 12 #: Unsupported type in SET_FIELD action. OFPBAC_BAD_SET_TYPE = 13 #: Length problem in SET_FIELD action. OFPBAC_BAD_SET_LEN = 14 #: Bad argument in SET_FIELD action. OFPBAC_BAD_SET_ARGUMENT = 15 class BadInstructionCode(IntEnum): """Error_msg 'code' values for OFPET_BAD_INSTRUCTION. 'data' contains at least the first 64 bytes of the failed request. """ #: Unknown instruction. OFPBIC_UNKNOWN_INST = 0 #: Switch or table does not support the instruction. OFPBIC_UNSUP_INST = 1 #: Invalid Table-ID specified. OFPBIC_BAD_TABLE_ID = 2 #: Metadata value unsupported by datapath. OFPBIC_UNSUP_METADATA = 3 #: Metadata mask value unsupported by datapath. OFPBIC_UNSUP_METADATA_MASK = 4 #: Unknown experimenter id specified. OFPBIC_BAD_EXPERIMENTER = 5 #: Unknown instruction for experimenter id. OFPBIC_BAD_EXP_TYPE = 6 #: Length problem in instructions. OFPBIC_BAD_LEN = 7 #: Permissions error. OFPBIC_EPERM = 8 class BadMatchCode(IntEnum): """Error_msg 'code' values for OFPET_BAD_MATCH. 'data' contains at least the first 64 bytes of the failed request. """ #: Unsupported match type specified by the match OFPBMC_BAD_TYPE = 0 #: Length problem in match. OFPBMC_BAD_LEN = 1 #: Match uses an unsupported tag/encap. OFPBMC_BAD_TAG = 2 #: Unsupported datalink addr mask - switch does not support arbitrary #: datalink address mask. OFPBMC_BAD_DL_ADDR_MASK = 3 #: Unsupported network addr mask - switch does not support arbitrary #: network address mask. OFPBMC_BAD_NW_ADDR_MASK = 4 #: Unsupported combination of fields masked or omitted in the match. OFPBMC_BAD_WILDCARDS = 5 #: Unsupported field type in the match. OFPBMC_BAD_FIELD = 6 #: Unsupported value in a match field. OFPBMC_BAD_VALUE = 7 #: Unsupported mask specified in the match, field is not dl-address or #: nw-address. OFPBMC_BAD_MASK = 8 #: A prerequisite was not met. OFPBMC_BAD_PREREQ = 9 #: A field type was duplicated. OFPBMC_DUP_FIELD = 10 #: Permissions error. OFPBMC_EPERM = 11 class BadRequestCode(IntEnum): """Error_msg 'code' values for OFPET_BAD_REQUEST. 'data' contains at least the first 64 bytes of the failed request. """ #: ofp_header.version not supported. OFPBRC_BAD_VERSION = 0 #: ofp_header.type not supported. OFPBRC_BAD_TYPE = 1 #: ofp_multipart_request.type not supported. OFPBRC_BAD_MULTIPART = 2 #: Experimenter id not supported (in ofp_experimenter_header or #: ofp_multipart_request or * ofp_multipart_reply). OFPBRC_BAD_EXPERIMENTER = 3 #: Experimenter type not supported. OFPBRC_BAD_EXP_TYPE = 4 #: Permissions error. OFPBRC_EPERM = 5 #: Wrong request length for type. OFPBRC_BAD_LEN = 6 #: Specified buffer has already been used. OFPBRC_BUFFER_EMPTY = 7 #: Specified buffer does not exist. OFPBRC_BUFFER_UNKNOWN = 8 #: Specified table-id invalid or does not * exist. OFPBRC_BAD_TABLE_ID = 9 #: Denied because controller is slave. OFPBRC_IS_SLAVE = 10 #: Invalid port. OFPBRC_BAD_PORT = 11 #: Invalid packet in packet-out. OFPBRC_BAD_PACKET = 12 #: ofp_multipart_request overflowed the assigned buffer. OFPBRC_MULTIPART_BUFFER_OVERFLOW = 13 # pylint: disable=invalid-name class ErrorType(IntEnum): """Values for ’type’ in ofp_error_message. These values are immutable: they will not change in future versions of the protocol (although new values may be added). """ #: Hello protocol failed OFPET_HELLO_FAILED = 0 #: Request was not understood OFPET_BAD_REQUEST = 1 #: Error in action description OFPET_BAD_ACTION = 2 #: Error in instruction list. OFPET_BAD_INSTRUCTION = 3 #: Error in match. OFPET_BAD_MATCH = 4 #: Problem modifying flow entry. OFPET_FLOW_MOD_FAILED = 5 #: Problem modifying group entry. OFPET_GROUP_MOD_FAILED = 6 #: Port mod request failed. OFPET_PORT_MOD_FAILED = 7 #: Table mod request failed. OFPET_TABLE_MOD_FAILED = 8 #: Queue operation failed. OFPET_QUEUE_OP_FAILED = 9 #: Switch config request failed. OFPET_SWITCH_CONFIG_FAILED = 10 #: Controller Role request failed. OFPET_ROLE_REQUEST_FAILED = 11 #: Error in meter. OFPET_METER_MOD_FAILED = 12 #: Setting table features failed. OFPET_TABLE_FEATURES_FAILED = 13 #: Experimenter error messages. OFPET_EXPERIMENTER = 0xffff def get_class(self): """Return a Code class based on current ErrorType value. Returns: enum.IntEnum: class referenced by current error type. """ classes = {'OFPET_HELLO_FAILED': HelloFailedCode, 'OFPET_BAD_REQUEST': BadRequestCode, 'OFPET_BAD_ACTION': BadActionCode, 'OFPET_BAD_INSTRUCTION': BadInstructionCode, 'OFPET_BAD_MATCH': BadMatchCode, 'OFPET_FLOW_MOD_FAILED': FlowModFailedCode, 'OFPET_GROUP_MOD_FAILED': GroupModFailedCode, 'OFPET_PORT_MOD_FAILED': PortModFailedCode, 'OFPET_QUEUE_OP_FAILED': QueueOpFailedCode, 'OFPET_SWITCH_CONFIG_FAILED': SwitchConfigFailedCode, 'OFPET_ROLE_REQUEST_FAILED': RoleRequestFailedCode, 'OFPET_METER_MOD_FAILED': MeterModFailedCode, 'OFPET_TABLE_MOD_FAILED': TableModFailedCode, 'OFPET_TABLE_FEATURES_FAILED': TableFeaturesFailedCode} return classes.get(self.name, GenericFailedCode) class FlowModFailedCode(IntEnum): """Error_msg 'code' values for OFPET_FLOW_MOD_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Unspecified error. OFPFMFC_UNKNOWN = 0 #: Flow not added because table was full. OFPFMFC_TABLE_FULL = 1 #: Table does not exist OFPFMFC_BAD_TABLE_ID = 2 #: Attempted to add overlapping flow with CHECK_OVERLAP flag set. OFPFMFC_OVERLAP = 3 #: Permissions error. OFPFMFC_EPERM = 4 #: Flow not added because of unsupported idle/hard timeout. OFPFMFC_BAD_TIMEOUT = 5 #: Unsupported or unknown command. OFPFMFC_BAD_COMMAND = 6 #: Unsupported or unknown flags. OFPFMFC_BAD_FLAGS = 7 class GroupModFailedCode(IntEnum): """Error_msg 'code' values for OFPET_GROUP_MOD_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Group not added because a group ADD attempted to replace an #: already-present group. FPGMFC_GROUP_EXISTS = 0 #: Group not added because Group specified is invalid. OFPGMFC_INVALID_GROUP = 1 #: Switch does not support unequal load sharing with select groups. OFPGMFC_WEIGHT_UNSUPPORTED = 2 #: The group table is full. OFPGMFC_OUT_OF_GROUPS = 3 #: The maximum number of action buckets for a group has been exceeded. OFPGMFC_OUT_OF_BUCKETS = 4 #: Switch does not support groups that forward to groups. OFPGMFC_CHAINING_UNSUPPORTED = 5 #: This group cannot watch the watch_port or watch_group specified. OFPGMFC_WATCH_UNSUPPORTED = 6 #: Group entry would cause a loop. OFPGMFC_LOOP = 7 #: Group not modified because a group MODIFY attempted to modify a #: non-existent group. OFPGMFC_UNKNOWN_GROUP = 8 #: Group not deleted because another group is forwarding to it. OFPGMFC_CHAINED_GROUP = 9 #: Unsupported or unknown group type. OFPGMFC_BAD_TYPE = 10 #: Unsupported or unknown command. OFPGMFC_BAD_COMMAND = 11 #: Error in bucket. OFPGMFC_BAD_BUCKET = 12 #: Error in watch port/group. OFPGMFC_BAD_WATCH = 13 #: Permissions error. OFPGMFC_EPERM = 14 class HelloFailedCode(IntEnum): """Error_msg 'code' values for OFPET_HELLO_FAILED. 'data' contains an ASCII text string that may give failure details. """ #: No compatible version OFPHFC_INCOMPATIBLE = 0 #: Permissions error OFPHFC_EPERM = 1 class MeterModFailedCode(IntEnum): """Error msg 'code' values for OFPET_METER_MOD_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Unspecified error. OFPMMFC_UNKNOWN = 0 #: Meter not added because a Meter ADD * attempted to replace an existing #: Meter. OFPMMFC_METER_EXISTS = 1 #: Meter not added because Meter specified * is invalid. OFPMMFC_INVALID_METER = 2 #: Meter not modified because a Meter MODIFY attempted to modify a #: non-existent Meter. OFPMMFC_UNKNOWN_METER = 3 #: Unsupported or unknown command. OFPMMFC_BAD_COMMAND = 4 #: Flag configuration unsupported. OFPMMFC_BAD_FLAGS = 5 #: Rate unsupported. OFPMMFC_BAD_RATE = 6 #: Burst size unsupported. OFPMMFC_BAD_BURST = 7 #: Band unsupported. OFPMMFC_BAD_BAND = 8 #: Band value unsupported. OFPMMFC_BAD_BAND_VALUE = 9 #: No more meters available. OFPMMFC_OUT_OF_METERS = 10 #: The maximum number of properties * for a meter has been exceeded. OFPMMFC_OUT_OF_BANDS = 11 class PortModFailedCode(IntEnum): """Error_msg 'code' values for OFPET_PORT_MOD_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Specified port number does not exist. OFPPMFC_BAD_PORT = 0 #: Specified hardware address does not * match the port number. OFPPMFC_BAD_HW_ADDR = 1 #: Specified config is invalid. OFPPMFC_BAD_CONFIG = 2 #: Specified advertise is invalid. OFPPMFC_BAD_ADVERTISE = 3 #: Permissions error. OFPPMFC_EPERM = 4 class QueueOpFailedCode(IntEnum): """Error msg 'code' values for OFPET_QUEUE_OP_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Invalid port (or port does not exist) OFPQOFC_BAD_PORT = 0 #: Queue does not exist OFPQOFC_BAD_QUEUE = 1 #: Permissions error OFPQOFC_EPERM = 2 class RoleRequestFailedCode(IntEnum): """Error msg 'code' values for OFPET_ROLE_REQUEST_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Stale Message: old generation_id. OFPRRFC_STALE = 0 #: Controller role change unsupported. OFPRRFC_UNSUP = 1 #: Invalid role. OFPRRFC_BAD_ROLE = 2 class SwitchConfigFailedCode(IntEnum): """Error msg 'code' values for OFPET_SWITCH_CONFIG_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Specified flags is invalid. OFPSCFC_BAD_FLAGS = 0 #: Specified len is invalid. OFPSCFC_BAD_LEN = 1 #: Permissions error. OFPQCFC_EPERM = 2 class TableFeaturesFailedCode(IntEnum): """Error msg 'code' values for OFPET_TABLE_FEATURES_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Specified table does not exist. OFPTFFC_BAD_TABLE = 0 #: Invalid metadata mask. OFPTFFC_BAD_METADATA = 1 #: Unknown property type. OFPTFFC_BAD_TYPE = 2 #: Length problem in properties. OFPTFFC_BAD_LEN = 3 #: Unsupported property value. OFPTFFC_BAD_ARGUMENT = 4 #: Permissions error. OFPTFFC_EPERM = 5 class TableModFailedCode(IntEnum): """Error_msg 'code' values for OFPET_TABLE_MOD_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Specified table does not exist. OFPTMFC_BAD_TABLE = 0 #: Specified config is invalid. OFPTMFC_BAD_CONFIG = 1 #: Permissions error. OFPTMFC_EPERM = 2 # Classes 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) 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. error_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") python-openflow-2019.2/pyof/v0x04/asynchronous/port_status.py0000644000175100001630000000265713577157232025137 0ustar runnerdocker00000000000000"""Defines an PortStatus Message.""" # System imports from enum import IntEnum # Local source tree imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import Pad, UBInt8 from pyof.v0x04.common.header import Header, Type from pyof.v0x04.common.port import Port # Third-party imports __all__ = ('PortStatus', 'PortReason') # Enums class PortReason(IntEnum): """What changed about the physical port.""" #: The port was added OFPPR_ADD = 0 #: The port was removed OFPPR_DELETE = 1 #: Some attribute of the port has changed OFPPR_MODIFY = 2 # Classes class PortStatus(GenericMessage): """A physical port has changed in the datapath.""" #: :class:`~pyof.v0x04.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.v0x04.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.v0x04.asynchronous.port_status.PortReason): Addition, deletion or modification. desc (~pyof.v0x04.common.port.Port): Port description. """ super().__init__(xid) self.reason = reason self.desc = desc python-openflow-2019.2/pyof/v0x04/asynchronous/flow_removed.py0000644000175100001630000000657713577157232025245 0ustar runnerdocker00000000000000"""The controller has requested to be notified when flows time out.""" # System imports from enum import IntEnum from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import UBInt8, UBInt16, UBInt32, UBInt64 # Local source tree imports from pyof.v0x04.common.flow_match import Match from pyof.v0x04.common.header import Header, Type __all__ = ('FlowRemoved', 'FlowRemovedReason') # Enums class FlowRemovedReason(IntEnum): """Why the flow was removed.""" #: Flow idle time exceeded idle_timeout OFPRR_IDLE_TIMEOUT = 0 #: Time exceeded hard_timeout OFPRR_HARD_TIMEOUT = 1 #: Evicted by a DELETE flow mod OFPRR_DELETE = 2 #: Group was removed. OFPRR_GROUP_DELETE = 3 # Classes class FlowRemoved(GenericMessage): """Flow removed (datapath -> controller). If the controller has requested to be notified when flow entries time out or are deleted from tables, the datapath does this with the OFPT_FLOW_REMOVED message. """ #: :class:`~pyof.v0x04.common.header.Header`: OpenFlow Header header = Header(message_type=Type.OFPT_FLOW_REMOVED) #: Opaque controller-issued identifier. cookie = UBInt64() #: Priority level of flow entry. priority = UBInt16() #: One of OFPRR_*. reason = UBInt8(enum_ref=FlowRemovedReason) #: ID of the table table_id = UBInt8() #: Time flow was alive in seconds. duration_sec = UBInt32() #: Time flow was alive in nanoseconds beyond duration_sec. duration_nsec = UBInt32() #: Idle timeout from original flow mod. idle_timeout = UBInt16() #: Hard timeout from original flow mod. hard_timeout = UBInt16() packet_count = UBInt64() byte_count = UBInt64() #: Description of fields. Variable size. #: :class:`~pyof.v0x04.common.flow_match.Match` match = Match() def __init__(self, xid=None, cookie=None, priority=None, reason=None, table_id=None, duration_sec=None, duration_nsec=None, idle_timeout=None, hard_timeout=None, packet_count=None, byte_count=None, match=None): """Assign parameters to object attributes. Args: xid (int): OpenFlow Header's xid. cookie (int): Opaque controller-issued identifier. priority (int): Priority level of flow entry. reason (~pyof.v0x04.asynchronous.flow_removed.FlowRemovedReason): Why the flow was removed. table_id (int): ID of the table. duration_sec (int): Time the flow was alive in seconds. duration_nsec (int): Time the flow was alive in nanoseconds in addition to duration_sec. idle_timeout (int): Idle timeout from original flow mod. hard_timeout (int): Hard timeout from original flow mod. packet_count (int): Number of packets. byte_count (int): Byte count. match (~pyof.v0x04.common.flow_match.Match): Fields' description. """ super().__init__(xid) self.cookie = cookie self.priority = priority self.reason = reason self.table_id = table_id self.duration_sec = duration_sec self.duration_nsec = duration_nsec self.idle_timeout = idle_timeout self.hard_timeout = hard_timeout self.packet_count = packet_count self.byte_count = byte_count self.match = match python-openflow-2019.2/pyof/v0x04/controller2switch/0000755000175100001630000000000013577157243023122 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/v0x04/controller2switch/features_reply.py0000644000175100001630000000506013577157232026524 0ustar runnerdocker00000000000000"""Defines Features Reply classes and related items.""" # System imports # Third-party imports # Local source tree imports from pyof.foundation.base import GenericBitMask, GenericMessage from pyof.foundation.basic_types import DPID, Pad, UBInt8, UBInt32 from pyof.v0x04.common.header import Header, Type __all__ = ('FeaturesReply', 'Capabilities', 'SwitchFeatures') class Capabilities(GenericBitMask): """Capabilities supported by the datapath.""" #: Flow statistics OFPC_FLOW_STATS = 1 << 0 #: Table statistics OFPC_TABLE_STATS = 1 << 1 #: Port statistics OFPC_PORT_STATS = 1 << 2 #: Group statistics. OFPC_GROUP_STATS = 1 << 3 #: Can reassembe IP fragments OFPC_IP_REASM = 1 << 5 #: Queue statistics OFPC_QUEUE_STATS = 1 << 6 #: Switch will block looping ports. OFPC_PORT_BLOCKED = 1 << 8 # Classes 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 = DPID() 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): """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 FeaturesReply(SwitchFeatures): """'OFPT_FEATURES_REPLY' message.""" python-openflow-2019.2/pyof/v0x04/controller2switch/role_reply.py0000644000175100001630000000205213577157232025645 0ustar runnerdocker00000000000000"""Request a change of the role of the controller.""" # System imports # Third-party imports # Local source tree imports from pyof.foundation.constants import UBINT64_MAX_VALUE from pyof.v0x04.common.header import Type from pyof.v0x04.controller2switch.common import ControllerRole, RoleBaseMessage __all__ = ('RoleReply',) # Classes class RoleReply(RoleBaseMessage): """RoleReply Message.""" def __init__(self, xid=None, role=ControllerRole.OFPCR_ROLE_NOCHANGE, generation_id=UBINT64_MAX_VALUE): """Create a RoleReply 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. The default value is -1. For this field, it's UBINT64_MAX_VALUE. """ super().__init__(xid, role, generation_id) self.header.message_type = Type.OFPT_ROLE_REPLY python-openflow-2019.2/pyof/v0x04/controller2switch/barrier_request.py0000644000175100001630000000070113577157232026666 0ustar runnerdocker00000000000000"""Defines Barrier Request message.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.v0x04.common.header import Header, Type __all__ = ('BarrierRequest',) # Classes 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) python-openflow-2019.2/pyof/v0x04/controller2switch/__init__.py0000644000175100001630000000007613577157232025234 0ustar runnerdocker00000000000000"""Controller to Switch and Switch to Controller Messages.""" python-openflow-2019.2/pyof/v0x04/controller2switch/role_request.py0000644000175100001630000000170213577157232026203 0ustar runnerdocker00000000000000"""Request a change of the role of the controller.""" # System imports # Third-party imports # Local source tree imports from pyof.v0x04.common.header import Type from pyof.v0x04.controller2switch.common import RoleBaseMessage __all__ = ('RoleRequest',) # Classes class RoleRequest(RoleBaseMessage): """RoleRequest Message. When the controller wants to change its role, it uses the OFPT_ROLE_REQUEST message. """ 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) self.header.message_type = Type.OFPT_ROLE_REQUEST python-openflow-2019.2/pyof/v0x04/controller2switch/port_mod.py0000644000175100001630000000350513577157232025320 0ustar runnerdocker00000000000000"""Modifications to the port from the controller.""" # System imports # Third-party imports # Local source tree imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import HWAddress, Pad, UBInt32 from pyof.v0x04.common.header import Header, Type from pyof.v0x04.common.port import PortConfig, PortFeatures __all__ = ('PortMod',) # Classes 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): """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.v0x04.common.port.PortConfig): Bitmap of OFPPC_* flags mask (~pyof.v0x04.common.port.PortConfig): Bitmap of OFPPC_* flags to be changed advertise (~pyof.v0x04.common.port.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 python-openflow-2019.2/pyof/v0x04/controller2switch/barrier_reply.py0000644000175100001630000000065713577157232026343 0ustar runnerdocker00000000000000"""Defines Barrier Reply message.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.v0x04.common.header import Header, Type __all__ = ('BarrierReply',) # Classes 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) python-openflow-2019.2/pyof/v0x04/controller2switch/group_mod.py0000644000175100001630000000525613577157232025475 0ustar runnerdocker00000000000000"""Modify Group Entry Message.""" from enum import IntEnum from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import ( FixedTypeList, Pad, UBInt8, UBInt16, UBInt32) from pyof.v0x04.common.header import Header, Type from pyof.v0x04.controller2switch.common import Bucket __all__ = ('GroupMod', 'GroupModCommand', 'GroupType', 'Group', 'ListOfBuckets') class Group(IntEnum): """Group numbering. Groups can use any number up to attr:`OFPG_MAX`.""" #: Last usable group number. OFPG_MAX = 0xffffff00 #: Fake groups. #: Represents all groups for group delete commands. OFPG_ALL = 0xfffffffc #: Wildcard group used only for flow stats requests. # Select all flows regardless of group (including flows with no group). OFPG_ANY = 0xffffffff class GroupModCommand(IntEnum): """Group commands.""" #: New group. OFPGC_ADD = 0 #: Modify all matching groups. OFPGC_MODIFY = 1 #: Delete all matching groups. OFPGC_DELETE = 2 class GroupType(IntEnum): """Group types. Range [128, 255] is reserved for experimental use.""" #: All (multicast/broadcast) group. OFPGT_ALL = 0 #: Select group. OFPGT_SELECT = 1 #: Indirect group. OFPGT_INDIRECT = 2 #: Fast failover group. OFPGT_FF = 3 class ListOfBuckets(FixedTypeList): """List of buckets. Represented by instances of Bucket. """ def __init__(self, items=None): """Create a ListOfBuckets with the optional parameters below. Args: items (Bucket): Instance or a list of instances. """ super().__init__(pyof_class=Bucket, items=items) 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 python-openflow-2019.2/pyof/v0x04/controller2switch/multipart_request.py0000644000175100001630000002631513577157232027272 0ustar runnerdocker00000000000000"""Controller requesting state from datapath.""" # System imports from enum import Enum # Local source tree imports from pyof.foundation.base import GenericMessage, GenericStruct from pyof.foundation.basic_types import ( BinaryData, FixedTypeList, Pad, UBInt8, UBInt16, UBInt32, UBInt64) from pyof.v0x04.common.flow_match import Match from pyof.v0x04.common.header import Header, Type from pyof.v0x04.common.port import PortNo from pyof.v0x04.controller2switch.common import ( ExperimenterMultipartHeader, MultipartType, TableFeatures) from pyof.v0x04.controller2switch.group_mod import Group from pyof.v0x04.controller2switch.meter_mod import Meter from pyof.v0x04.controller2switch.table_mod import Table # Third-party imports __all__ = ('MultipartRequest', 'MultipartRequestFlags', 'AggregateStatsRequest', 'FlowStatsRequest', 'PortStatsRequest', 'QueueStatsRequest', 'GroupStatsRequest', 'MeterMultipartRequest') # Enum class MultipartRequestFlags(Enum): """Flags for MultipartRequest.""" #: No more requests to follow (This is not part of spec). Thanks @jondef95 OFPMPF_REQ_NONE = 0 #: More requests to follow OFPMPF_REQ_MORE = 1 << 0 # Classes class MultipartRequest(GenericMessage): """Request datapath state. While the system is running, the controller may request state from the datapath using the OFPT_MULTIPART_REQUEST message. """ #: Openflow :class:`~pyof.v0x04.common.header.Header` header = Header(message_type=Type.OFPT_MULTIPART_REQUEST) #: One of the OFPMP_* constants. multipart_type = UBInt16(enum_ref=MultipartType) #: OFPMPF_REQ_* flags. flags = UBInt16(enum_ref=MultipartRequestFlags) #: Padding pad = Pad(4) #: Body of the request body = BinaryData() def __init__(self, xid=None, multipart_type=None, flags=0, body=b''): """Create a MultipartRequest with the optional parameters below. Args: xid (int): xid to the header. multipart_type (int): One of the OFPMP_* constants. flags (int): OFPMPF_REQ_* flags. body (bytes): Body of the request. """ super().__init__(xid) self.multipart_type = multipart_type self.flags = flags self.body = body def pack(self, value=None): """Pack a MultipartRequest using the object's attributes. This method will pack the attribute body and multipart_type before pack the MultipartRequest object, then will return this struct as a binary data. Args: value (:class:`~MultipartRequest`): Object to be packed. Returns: bytes: Binary data with MultipartRequest packed. """ buff = self.body if not value: value = self.body if value: if isinstance(value, (list, FixedTypeList)): obj = self._get_body_instance() obj.extend(value) elif hasattr(value, 'pack'): obj = value self.body = obj.pack() multipart_packed = super().pack() self.body = buff return multipart_packed def unpack(self, buff, 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. It is an inplace method and it receives the binary data of the message **without the header**. This class' unpack method is like the :meth:`.GenericMessage.unpack` one, except for the ``body`` attribute which has its type determined by the ``multipart_type`` attribute. Args: buff (bytes): Binary data package to be unpacked, without the header. """ super().unpack(buff[offset:]) self._unpack_body() def _unpack_body(self): """Unpack `body` replace it by the result.""" obj = self._get_body_instance() obj.unpack(self.body.value) self.body = obj def _get_body_instance(self): """Return the body instance.""" simple_body = { MultipartType.OFPMP_FLOW: FlowStatsRequest, MultipartType.OFPMP_AGGREGATE: AggregateStatsRequest, MultipartType.OFPMP_PORT_STATS: PortStatsRequest, MultipartType.OFPMP_QUEUE: QueueStatsRequest, MultipartType.OFPMP_GROUP: GroupStatsRequest, MultipartType.OFPMP_METER: MeterMultipartRequest, MultipartType.OFPMP_EXPERIMENTER: ExperimenterMultipartHeader } array_of_bodies = {MultipartType.OFPMP_TABLE_FEATURES: TableFeatures} if isinstance(self.multipart_type, UBInt16): self.multipart_type = self.multipart_type.enum_ref( self.multipart_type.value) pyof_class = simple_body.get(self.multipart_type, None) if pyof_class: return pyof_class() array_of_class = array_of_bodies.get(self.multipart_type, None) if array_of_class: return FixedTypeList(pyof_class=array_of_class) return BinaryData(b'') class AggregateStatsRequest(GenericStruct): """Body for ofp_stats_request of type OFPST_AGGREGATE.""" #: ID of table to read (from ofp_table_stats) OFPTT_ALL for all tables. table_id = UBInt8() #: Align to 32 bits. pad = Pad(3) #: Require matching entries to include this as an output port. A value of #: OFPP_ANY indicates no restriction. out_port = UBInt32() #: Require matching entries to include this as an output group. A value of #: OFPG_ANY indicates no restriction. out_group = UBInt32() #: Align to 64 bits pad2 = Pad(4) #: Require matching entries to contain this cookie value cookie = UBInt64() #: Mask used to restrict the cookie bits that must match. A value of 0 #: indicates no restriction. cookie_mask = UBInt64() #: Fields to match. Variable size. match = Match() def __init__(self, table_id=Table.OFPTT_ALL, out_port=PortNo.OFPP_ANY, out_group=Group.OFPG_ANY, cookie=0, cookie_mask=0, match=None): """Create a AggregateStatsRequest with the optional parameters below. Args: table_id (int): ID of table to read (from ofp_table_stats) OFPTT_ALL for all tables. out_port (int): Require matching entries to include this as an output port. A value of OFPP_ANY indicates no restriction. out_group (int): Require matching entries to include this as an output group. A value of OFPG_ANY indicates no restriction. cookie (int): Require matching entries to contain this cookie value cookie_mask (int): Mask used to restrict the cookie bits that must match. A value of 0 indicates no restriction. match (~pyof.v0x04.common.flow_match.Match): Fields to match. Variable size """ super().__init__() self.table_id = table_id self.out_port = out_port self.out_group = out_group self.cookie = cookie self.cookie_mask = cookie_mask self.match = Match() if match is None else match class FlowStatsRequest(GenericStruct): """Body for ofp_stats_request of type OFPST_FLOW.""" table_id = UBInt8() #: Align to 32 bits. pad = Pad(3) out_port = UBInt32() out_group = UBInt32() pad2 = Pad(4) cookie = UBInt64() cookie_mask = UBInt64() match = Match() def __init__(self, table_id=Table.OFPTT_ALL, out_port=PortNo.OFPP_ANY, out_group=Group.OFPG_ANY, cookie=0, cookie_mask=0, match=None): """Create a FlowStatsRequest with the optional parameters below. Args: table_id (int): ID of table to read (from pyof_table_stats) 0xff for all tables or 0xfe for emergency. out_port (:class:`int`, :class:`~pyof.v0x04.common.port.PortNo`): Require matching entries to include this as an output port. A value of :attr:`.PortNo.OFPP_ANY` indicates no restriction. out_group: Require matching entries to include this as an output group. A value of :attr:`Group.OFPG_ANY` indicates no restriction. cookie: Requires matching entries to contain this cookie value cookie_mask: Mask used to restrict the cookie bits that must match. A value of 0 indicates no restriction. match (~pyof.v0x04.common.flow_match.Match): Fields to match. """ super().__init__() self.table_id = table_id self.out_port = out_port self.out_group = out_group self.cookie = cookie self.cookie_mask = cookie_mask self.match = Match() if match is None else match class PortStatsRequest(GenericStruct): """Body for ofp_stats_request of type OFPST_PORT.""" port_no = UBInt32() #: Align to 64-bits. pad = Pad(4) def __init__(self, port_no=PortNo.OFPP_ANY): """Create a PortStatsRequest with the optional parameters below. Args: port_no (:class:`int`, :class:`~pyof.v0x04.common.port.PortNo`): :attr:`StatsType.OFPST_PORT` message must request statistics either for a single port (specified in ``port_no``) or for all ports (if ``port_no`` == :attr:`.PortNo.OFPP_ANY`). """ super().__init__() self.port_no = port_no class QueueStatsRequest(GenericStruct): """Implements the request body of a ``port_no``.""" port_no = UBInt32() queue_id = UBInt32() def __init__(self, port_no=PortNo.OFPP_ANY, queue_id=0xffffffff): """Create a QueueStatsRequest with the optional parameters below. Args: port_no (:class:`int`, :class:`~pyof.v0x04.common.port.Port`): All ports if :attr:`.Port.OFPP_ALL`. queue_id (int): All queues if OFPQ_ALL (``0xfffffff``). """ super().__init__() self.port_no = port_no self.queue_id = queue_id class GroupStatsRequest(GenericStruct): """Body of OFPMP_GROUP request.""" #: Group id. All groups is OFPG_ALL group_id = UBInt32() #: Align to 64 bits pad = Pad(4) def __init__(self, group_id=Group.OFPG_ALL): """Create a GroupStatsRequest with the optional parameters below. Args: group_id(int): ID of group to read. OFPG_ALL to request informatio for all groups. """ super().__init__() self.group_id = group_id class MeterMultipartRequest(GenericStruct): """MeterMultipartRequest structure. This class represents the structure for ofp_meter_multipart_request. This structure is a body of OFPMP_METER and OFPMP_METER_CONFIG requests. """ # Meter instance, or OFPM_ALL. meter_id = UBInt32() # Align to 64 bits. pad = Pad(4) def __init__(self, meter_id=Meter.OFPM_ALL): """Create a MeterMultipartRequest with the optional parameters below. Args: meter_id(Meter): Meter Indentify.The value Meter.OFPM_ALL is used to refer to all Meters on the switch. """ super().__init__() self.meter_id = meter_id python-openflow-2019.2/pyof/v0x04/controller2switch/queue_get_config_request.py0000644000175100001630000000212113577157232030546 0ustar runnerdocker00000000000000"""Query the switch for configured queues on a port.""" # System imports # Third-party imports # Local source tree imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import Pad, UBInt32 from pyof.v0x04.common.header import Header, Type from pyof.v0x04.common.port import PortNo __all__ = ('QueueGetConfigRequest',) class QueueGetConfigRequest(GenericMessage): """Query structure for configured queues on a port.""" #: Openflow :class:`~pyof.v0x04.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 python-openflow-2019.2/pyof/v0x04/controller2switch/features_request.py0000644000175100001630000000065713577157232027070 0ustar runnerdocker00000000000000"""Defines Features Request classes and related items.""" from pyof.foundation.base import GenericMessage from pyof.v0x04.common.header import Header, Type __all__ = ('FeaturesRequest',) # Classes 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) python-openflow-2019.2/pyof/v0x04/controller2switch/packet_out.py0000644000175100001630000001205613577157232025634 0ustar runnerdocker00000000000000"""For the controller to send a packet out through the datapath.""" from copy import deepcopy from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData, Pad, UBInt16, UBInt32 from pyof.foundation.constants import UBINT32_MAX_VALUE from pyof.foundation.exceptions import PackException, ValidationError from pyof.v0x04.common.action import ListOfActions from pyof.v0x04.common.header import Header, Type from pyof.v0x04.common.port import PortNo __all__ = ('PacketOut',) # Classes #: in_port valid virtual port values, for validation _VIRT_IN_PORTS = (PortNo.OFPP_LOCAL, PortNo.OFPP_CONTROLLER, PortNo.OFPP_ANY) class PacketOut(GenericMessage): """Send packet (controller -> datapath).""" #: Openflow :class:`~pyof.v0x04.common.header.Header` header = Header(message_type=Type.OFPT_PACKET_OUT) #: ID assigned by datapath (OFP_NO_BUFFER if none). buffer_id = UBInt32() #: Packet’s input port or OFPP_CONTROLLER. in_port = UBInt32() #: Size of action array in bytes. actions_len = UBInt16() #: Padding pad = Pad(6) #: Action List. actions = ListOfActions() #: Packet data. The length is inferred from the length field in the header. #: (Only meaningful if buffer_id == -1.) data = BinaryData() def __init__(self, xid=None, buffer_id=UBINT32_MAX_VALUE, in_port=PortNo.OFPP_CONTROLLER, actions=None, data=b''): """Create a PacketOut with the optional parameters below. Args: xid (int): xid of the message header. buffer_id (int): ID assigned by datapath (-1 if none). In this case UBINT32_MAX_VALUE is -1 for the field. in_port (:class:`int`, :class:`~pyof.v0x04.common.port.Port`): Packet's input port (:attr:`Port.OFPP_NONE` if none). Virtual ports OFPP_IN_PORT, OFPP_TABLE, OFPP_NORMAL, OFPP_FLOOD, and OFPP_ALL cannot be used as input port. actions (:class:`~pyof.v0x04.common.action.ListOfActions`): List of Action instances. data (bytes): Packet data. The length is inferred from the length field in the header. (Only meaningful if ``buffer_id`` == -1). """ super().__init__(xid) self.buffer_id = buffer_id self.in_port = in_port self.actions = [] if actions is None else actions self.data = data def validate(self): """Validate the entire message.""" if not super().is_valid(): raise ValidationError() self._validate_in_port() def is_valid(self): """Answer if this message is valid.""" try: self.validate() return True except ValidationError: return False def pack(self, value=None): """Update the action_len attribute and call super's pack.""" if value is None: self._update_actions_len() return super().pack() elif isinstance(value, type(self)): return value.pack() else: msg = "{} is not an instance of {}".format(value, type(self).__name__) raise PackException(msg) def unpack(self, buff, 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. It is an inplace method and it receives the binary data of the message **without the header**. This class' unpack method is like the :meth:`.GenericMessage.unpack` one, except for the ``actions`` attribute which has a length determined by the ``actions_len`` attribute. Args: buff (bytes): Binary data package to be unpacked, without the header. offset (int): Where to begin unpacking. """ begin = offset for attribute_name, class_attribute in self.get_class_attributes(): if type(class_attribute).__name__ != "Header": attribute = deepcopy(class_attribute) if attribute_name == 'actions': length = self.actions_len.value attribute.unpack(buff[begin:begin+length]) else: attribute.unpack(buff, begin) setattr(self, attribute_name, attribute) begin += attribute.get_size() def _update_actions_len(self): """Update the actions_len field based on actions value.""" if isinstance(self.actions, ListOfActions): self.actions_len = self.actions.get_size() else: self.actions_len = ListOfActions(self.actions).get_size() def _validate_in_port(self): is_valid_range = self.in_port > 0 and self.in_port <= PortNo.OFPP_MAX is_valid_virtual_in_ports = self.in_port in _VIRT_IN_PORTS if (is_valid_range or is_valid_virtual_in_ports) is False: raise ValidationError(f'{self.in_port} is not a valid input port.') python-openflow-2019.2/pyof/v0x04/controller2switch/queue_get_config_reply.py0000644000175100001630000000261513577157232030221 0ustar runnerdocker00000000000000"""Switch replies to controller.""" # System imports # Third-party imports # Local source tree imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import Pad, UBInt32 from pyof.v0x04.common.header import Header, Type from pyof.v0x04.common.port import PortNo from pyof.v0x04.common.queue import ListOfQueues __all__ = ('QueueGetConfigReply',) class QueueGetConfigReply(GenericMessage): """Class implements the response to the config request.""" #: Openflow :class:`~pyof.v0x04.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.v0x04.common.port.PortNo`): Target port for the query. queue (:class:`~pyof.v0x04.common.queue.ListOfQueues`): List of configured queues. """ super().__init__(xid) self.port = port self.queues = [] if queues is None else queues python-openflow-2019.2/pyof/v0x04/controller2switch/set_config.py0000644000175100001630000000172613577157232025620 0ustar runnerdocker00000000000000"""Define SetConfig message.""" # System imports # Third-party imports # Local imports from pyof.v0x04.common.action import ControllerMaxLen from pyof.v0x04.common.header import Type from pyof.v0x04.controller2switch.common import ConfigFlag, SwitchConfig __all__ = ('SetConfig',) class SetConfig(SwitchConfig): """Set config message.""" def __init__(self, xid=None, flags=ConfigFlag.OFPC_FRAG_NORMAL, miss_send_len=ControllerMaxLen.OFPCML_NO_BUFFER): """Create a SetConfig with the optional parameters below. Args: xid (int): xid to be used on the message header. flags (:class:`~pyof.v0x01.controller2switch.common.ConfigFlag`): OFPC_* flags. miss_send_len (int): UBInt16 max bytes of new flow that the datapath should send to the controller. """ super().__init__(xid, flags, miss_send_len) self.header.message_type = Type.OFPT_SET_CONFIG python-openflow-2019.2/pyof/v0x04/controller2switch/multipart_reply.py0000644000175100001630000006147313577157232026741 0ustar runnerdocker00000000000000"""Controller replying state from datapath.""" # System imports from enum import Enum # Local source tree imports from pyof.foundation.base import GenericBitMask, GenericMessage, GenericStruct from pyof.foundation.basic_types import ( BinaryData, Char, FixedTypeList, Pad, UBInt8, UBInt16, UBInt32, UBInt64) from pyof.foundation.constants import DESC_STR_LEN, SERIAL_NUM_LEN from pyof.v0x04.common.flow_instructions import ListOfInstruction from pyof.v0x04.common.flow_match import Match from pyof.v0x04.common.header import Header, Type from pyof.v0x04.common.port import Port from pyof.v0x04.controller2switch.common import ( Bucket, ExperimenterMultipartHeader, ListOfBucketCounter, MultipartType, TableFeatures) from pyof.v0x04.controller2switch.meter_mod import ( ListOfMeterBandHeader, MeterBandType, MeterFlags) # Third-party imports __all__ = ('MultipartReply', 'MultipartReplyFlags', 'AggregateStatsReply', 'Desc', 'FlowStats', 'PortStats', 'QueueStats', 'GroupDescStats', 'GroupFeatures', 'GroupStats', 'MeterConfig', 'MeterFeatures', 'BandStats', 'ListOfBandStats', 'MeterStats', 'GroupCapabilities', 'TableStats') # Enum class MultipartReplyFlags(Enum): """Flags for MultipartReply.""" #: More replies to follow. OFPMPF_REPLY_MORE = 1 << 0 class GroupCapabilities(GenericBitMask): """Group configuration flags.""" #: Support weight for select groups. OFPGFC_SELECT_WEIGHT = 1 << 0 #: Support liveness for select groups. OFPGFC_SELECT_LIVENESS = 1 << 1 #: Support chaining groups. OFPGFC_CHAINING = 1 << 2 #: Chack chaining for loops and delete. OFPGFC_CHAINING_CHECKS = 1 << 3 # Classes 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. """ #: Openflow :class:`~pyof.v0x04.common.header.Header` header = Header(message_type=Type.OFPT_MULTIPART_REPLY) #: One of the OFPMP_* constants. multipart_type = UBInt16(enum_ref=MultipartType) #: OFPMPF_REPLY_* flags. flags = UBInt16() #: Padding pad = Pad(4) #: Body of the reply body = BinaryData() def __init__(self, xid=None, multipart_type=None, flags=None, body=b''): """Create a MultipartReply with the optional parameters below. 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 def pack(self, value=None): """Pack a StatsReply using the object's attributes. This method will pack the attribute body and multipart_type before pack the StatsReply object, then will return this struct as a binary data. Returns: stats_reply_packed (bytes): Binary data with StatsReply packed. """ buff = self.body if not value: value = self.body if value: if isinstance(value, (list, FixedTypeList)): obj = self._get_body_instance() obj.extend(value) elif hasattr(value, 'pack'): obj = value self.body = obj.pack() multipart_packed = super().pack() self.body = buff return multipart_packed def unpack(self, buff, 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. It is an inplace method and it receives the binary data of the message **without the header**. This class' unpack method is like the :meth:`.GenericMessage.unpack` one, except for the ``body`` attribute which has its type determined by the ``multipart_type`` attribute. Args: buff (bytes): Binary data package to be unpacked, without the header. """ super().unpack(buff[offset:]) self._unpack_body() def _unpack_body(self): """Unpack `body` replace it by the result.""" obj = self._get_body_instance() obj.unpack(self.body.value) self.body = obj def _get_body_instance(self): """Return the body instance.""" exp_header = ExperimenterMultipartHeader simple_body = {MultipartType.OFPMP_DESC: Desc, MultipartType.OFPMP_GROUP_FEATURES: GroupFeatures, MultipartType.OFPMP_METER_FEATURES: MeterFeatures, MultipartType.OFPMP_EXPERIMENTER: exp_header} array_of_bodies = {MultipartType.OFPMP_FLOW: FlowStats, MultipartType.OFPMP_AGGREGATE: AggregateStatsReply, MultipartType.OFPMP_TABLE: TableStats, MultipartType.OFPMP_PORT_STATS: PortStats, MultipartType.OFPMP_QUEUE: QueueStats, MultipartType.OFPMP_GROUP: GroupStats, MultipartType.OFPMP_GROUP_DESC: GroupDescStats, MultipartType.OFPMP_METER: MeterStats, MultipartType.OFPMP_METER_CONFIG: MeterConfig, MultipartType.OFPMP_TABLE_FEATURES: TableFeatures, MultipartType.OFPMP_PORT_DESC: Port} if isinstance(self.multipart_type, UBInt16): self.multipart_type = self.multipart_type.enum_ref( self.multipart_type.value) pyof_class = simple_body.get(self.multipart_type, None) if pyof_class: return pyof_class() array_of_class = array_of_bodies.get(self.multipart_type, None) if array_of_class: return FixedTypeList(pyof_class=array_of_class) return BinaryData(b'') # MultipartReply Body class AggregateStatsReply(GenericStruct): """Body of reply to OFPMP_AGGREGATE request.""" #: Number of packets in flows. packet_count = UBInt64() #: Number of bytes in flows. byte_count = UBInt64() #: Number of flows. flow_count = UBInt32() #: Align to 64 bits pad = Pad(4) def __init__(self, packet_count=None, byte_count=None, flow_count=None): """Create a AggregateStatsReply with the optional parameters below. Args: packet_count (int): Number of packets in flows byte_count (int): Number of bytes in flows flow_count (int): Number of flows """ super().__init__() self.packet_count = packet_count self.byte_count = byte_count self.flow_count = flow_count class Desc(GenericStruct): """Information available from the OFPST_DESC stats request. Information about the switch manufacturer, hardware revision, software revision, serial number and a description field. """ #: Manufacturer description mfr_desc = Char(length=DESC_STR_LEN) #: Hardware description hw_desc = Char(length=DESC_STR_LEN) #: Software description sw_desc = Char(length=DESC_STR_LEN) #: Serial number serial_num = Char(length=SERIAL_NUM_LEN) #: Datapath description dp_desc = Char(length=DESC_STR_LEN) def __init__(self, mfr_desc=None, hw_desc=None, sw_desc=None, serial_num=None, dp_desc=None): """Create a Desc with the optional parameters below. Args: mfr_desc (str): Manufacturer description hw_desc (str): Hardware description sw_desc (str): Software description serial_num (str): Serial number dp_desc (str): Datapath description """ super().__init__() self.mfr_desc = mfr_desc self.hw_desc = hw_desc self.sw_desc = sw_desc self.serial_num = serial_num self.dp_desc = dp_desc class FlowStats(GenericStruct): """Body of reply to OFPST_FLOW request.""" length = UBInt16() table_id = UBInt8() #: Align to 32 bits. pad = Pad(1) duration_sec = UBInt32() duration_nsec = UBInt32() priority = UBInt16() idle_timeout = UBInt16() hard_timeout = UBInt16() flags = UBInt16() #: Align to 64-bits pad2 = Pad(4) cookie = UBInt64() packet_count = UBInt64() byte_count = UBInt64() match = Match() instructions = ListOfInstruction() def __init__(self, length=None, table_id=None, duration_sec=None, duration_nsec=None, priority=None, idle_timeout=None, hard_timeout=None, flags=None, cookie=None, packet_count=None, byte_count=None, match=None, instructions=None): """Create a FlowStats with the optional parameters below. Args: length (int): Length of this entry. table_id (int): ID of table flow came from. duration_sec (int): Time flow has been alive in seconds. duration_nsec (int): Time flow has been alive in nanoseconds in addition to duration_sec. priority (int): Priority of the entry. Only meaningful when this is not an exact-match entry. idle_timeout (int): Number of seconds idle before expiration. hard_timeout (int): Number of seconds before expiration. cookie (int): Opaque controller-issued identifier. packet_count (int): Number of packets in flow. byte_count (int): Number of bytes in flow. match (~pyof.v0x04.common.flow_match.Match): Description of fields. """ super().__init__() self.length = length self.table_id = table_id self.duration_sec = duration_sec self.duration_nsec = duration_nsec self.priority = priority self.idle_timeout = idle_timeout self.hard_timeout = hard_timeout self.flags = flags self.cookie = cookie self.packet_count = packet_count self.byte_count = byte_count self.match = match self.instructions = instructions or [] def unpack(self, buff, offset=0): """Unpack a binary message into this object's attributes. Pass the correct length for list unpacking. Args: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. """ unpack_length = UBInt16() unpack_length.unpack(buff, offset) super().unpack(buff[:offset+unpack_length], offset) class PortStats(GenericStruct): """Body of reply to OFPST_PORT request. If a counter is unsupported, set the field to all ones. """ port_no = UBInt32() #: Align to 64-bits. pad = Pad(4) rx_packets = UBInt64() tx_packets = UBInt64() rx_bytes = UBInt64() tx_bytes = UBInt64() rx_dropped = UBInt64() tx_dropped = UBInt64() rx_errors = UBInt64() tx_errors = UBInt64() rx_frame_err = UBInt64() rx_over_err = UBInt64() rx_crc_err = UBInt64() collisions = UBInt64() duration_sec = UBInt32() duration_nsec = UBInt32() def __init__(self, port_no=None, rx_packets=None, tx_packets=None, rx_bytes=None, tx_bytes=None, rx_dropped=None, tx_dropped=None, rx_errors=None, tx_errors=None, rx_frame_err=None, rx_over_err=None, rx_crc_err=None, collisions=None, duration_sec=None, duration_nsec=None): """Create a PortStats with the optional parameters below. Args: port_no (:class:`int`, :class:`~pyof.v0x04.common.port.Port`): Port number. rx_packets (int): Number of received packets. tx_packets (int): Number of transmitted packets. rx_bytes (int): Number of received bytes. tx_bytes (int): Number of transmitted bytes. rx_dropped (int): Number of packets dropped by RX. tx_dropped (int): Number of packets dropped by TX. rx_errors (int): Number of receive errors. This is a super-set of more specific receive errors and should be greater than or equal to the sum of all rx_*_err values. tx_errors (int): Number of transmit errors. This is a super-set of more specific transmit errors and should be greater than or equal to the sum of all tx_*_err values (none currently defined). rx_frame_err (int): Number of frame alignment errors. rx_over_err (int): Number of packets with RX overrun. rx_crc_err (int): Number of CRC errors. collisions (int): Number of collisions. duration_sec (int): Time port has been alive in seconds duration_nsec (int): Time port has been alive in nanoseconds beyond duration_sec """ super().__init__() self.port_no = port_no self.rx_packets = rx_packets self.tx_packets = tx_packets self.rx_bytes = rx_bytes self.tx_bytes = tx_bytes self.rx_dropped = rx_dropped self.tx_dropped = tx_dropped self.rx_errors = rx_errors self.tx_errors = tx_errors self.rx_frame_err = rx_frame_err self.rx_over_err = rx_over_err self.rx_crc_err = rx_crc_err self.collisions = collisions self.duration_sec = duration_sec self.duration_nsec = duration_nsec class QueueStats(GenericStruct): """Implements the reply body of a port_no.""" port_no = UBInt32() queue_id = UBInt32() tx_bytes = UBInt64() tx_packets = UBInt64() tx_errors = UBInt64() duration_sec = UBInt32() duration_nsec = UBInt32() def __init__(self, port_no=None, queue_id=None, tx_bytes=None, tx_packets=None, tx_errors=None, duration_sec=None, duration_nsec=None): """Create a QueueStats with the optional parameters below. Args: port_no (:class:`int`, :class:`~pyof.v0x04.common.port.Port`): Port Number. queue_id (int): Queue ID. tx_bytes (int): Number of transmitted bytes. tx_packets (int): Number of transmitted packets. tx_errors (int): Number of packets dropped due to overrun. duration_sec (int): Time queue has been alive in seconds. duration_nsec (int): Time queue has been alive in nanoseconds beyond duration_sec. """ super().__init__() self.port_no = port_no self.queue_id = queue_id self.tx_bytes = tx_bytes self.tx_packets = tx_packets self.tx_errors = tx_errors self.duration_sec = duration_sec self.duration_nsec = duration_nsec class GroupDescStats(GenericStruct): """Body of reply to OFPMP_GROUP_DESC request.""" length = UBInt16() group_type = UBInt8() #: Pad to 64 bits. pad = Pad(1) group_id = UBInt32() buckets = FixedTypeList(Bucket) def __init__(self, length=None, group_type=None, group_id=None, buckets=None): """Create a GroupDescStats with the optional parameters below. Args: length (int): Length of this entry. group_type (|GroupType_v0x04|): One of OFPGT_*. group_id (int): Group identifier. buckets (|ListOfBuckets_v0x04|): List of buckets in group. """ super().__init__() self.length = length self.group_type = group_type self.group_id = group_id self.buckets = buckets class GroupFeatures(GenericStruct): """Body of reply to OFPMP_GROUP_FEATURES request.Group features.""" types = UBInt32() capabilities = UBInt32(enum_ref=GroupCapabilities) max_groups1 = UBInt32() max_groups2 = UBInt32() max_groups3 = UBInt32() max_groups4 = UBInt32() actions1 = UBInt32() actions2 = UBInt32() actions3 = UBInt32() actions4 = UBInt32() def __init__(self, types=None, capabilities=None, max_groups1=None, max_groups2=None, max_groups3=None, max_groups4=None, actions1=None, actions2=None, actions3=None, actions4=None): """Create a GroupFeatures with the optional parameters below. Args: types: Bitmap of OFPGT_* values supported. capabilities: Bitmap of OFPGFC_* capability supported. max_groups: 4-position array; Maximum number of groups for each type. actions: 4-position array; Bitmaps of OFPAT_* that are supported. """ super().__init__() self.types = types self.capabilities = capabilities self.max_groups1 = max_groups1 self.max_groups2 = max_groups2 self.max_groups3 = max_groups3 self.max_groups4 = max_groups4 self.actions1 = actions1 self.actions2 = actions2 self.actions3 = actions3 self.actions4 = actions4 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 = ListOfBucketCounter() 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 MeterConfig(GenericStruct): """MeterConfig is a class to represent ofp_meter_config structure. Body of reply to OFPMP_METER_CONFIG request. """ # Length of this entry. length = UBInt16() # All OFPMC_* that apply. flags = UBInt16(enum_ref=MeterFlags) # Meter instance or OFPM_ALL . meter_id = UBInt32() # The bands length is inferred from the length field. bands = ListOfMeterBandHeader() def __init__(self, flags=MeterFlags.OFPMF_STATS, meter_id=None, bands=None): """Create a MeterConfig with the optional parameters below. Args: flags (|MeterFlags_v0x04|): Meter configuration flags.The default value is MeterFlags.OFPMF_STATS meter_id (|Meter_v0x04|): Meter Indentify.The value Meter.OFPM_ALL is used to refer to all Meters on the switch. bands(list): List of MeterBandHeader instances. """ super().__init__() self.flags = flags self.meter_id = meter_id self.bands = bands if bands else [] class MeterFeatures(GenericStruct): """Body of reply to OFPMP_METER_FEATURES request. Meter features.""" max_meter = UBInt32() band_types = UBInt32(enum_ref=MeterBandType) capabilities = UBInt32(enum_ref=MeterFlags) max_bands = UBInt8() max_color = UBInt8() pad = Pad(2) def __init__(self, max_meter=None, band_types=None, capabilities=None, max_bands=None, max_color=None): """Create a MeterFeatures with the optional parameters below. Args: max_meter(int): Maximum number of meters. band_types (|MeterBandType_v0x04|): Bitmaps of OFPMBT_* values supported. capabilities (|MeterFlags_v0x04|): Bitmaps of "ofp_meter_flags". max_bands(int): Maximum bands per meters max_color(int): Maximum color value """ super().__init__() self.max_meter = max_meter self.band_types = band_types self.capabilities = capabilities self.max_bands = max_bands self.max_color = max_color class BandStats(GenericStruct): """Band Statistics. Statistics for each meter band. """ packet_band_count = UBInt64() byte_band_count = UBInt64() def __init__(self, packet_band_count=None, byte_band_count=None): """Create a BandStats with the optional parameters below. Args: packet_band_count(int): Number of packets in band. byte_band_count(int): Number of bytes in band. """ super().__init__() self.packet_band_count = packet_band_count self.byte_band_count = byte_band_count class ListOfBandStats(FixedTypeList): """List of BandStats. Represented by instances of BandStats. """ def __init__(self, items=None): """Create a ListOfBandStats with the optional parameters below. Args: items (|BandStats_v0x04|): Instance or a list of instances. """ super().__init__(pyof_class=BandStats, items=items) class MeterStats(GenericStruct): """Meter Statistics. Body of reply to OFPMP_METER request. """ meter_id = UBInt32() length = UBInt16() pad = Pad(6) flow_count = UBInt32() packet_in_count = UBInt64() byte_in_count = UBInt64() duration_sec = UBInt32() duration_nsec = UBInt32() band_stats = ListOfBandStats() def __init__(self, meter_id=None, flow_count=None, packet_in_count=None, byte_in_count=None, duration_sec=None, duration_nsec=None, band_stats=None): """Create a MeterStats with the optional parameters below. Args: meter_id (|Meter_v0x04|): Meter instance. flow_count(int): Number of flows bound to meter. packet_in_count(int): Number of packets in input. byte_in_count(int): Number of bytes in input. duration_sec(int): Time meter has been alive in seconds. duration_nsec(int): Time meter has been alive in nanoseconds beyond duration_sec. band_stats(list): Instances of BandStats """ super().__init__() self.meter_id = meter_id self.flow_count = flow_count self.packet_in_count = packet_in_count self.byte_in_count = byte_in_count self.duration_sec = duration_sec self.duration_nsec = duration_nsec self.band_stats = band_stats if band_stats else [] self.update_length() def update_length(self): """Update length attribute with current struct length.""" self.length = self.get_size() def pack(self, value=None): """Pack method used to update the length of instance and packing. Args: value: Structure to be packed. """ self.update_length() return super().pack(value) 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) length.unpack(buff, offset=offset+MeterStats.meter_id.get_size()) super().unpack(buff[:offset+length.value], offset=offset) class TableStats(GenericStruct): """Body of reply to OFPST_TABLE request.""" table_id = UBInt8() #: Align to 32-bits. pad = Pad(3) active_count = UBInt32() lookup_count = UBInt64() matched_count = UBInt64() def __init__(self, table_id=None, active_count=None, lookup_count=None, matched_count=None): """Create a TableStats with the optional parameters below. Args: table_id (int): Identifier of table. Lower numbered tables are consulted first. active_count (int): Number of active entries. lookup_count (int): Number of packets looked up in table. matched_count (int): Number of packets that hit table. """ super().__init__() self.table_id = table_id self.active_count = active_count self.lookup_count = lookup_count self.matched_count = matched_count python-openflow-2019.2/pyof/v0x04/controller2switch/flow_mod.py0000644000175100001630000001103713577157232025302 0ustar runnerdocker00000000000000"""Modifications to the flow table from the controller.""" # System imports from enum import IntEnum # Local source tree imports from pyof.foundation.base import GenericBitMask, GenericMessage from pyof.foundation.basic_types import Pad, UBInt8, UBInt16, UBInt32, UBInt64 from pyof.v0x04.common.constants import OFP_NO_BUFFER from pyof.v0x04.common.flow_instructions import ListOfInstruction from pyof.v0x04.common.flow_match import Match from pyof.v0x04.common.header import Header, Type from pyof.v0x04.common.port import PortNo from pyof.v0x04.controller2switch.group_mod import Group __all__ = ('FlowMod', 'FlowModCommand', 'FlowModFlags') # Enums class FlowModCommand(IntEnum): """List the possible commands for a flow.""" #: New flow OFPFC_ADD = 0 #: Modify all matching flows OFPFC_MODIFY = 1 #: Modify entry strictly matching wildcards and priority. OFPFC_MODIFY_STRICT = 2 #: Delete all matching flows OFPFC_DELETE = 3 #: Strictly match wildcards and priority OFPFC_DELETE_STRICT = 4 class FlowModFlags(GenericBitMask): """Types to be used in Flags field.""" #: Send flow removed message when flow expires or is deleted OFPFF_SEND_FLOW_REM = 1 << 0 #: Check for overlapping entries first. OFPFF_CHECK_OVERLAP = 1 << 1 #: Reset flow packet and byte counts. OFPFF_RESET_COUNTS = 1 << 2 #: Don’t keep track of packet count. OFPFF_NO_PKT_COUNTS = 1 << 3 #: Don’t keep track of byte count. OFPFF_NO_BYT_COUNTS = 1 << 4 # Classes class FlowMod(GenericMessage): """Modifies the flow table from the controller.""" header = Header(message_type=Type.OFPT_FLOW_MOD) cookie = UBInt64() cookie_mask = UBInt64() # Flow actions table_id = UBInt8() command = UBInt8(enum_ref=FlowModCommand) idle_timeout = UBInt16() hard_timeout = UBInt16() priority = UBInt16() buffer_id = UBInt32() out_port = UBInt32() out_group = UBInt32() flags = UBInt16(enum_ref=FlowModFlags) pad = Pad(2) match = Match() instructions = ListOfInstruction() def __init__(self, xid=None, cookie=0, cookie_mask=0, table_id=0, command=None, idle_timeout=0, hard_timeout=0, priority=0, buffer_id=OFP_NO_BUFFER, out_port=PortNo.OFPP_ANY, out_group=Group.OFPG_ANY, flags=FlowModFlags.OFPFF_SEND_FLOW_REM, match=None, instructions=None): """Create a FlowMod with the optional parameters below. Args: xid (int): xid to be used on the message header. cookie (int): Opaque controller-issued identifier. cookie_mask (int): Mask used to restrict the cookie bits that must match when the command is OFPFC_MODIFY* or OFPFC_DELETE*. A value of 0 indicates no restriction. table_id (int): ID of the table to put the flow in. For OFPFC_DELETE_* commands, OFPTT_ALL can also be used to delete matching flows from all tables. command (~pyof.v0x04.controller2switch.flow_mod.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_id (int): Buffered packet to apply to, or OFP_NO_BUFFER. Not meaningful for OFPFC_DELETE*. out_port (int): For OFPFC_DELETE* commands, require matching entries to include this as an output port. A value of OFPP_ANY indicates no restriction. out_group (int): For OFPFC_DELETE* commands, require matching entries to include this as an output group. A value of OFPG_ANY indicates no restriction. flags (~pyof.v0x04.controller2switch.flow_mod.FlowModFlags): One of OFPFF_*. match (~pyof.v0x04.common.flow_match.Match): Fields to match. Variable size. """ super().__init__(xid) self.cookie = cookie self.cookie_mask = cookie_mask self.table_id = table_id 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.out_group = out_group self.flags = flags self.match = Match() if match is None else match self.instructions = instructions or ListOfInstruction() python-openflow-2019.2/pyof/v0x04/controller2switch/get_config_reply.py0000644000175100001630000000153113577157232027011 0ustar runnerdocker00000000000000"""Defines Get Config Reply message.""" # System imports # Third-party imports from pyof.v0x04.common.header import Type from pyof.v0x04.controller2switch.common import SwitchConfig __all__ = ('GetConfigReply',) class GetConfigReply(SwitchConfig): """Get Config Reply message.""" def __init__(self, xid=None, flags=None, miss_send_len=None): """Create a GetConfigReply with the optional parameters below. Args: xid (int): xid to be used on the message header. flags (~pyof.v0x04.controller2switch.common.ConfigFlag): OFPC_* flags. miss_send_len (int): UBInt16 max bytes of new flow that the datapath should send to the controller. """ super().__init__(xid, flags, miss_send_len) self.header.message_type = Type.OFPT_GET_CONFIG_REPLY python-openflow-2019.2/pyof/v0x04/controller2switch/common.py0000644000175100001630000005557113577157232024777 0ustar runnerdocker00000000000000"""Defines common structures and enums for controller2switch.""" # System imports from enum import IntEnum from pyof.foundation.base import GenericMessage, GenericStruct from pyof.foundation.basic_types import ( Char, FixedTypeList, Pad, UBInt8, UBInt16, UBInt32, UBInt64) from pyof.foundation.constants import OFP_MAX_TABLE_NAME_LEN from pyof.v0x04.asynchronous.flow_removed import FlowRemovedReason from pyof.v0x04.asynchronous.packet_in import PacketInReason from pyof.v0x04.asynchronous.port_status import PortReason # Local source tree imports from pyof.v0x04.common.action import ( ActionHeader, ControllerMaxLen, ListOfActions) from pyof.v0x04.common.flow_instructions import ListOfInstruction from pyof.v0x04.common.flow_match import ListOfOxmHeader from pyof.v0x04.common.header import Header from pyof.v0x04.controller2switch.table_mod import Table __all__ = ('ConfigFlag', 'ControllerRole', 'TableFeaturePropType', 'MultipartType', 'Bucket', 'BucketCounter', 'ListOfBucketCounter', 'ExperimenterMultipartHeader', 'Property', 'InstructionsProperty', 'NextTablesProperty', 'ActionsProperty', 'OxmProperty', 'ListOfProperty', 'TableFeatures') # Enum class ConfigFlag(IntEnum): """Handling of IP fragments.""" #: No special handling for fragments. OFPC_FRAG_NORMAL = 0 #: Drop fragments. OFPC_FRAG_DROP = 1 #: Reassemble (only if OFPC_IP_REASM set). OFPC_FRAG_REASM = 2 OFPC_FRAG_MASK = 3 class ControllerRole(IntEnum): """Controller roles.""" #: Don’t change current role. OFPCR_ROLE_NOCHANGE = 0 #: Default role, full access. OFPCR_ROLE_EQUAL = 1 #: Full access, at most one master. OFPCR_ROLE_MASTER = 2 #: Read-only access. OFPCR_ROLE_SLAVE = 3 class TableFeaturePropType(IntEnum): """Table Property Types. Low order bit cleared indicates a property for a regular Flow Entry. Low order bit set indicates a property for the Table-Miss Flow Entry. """ # Instructions property. OFPTFPT_INSTRUCTIONS = 0 # Instructions for table-miss. OFPTFPT_INSTRUCTIONS_MISS = 1 # Next Table property. OFPTFPT_NEXT_TABLES = 2 # Next Table for table-miss. OFPTFPT_NEXT_TABLES_MISS = 3 # Write Actions property. OFPTFPT_WRITE_ACTIONS = 4 # Write Actions for table-miss. OFPTFPT_WRITE_ACTIONS_MISS = 5 # Apply Actions property. OFPTFPT_APPLY_ACTIONS = 6 # Apply Actions for table-miss. OFPTFPT_APPLY_ACTIONS_MISS = 7 # Match property. OFPTFPT_MATCH = 8 # Wildcards property. OFPTFPT_WILDCARDS = 10 # Write Set-Field property. OFPTFPT_WRITE_SETFIELD = 12 # Write Set-Field for table-miss. OFPTFPT_WRITE_SETFIELD_MISS = 13 # Apply Set-Field property. OFPTFPT_APPLY_SETFIELD = 14 # Apply Set-Field for table-miss. OFPTFPT_APPLY_SETFIELD_MISS = 15 # Experimenter property. OFPTFPT_EXPERIMENTER = 0xFFFE # Experimenter for table-miss. OFPTFPT_EXPERIMENTER_MISS = 0xFFFF def find_class(self): """Return a class related with this type.""" if self.value <= 1: return InstructionsProperty elif self.value <= 3: return NextTablesProperty elif self.value <= 7: return ActionsProperty return OxmProperty class MultipartType(IntEnum): """Types of Multipart Messages, both Request and Reply.""" #: Description of this OpenFlow switch. #: The request body is empty. #: The reply body is struct ofp_desc. OFPMP_DESC = 0 #: Individual flow statistics. #: The request body is struct ofp_flow_stats_request. #: The reply body is an array of struct ofp_flow_stats. OFPMP_FLOW = 1 #: Aggregate flow statistics. #: The request body is struct ofp_aggregate_stats_request. #: The reply body is struct ofp_aggregate_stats_reply. OFPMP_AGGREGATE = 2 #: Flow table statistics. #: The request body is empty. #: The reply body is an array of struct ofp_table_stats. OFPMP_TABLE = 3 #: Port statistics. #: The request body is struct ofp_port_stats_request. #: The reply body is an array of struct ofp_port_stats. OFPMP_PORT_STATS = 4 #: Queue statistics for a port. #: The request body is struct ofp_queue_stats_request. #: The reply body is an array of struct ofp_queue_stats. OFPMP_QUEUE = 5 #: Group counter statistics. #: The request body is struct ofp_group_stats_request. #: The reply is an array of struct ofp_group_stats. OFPMP_GROUP = 6 #: Group description. #: The request body is empty. #: The reply body is an array of struct ofp_group_desc_stats. OFPMP_GROUP_DESC = 7 #: Group features. #: The request body is empty. #: The reply body is struct ofp_group_features. OFPMP_GROUP_FEATURES = 8 #: Meter statistics. #: The request body is struct ofp_meter_multipart_requests. #: The reply body is an array of struct ofp_meter_stats. OFPMP_METER = 9 #: Meter configuration. #: The request body is struct ofp_meter_multipart_requests. #: The reply body is an array of struct ofp_meter_config. OFPMP_METER_CONFIG = 10 #: Meter features. #: The request body is empty. #: The reply body is struct ofp_meter_features. OFPMP_METER_FEATURES = 11 #: Table features. #: The request body is either empty or contains an array of #: struct ofp_table_features containing the controller’s desired view of #: the switch. If the switch is unable to set the specified view an error #: is returned. #: The reply body is an array of struct ofp_table_features. OFPMP_TABLE_FEATURES = 12 #: Port description. #: The request body is empty. #: The reply body is an array of struct ofp_port. OFPMP_PORT_DESC = 13 #: Experimenter extension. #: The request and reply bodies begin with #: struct ofp_experimenter_multipart_header. #: The request and reply bodies are otherwise experimenter-defined. OFPMP_EXPERIMENTER = 0xffff # Classes 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 def unpack(self, buff, offset=0): """Unpack bucket. Bucket has a dynamic content with length as first field. The length is needed to compute the total buffer offset. """ length = UBInt16() length.unpack(buff, offset=offset) super().unpack(buff[:offset + length.value], offset=offset) def get_size(self, value=None): """ Return the Bucket length. If the object length is None, returns the minimum size. """ if self.length is None: return super().get_size() return self.length class BucketCounter(GenericStruct): """Used in group stats replies.""" #: Number of packets processed by bucket. packet_count = UBInt64() #: Number of bytes processed by bucket. byte_count = UBInt64() def __init__(self, packet_count=None, byte_count=None): """Create BucketCounter with the optional parameters below. Args: packet_count (int): Number of packets processed by bucket. byte_count (int): Number of bytes processed by bucket. """ super().__init__() self.packet_count = packet_count self.byte_count = byte_count class ListOfBucketCounter(FixedTypeList): """List of BucketCounter. Represented by instances of BucketCounter. """ def __init__(self, items=None): """Create a ListOfBucketCounter with the optional parameters below. Args: items (|BucketCounter_v0x04|): Instance or a list of instances. """ super().__init__(pyof_class=BucketCounter, items=items) # Base Classes for other messages - not meant to be directly used, so, because # of that, they will not be inserted on the __all__ attribute. 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 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 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 # Multipart body class ExperimenterMultipartHeader(GenericStruct): """Body for ofp_multipart_request/reply of type OFPMP_EXPERIMENTER.""" experimenter = UBInt32() exp_type = UBInt32() #: Followed by experimenter-defined arbitrary data. def __init__(self, experimenter=None, exp_type=None): """Create a ExperimenterMultipartHeader with the parameters below. Args: experimenter: Experimenter ID which takes the same form as in struct ofp_experimenter_header ( :class:`~pyof.v0x04.symmetric.experimenter.ExperimenterHeader`) exp_type: Experimenter defined. """ super().__init__() self.experimenter = experimenter self.exp_type = exp_type class Property(GenericStruct): """Table Property class. This class represents a Table Property generic structure. """ property_type = UBInt16(enum_ref=TableFeaturePropType) length = UBInt16(4) def __init__(self, property_type=None): """Create a Property with the optional parameters below. Args: type(|TableFeaturePropType_v0x04|): Property Type value of this instance. """ super().__init__() self.property_type = property_type def pack(self, value=None): """Pack method used to update the length of instance and packing. Args: value: Structure to be packed. """ self.update_length() return super().pack(value) 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. """ property_type = UBInt16(enum_ref=TableFeaturePropType) property_type.unpack(buff, offset) self.__class__ = TableFeaturePropType(property_type.value).find_class() length = UBInt16() length.unpack(buff, offset=offset+2) super().unpack(buff[:offset+length.value], offset=offset) def update_length(self): """Update the length of current instance.""" self.length = self.get_size() class InstructionsProperty(Property): """Instructions property. This class represents Property with the following types: OFPTFPT_INSTRUCTIONS OFPTFPT_INSTRUCTIONS_MISS """ instruction_ids = ListOfInstruction() def __init__(self, property_type=TableFeaturePropType.OFPTFPT_INSTRUCTIONS, instruction_ids=None): """Create a InstructionsProperty with the optional parameters below. Args: type(|TableFeaturePropType_v0x04|): Property Type value of this instance. next_table_ids(|ListOfInstruction_v0x04|): List of InstructionGotoTable instances. """ super().__init__(property_type=property_type) self.instruction_ids = instruction_ids if instruction_ids else [] self.update_length() class NextTablesProperty(Property): """Next Tables Property. This class represents Property with the following types: OFPTFPT_NEXT_TABLES OFPTFPT_NEXT_TABLES_MISS """ next_table_ids = ListOfInstruction() def __init__(self, property_type=TableFeaturePropType.OFPTFPT_NEXT_TABLES, next_table_ids=None): """Create a NextTablesProperty with the optional parameters below. Args: type(|TableFeaturePropType_v0x04|): Property Type value of this instance. next_table_ids (|ListOfInstruction_v0x04|): List of InstructionGotoTable instances. """ super().__init__(property_type) self.next_table_ids = (ListOfInstruction() if next_table_ids is None else next_table_ids) self.update_length() class ActionsProperty(Property): """Actions Property. This class represents Property with the following type: OFPTFPT_WRITE_ACTIONS OFPTFPT_WRITE_ACTIONS_MISS OFPTFPT_APPLY_ACTIONS OFPTFPT_APPLY_ACTIONS_MISS """ action_ids = ListOfActions() def __init__(self, property_type=TableFeaturePropType.OFPTFPT_WRITE_ACTIONS, action_ids=None): """Create a ActionsProperty with the optional parameters below. Args: type(|TableFeaturePropType_v0x04|): Property Type value of this instance. action_ids(|ListOfActions_v0x04|): List of Action instances. """ super().__init__(property_type) self.action_ids = action_ids if action_ids else ListOfActions() self.update_length() class OxmProperty(Property): """Match, Wildcard or Set-Field property. This class represents Property with the following types: OFPTFPT_MATCH OFPTFPT_WILDCARDS OFPTFPT_WRITE_SETFIELD OFPTFPT_WRITE_SETFIELD_MISS OFPTFPT_APPLY_SETFIELD OFPTFPT_APPLY_SETFIELD_MISS """ oxm_ids = ListOfOxmHeader() def __init__(self, property_type=TableFeaturePropType.OFPTFPT_MATCH, oxm_ids=None): """Create an OxmProperty with the optional parameters below. Args: type(|TableFeaturePropType_v0x04|): Property Type value of this instance. oxm_ids(|ListOfOxmHeader_v0x04|): List of OxmHeader instances. """ super().__init__(property_type) self.oxm_ids = ListOfOxmHeader() if oxm_ids is None else oxm_ids self.update_length() class ListOfProperty(FixedTypeList): """List of Table Property. Represented by instances of Property. """ def __init__(self, items=None): """Create a ListOfProperty with the optional parameters below. Args: items (|Property_v0x04|): Instance or a list of instances. """ super().__init__(pyof_class=Property, items=items) class TableFeatures(GenericStruct): """Abstration of common class Table Features. Body for MultipartRequest of type OFPMP_TABLE_FEATURES. Body of reply to OFPMP_TABLE_FEATURES request. """ length = UBInt16() # /* Identifier of table. Lower numbered tables are consulted first. */ table_id = UBInt8() # /* Align to 64-bits. */ pad = Pad(5) name = Char(length=OFP_MAX_TABLE_NAME_LEN) # /* Bits of metadata table can match. */ metadata_match = UBInt64() # /* Bits of metadata table can write. */ metadata_write = UBInt64() # /* Bitmap of OFPTC_* values */ config = UBInt32() # /* Max number of entries supported. */ max_entries = UBInt32() # /* Table Feature Property list */ properties = ListOfProperty() def __init__(self, table_id=Table.OFPTT_ALL, name="", metadata_match=0xFFFFFFFFFFFFFFFF, metadata_write=0xFFFFFFFFFFFFFFFF, config=0, max_entries=0, properties=None): """Create a TableFeatures with the optional parameters below. Args: table_id(int): Indetifier of table.The default value OFPTT_ALL(``0xff``) will apply the configuration to all tables in the switch. name(Char): Characters representing the table name. metadata_match(int): Indicate the bits of the metadata field that the table can match on.The default value ``0xFFFFFFFFFFFFFFFF`` indicates that the table can match the full metadata field. metadata_write(int): Indicates the bits of the metadata field that the table can write using the OFPIT_WRITE_METADATA instruction. The default value ``0xFFFFFFFFFFFFFFFF`` indicates that the table can write the full metadata field. config(int): Field reseved for future use. max_entries(int): Describe the maximum number of flow entries that can be inserted into that table. properties(~pyof.v0x04.controller2switch.common.ListOfProperty): List of Property intances. """ super().__init__() self.table_id = table_id self.name = name self.metadata_match = metadata_match self.metadata_write = metadata_write self.config = config self.max_entries = max_entries self.properties = (ListOfProperty() if properties is None else properties) self.update_length() def pack(self, value=None): """Pack method used to update the length of instance and packing. Args: value: Structure to be packed. """ self.update_length() return super().pack(value) def update_length(self): """Update the length of current instance.""" self.length = self.get_size() 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) python-openflow-2019.2/pyof/v0x04/controller2switch/table_mod.py0000644000175100001630000000246413577157232025426 0ustar runnerdocker00000000000000"""Flow Table Modification message.""" from enum import IntEnum from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import Pad, UBInt8, UBInt32 from pyof.v0x04.common.header import Header, Type __all__ = ('Table', 'TableMod') class Table(IntEnum): """Table numbering. Tables can use any number up to OFPT_MAX.""" #: Last usable table number. OFPTT_MAX = 0xfe # Fake tables. #: Wildcard table used for table config, flow stats and flow deletes. OFPTT_ALL = 0xff 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=Table.OFPTT_ALL, config=3): """Assing parameters to object attributes. Args: xid (int): :class:`~pyof.v0x04.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 python-openflow-2019.2/pyof/v0x04/controller2switch/set_async.py0000644000175100001630000000323513577157232025465 0ustar runnerdocker00000000000000"""Define SetAsync message. Sets whether a controller should receive a given asynchronous message that is generated by the switch. """ # System imports # Third-party imports # Local imports from pyof.v0x04.common.header import Type from pyof.v0x04.controller2switch.common import AsyncConfig __all__ = ('SetAsync',) class SetAsync(AsyncConfig): """SetAsync message. Sets whether a controller should receive a given asynchronous message that is generated by the switch. """ 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): """Set attributes for SetAsync message. Args: xid (int): xid to be used on the message header. packet_in_mask1 (|PacketInReason_v0x04|): A instance of PacketInReason packet_in_mask2 (|PacketInReason_v0x04|): A instance of PacketInReason port_status_mask1 (|PortReason_v0x04|): A instance of PortReason port_status_mask2 (|PortReason_v0x04|): A instance of PortReason flow_removed_mask1 (|FlowRemoved_v0x04|): A instance of FlowRemoved. flow_removed_mask2 (|FlowRemoved_v0x04|): A instance of FlowRemoved. """ super().__init__(xid, packet_in_mask1, packet_in_mask2, port_status_mask1, port_status_mask2, flow_removed_mask1, flow_removed_mask2) self.header.message_type = Type.OFPT_SET_ASYNC python-openflow-2019.2/pyof/v0x04/controller2switch/meter_mod.py0000644000175100001630000001435513577157232025455 0ustar runnerdocker00000000000000"""Meter modification message.""" from enum import IntEnum from pyof.foundation.base import GenericBitMask, GenericMessage from pyof.foundation.basic_types import ( FixedTypeList, GenericStruct, Pad, UBInt8, UBInt16, UBInt32) from pyof.v0x04.common.header import Header, Type __all__ = ('MeterMod', 'Meter', 'MeterModCommand', 'MeterFlags', 'MeterBandHeader', 'MeterBandType', 'MeterBandDrop', 'MeterBandDscpRemark', 'MeterBandExperimenter') class Meter(IntEnum): """Meter numbering. Flow meters can use any number up to OFPM_MAX.""" #: Last usable meter. OFPM_MAX = 0xffff0000 # Virtual meters. #: Meter for slow datapath, if any. OFPM_SLOWPATH = 0xfffffffd #: Meter for controller connection. OFPM_CONTROLLER = 0xfffffffe #: Represents all meters for stat requests commands. OFPM_ALL = 0xffffffff class MeterModCommand(IntEnum): """Meter commands.""" #: New meter. OFPMC_ADD = 0 #: Modify specified meter. OFPMC_MODIFY = 1 #: Delete specified meter. OFPMC_DELETE = 2 class MeterFlags(GenericBitMask): """Meter configuration flags.""" #: Rate value in kb/s (kilo-bit per second). OFPMF_KBPS = 1 << 0 #: Rate value in packet/sec. OFPMF_PKTPS = 1 << 1 #: Do burst size. OFPMF_BURST = 1 << 2 #: Collect statistics. OFPMF_STATS = 1 << 3 class MeterBandType(IntEnum): """Meter band types.""" #: Drop packet. OFPMBT_DROP = 1 #: Remark DSCP in the IP header. OFPMBT_DSCP_REMARK = 2 #: Experimenter meter band. OFPMBT_EXPERIMENTER = 0xFFFF def find_class(self): """Return a class related with this type.""" types = {1: MeterBandDrop, 2: MeterBandDscpRemark, 3: MeterBandExperimenter} return types[self.value] class MeterBandHeader(GenericStruct): """Common header for all meter bands.""" band_type = UBInt16(enum_ref=MeterBandType) length = UBInt16() rate = UBInt32() burst_size = UBInt32() def __init__(self, band_type=None, rate=None, burst_size=None): """Create a MeterBandHeader with the optional parameters below. Args: band_type (MeterBandType): One of OFPMBT_*. rate (int): Rate for this band. burst_size (int): Size of bursts. """ super().__init__() self.band_type = band_type self.rate = rate self.burst_size = burst_size self.update_length() def update_length(self): """Update the length attribute of current instance.""" self.length = self.get_size() 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 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): """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 MeterBandDrop(MeterBandHeader): """OFPMBT_DROP band - drop packets.""" pad = Pad(4) def __init__(self, rate=None, burst_size=None): """Create a MeterBandDrop with the optional parameters below. Args: rate (int): Rate for dropping packets. burst_size (int): Size of bursts. """ super().__init__(MeterBandType.OFPMBT_DROP, rate, burst_size) class MeterBandDscpRemark(MeterBandHeader): """OFPMBT_DSCP_REMARK band - Remark DSCP in the IP header.""" prec_level = UBInt8() pad = Pad(3) def __init__(self, rate=None, burst_size=None, prec_level=None): """Create a MeterBandDscpRemark with the optional parameters below. Args: rate (int): Rate for remarking packets. burst_size (int): Size of bursts. prec_level (int): Number of precendence level to substract. """ super().__init__(MeterBandType.OFPMBT_DSCP_REMARK, rate, burst_size) self.prec_level = prec_level class MeterBandExperimenter(MeterBandHeader): """OFPMBT_EXPERIMENTER band - Write actions in action set.""" experimenter = UBInt32() def __init__(self, rate=None, burst_size=None, experimenter=None): """Create a MeterBandExperimenter with the optional parameters below. Args: rate (int): Rate for remarking packets. burst_size (int): Size of bursts. experimenter (int): Experimenter ID which takes the same form as in :class:`.ExperimenterHeader`. """ super().__init__(MeterBandType.OFPMBT_EXPERIMENTER, rate, burst_size) self.experimenter = experimenter class ListOfMeterBandHeader(FixedTypeList): """List of MeterBandHeader. Represented by instances of MeterBandHeader. """ def __init__(self, items=None): """Create a ListOfMeterBandHeader with the optional parameters below. Args: items (MeterBandHeader): Instance or a list of instances. """ super().__init__(pyof_class=MeterBandHeader, items=items) python-openflow-2019.2/pyof/v0x04/controller2switch/get_async_reply.py0000644000175100001630000000307013577157232026661 0ustar runnerdocker00000000000000"""Define GetAsyncReply message. Response to the GetAsyncRequest message. """ # System imports # Third-party imports # Local imports from pyof.v0x04.common.header import Type from pyof.v0x04.controller2switch.common import AsyncConfig __all__ = ('GetAsyncReply',) class GetAsyncReply(AsyncConfig): """GetAsyncReply message. Response to GetAsyncRequest message. """ 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): """Set attributes for GetAsyncReply message. Args: xid (int): xid to be used on the message header. packet_in_mask1 (|PacketInReason_v0x04|): A instance of PacketInReason packet_in_mask2 (|PacketInReason_v0x04|): A instance of PacketInReason port_status_mask1 (|PortReason_v0x04|): A instance of PortReason port_status_mask2 (|PortReason_v0x04|): A instance of PortReason flow_removed_mask1 (|FlowRemoved_v0x04|): A instance of FlowRemoved. flow_removed_mask2 (|FlowRemoved_v0x04|): A instance of FlowRemoved. """ super().__init__(xid, packet_in_mask1, packet_in_mask2, port_status_mask1, port_status_mask2, flow_removed_mask1, flow_removed_mask2) self.header.message_type = Type.OFPT_GET_ASYNC_REPLY python-openflow-2019.2/pyof/v0x04/controller2switch/get_config_request.py0000644000175100001630000000053213577157232027346 0ustar runnerdocker00000000000000"""Defines Get Config Request classes and related items.""" from pyof.foundation.base import GenericMessage from pyof.v0x04.common.header import Header, Type __all__ = ('GetConfigRequest',) # Classe class GetConfigRequest(GenericMessage): """Get Config Request message.""" header = Header(message_type=Type.OFPT_GET_CONFIG_REQUEST) python-openflow-2019.2/pyof/v0x04/controller2switch/get_async_request.py0000644000175100001630000000104713577157232027220 0ustar runnerdocker00000000000000"""Get Async Request Message.""" # System imports # Third-party imports # Local imports from pyof.foundation.base import GenericMessage from pyof.v0x04.common.header import Header, Type __all__ = ('GetAsyncRequest',) 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) python-openflow-2019.2/pyof/v0x04/common/0000755000175100001630000000000013577157243020723 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/v0x04/common/__init__.py0000644000175100001630000000006313577157232023031 0ustar runnerdocker00000000000000"""Common structures used on OpenFlow Protocol.""" python-openflow-2019.2/pyof/v0x04/common/flow_match.py0000644000175100001630000003415413577157232023425 0ustar runnerdocker00000000000000"""Match strucutre and related enums. An OpenFlow match is composed of a flow match header and a sequence of zero or more flow match fields. """ # System imports from enum import Enum, IntEnum from math import ceil # Local source tree imports from pyof.foundation.base import GenericStruct from pyof.foundation.basic_types import ( BinaryData, FixedTypeList, Pad, UBInt8, UBInt16, UBInt32) from pyof.foundation.exceptions import PackException, UnpackException __all__ = ('Ipv6ExtHdrFlags', 'ListOfOxmHeader', 'Match', 'MatchType', 'OxmClass', 'OxmExperimenterHeader', 'OxmMatchFields', 'OxmOfbMatchField', 'OxmTLV', 'VlanId') class Ipv6ExtHdrFlags(Enum): """Bit definitions for IPv6 Extension Header pseudo-field.""" #: "No next header" encountered. OFPIEH_NONEXT = 1 << 0 #: Encrypted Sec Payload header present. OFPIEH_ESP = 1 << 1 #: Authentication header present. OFPIEH_AUTH = 1 << 2 #: 1 or 2 dest headers present. OFPIEH_DEST = 1 << 3 #: Fragment header present. OFPIEH_FRAG = 1 << 4 #: Router header present. OFPIEH_ROUTER = 1 << 5 #: Hop-by-hop header present. OFPIEH_HOP = 1 << 6 #: Unexpected repeats encountered. OFPIEH_UNREP = 1 << 7 #: Unexpected sequencing encountered. OFPIEH_UNSEQ = 1 << 8 class OxmOfbMatchField(IntEnum): """OXM Flow match field types for OpenFlow basic class. A switch is not required to support all match field types, just those listed in the Table 10. Those required match fields don’t need to be implemented in the same table lookup. The controller can query the switch about which other fields it supports. """ #: Switch input port. OFPXMT_OFB_IN_PORT = 0 #: Switch physical input port. OFPXMT_OFB_IN_PHY_PORT = 1 #: Metadata passed between tables. OFPXMT_OFB_METADATA = 2 #: Ethernet destination address. OFPXMT_OFB_ETH_DST = 3 #: Ethernet source address. OFPXMT_OFB_ETH_SRC = 4 #: Ethernet frame type. OFPXMT_OFB_ETH_TYPE = 5 #: VLAN id. OFPXMT_OFB_VLAN_VID = 6 #: VLAN priority. OFPXMT_OFB_VLAN_PCP = 7 #: IP DSCP (6 bits in ToS field). OFPXMT_OFB_IP_DSCP = 8 #: IP ECN (2 bits in ToS field). OFPXMT_OFB_IP_ECN = 9 #: IP protocol. OFPXMT_OFB_IP_PROTO = 10 #: IPv4 source address. OFPXMT_OFB_IPV4_SRC = 11 #: IPv4 destination address. OFPXMT_OFB_IPV4_DST = 12 #: TCP source port. OFPXMT_OFB_TCP_SRC = 13 #: TCP destination port. OFPXMT_OFB_TCP_DST = 14 #: UDP source port. OFPXMT_OFB_UDP_SRC = 15 #: UDP destination port. OFPXMT_OFB_UDP_DST = 16 #: SCTP source port. OFPXMT_OFB_SCTP_SRC = 17 #: SCTP destination port. OFPXMT_OFB_SCTP_DST = 18 #: ICMP type. OFPXMT_OFB_ICMPV4_TYPE = 19 #: ICMP code. OFPXMT_OFB_ICMPV4_CODE = 20 #: ARP opcode. OFPXMT_OFB_ARP_OP = 21 #: ARP source IPv4 address. OFPXMT_OFB_ARP_SPA = 22 #: ARP target IPv4 address. OFPXMT_OFB_ARP_TPA = 23 #: ARP source hardware address. OFPXMT_OFB_ARP_SHA = 24 #: ARP target hardware address. OFPXMT_OFB_ARP_THA = 25 #: IPv6 source address. OFPXMT_OFB_IPV6_SRC = 26 #: IPv6 destination address. OFPXMT_OFB_IPV6_DST = 27 #: IPv6 Flow Label OFPXMT_OFB_IPV6_FLABEL = 28 #: ICMPv6 type. OFPXMT_OFB_ICMPV6_TYPE = 29 #: ICMPv6 code. OFPXMT_OFB_ICMPV6_CODE = 30 #: Target address for ND. OFPXMT_OFB_IPV6_ND_TARGET = 31 #: Source link-layer for ND. OFPXMT_OFB_IPV6_ND_SLL = 32 #: Target link-layer for ND. OFPXMT_OFB_IPV6_ND_TLL = 33 #: MPLS label. OFPXMT_OFB_MPLS_LABEL = 34 #: MPLS TC. OFPXMT_OFB_MPLS_TC = 35 #: MPLS BoS bit. OFPXMT_OFP_MPLS_BOS = 36 #: PBB I-SID. OFPXMT_OFB_PBB_ISID = 37 #: Logical Port Metadata. OFPXMT_OFB_TUNNEL_ID = 38 #: IPv6 Extension Header pseudo-field OFPXMT_OFB_IPV6_EXTHDR = 39 class MatchType(IntEnum): """Indicates the match structure in use. The match type is placed in the type field at the beginning of all match structures. The "OpenFlow Extensible Match" type corresponds to OXM TLV format described below and must be supported by all OpenFlow switches. Extensions that define other match types may be published on the ONF wiki. Support for extensions is optional """ #: Deprecated OFPMT_STANDARD = 0 #: OpenFlow Extensible Match OFPMT_OXM = 1 class OxmClass(IntEnum): """OpenFlow Extensible Match (OXM) Class IDs. The high order bit differentiate reserved classes from member classes. Classes 0x0000 to 0x7FFF are member classes, allocated by ONF. Classes 0x8000 to 0xFFFE are reserved classes, reserved for standardisation. """ #: Backward compatibility with NXM OFPXMC_NXM_0 = 0x0000 #: Backward compatibility with NXM OFPXMC_NXM_1 = 0x0001 #: Basic class for OpenFlow OFPXMC_OPENFLOW_BASIC = 0x8000 #: Experimenter class OFPXMC_EXPERIMENTER = 0xFFFF class VlanId(IntEnum): """Indicates conditions of the Vlan. The VLAN id is 12-bits, so we can use the entire 16 bits to indicate special conditions. """ #: Bit that indicate that a VLAN id is set. OFPVID_PRESENT = 0x1000 #: No VLAN id was set OFPVID_NONE = 0x0000 # Classes class OxmTLV(GenericStruct): """Oxm (OpenFlow Extensible Match) TLV.""" oxm_class = UBInt16(enum_ref=OxmClass) oxm_field_and_mask = UBInt8() oxm_length = UBInt8() oxm_value = BinaryData() def __init__(self, oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=None, oxm_hasmask=False, oxm_value=None): """Create an OXM TLV struct with the optional parameters below. Args: oxm_class (OxmClass): Match class: member class or reserved class oxm_field (OxmMatchFields, OxmOfbMatchField): Match field within the class oxm_hasmask (bool): Set if OXM include a bitmask in payload oxm_value (bytes): OXM Payload """ super().__init__() self.oxm_class = oxm_class self.oxm_field_and_mask = None self.oxm_length = None self.oxm_value = oxm_value # Attributes that are not packed self.oxm_field = oxm_field self.oxm_hasmask = oxm_hasmask def unpack(self, buff, offset=0): """Unpack the buffer into a OxmTLV. Args: buff (bytes): The binary data to be unpacked. offset (int): If we need to shift the beginning of the data. """ super().unpack(buff, offset) # Recover field from field_and_hasmask. try: self.oxm_field = self._unpack_oxm_field() except ValueError as exception: raise UnpackException(exception) # The last bit of field_and_mask is oxm_hasmask self.oxm_hasmask = (self.oxm_field_and_mask & 1) == 1 # as boolean # Unpack oxm_value that has oxm_length bytes start = offset + 4 # 4 bytes: class, field_and_mask and length end = start + self.oxm_length self.oxm_value = buff[start:end] def _unpack_oxm_field(self): """Unpack oxm_field from oxm_field_and_mask. Returns: :class:`OxmOfbMatchField`, int: oxm_field from oxm_field_and_mask. Raises: ValueError: If oxm_class is OFPXMC_OPENFLOW_BASIC but :class:`OxmOfbMatchField` has no such integer value. """ field_int = self.oxm_field_and_mask >> 1 # We know that the class below requires a subset of the ofb enum if self.oxm_class == OxmClass.OFPXMC_OPENFLOW_BASIC: return OxmOfbMatchField(field_int) return field_int def _update_length(self): """Update length field. Update the oxm_length field with the packed payload length. """ payload = type(self).oxm_value.pack(self.oxm_value) self.oxm_length = len(payload) def pack(self, value=None): """Join oxm_hasmask bit and 7-bit oxm_field.""" if value is not None: return value.pack() # Set oxm_field_and_mask instance attribute # 1. Move field integer one bit to the left try: field_int = self._get_oxm_field_int() except ValueError as exception: raise PackException(exception) field_bits = field_int << 1 # 2. hasmask bit hasmask_bit = self.oxm_hasmask & 1 # 3. Add hasmask bit to field value self.oxm_field_and_mask = field_bits + hasmask_bit self._update_length() return super().pack(value) def _get_oxm_field_int(self): """Return a valid integer value for oxm_field. Used while packing. Returns: int: valid oxm_field value. Raises: ValueError: If :attribute:`oxm_field` is bigger than 7 bits or should be :class:`OxmOfbMatchField` and the enum has no such value. """ if self.oxm_class == OxmClass.OFPXMC_OPENFLOW_BASIC: return OxmOfbMatchField(self.oxm_field).value elif not isinstance(self.oxm_field, int) or self.oxm_field > 127: raise ValueError('oxm_field above 127: "{self.oxm_field}".') return self.oxm_field class OxmMatchFields(FixedTypeList): """Generic Openflow EXtensible Match header. Abstract class that can be instantiated as Match or OxmExperimenterHeader. """ def __init__(self, items=None): """Initialize ``items`` attribute. Args: items (OxmHeader): Instance or a list of instances. """ super().__init__(pyof_class=OxmTLV, items=items) class Match(GenericStruct): """Describes the flow match header structure. These are the fields to match against flows. The :attr:`~match_type` field is set to :attr:`~MatchType.OFPMT_OXM` and :attr:`length` field is set to the actual length of match structure including all match fields. The payload of the OpenFlow match is a set of OXM Flow match fields. """ #: One of OFPMT_* match_type = UBInt16(enum_ref=MatchType) #: Length of Match (excluding padding) length = UBInt16() oxm_match_fields = OxmMatchFields() def __init__(self, match_type=MatchType.OFPMT_OXM, oxm_match_fields=None): """Describe the flow match header structure. Args: match_type (MatchType): One of OFPMT_* (MatchType) items. length (int): Length of Match (excluding padding) followed by Exactly (length - 4) (possibly 0) bytes containing OXM TLVs, then exactly ((length + 7)/8*8 - length) (between 0 and 7) bytes of all-zero bytes. oxm_fields (OxmMatchFields): Sample description. """ super().__init__() self.match_type = match_type self.oxm_match_fields = oxm_match_fields or OxmMatchFields() self._update_match_length() def _update_match_length(self): """Update the match length field.""" self.length = super().get_size() def pack(self, value=None): """Pack and complete the last byte by padding.""" if isinstance(value, Match): return value.pack() elif value is None: self._update_match_length() packet = super().pack() return self._complete_last_byte(packet) raise PackException(f'Match can\'t unpack "{value}".') def _complete_last_byte(self, packet): """Pad until the packet length is a multiple of 8 (bytes).""" padded_size = self.get_size() padding_bytes = padded_size - len(packet) if padding_bytes > 0: packet += Pad(padding_bytes).pack() return packet def get_size(self, value=None): """Return the packet length including the padding (multiple of 8).""" if isinstance(value, Match): return value.get_size() elif value is None: current_size = super().get_size() return ceil(current_size / 8) * 8 raise ValueError(f'Invalid value "{value}" for Match.get_size()') def unpack(self, buff, offset=0): """Discard padding bytes using the unpacked length attribute.""" begin = offset for name, value in list(self.get_class_attributes())[:-1]: size = self._unpack_attribute(name, value, buff, begin) begin += size self._unpack_attribute('oxm_match_fields', type(self).oxm_match_fields, buff[:offset+self.length], begin) def get_field(self, field_type): """Return the value for the 'field_type' field in oxm_match_fields. Args: field_type (~pyof.v0x04.common.flow_match.OxmOfbMatchField, ~pyof.v0x04.common.flow_match.OxmMatchFields): The type of the OXM field you want the value. Returns: The integer number of the 'field_type' if it exists. Otherwise return None. """ for field in self.oxm_match_fields: if field.oxm_field == field_type: return field.oxm_value return None class OxmExperimenterHeader(GenericStruct): """Header for OXM experimenter match fields.""" #: oxm_class = OFPXMC_EXPERIMENTER oxm_header = UBInt32(OxmClass.OFPXMC_EXPERIMENTER, enum_ref=OxmClass) #: Experimenter ID which takes the same form as in struct #: ofp_experimenter_header experimenter = UBInt32() def __init__(self, experimenter=None): """Initialize ``experimenter`` attribute. Args: experimenter (int): Experimenter ID which takes the same form as in struct ofp_experimenter_header """ super().__init__() self.experimenter = experimenter class ListOfOxmHeader(FixedTypeList): """List of Openflow Extensible Match header instances. Represented by instances of OxmHeader. """ def __init__(self, items=None): """Initialize ``items`` attribute. Args: items (OxmHeader): Instance or a list of instances. """ super().__init__(pyof_class=OxmTLV, items=items) python-openflow-2019.2/pyof/v0x04/common/queue.py0000644000175100001630000001304213577157232022417 0ustar runnerdocker00000000000000"""Defines OpenFlow queues structures and related items.""" # System imports from enum import IntEnum # Local source tree imports from pyof.foundation.base import GenericStruct from pyof.foundation.basic_types import ( BinaryData, FixedTypeList, Pad, UBInt16, UBInt32) # Third-party imports __all__ = ('ListOfProperties', 'ListOfQueues', 'PacketQueue', 'QueueProperties', 'QueuePropExperimenter', 'QueuePropHeader', 'QueuePropMaxRate', 'QueuePropMinRate') # Enums class QueueProperties(IntEnum): """Describe queue properties.""" #: Minimum datarate guaranteed OFPQT_MIN_RATE = 1 #: Maximum datarate guaranteed OFPQT_MAX_RATE = 2 #: Experimenter defined property OFPQT_EXPERIMENTER = 0xffff # Classes class QueuePropHeader(GenericStruct): """Describe the header of each queue property.""" #: One of OFPQT_* queue_property = UBInt16(enum_ref=QueueProperties) #: Length of property, including this header length = UBInt16() #: 64-bit alignment pad = Pad(4) def __init__(self, queue_property=None, length=None): """Create a QueuePropHeader with the optional parameters below. Args: queue_property (~pyof.v0x04.common.queue.QueueProperties): The queue property. length (int): Length of property, including this header. """ super().__init__() self.queue_property = queue_property self.length = length class ListOfProperties(FixedTypeList): """List of properties. Represented by instances of :class:`QueuePropHeader` and used on :class:`PacketQueue` objects. """ def __init__(self, items=None): """Create a ListOfProperties with the optional parameters below. Args: items (:class:`list` of/or :class:`QueuePropHeader`): :class:`QueuePropHeader` instance or list of instances. """ super().__init__(pyof_class=QueuePropHeader, items=items) class PacketQueue(GenericStruct): """Describe a queue.""" #: id for the specific queue queue_id = UBInt32() #: Port this queue is attached to. port = UBInt32() #: Length, in bytes, of this queue desc. length = UBInt16() #: 64-bit alignment. pad = Pad(6) #: List of properties properties = ListOfProperties() def __init__(self, queue_id=None, port=None, length=None, properties=None): """Create a PacketQueue with the optional parameters below. Args: queue_id (int): ID of the specific queue. port (int): Port his queue is attached to. length (int): Length in bytes of this queue desc. properties(~pyof.v0x04.common.queue.ListOfProperties): Queue's list of properties. Default is an empty list. """ super().__init__() self.queue_id = queue_id self.port = port self.length = length self.properties = [] if properties is None else properties class ListOfQueues(FixedTypeList): """List of queues. Represented by instances of :class:`PacketQueue` and used on :class:`QueueGetConfigReply` objects. """ def __init__(self, items=None): """Create a ListOfQueues with the optional parameters below. Args: items (:class:`list` of/or :class:`PacketQueue`): :class:`PacketQueue` instance or list of instances. """ super().__init__(pyof_class=PacketQueue, items=items) class QueuePropExperimenter(GenericStruct): """Experimenter queue property uses the following structure and fields.""" prop_header = QueuePropHeader( queue_property=QueueProperties.OFPQT_EXPERIMENTER, length=16) #: Experimenter ID which takes the same form as in struct #: ofp_experimenter_header experimenter = UBInt32() #: 64-bit alignmet. pad = Pad(4) #: Experimenter defined data. data = BinaryData() def __init__(self, experimenter=None, data=None): """Create a QueuePropExperimenter with the optional parameters below. Args: experimenter (int): Experimenter ID which takes the same form as in struct ofp_experimenter_header. data (bytes): Experimenter defined data. """ super().__init__() self.experimenter = experimenter self.data = data 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 QueuePropMinRate(GenericStruct): """Minimum-rate queue property uses the following structure and fields.""" prop_header = QueuePropHeader( queue_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): """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 python-openflow-2019.2/pyof/v0x04/common/port.py0000644000175100001630000002260013577157232022257 0ustar runnerdocker00000000000000"""Defines physical port classes and related items.""" # System imports from enum import IntEnum # Local source tree imports from pyof.foundation.base import GenericBitMask, GenericStruct from pyof.foundation.basic_types import ( Char, FixedTypeList, HWAddress, Pad, UBInt32) from pyof.foundation.constants import OFP_MAX_PORT_NAME_LEN # Third-party imports __all__ = ('ListOfPorts', 'Port', 'PortNo', 'PortConfig', 'PortFeatures', 'PortState') class PortNo(IntEnum): """Port numbering. Ports are numbered starting from 1. """ #: Maximum number of physical and logical switch ports. OFPP_MAX = 0xffffff00 # Reserved OpenFlow port (fake output "ports") #: Send the packet out the input port. This reserved port must be #: explicitly used in order to send back out of the input port. OFPP_IN_PORT = 0xfffffff8 #: Submit the packet to the first flow table #: NB: This destination port can only be used in packet-out messages. OFPP_TABLE = 0xfffffff9 #: Process with normal L2/L3 switching. OFPP_NORMAL = 0xfffffffa #: All physical ports in VLAN, except input port and thos blocked or link #: down. OFPP_FLOOD = 0xfffffffb #: All physical ports except input port OFPP_ALL = 0xfffffffc #: Send to controller OFPP_CONTROLLER = 0xfffffffd #: Local openflow "port" OFPP_LOCAL = 0xfffffffe #: Wildcard port used only for flow mod (delete) and flow stats requests. #: Selects all flows regardless of output port (including flows with no #: output port). OFPP_ANY = 0xffffffff class PortConfig(GenericBitMask): """Flags to indicate behavior of the physical port. These flags are used in :class:`Port` to describe the current configuration. They are used in the :class:`~pyof.v0x04.controller2switch.port_mod.PortMod` message to configure the port's behavior. The :attr:`OFPPC_PORT_DOWN` bit indicates that the port has been administratively brought down and should not be used by OpenFlow. The :attr:`~OFPPC_NO_RECV` bit indicates that packets received on that port should be ignored. The :attr:`OFPPC_NO_FWD` bit indicates that OpenFlow should not send packets to that port. The :attr:`OFPPC_NO_PACKET_IN` bit indicates that packets on that port that generate a table miss should never trigger a packet-in message to the controller. In general, the port config bits are set by the controller and not changed by the switch. Those bits may be useful for the controller to implement protocols such as STP or BFD. If the port config bits are changed by the switch through another administrative interface, the switch sends an :attr:`OFPT_PORT_STATUS` message to notify the controller of the change. """ #: Port is administratively down. OFPPC_PORT_DOWN = 1 << 0 #: Drop all packets received by port. OFPPC_NO_RECV = 1 << 2 #: Drop packets forwarded to port. OFPPC_NO_FWD = 1 << 5 #: Do not send packet-in msgs for port. OFPPC_NO_PACKET_IN = 1 << 6 class PortFeatures(GenericBitMask): """Physical ports features. The curr, advertised, supported, and peer fields indicate link modes (speed and duplexity), link type (copper/fiber) and link features (autonegotiation and pause). Multiple of these flags may be set simultaneously. If none of the port speed flags are set, the max_speed or curr_speed are used. The curr_speed and max_speed fields indicate the current and maximum bit rate (raw transmission speed) of the link in kbps. The number should be rounded to match common usage. For example, an optical 10 Gb Ethernet port should have this field set to 10000000 (instead of 10312500), and an OC-192 port should have this field set to 10000000 (instead of 9953280). The max_speed fields indicate the maximum configured capacity of the link, whereas the curr_speed indicates the current capacity. If the port is a LAG with 3 links of 1Gb/s capacity, with one of the ports of the LAG being down, one port auto-negotiated at 1Gb/s and 1 port auto-negotiated at 100Mb/s, the max_speed is 3 Gb/s and the curr_speed is 1.1 Gb/s. """ #: 10 Mb half-duplex rate support. OFPPF_10MB_HD = 1 << 0 #: 10 Mb full-duplex rate support. OFPPF_10MB_FD = 1 << 1 #: 100 Mb half-duplex rate support. OFPPF_100MB_HD = 1 << 2 #: 100 Mb full-duplex rate support. OFPPF_100MB_FD = 1 << 3 #: 1 Gb half-duplex rate support. OFPPF_1GB_HD = 1 << 4 #: 1 Gb full-duplex rate support. OFPPF_1GB_FD = 1 << 5 #: 10 Gb full-duplex rate support. OFPPF_10GB_FD = 1 << 6 #: 40 Gb full-duplex rate support. OFPPF_40GB_FD = 1 << 7 #: 100 Gb full-duplex rate support. OFPPF_100GB_FD = 1 << 8 #: 1 Tb full-duplex rate support. OFPPF_1TB_FD = 1 << 9 #: Other rate, not in the list OFPPF_OTHER = 1 << 10 #: Copper medium. OFPPF_COPPER = 1 << 11 #: Fiber medium. OFPPF_FIBER = 1 << 12 #: Auto-negotiation. OFPPF_AUTONEG = 1 << 13 #: Pause. OFPPF_PAUSE = 1 << 14 #: Asymmetric pause. OFPPF_PAUSE_ASYM = 1 << 15 class PortState(GenericBitMask): """Current state of the physical port. These are not configurable from the controller. The port state bits represent the state of the physical link or switch protocols outside of OpenFlow. The :attr:`~PortConfig.OFPPS_LINK_DOWN` bit indicates the the physical link is not present. The :attr:`~PortConfig.OFPPS_BLOCKED` bit indicates that a switch protocol outside of OpenFlow, such as 802.1D Spanning Tree, is preventing the use of that port with :attr:`~PortConfig.OFPP_FLOOD`. All port state bits are read-only and cannot be changed by the controller. When the port flags are changed, the switch sends an :attr:`v0x04.common.header.Type.OFPT_PORT_STATUS` message to notify the controller of the change. """ #: Not physical link present. OFPPS_LINK_DOWN = 1 << 0 #: Port is blocked. OFPPS_BLOCKED = 1 << 1 #: Live for Fast Failover Group. OFPPS_LIVE = 1 << 2 # Classes class Port(GenericStruct): """Description of a port. The port_no field uniquely identifies a port within a switch. The hw_addr field typically is the MAC address for the port; :data:`.OFP_MAX_ETH_ALEN` is 6. The name field is a null-terminated string containing a human-readable name for the interface. The value of :data:`.OFP_MAX_PORT_NAME_LEN` is 16. :attr:`curr`, :attr:`advertised`, :attr:`supported` and :attr:`peer` fields indicate link modes (speed and duplexity), link type (copper/fiber) and link features (autonegotiation and pause). They are bitmaps of :class:`PortFeatures` enum values that describe features. Multiple of these flags may be set simultaneously. If none of the port speed flags are set, the :attr:`max_speed` or :attr:`curr_speed` are used. """ port_no = UBInt32() pad = Pad(4) hw_addr = HWAddress() pad2 = Pad(2) name = Char(length=OFP_MAX_PORT_NAME_LEN) config = UBInt32(enum_ref=PortConfig) state = UBInt32(enum_ref=PortState) curr = UBInt32(enum_ref=PortFeatures) advertised = UBInt32(enum_ref=PortFeatures) supported = UBInt32(enum_ref=PortFeatures) peer = UBInt32(enum_ref=PortFeatures) curr_speed = UBInt32() max_speed = UBInt32() def __init__(self, port_no=None, hw_addr=None, name=None, config=None, state=None, curr=None, advertised=None, supported=None, peer=None, curr_speed=None, max_speed=None): """Create a Port with the optional parameters below. Args: port_no (int): Port number. hw_addr (HWAddress): Hardware address. name (str): Null-terminated name. config (~pyof.v0x04.common.port.PortConfig): Bitmap of OFPPC* flags. state (~pyof.v0x04.common.port.PortState): Bitmap of OFPPS* flags. curr (~pyof.v0x04.common.port.PortFeatures): Current features. advertised (~pyof.v0x04.common.port.PortFeatures): Features being advertised by the port. supported (~pyof.v0x04.common.port.PortFeatures): Features supported by the port. peer (~pyof.v0x04.common.port.PortFeatures): Features advertised by peer. curr_speed (int): Current port bitrate in kbps. max_speed (int): Max port bitrate in kbps. """ super().__init__() self.port_no = port_no self.hw_addr = hw_addr self.name = name self.config = config self.state = state self.curr = curr self.advertised = advertised self.supported = supported self.peer = peer self.curr_speed = curr_speed self.max_speed = max_speed class ListOfPorts(FixedTypeList): """List of Ports. Represented by instances of :class:`Port` and used on :class:`~pyof.v0x04.controller2switch.features_reply.FeaturesReply`/ :class:`~pyof.v0x04.controller2switch.features_reply.SwitchFeatures` objects. """ def __init__(self, items=None): """Create a ListOfPort with the optional parameters below. Args: items (:class:`list`, :class:`~pyof.v0x04.common.port.Port`): One :class:`~pyof.v0x04.common.port.Port` instance or list. """ super().__init__(pyof_class=Port, items=items) python-openflow-2019.2/pyof/v0x04/common/flow_instructions.py0000644000175100001630000001773113577157232025077 0ustar runnerdocker00000000000000"""Flow instructions to be executed. The flow instructions associated with a flow table entry are executed when a flow matches the entry. """ # System imports from enum import IntEnum # Local source tree imports from pyof.foundation.base import GenericStruct from pyof.foundation.basic_types import ( FixedTypeList, Pad, UBInt8, UBInt16, UBInt32, UBInt64) from pyof.foundation.exceptions import PackException from pyof.v0x04.common.action import ListOfActions from pyof.v0x04.controller2switch.meter_mod import Meter # Third-party imports __all__ = ('InstructionApplyAction', 'InstructionClearAction', 'InstructionGotoTable', 'InstructionMeter', 'InstructionType', 'InstructionWriteAction', 'InstructionWriteMetadata', 'ListOfInstruction') # Enums class InstructionType(IntEnum): """List of instructions that are currently defined.""" #: Setup the next table in the lookup pipeline OFPIT_GOTO_TABLE = 1 #: Setup the metadata field for use later in pipeline OFPIT_WRITE_METADATA = 2 #: Write the action(s) onto the datapath action set OFPIT_WRITE_ACTIONS = 3 #: Applies the action(s) immediately OFPIT_APPLY_ACTIONS = 4 #: Clears all actions from the datapath action set OFPIT_CLEAR_ACTIONS = 5 #: Apply meter (rate limiter) OFPIT_METER = 6 #: Experimenter instruction OFPIT_EXPERIMENTER = 0xFFFF def find_class(self): """Return a class related with this type.""" classes = {1: InstructionGotoTable, 2: InstructionWriteMetadata, 3: InstructionWriteAction, 4: InstructionApplyAction, 5: InstructionClearAction, 6: InstructionMeter} return classes.get(self.value, None) # Classes class Instruction(GenericStruct): """Generic Instruction class. This class represents a Generic Instruction that can be instanciated as 'InstructionApplyAction', 'InstructionClearAction', 'InstructionGotoTable', 'InstructionMeter', 'InstructionWriteAction', 'InstructionWriteMetadata'. """ instruction_type = UBInt16(enum_ref=InstructionType) length = UBInt16() def __init__(self, instruction_type=None): """Create a Instruction with the optional parameters below. Args: instruction_type(InstructionType): Type of instruction. """ super().__init__() self.instruction_type = instruction_type def pack(self, value=None): """Update the length and pack the massege into binary data. Returns: bytes: A binary data that represents the Message. Raises: Exception: If there are validation errors. """ if value is None: self.update_length() return super().pack() elif isinstance(value, type(self)): return value.pack() else: msg = "{} is not an instance of {}".format(value, type(self).__name__) raise PackException(msg) def update_length(self): """Update length attribute.""" self.length = self.get_size() 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. """ instruction_type = UBInt16(enum_ref=InstructionType) instruction_type.unpack(buff, offset) self.__class__ = InstructionType(instruction_type.value).find_class() length = UBInt16() length.unpack(buff, offset=offset+2) super().unpack(buff[:offset+length.value], offset) class InstructionApplyAction(Instruction): """Instruction structure for OFPIT_APPLY_ACTIONS. The :attr:`~actions` field is treated as a list, and the actions are applied to the packet in-order. """ #: Align to 64-bits pad = Pad(4) #: Actions associated with OFPIT_APPLY_ACTIONS actions = ListOfActions() def __init__(self, actions=None): """Create a InstructionApplyAction with the optional parameters below. Args: actions (:class:`~.actions.ListOfActions`): Actions associated with OFPIT_APPLY_ACTIONS. """ super().__init__(InstructionType.OFPIT_APPLY_ACTIONS) self.actions = actions if actions else [] class InstructionClearAction(Instruction): """Instruction structure for OFPIT_CLEAR_ACTIONS. This structure does not contain any actions. """ #: Align to 64-bits pad = Pad(4) #: OFPIT_CLEAR_ACTIONS does not have any action on the list of actions. actions = ListOfActions() def __init__(self, actions=None): """Create a InstructionClearAction with the optional parameters below. Args: actions (:class:`~.actions.ListOfActions`): Actions associated with OFPIT_CLEAR_ACTIONS. """ super().__init__(InstructionType.OFPIT_CLEAR_ACTIONS) self.actions = actions if actions else [] class InstructionGotoTable(Instruction): """Instruction structure for OFPIT_GOTO_TABLE.""" #: Set next table in the lookup pipeline. table_id = UBInt8() #: Pad to 64 bits. pad = Pad(3) def __init__(self, table_id=Meter.OFPM_ALL): """Create a InstructionGotoTable with the optional parameters below. Args: length (int): Length of this struct in bytes. table_id (int): set next table in the lookup pipeline. """ super().__init__(InstructionType.OFPIT_GOTO_TABLE) self.table_id = table_id class InstructionMeter(Instruction): """Instruction structure for OFPIT_METER. meter_id indicates which meter to apply on the packet. """ #: Meter instance. meter_id = UBInt32() def __init__(self, meter_id=Meter.OFPM_ALL): """Create a InstructionMeter with the optional parameters below. Args: meter_id (int): Meter instance. """ super().__init__(InstructionType.OFPIT_METER) self.meter_id = meter_id class InstructionWriteAction(Instruction): """Instruction structure for OFPIT_WRITE_ACTIONS. The actions field must be treated as a SET, so the actions are not repeated. """ #: Align to 64-bits pad = Pad(4) #: Actions associated with OFPIT_WRITE_ACTIONS actions = ListOfActions() def __init__(self, actions=None): """Create a InstructionWriteAction with the optional parameters below. Args: actions (:class:`~.actions.ListOfActions`): Actions associated with OFPIT_WRITE_ACTIONS. """ super().__init__(InstructionType.OFPIT_WRITE_ACTIONS) self.actions = actions if actions else [] class InstructionWriteMetadata(Instruction): """Instruction structure for OFPIT_WRITE_METADATA.""" #: 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 ListOfInstruction(FixedTypeList): """List of Instructions. Represented by instances of Instruction. """ def __init__(self, items=None): """Create ListOfInstruction with the optional parameters below. Args: items (:class:`~pyof.v0x04.common.flow_instructions.Instruction`): Instance or a list of instances. """ super().__init__(pyof_class=Instruction, items=items) python-openflow-2019.2/pyof/v0x04/common/utils.py0000644000175100001630000001463613577157232022445 0ustar runnerdocker00000000000000"""Helper python-openflow functions.""" # System imports # Third-party imports # Local source tree imports # Importing asynchronous messages from pyof.v0x04.asynchronous.error_msg import ErrorMsg from pyof.v0x04.asynchronous.flow_removed import FlowRemoved from pyof.v0x04.asynchronous.packet_in import PacketIn from pyof.v0x04.asynchronous.port_status import PortStatus # Importing controller2switch messages from pyof.v0x04.common.header import Header, Type from pyof.v0x04.controller2switch.barrier_reply import BarrierReply from pyof.v0x04.controller2switch.barrier_request import BarrierRequest from pyof.v0x04.controller2switch.features_reply import FeaturesReply from pyof.v0x04.controller2switch.features_request import FeaturesRequest from pyof.v0x04.controller2switch.flow_mod import FlowMod from pyof.v0x04.controller2switch.get_async_reply import GetAsyncReply from pyof.v0x04.controller2switch.get_async_request import GetAsyncRequest from pyof.v0x04.controller2switch.get_config_reply import GetConfigReply from pyof.v0x04.controller2switch.get_config_request import GetConfigRequest from pyof.v0x04.controller2switch.group_mod import GroupMod from pyof.v0x04.controller2switch.meter_mod import MeterMod from pyof.v0x04.controller2switch.multipart_reply import MultipartReply from pyof.v0x04.controller2switch.multipart_request import MultipartRequest from pyof.v0x04.controller2switch.packet_out import PacketOut from pyof.v0x04.controller2switch.port_mod import PortMod from pyof.v0x04.controller2switch.queue_get_config_reply import ( QueueGetConfigReply) from pyof.v0x04.controller2switch.queue_get_config_request import ( QueueGetConfigRequest) from pyof.v0x04.controller2switch.role_reply import RoleReply from pyof.v0x04.controller2switch.role_request import RoleRequest from pyof.v0x04.controller2switch.set_async import SetAsync from pyof.v0x04.controller2switch.set_config import SetConfig from pyof.v0x04.controller2switch.table_mod import TableMod # Importing symmetric messages from pyof.v0x04.symmetric.echo_reply import EchoReply from pyof.v0x04.symmetric.echo_request import EchoRequest from pyof.v0x04.symmetric.experimenter import ExperimenterHeader from pyof.v0x04.symmetric.hello import Hello __all__ = ('MESSAGE_TYPES', 'new_message_from_header', 'new_message_from_message_type', 'unpack_message') MESSAGE_TYPES = { # Symetric/Immutable messages str(Type.OFPT_HELLO): Hello, str(Type.OFPT_ERROR): ErrorMsg, str(Type.OFPT_ECHO_REQUEST): EchoRequest, str(Type.OFPT_ECHO_REPLY): EchoReply, str(Type.OFPT_EXPERIMENTER): ExperimenterHeader, # Switch configuration messages # Controller/Switch messages str(Type.OFPT_FEATURES_REQUEST): FeaturesRequest, str(Type.OFPT_FEATURES_REPLY): FeaturesReply, str(Type.OFPT_GET_CONFIG_REQUEST): GetConfigRequest, str(Type.OFPT_GET_CONFIG_REPLY): GetConfigReply, str(Type.OFPT_SET_CONFIG): SetConfig, # Async messages str(Type.OFPT_PACKET_IN): PacketIn, str(Type.OFPT_FLOW_REMOVED): FlowRemoved, str(Type.OFPT_PORT_STATUS): PortStatus, # Controller command messages # Controller/Switch message str(Type.OFPT_PACKET_OUT): PacketOut, str(Type.OFPT_FLOW_MOD): FlowMod, str(Type.OFPT_GROUP_MOD): GroupMod, str(Type.OFPT_PORT_MOD): PortMod, str(Type.OFPT_TABLE_MOD): TableMod, # Multipart messages. # Controller/Switch message str(Type.OFPT_MULTIPART_REPLY): MultipartReply, str(Type.OFPT_MULTIPART_REQUEST): MultipartRequest, # Barrier messages # Controller/Switch message str(Type.OFPT_BARRIER_REQUEST): BarrierRequest, str(Type.OFPT_BARRIER_REPLY): BarrierReply, # Queue Configuration messages # Controller/Switch message str(Type.OFPT_QUEUE_GET_CONFIG_REQUEST): QueueGetConfigRequest, str(Type.OFPT_QUEUE_GET_CONFIG_REPLY): QueueGetConfigReply, # Controller role change request message # Controller/Switch message str(Type.OFPT_ROLE_REQUEST): RoleRequest, str(Type.OFPT_ROLE_REPLY): RoleReply, # Asynchronous message configuration # Controller/Switch message str(Type.OFPT_GET_ASYNC_REQUEST): GetAsyncRequest, str(Type.OFPT_GET_ASYNC_REPLY): GetAsyncReply, str(Type.OFPT_SET_ASYNC): SetAsync, # Meters and rate limiters configuration messages # Controller/Switch message str(Type.OFPT_METER_MOD): MeterMod, } def new_message_from_message_type(message_type): """Given an OpenFlow Message Type, return an empty message of that type. Args: messageType (:class:`~pyof.v0x04.common.header.Type`): Python-openflow message. Returns: Empty OpenFlow message of the requested message type. Raises: KytosUndefinedMessageType: Unkown Message_Type. """ message_type = str(message_type) if message_type not in MESSAGE_TYPES: msg = "Define class for {} in {}".format(message_type, __file__) raise ValueError(msg) message_class = MESSAGE_TYPES.get(message_type) message_instance = message_class() return message_instance def new_message_from_header(header): """Given an OF Header, return an empty message of header's message_type. Args: header (:class:`~pyof.v0x04.common.header.Header`): Unpacked OpenFlow Header. Returns: Empty OpenFlow message of the same type of message_type attribute from the given header. The header attribute of the message will be populated. Raises: KytosUndefinedMessageType: Unkown Message_Type. """ message_type = header.message_type if not isinstance(message_type, Type): try: if isinstance(message_type, str): message_type = Type[message_type] elif isinstance(message_type, int): message_type = Type(message_type) except ValueError: raise ValueError message = new_message_from_message_type(message_type) message.header.xid = header.xid message.header.length = header.length return message 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 python-openflow-2019.2/pyof/v0x04/common/action.py0000644000175100001630000003232713577157232022557 0ustar runnerdocker00000000000000"""Defines actions that may be associated with flows packets.""" # System imports from enum import IntEnum from math import ceil # Local source tree imports from pyof.foundation.base import GenericStruct from pyof.foundation.basic_types import ( BinaryData, FixedTypeList, Pad, UBInt8, UBInt16, UBInt32) from pyof.v0x04.common.flow_match import OxmTLV # Third-party imports __all__ = ('ActionExperimenter', 'ActionGroup', 'ActionHeader', 'ActionCopyTTLIn', 'ActionCopyTTLOut', 'ActionDecMPLSTTL', 'ActionSetMPLSTTL', 'ActionDecNWTTL', 'ActionSetNWTTL', 'ActionOutput', 'ActionPopMPLS', 'ActionPopPBB', 'ActionPopVLAN', 'ActionPush', 'ActionSetField', 'ActionSetQueue', 'ActionType', 'ControllerMaxLen', 'ListOfActions') # Enums class ActionType(IntEnum): """Actions associated with flows and packets.""" #: Output to switch port. OFPAT_OUTPUT = 0 #: Copy TTL "outwards" -- from next-to-outermost to outermost OFPAT_COPY_TTL_OUT = 11 #: Copy TTL "inwards" -- from outermost to next-to-outermost OFPAT_COPY_TTL_IN = 12 #: MPLS TTL OFPAT_SET_MPLS_TTL = 15 #: Decrement MPLS TTL OFPAT_DEC_MPLS_TTL = 16 #: Push a new VLAN tag OFPAT_PUSH_VLAN = 17 #: Pop the outer VLAN tag OFPAT_POP_VLAN = 18 #: Push a new MPLS tag OFPAT_PUSH_MPLS = 19 #: Pop the outer MPLS tag OFPAT_POP_MPLS = 20 #: Set queue id when outputting to a port OFPAT_SET_QUEUE = 21 #: Apply group. OFPAT_GROUP = 22 #: IP TTL. OFPAT_SET_NW_TTL = 23 #: Decrement IP TTL. OFPAT_DEC_NW_TTL = 24 #: Set a header field using OXM TLV format. OFPAT_SET_FIELD = 25 #: Push a new PBB service tag (I-TAG) OFPAT_PUSH_PBB = 26 #: Pop the outer PBB service tag (I-TAG) OFPAT_POP_PBB = 27 #: Experimenter type OFPAT_EXPERIMENTER = 0xffff class ControllerMaxLen(IntEnum): """A max_len of OFPCML_NO_BUFFER means not to buffer. The packet should be sent. """ #: maximum max_len value which can be used to request a specific byte #: length. OFPCML_MAX = 0xffe5 #: indicates that no buffering should be applied and the whole packet is to #: be sent to the controller. OFPCML_NO_BUFFER = 0xffff # Classes class ActionHeader(GenericStruct): """Action header that is common to all actions. The length includes the header and any padding used to make the action 64-bit aligned. NB: The length of an action *must* always be a multiple of eight. """ #: One of OFPAT_*. action_type = UBInt16(enum_ref=ActionType) #: Length of action, including this header. This is the length of actions, #: including any padding to make it 64-bit aligned. length = UBInt16() # Pad for 64-bit alignment. # This should not be implemented, as each action type has its own padding. # pad = Pad(4) _allowed_types = () def __init__(self, action_type=None, length=None): """Create an ActionHeader with the optional parameters below. Args: action_type (~pyof.v0x04.common.action.ActionType): The type of the action. length (int): Length of action, including this header. """ super().__init__() self.action_type = action_type self.length = length def get_size(self, value=None): """Return the action length including the padding (multiple of 8).""" if isinstance(value, ActionHeader): return value.get_size() elif value is None: current_size = super().get_size() return ceil(current_size / 8) * 8 raise ValueError(f'Invalid value "{value}" for Action*.get_size()') def unpack(self, buff, 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: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: Exception: If there is a struct unpacking error. """ self.action_type = UBInt16(enum_ref=ActionType) self.action_type.unpack(buff, offset) self.length = UBInt16() self.length.unpack(buff, offset=offset+2) for cls in ActionHeader.__subclasses__(): if self.action_type.value in cls.get_allowed_types(): self.__class__ = cls break super().unpack(buff[:offset+self.length], offset) @classmethod def get_allowed_types(cls): """Return allowed types for the class.""" return cls._allowed_types class ActionExperimenter(ActionHeader): """Action structure for OFPAT_EXPERIMENTER.""" experimenter = UBInt32() body = BinaryData() _allowed_types = ActionType.OFPAT_EXPERIMENTER, def __init__(self, length=None, experimenter=None, body=None): """Create ActionExperimenterHeader with the optional parameters below. Args: experimenter (int): The experimenter field is the Experimenter ID, which takes the same form as in struct ofp_experimenter. body(bytes): The body of the experimenter. It is vendor-defined, so it is left as it is. """ super().__init__(action_type=ActionType.OFPAT_EXPERIMENTER) self.length = length self.experimenter = experimenter self.body = body class ActionGroup(ActionHeader): """Action structure for OFPAT_GROUP.""" group_id = UBInt32() _allowed_types = ActionType.OFPAT_GROUP, def __init__(self, group_id=None): """Create an ActionGroup with the optional parameters below. Args: group_id (int): The group_id indicates the group used to process this packet. The set of buckets to apply depends on the group type. """ super().__init__(action_type=ActionType.OFPAT_GROUP, length=8) self.group_id = group_id class ActionDecMPLSTTL(ActionHeader): """Action structure for OFPAT_DEC_MPLS_TTL.""" pad = Pad(4) _allowed_types = ActionType.OFPAT_DEC_MPLS_TTL, def __init__(self): """Create an ActionDecMPLSTTL.""" super().__init__(action_type=ActionType.OFPAT_DEC_MPLS_TTL, length=8) class ActionSetMPLSTTL(ActionHeader): """Action structure for OFPAT_SET_MPLS_TTL.""" mpls_ttl = UBInt8() pad = Pad(3) _allowed_types = ActionType.OFPAT_SET_MPLS_TTL, def __init__(self, mpls_ttl=None): """Create an ActionSetMPLSTTL with the optional parameters below. Args: mpls_ttl (int): The mpls_ttl field is the MPLS TTL to set. """ super().__init__(action_type=ActionType.OFPAT_SET_MPLS_TTL, length=8) self.mpls_ttl = mpls_ttl class ActionCopyTTLIn(ActionHeader): """Action structure for OFPAT_COPY_TTL_IN.""" pad = Pad(4) _allowed_types = ActionType.OFPAT_COPY_TTL_IN, def __init__(self): """Create an ActionCopyTTLIn.""" super().__init__(action_type=ActionType.OFPAT_COPY_TTL_IN, length=8) class ActionCopyTTLOut(ActionHeader): """Action structure for OFPAT_COPY_TTL_OUT.""" pad = Pad(4) _allowed_types = ActionType.OFPAT_COPY_TTL_OUT, def __init__(self): """Create an ActionCopyTTLOut.""" super().__init__(action_type=ActionType.OFPAT_COPY_TTL_OUT, length=8) class ActionPopVLAN(ActionHeader): """Action structure for OFPAT_POP_VLAN.""" pad = Pad(4) _allowed_types = ActionType.OFPAT_POP_VLAN, def __init__(self): """Create an ActionPopVLAN.""" super().__init__(action_type=ActionType.OFPAT_POP_VLAN, length=8) class ActionPopPBB(ActionHeader): """Action structure for OFPAT_POP_PBB.""" pad = Pad(4) _allowed_types = ActionType.OFPAT_POP_PBB, def __init__(self): """Create an ActionPopPBB.""" super().__init__(action_type=ActionType.OFPAT_POP_PBB, length=8) class ActionDecNWTTL(ActionHeader): """Action structure for OFPAT_DEC_NW_TTL.""" pad = Pad(4) _allowed_types = ActionType.OFPAT_DEC_NW_TTL, def __init__(self): """Create a ActionDecNWTTL.""" super().__init__(action_type=ActionType.OFPAT_DEC_NW_TTL, length=8) class ActionSetNWTTL(ActionHeader): """Action structure for OFPAT_SET_NW_TTL.""" nw_ttl = UBInt8() pad = Pad(3) _allowed_types = ActionType.OFPAT_SET_NW_TTL, def __init__(self, nw_ttl=None): """Create an ActionSetNWTTL with the optional parameters below. Args: nw_ttl (int): the TTL address to set in the IP header. """ super().__init__(action_type=ActionType.OFPAT_SET_NW_TTL, length=8) self.nw_ttl = nw_ttl class ActionOutput(ActionHeader): """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. """ port = UBInt32() max_length = UBInt16() pad = Pad(6) _allowed_types = ActionType.OFPAT_OUTPUT, def __init__(self, port=None, max_length=ControllerMaxLen.OFPCML_NO_BUFFER): """Create a ActionOutput with the optional parameters below. Args: port (:class:`Port` or :class:`int`): Output port. max_length (int): Max length to send to controller. """ super().__init__(action_type=ActionType.OFPAT_OUTPUT, length=16) self.port = port self.max_length = max_length 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 ActionPush(ActionHeader): """Action structure for OFPAT_PUSH_[VLAN/MPLS/PBB].""" ethertype = UBInt16() pad = Pad(2) _allowed_types = (ActionType.OFPAT_PUSH_VLAN, ActionType.OFPAT_PUSH_MPLS, ActionType.OFPAT_PUSH_PBB) def __init__(self, action_type=None, ethertype=None): """Create a ActionPush with the optional parameters below. Args: action_type (:class:`ActionType`): indicates which tag will be pushed (VLAN, MPLS, PBB). ethertype (int): indicates the Ethertype of the new tag. """ super().__init__(action_type, length=8) self.ethertype = ethertype class ActionSetField(ActionHeader): """Action structure for OFPAT_SET_FIELD.""" field = OxmTLV() _allowed_types = ActionType.OFPAT_SET_FIELD, def __init__(self, field=None): """Create a ActionSetField with the optional parameters below. Args: length (int): length padded to 64 bits, followed by exactly oxm_len bytes containing a single OXM TLV, then exactly ((oxm_len + 4) + 7)/8*8 - (oxm_len + 4) (between 0 and 7) bytes of all-zero bytes field (:class:`OxmTLV`): OXM field and value. """ super().__init__(action_type=ActionType.OFPAT_SET_FIELD) self.field = OxmTLV() if field is None else field def pack(self, value=None): """Pack this structure updating the length and padding it.""" self._update_length() packet = super().pack() return self._complete_last_byte(packet) def _update_length(self): """Update the length field of the struct.""" action_length = 4 + len(self.field.pack()) overflow = action_length % 8 self.length = action_length if overflow: self.length = action_length + 8 - overflow def _complete_last_byte(self, packet): """Pad until the packet length is a multiple of 8 (bytes).""" padded_size = self.length padding_bytes = padded_size - len(packet) if padding_bytes > 0: packet += Pad(padding_bytes).pack() return packet class ActionSetQueue(ActionHeader): """Action structure for OFPAT_SET_QUEUE.""" queue_id = UBInt32() _allowed_types = ActionType.OFPAT_SET_QUEUE, def __init__(self, queue_id=None): """Create an ActionSetQueue with the optional parameters below. Args: queue_id (int): The queue_id send packets to given queue on port. """ super().__init__(action_type=ActionType.OFPAT_SET_QUEUE, length=8) self.queue_id = queue_id class ListOfActions(FixedTypeList): """List of actions. Represented by instances of ActionHeader and used on ActionHeader objects. """ def __init__(self, items=None): """Create a ListOfActions with the optional parameters below. Args: items (~pyof.v0x04.common.action.ActionHeader): Instance or a list of instances. """ super().__init__(pyof_class=ActionHeader, items=items) python-openflow-2019.2/pyof/v0x04/common/constants.py0000644000175100001630000000015113577157232023304 0ustar runnerdocker00000000000000"""Here we have the constants related to v0x04 version.""" OFP_VERSION = 0x04 OFP_NO_BUFFER = 0xffffffff python-openflow-2019.2/pyof/v0x04/common/header.py0000644000175100001630000000513013577157232022522 0ustar runnerdocker00000000000000"""Defines Header classes and related items.""" # System imports from enum import IntEnum # Local source tree imports from pyof.foundation.base import GenericStruct from pyof.foundation.basic_types import UBInt8, UBInt16, UBInt32 from pyof.v0x04.common.constants import OFP_VERSION # Third-party imports __all__ = ('Header', 'Type') # Enums class Type(IntEnum): """Enumeration of Message Types.""" # Symetric/Immutable messages OFPT_HELLO = 0 OFPT_ERROR = 1 OFPT_ECHO_REQUEST = 2 OFPT_ECHO_REPLY = 3 OFPT_EXPERIMENTER = 4 # Switch configuration messages # Controller/Switch messages OFPT_FEATURES_REQUEST = 5 OFPT_FEATURES_REPLY = 6 OFPT_GET_CONFIG_REQUEST = 7 OFPT_GET_CONFIG_REPLY = 8 OFPT_SET_CONFIG = 9 # Async messages OFPT_PACKET_IN = 10 OFPT_FLOW_REMOVED = 11 OFPT_PORT_STATUS = 12 # Controller command messages # Controller/Switch message OFPT_PACKET_OUT = 13 OFPT_FLOW_MOD = 14 OFPT_GROUP_MOD = 15 OFPT_PORT_MOD = 16 OFPT_TABLE_MOD = 17 # Multipart messages. # Controller/Switch message OFPT_MULTIPART_REQUEST = 18 OFPT_MULTIPART_REPLY = 19 # Barrier messages # Controller/Switch message OFPT_BARRIER_REQUEST = 20 OFPT_BARRIER_REPLY = 21 # Queue Configuration messages # Controller/Switch message OFPT_QUEUE_GET_CONFIG_REQUEST = 22 OFPT_QUEUE_GET_CONFIG_REPLY = 23 # Controller role change request message # Controller/Switch message OFPT_ROLE_REQUEST = 24 OFPT_ROLE_REPLY = 25 # Asynchronous message configuration # Controller/Switch message OFPT_GET_ASYNC_REQUEST = 26 OFPT_GET_ASYNC_REPLY = 27 OFPT_SET_ASYNC = 28 # Meters and rate limiters configuration messages # Controller/Switch message OFPT_METER_MOD = 29 # Classes class Header(GenericStruct): """Representation of an OpenFlow message Header.""" version = UBInt8(OFP_VERSION) message_type = UBInt8(enum_ref=Type) length = UBInt16() xid = UBInt32() def __init__(self, message_type=None, length=None, xid=None): """Create a Header with the optional parameters below. Args: message_type (~pyof.v0x04.common.header.Type): One of the OFPT_* constants. length (int): Length including this ofp_header. xid (int): Transaction id associated with this packet. Replies use the same id as was in the request to facilitate pairing. """ super().__init__() self.message_type = message_type self.length = length self.xid = xid python-openflow-2019.2/pyof/v0x01/0000755000175100001630000000000013577157243017430 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/v0x01/__init__.py0000644000175100001630000000007213577157232021536 0ustar runnerdocker00000000000000"""The ofx parser package - spec version 0x01 (1.0.0).""" python-openflow-2019.2/pyof/v0x01/symmetric/0000755000175100001630000000000013577157243021444 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/v0x01/symmetric/vendor_header.py0000644000175100001630000000201513577157232024617 0ustar runnerdocker00000000000000"""Defines Vendor message.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData, UBInt32 from pyof.v0x01.common.header import Header, Type __all__ = ('VendorHeader',) # Classes class VendorHeader(GenericMessage): """OpenFlow Vendor message. This message does not contain a body beyond the OpenFlow Header. """ header = Header(message_type=Type.OFPT_VENDOR) vendor = UBInt32() data = BinaryData() def __init__(self, xid=None, vendor=None, data=None): """Create a VendorHeader with the options parameters below. Args: xid (int): xid to be used on the message header. vendor (int): Vendor ID: MSB 0: low-order bytes are IEEE OUI. MSB != 0: defined by OpenFlow consortium. data (BinaryData): Vendor-defined arbitrary additional data. """ super().__init__(xid) self.vendor = vendor self.data = data python-openflow-2019.2/pyof/v0x01/symmetric/__init__.py0000644000175100001630000000003213577157232023546 0ustar runnerdocker00000000000000"""Symmetric Messages.""" python-openflow-2019.2/pyof/v0x01/symmetric/hello.py0000644000175100001630000000074213577157232023122 0ustar runnerdocker00000000000000"""Defines Hello message.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData from pyof.v0x01.common.header import Header, Type __all__ = ('Hello',) # Classes 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) elements = BinaryData() python-openflow-2019.2/pyof/v0x01/symmetric/echo_request.py0000644000175100001630000000150413577157232024502 0ustar runnerdocker00000000000000"""Defines Echo Request message during the handshake.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData from pyof.v0x01.common.header import Header, Type __all__ = ('EchoRequest',) # Classes 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=None): """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 python-openflow-2019.2/pyof/v0x01/symmetric/echo_reply.py0000644000175100001630000000147413577157232024153 0ustar runnerdocker00000000000000"""Defines Echo Reply message during the handshake.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData from pyof.v0x01.common.header import Header, Type __all__ = ('EchoReply',) # Classes 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): """Create an EchoReply 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 python-openflow-2019.2/pyof/v0x01/asynchronous/0000755000175100001630000000000013577157243022163 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/v0x01/asynchronous/__init__.py0000644000175100001630000000003513577157232024270 0ustar runnerdocker00000000000000"""Asynchronous messages.""" python-openflow-2019.2/pyof/v0x01/asynchronous/packet_in.py0000644000175100001630000000407213577157232024473 0ustar runnerdocker00000000000000"""For packets received by the datapath and sent to the controller.""" # System imports from enum import IntEnum from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import ( BinaryData, Pad, UBInt8, UBInt16, UBInt32) from pyof.v0x01.common.constants import NO_BUFFER from pyof.v0x01.common.header import Header, Type # Third-party imports __all__ = ('PacketIn', 'PacketInReason') # Enums class PacketInReason(IntEnum): """Reason why this packet is being sent to the controller.""" #: No matching flow OFPR_NO_MATCH = 0 #: Action explicitly output to controller OFPR_ACTION = 1 # Classes 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 python-openflow-2019.2/pyof/v0x01/asynchronous/error_msg.py0000644000175100001630000001646113577157232024542 0ustar runnerdocker00000000000000"""Defines an Error Message.""" # System imports from enum import IntEnum from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData, UBInt16 from pyof.foundation.exceptions import PackException # Do not import new_message_from_header directly to avoid cyclic import. from pyof.v0x01.common.header import Header, Type __all__ = ('ErrorMsg', 'ErrorType', 'BadActionCode', 'BadRequestCode', 'FlowModFailedCode', 'HelloFailedCode', 'PortModFailedCode', 'QueueOpFailedCode', 'GenericFailedCode') # Enums class ErrorType(IntEnum): """Values for ’type’ in ofp_error_message. These values are immutable: they will not change in future versions of the protocol (although new values may be added). """ #: Hello protocol failed OFPET_HELLO_FAILED = 0 #: Request was not understood OFPET_BAD_REQUEST = 1 #: Error in action description OFPET_BAD_ACTION = 2 #: Problem in modifying Flow entry OFPET_FLOW_MOD_FAILED = 3 #: Problem in modifying Port entry OFPET_PORT_MOD_FAILED = 4 #: Problem in modifying Queue entry OFPET_QUEUE_OP_FAILED = 5 def get_class(self): """Return a Code class based on current ErrorType value. Returns: enum.IntEnum: class referenced by current error type. """ classes = {'OFPET_HELLO_FAILED': HelloFailedCode, 'OFPET_BAD_REQUEST': BadRequestCode, 'OFPET_BAD_ACTION': BadActionCode, 'OFPET_FLOW_MOD_FAILED': FlowModFailedCode, 'OFPET_PORT_MOD_FAILED': PortModFailedCode, 'OFPET_QUEUE_OP_FAILED': QueueOpFailedCode} return classes.get(self.name, GenericFailedCode) class GenericFailedCode(IntEnum): """Error_msg 'code' values for OFPET_BAD_ACTION. 'data' contains at least the first 64 bytes of the failed request. """ #: Unknown error GENERIC_ERROR = 0 class HelloFailedCode(IntEnum): """Error_msg 'code' values for OFPET_HELLO_FAILED. 'data' contains an ASCII text string that may give failure details. """ #: No compatible version OFPHFC_INCOMPATIBLE = 0 #: Permissions error OFPHFC_EPERM = 1 class BadRequestCode(IntEnum): """Error_msg 'code' values for OFPET_BAD_REQUEST. 'data' contains at least the first 64 bytes of the failed request. """ #: ofp_header.version not supported. OFPBRC_BAD_VERSION = 0 #: ofp_header.type not supported. OFPBRC_BAD_TYPE = 1 #: ofp_stats_request.type not supported. OFPBRC_BAD_STAT = 2 #: Vendor not supported (in ofp_vendor_header or ofp_stats_request or #: ofp_stats_reply). OFPBRC_BAD_VENDOR = 3 #: Vendor subtype not supported. OFPBRC_BAD_SUBTYPE = 4 #: Permissions error. OFPBRC_EPERM = 5 #: Wrong request length for type. OFPBRC_BAD_LEN = 6 #: Specified buffer has already been used. OFPBRC_BUFFER_EMPTY = 7 #: Specified buffer does not exist. OFPBRC_BUFFER_UNKNOWN = 8 class BadActionCode(IntEnum): """Error_msg 'code' values for OFPET_BAD_ACTION. 'data' contains at least the first 64 bytes of the failed request. """ #: Unknown action type OFPBAC_BAD_TYPE = 0 #: Length problem in actions OFPBAC_BAD_LEN = 1 #: Unknown vendor id specified OFPBAC_BAD_VENDOR = 2 #: Unknown action type for vendor id OFPBAC_BAD_VENDOR_TYPE = 3 #: Problem validating output action OFPBAC_BAD_OUT_PORT = 4 #: Bad action argument OFPBAC_BAD_ARGUMENT = 5 #: Permissions error OFPBAC_EPERM = 6 #: Can’t handle this many actions OFPBAC_TOO_MANY = 7 #: Problem validating output queue OFPBAC_BAD_QUEUE = 8 class FlowModFailedCode(IntEnum): """Error_msg 'code' values for OFPET_FLOW_MOD_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Flow not added because of full tables OFPFMFC_ALL_TABLES_FULL = 0 #: Attempted to add overlapping flow with CHECK_OVERLAP flag set OFPFMFC_OVERLAP = 1 #: Permissions error OFPFMFC_EPERM = 2 #: Flow not added because of non-zero idle/hard timeout OFPFMFC_BAD_EMERG_TIMEOUT = 3 #: Unknown command OFPFMFC_BAD_COMMAND = 4 #: Unsupported action list - cannot process in the order specified OFPFMFC_UNSUPPORTED = 5 class PortModFailedCode(IntEnum): """Error_msg 'code' values for OFPET_PORT_MOD_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Specified port does not exist OFPPMFC_BAD_PORT = 0 #: Specified hardware address is wrong OFPPMFC_BAD_HW_ADDR = 1 class QueueOpFailedCode(IntEnum): """Error msg 'code' values for OFPET_QUEUE_OP_FAILED. 'data' contains at least the first 64 bytes of the failed request. """ #: Invalid port (or port does not exist) OFPQOFC_BAD_PORT = 0 #: Queue does not exist OFPQOFC_BAD_QUEUE = 1 #: Permissions error OFPQOFC_EPERM = 2 # Classes class ErrorMsg(GenericMessage): """OpenFlow Error Message. This message does not contain a body in addition to the OpenFlow Header. """ #: :class:`~pyof.v0x01.common.header.Header`: OpenFlow Header header = Header(message_type=Type.OFPT_ERROR) error_type = UBInt16(enum_ref=ErrorType) code = UBInt16() 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 (:class:`ErrorType`): Error type. code (enum.IntEnum): Error code. data (bytes): Its content is based on the error type and code. """ super().__init__(xid) self.error_type = error_type self.code = code self.data = data def pack(self, value=None): """Pack the value as a binary representation. :attr:`data` is packed before the calling :meth:`.GenericMessage.pack`. After that, :attr:`data`'s value is restored. Returns: bytes: The binary representation. Raises: :exc:`~.exceptions.PackException`: If pack fails. """ if value is None: data_backup = None if self.data is not None and not isinstance(self.data, bytes): data_backup = self.data self.data = self.data.pack() packed = super().pack() if data_backup is not None: self.data = data_backup return packed elif isinstance(value, type(self)): return value.pack() else: msg = "{} is not an instance of {}".format(value, type(self).__name__) raise PackException(msg) def unpack(self, buff, 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. """ super().unpack(buff, offset) code_class = ErrorType(self.error_type).get_class() self.code = code_class(self.code) python-openflow-2019.2/pyof/v0x01/asynchronous/port_status.py0000644000175100001630000000252613577157232025127 0ustar runnerdocker00000000000000"""Defines an Error Message.""" # System imports from enum import IntEnum from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import Pad, UBInt8 # Local source tree imports from pyof.v0x01.common.header import Header, Type from pyof.v0x01.common.phy_port import PhyPort # Third-party imports __all__ = ('PortStatus', 'PortReason') # Enums class PortReason(IntEnum): """What changed about the physical port.""" #: The port was added OFPPR_ADD = 0 #: The port was removed OFPPR_DELETE = 1 #: Some attribute of the port has changed OFPPR_MODIFY = 2 # Classes class PortStatus(GenericMessage): """A physical port has changed in the datapath.""" #: :class:`~pyof.v0x01.common.header.Header`: OpenFlow Header header = Header(message_type=Type.OFPT_PORT_STATUS) reason = UBInt8(enum_ref=PortReason) #: Align to 32-bits. pad = Pad(7) desc = PhyPort() def __init__(self, xid=None, reason=None, desc=None): """Assign parameters to object attributes. Args: xid (int): Header's xid. reason (~pyof.v0x01.asynchronous.port_status.PortReason): Addition, deletion or modification. desc (PhyPort): Port description. """ super().__init__(xid) self.reason = reason self.desc = desc python-openflow-2019.2/pyof/v0x01/asynchronous/flow_removed.py0000644000175100001630000000517013577157232025226 0ustar runnerdocker00000000000000"""The controller has requested to be notified when flows time out.""" # System imports from enum import IntEnum from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import Pad, UBInt8, UBInt16, UBInt32, UBInt64 # Local source tree imports from pyof.v0x01.common.flow_match import Match from pyof.v0x01.common.header import Header, Type __all__ = ('FlowRemoved', 'FlowRemovedReason') # Enums class FlowRemovedReason(IntEnum): """Why the flow was removed.""" #: Flow idle time exceeded idle_timeout OFPRR_IDLE_TIMEOUT = 0 #: Time exceeded hard_timeout OFPRR_HARD_TIMEOUT = 1 #: Evicted by a DELETE flow mod OFPRR_DELETE = 2 # Classes class FlowRemoved(GenericMessage): """Flow removed (datapath -> controller).""" #: :class:`~pyof.v0x01.common.header.Header`: OpenFlow Header header = Header(message_type=Type.OFPT_FLOW_REMOVED) #: :class:`~pyof.v0x01.common.flow_match.Match`: OpenFlow Header match = Match() cookie = UBInt64() priority = UBInt16() reason = UBInt8(enum_ref=FlowRemovedReason) #: Align to 32-bits. pad = Pad(1) duration_sec = UBInt32() duration_nsec = UBInt32() idle_timeout = UBInt16() #: Align to 64-bits. pad2 = Pad(2) packet_count = UBInt64() byte_count = UBInt64() def __init__(self, xid=None, match=None, cookie=None, priority=None, reason=None, duration_sec=None, duration_nsec=None, idle_timeout=None, packet_count=None, byte_count=None): """Assign parameters to object attributes. Args: xid (int): OpenFlow Header's xid. match (~pyof.v0x01.common.flow_match.Match): Fields' description. cookie (int): Opaque controller-issued identifier. priority (int): Priority level of flow entry. reason (~pyof.v0x01.asynchronous.flow_removed.FlowRemovedReason): Why the flow was removed. duration_sec (int): Time the flow was alive in seconds. duration_nsec (int): Time the flow was alive in nanoseconds in addition to duration_sec. idle_timeout (int): Idle timeout from original flow mod. packet_count (int): Number of packets. byte_count (int): Byte count. """ super().__init__(xid) self.match = match self.cookie = cookie self.priority = priority self.reason = reason self.duration_sec = duration_sec self.duration_nsec = duration_nsec self.idle_timeout = idle_timeout self.packet_count = packet_count self.byte_count = byte_count python-openflow-2019.2/pyof/v0x01/controller2switch/0000755000175100001630000000000013577157243023117 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/v0x01/controller2switch/features_reply.py0000644000175100001630000000533413577157232026525 0ustar runnerdocker00000000000000"""Define Features Reply classes and related items.""" # Local source tree imports from pyof.foundation.base import GenericBitMask, GenericMessage from pyof.foundation.basic_types import DPID, Pad, UBInt8, UBInt32 from pyof.v0x01.common.action import ActionType from pyof.v0x01.common.header import Header, Type from pyof.v0x01.common.phy_port import ListOfPhyPorts __all__ = ('FeaturesReply', 'Capabilities', 'SwitchFeatures') class Capabilities(GenericBitMask): """Capabilities supported by the datapath.""" #: Flow statistics OFPC_FLOW_STATS = 1 << 0 #: Table statistics OFPC_TABLE_STATS = 1 << 1 #: Port statistics OFPC_PORT_STATS = 1 << 2 #: 802.1d spanning tree OFPC_STP = 1 << 3 #: Reserved, must be zero OFPC_RESERVED = 1 << 4 #: Can reassembe IP fragments OFPC_IP_REASM = 1 << 5 #: Queue statistics OFPC_QUEUE_STATS = 1 << 6 #: Match IP addresses in ARP pkts OFPC_ARP_MATCH_IP = 1 << 7 # Classes 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 = DPID() n_buffers = UBInt32() n_tables = UBInt8() #: Align to 64-bits. pad = Pad(3) # Features capabilities = UBInt32(enum_ref=Capabilities) actions = UBInt32(enum_ref=ActionType) ports = ListOfPhyPorts() def __init__(self, xid=None, datapath_id=None, n_buffers=None, n_tables=None, capabilities=None, actions=None, ports=None): """Create a SwitchFeatures with the optional parameters below. Args: xid (int): xid to be used on the message header. datapath_id (:class:`str` or :class:`.DPID`): datapath unique ID. The lower 48-bits are for MAC address, while the upper 16-bits are implementer-defined. n_buffers (int): UBInt32 max packets buffered at once. n_tables (int): UBInt8 number of tables supported by datapath. capabilities (int): UBInt32 bitmap of supported capabilities. actions (int): UBInt32 Bitmap of supported "action_type"s. ports (int): Port definitions. """ super().__init__(xid) self.datapath_id = datapath_id self.n_buffers = n_buffers self.n_tables = n_tables self.capabilities = capabilities self.actions = actions self.ports = [] if ports is None else ports class FeaturesReply(SwitchFeatures): """'OFPT_FEATURES_REPLY' message.""" python-openflow-2019.2/pyof/v0x01/controller2switch/barrier_request.py0000644000175100001630000000070113577157232026663 0ustar runnerdocker00000000000000"""Defines Barrier Request message.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.v0x01.common.header import Header, Type __all__ = ('BarrierRequest',) # Classes 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) python-openflow-2019.2/pyof/v0x01/controller2switch/__init__.py0000644000175100001630000000007613577157232025231 0ustar runnerdocker00000000000000"""Controller to Switch and Switch to Controller Messages.""" python-openflow-2019.2/pyof/v0x01/controller2switch/port_mod.py0000644000175100001630000000340413577157232025313 0ustar runnerdocker00000000000000"""Modifications to the port from the controller.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import HWAddress, Pad, UBInt16, UBInt32 # Local source tree imports from pyof.v0x01.common.header import Header, Type from pyof.v0x01.common.phy_port import PortConfig, PortFeatures __all__ = ('PortMod',) # Classes class PortMod(GenericMessage): """Implement messages to modify the physical port behavior.""" header = Header(message_type=Type.OFPT_PORT_MOD) port_no = UBInt16() hw_addr = HWAddress() config = UBInt32(enum_ref=PortConfig) mask = UBInt32(enum_ref=PortConfig) advertise = UBInt32(enum_ref=PortFeatures) #: Pad to 64-bits. pad = Pad(4) def __init__(self, xid=None, port_no=None, hw_addr=None, config=None, mask=None, advertise=None): """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.v0x01.common.phy_port.PortConfig): Bitmap of OFPPC_* flags mask (~pyof.v0x01.common.phy_port.PortConfig): Bitmap of OFPPC_* flags to be changed advertise (~pyof.v0x01.common.phy_port.PortFeatures): Bitmap of "ofp_port_features"s """ super().__init__(xid) self.port_no = port_no self.hw_addr = hw_addr self.config = config self.mask = mask self.advertise = advertise python-openflow-2019.2/pyof/v0x01/controller2switch/barrier_reply.py0000644000175100001630000000065713577157232026340 0ustar runnerdocker00000000000000"""Defines Barrier Reply message.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.v0x01.common.header import Header, Type __all__ = ('BarrierReply',) # Classes 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) python-openflow-2019.2/pyof/v0x01/controller2switch/queue_get_config_request.py0000644000175100001630000000165313577157232030554 0ustar runnerdocker00000000000000"""Query the switch for configured queues on a port.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import Pad, UBInt16 # Local source tree imports from pyof.v0x01.common.header import Header, Type from pyof.v0x01.common.phy_port import Port __all__ = ('QueueGetConfigRequest',) 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 python-openflow-2019.2/pyof/v0x01/controller2switch/features_request.py0000644000175100001630000000065713577157232027065 0ustar runnerdocker00000000000000"""Defines Features Request classes and related items.""" from pyof.foundation.base import GenericMessage from pyof.v0x01.common.header import Header, Type __all__ = ('FeaturesRequest',) # Classes 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) python-openflow-2019.2/pyof/v0x01/controller2switch/stats_reply.py0000644000175100001630000000663213577157232026047 0ustar runnerdocker00000000000000"""Response the stat request packet from the controller.""" from importlib import import_module from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData, FixedTypeList, UBInt16 from pyof.v0x01.common.header import Header, Type from pyof.v0x01.controller2switch.common import DescStats, StatsType __all__ = ('StatsReply',) class StatsReply(GenericMessage): """Class implements the response to the stats request.""" #: OpenFlow :class:`~pyof.v0x01.common.header.Header` header = Header(message_type=Type.OFPT_STATS_REPLY) body_type = UBInt16(enum_ref=StatsType) flags = UBInt16() body = BinaryData() def __init__(self, xid=None, body_type=None, flags=None, body=b''): """Create a StatsReply with the optional parameters below. Args: xid (int): xid to be used on the message header. body_type (StatsType): One of the OFPST_* constants. flags (int): OFPSF_REQ_* flags (none yet defined). body (BinaryData): Body of the request. """ super().__init__(xid) self.body_type = body_type self.flags = flags self.body = body def pack(self, value=None): """Pack a StatsReply using the object's attributes. This method will pack the attribute body and body_type before pack the StatsReply object, then will return this struct as a binary data. Returns: bytes: Binary data with StatsReply packed. """ buff = self.body if not value: value = self.body if value and hasattr(value, 'pack'): self.body = BinaryData(value.pack()) stats_reply_packed = super().pack() self.body = buff return stats_reply_packed def unpack(self, buff, 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. It is an inplace method and it receives the binary data of the message **without the header**. This class' unpack method is like the :meth:`.GenericMessage.unpack` one, except for the ``body`` attribute which has its type determined by the ``body_type`` attribute. Args: buff (bytes): Binary data package to be unpacked, without the header. """ super().unpack(buff[offset:]) self._unpack_body() def _unpack_body(self): """Unpack `body` replace it by the result.""" obj = self._get_body_instance() obj.unpack(self.body.value) self.body = obj def _get_body_instance(self): """Return the body instance.""" pyof_class = self._get_body_class() if pyof_class is None: return BinaryData(b'') elif pyof_class is DescStats: return pyof_class() return FixedTypeList(pyof_class=pyof_class) def _get_body_class(self): if isinstance(self.body_type, (int, UBInt16)): self.body_type = self.body_type.enum_ref(self.body_type.value) body_name = self.body_type.name.replace('OFPST_', '').title() module = import_module('pyof.v0x01.controller2switch.common') for class_name in module.__all__: if 'Request' not in class_name and body_name in class_name: return getattr(module, class_name) return None python-openflow-2019.2/pyof/v0x01/controller2switch/packet_out.py0000644000175100001630000001177713577157232025642 0ustar runnerdocker00000000000000"""For the controller to send a packet out through the datapath.""" from copy import deepcopy from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData, UBInt16, UBInt32 from pyof.foundation.exceptions import PackException, ValidationError from pyof.v0x01.common.action import ListOfActions from pyof.v0x01.common.constants import NO_BUFFER from pyof.v0x01.common.header import Header, Type from pyof.v0x01.common.phy_port import Port __all__ = ('PacketOut',) # Classes #: in_port valid virtual port values, for validation _VIRT_IN_PORTS = (Port.OFPP_LOCAL, Port.OFPP_CONTROLLER, Port.OFPP_NONE) class PacketOut(GenericMessage): """Send packet (controller -> datapath).""" #: :class:`~pyof.v0x01.common.header.Header` header = Header(message_type=Type.OFPT_PACKET_OUT) buffer_id = UBInt32() in_port = UBInt16() actions_len = UBInt16() actions = ListOfActions() data = BinaryData() def __init__(self, xid=None, buffer_id=NO_BUFFER, in_port=Port.OFPP_NONE, actions=None, data=b''): """Create a PacketOut with the optional parameters below. Args: xid (int): xid of the message header. buffer_id (int): ID assigned by datapath (-1 if none). in_port (:class:`int` / :class:`~pyof.v0x01.common.phy_port.Port`): Packet's input port (:attr:`.Port.OFPP_NONE` if none). Virtual ports :attr:`Port.OFPP_IN_PORT`, :attr:`Port.OFPP_TABLE`, :attr:`Port.OFPP_NORMAL`, :attr:`Port.OFPP_FLOOD`, and :attr:`Port.OFPP_ALL` cannot be used as input port. actions (~pyof.v0x01.common.action.ListOfActions): List of Actions. data (bytes): Packet data. The length is inferred from the length field in the header. (Only meaningful if ``buffer_id`` == -1). """ super().__init__(xid) self.buffer_id = buffer_id self.in_port = in_port self.actions = [] if actions is None else actions self.data = data def validate(self): """Validate the entire message.""" if not super().is_valid(): raise ValidationError() self._validate_in_port() def is_valid(self): """Answer if this message is valid.""" try: self.validate() return True except ValidationError: return False def pack(self, value=None): """Update the action_len attribute and call super's pack.""" if value is None: self._update_actions_len() return super().pack() elif isinstance(value, type(self)): return value.pack() else: msg = "{} is not an instance of {}".format(value, type(self).__name__) raise PackException(msg) def unpack(self, buff, 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. It is an inplace method and it receives the binary data of the message **without the header**. This class' unpack method is like the :meth:`.GenericMessage.unpack` one, except for the ``actions`` attribute which has a length determined by the ``actions_len`` attribute. Args: buff (bytes): Binary data package to be unpacked, without the header. offset (int): Where to begin unpacking. """ begin = offset for attribute_name, class_attribute in self.get_class_attributes(): if type(class_attribute).__name__ != "Header": attribute = deepcopy(class_attribute) if attribute_name == 'actions': length = self.actions_len.value attribute.unpack(buff[begin:begin+length]) else: attribute.unpack(buff, begin) setattr(self, attribute_name, attribute) begin += attribute.get_size() def _update_actions_len(self): """Update the actions_len field based on actions value.""" if isinstance(self.actions, ListOfActions): self.actions_len = self.actions.get_size() else: self.actions_len = ListOfActions(self.actions).get_size() def _validate_in_port(self): """Validate in_port attribute. A valid port is either: * Greater than 0 and less than or equals to Port.OFPP_MAX * One of the valid virtual ports: Port.OFPP_LOCAL, Port.OFPP_CONTROLLER or Port.OFPP_NONE Raises: ValidationError: If in_port is an invalid port. """ is_valid_range = self.in_port > 0 and self.in_port <= Port.OFPP_MAX is_valid_virtual_in_ports = self.in_port in _VIRT_IN_PORTS if (is_valid_range or is_valid_virtual_in_ports) is False: raise ValidationError(f'{self.in_port} is not a valid input port.') python-openflow-2019.2/pyof/v0x01/controller2switch/queue_get_config_reply.py0000644000175100001630000000223513577157232030214 0ustar runnerdocker00000000000000"""Switch replies to controller.""" # System imports # Third-party imports from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import Pad, UBInt16 # Local source tree imports from pyof.v0x01.common.header import Header, Type from pyof.v0x01.common.phy_port import Port from pyof.v0x01.common.queue import ListOfQueues __all__ = ('QueueGetConfigReply',) class QueueGetConfigReply(GenericMessage): """Class implements the response to the config request.""" header = Header(message_type=Type.OFPT_QUEUE_GET_CONFIG_REPLY) port = UBInt16(enum_ref=Port) #: Pad to 64-bits. pad = Pad(6) 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 (~pyof.v0x01.common.phy_port.Port): Target port for the query. queue (~pyof.v0x01.common.queue.ListOfQueues): List of configured queues. """ super().__init__(xid) self.port = port self.queues = [] if queues is None else queues python-openflow-2019.2/pyof/v0x01/controller2switch/set_config.py0000644000175100001630000000150613577157232025611 0ustar runnerdocker00000000000000"""Define SetConfig message.""" # System imports # Third-party imports # Local imports from pyof.v0x01.common.header import Type from pyof.v0x01.controller2switch.common import SwitchConfig __all__ = ('SetConfig',) class SetConfig(SwitchConfig): """Set config message.""" def __init__(self, xid=None, flags=None, miss_send_len=None): """Create a SetConfig with the optional parameters below. Args: xid (int): xid to be used on the message header. flags (~pyof.v0x01.controller2switch.common.ConfigFlag): OFPC_* flags. miss_send_len (int): UBInt16 max bytes of new flow that the datapath should send to the controller. """ super().__init__(xid, flags, miss_send_len) self.header.message_type = Type.OFPT_SET_CONFIG python-openflow-2019.2/pyof/v0x01/controller2switch/flow_mod.py0000644000175100001630000000701713577157232025302 0ustar runnerdocker00000000000000"""Modifications to the flow table from the controller.""" # System imports from enum import IntEnum from pyof.foundation.base import GenericBitMask, GenericMessage from pyof.foundation.basic_types import UBInt16, UBInt32, UBInt64 from pyof.v0x01.common.action import ListOfActions from pyof.v0x01.common.constants import NO_BUFFER # Local source tree imports from pyof.v0x01.common.flow_match import Match from pyof.v0x01.common.header import Header, Type from pyof.v0x01.common.phy_port import Port # Third-party imports __all__ = ('FlowMod', 'FlowModCommand', 'FlowModFlags') # Enums class FlowModCommand(IntEnum): """List the possible commands for a flow.""" #: New flow OFPFC_ADD = 0 #: Modify all flows OFPFC_MODIFY = 1 #: Modify entry strictly matching wildcards OFPFC_MODIFY_STRICT = 2 #: Delete all matching flows OFPFC_DELETE = 3 #: Strictly match wildcards and priority OFPFC_DELETE_STRICT = 4 class FlowModFlags(GenericBitMask): """Types to be used in Flags field.""" #: Send flow removed message when flow expires or is deleted OFPFF_SEND_FLOW_REM = 1 << 0 #: Check for overlapping entries first OFPFF_CHECK_OVERLAP = 1 << 1 #: Remark this is for emergency OFPFF_EMERG = 1 << 2 # Classes 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_SEND_FLOW_REM, actions=None): """Create a FlowMod with the optional parameters below. Args: xid (int): xid to be used on the message header. match (~pyof.v0x01.common.flow_match.Match): Fields to match. cookie (int): Opaque controller-issued identifier. command (~pyof.v0x01.controller2switch.flow_mod.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 (~pyof.v0x01.common.phy_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 (~pyof.v0x01.controller2switch.flow_mod.FlowModFlags): One of OFPFF_*. actions (~pyof.v0x01.common.action.ListOfActions): The action length is inferred from the length field in the header. """ super().__init__(xid) self.match = match or 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 = actions or [] python-openflow-2019.2/pyof/v0x01/controller2switch/get_config_reply.py0000644000175100001630000000153113577157232027006 0ustar runnerdocker00000000000000"""Defines Get Config Reply message.""" # System imports # Third-party imports from pyof.v0x01.common.header import Type from pyof.v0x01.controller2switch.common import SwitchConfig __all__ = ('GetConfigReply',) class GetConfigReply(SwitchConfig): """Get Config Reply message.""" def __init__(self, xid=None, flags=None, miss_send_len=None): """Create a GetConfigReply with the optional parameters below. Args: xid (int): xid to be used on the message header. flags (~pyof.v0x01.controller2switch.common.ConfigFlag): OFPC_* flags. miss_send_len (int): UBInt16 max bytes of new flow that the datapath should send to the controller. """ super().__init__(xid, flags, miss_send_len) self.header.message_type = Type.OFPT_GET_CONFIG_REPLY python-openflow-2019.2/pyof/v0x01/controller2switch/common.py0000644000175100001630000003773113577157232024772 0ustar runnerdocker00000000000000"""Defines common structures and enums for controller2switch.""" # System imports from enum import IntEnum from pyof.foundation.base import GenericMessage, GenericStruct from pyof.foundation.basic_types import ( BinaryData, Char, Pad, UBInt8, UBInt16, UBInt32, UBInt64) from pyof.foundation.constants import ( DESC_STR_LEN, OFP_MAX_TABLE_NAME_LEN, SERIAL_NUM_LEN) # Local source tree imports from pyof.v0x01.common.action import ListOfActions from pyof.v0x01.common.flow_match import FlowWildCards, Match from pyof.v0x01.common.header import Header from pyof.v0x01.common.phy_port import Port # Third-party imports __all__ = ('ConfigFlag', 'StatsType', 'AggregateStatsReply', 'AggregateStatsRequest', 'DescStats', 'FlowStats', 'FlowStatsRequest', 'PortStats', 'PortStatsRequest', 'QueueStats', 'QueueStatsRequest', 'TableStats', 'VendorStats', 'VendorStatsRequest') # Enums class ConfigFlag(IntEnum): """Configuration Flags. Handling of IP Fragments.""" #: No special handling for fragments OFPC_FRAG_NORMAL = 0 #: Drop fragments OFPC_FRAG_DROP = 1 #: Reassemble (only if OFPC_IP_REASM set) OFPC_FRAG_REASM = 2 OFPC_FRAG_MASK = 3 class StatsType(IntEnum): """Type field to be used both in both request and reply. It specifies the kind of information being passed and determines how the body field is interpreted. """ #: Description of this OpenFlow switch. The request body is empty. OFPST_DESC = 0 #: Individual flow statistics. The request body is struct #: ofp_flow_stats_request. OFPST_FLOW = 1 #: Aggregate flow statistics. The request body is struct #: ofp_aggregate_stats_request. OFPST_AGGREGATE = 2 #: Flow table statistics. The request body is empty. OFPST_TABLE = 3 #: Physical port statistics. The request body is empty. OFPST_PORT = 4 #: Queue statistics for a port. The request body defines the port OFPST_QUEUE = 5 #: Vendor extension. The request and reply bodies begin with a 32-bit #: vendor ID OFPST_VENDOR = 0xffff # Classes class SwitchConfig(GenericMessage): """Used as base class for SET_CONFIG and GET_CONFIG_REPLY messages.""" header = Header() flags = UBInt16(enum_ref=ConfigFlag) miss_send_len = UBInt16() def __init__(self, xid=None, flags=None, miss_send_len=None): """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 def __repr__(self): """Show a full representation of the object.""" return "%s(xid=%r, flags=%s, miss_send_len=%r)" \ % (self.__class__.__name__, self.header.xid, self.flags, self.miss_send_len) class AggregateStatsReply(GenericStruct): """Body of reply to OFPST_AGGREGATE request.""" packet_count = UBInt64() byte_count = UBInt64() flow_count = UBInt32() #: Align to 64 bits pad = Pad(4) def __init__(self, packet_count=None, byte_count=None, flow_count=None): """Create a AggregateStatsReply with the optional parameters below. Args: packet_count (int): Number of packets in flows byte_count (int): Number of bytes in flows flow_count (int): Number of flows """ super().__init__() self.packet_count = packet_count self.byte_count = byte_count self.flow_count = flow_count class AggregateStatsRequest(GenericStruct): """Body for ofp_stats_request of type OFPST_AGGREGATE.""" match = Match() table_id = UBInt8() #: Align to 32 bits pad = Pad(1) out_port = UBInt16() def __init__(self, match=Match(), table_id=0xff, out_port=Port.OFPP_NONE): """Create a AggregateStatsRequest with the optional parameters below. Args: match (~pyof.v0x01.common.flow_match.Match): Fields to match. table_id (int): ID of table to read (from pyof_table_stats) 0xff for all tables or 0xfe for emergency. out_port (int): Require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. """ super().__init__() self.match = match self.table_id = table_id self.out_port = out_port class DescStats(GenericStruct): """Information available from the OFPST_DESC stats request. Information about the switch manufacturer, hardware revision, software revision, serial number and a description field. """ mfr_desc = Char(length=DESC_STR_LEN) hw_desc = Char(length=DESC_STR_LEN) sw_desc = Char(length=DESC_STR_LEN) serial_num = Char(length=SERIAL_NUM_LEN) dp_desc = Char(length=DESC_STR_LEN) def __init__(self, mfr_desc=None, hw_desc=None, sw_desc=None, serial_num=None, dp_desc=None): """Create a DescStats with the optional parameters below. Args: mfr_desc (str): Manufacturer description hw_desc (str): Hardware description sw_desc (str): Software description serial_num (str): Serial number dp_desc (str): Human readable description of datapath """ super().__init__() self.mfr_desc = mfr_desc self.hw_desc = hw_desc self.sw_desc = sw_desc self.serial_num = serial_num self.dp_desc = dp_desc class FlowStats(GenericStruct): """Body of reply to OFPST_FLOW request.""" length = UBInt16() table_id = UBInt8() #: Align to 32 bits. pad = Pad(1) match = Match() duration_sec = UBInt32() duration_nsec = UBInt32() priority = UBInt16() idle_timeout = UBInt16() hard_timeout = UBInt16() #: Align to 64-bits pad2 = Pad(6) cookie = UBInt64() packet_count = UBInt64() byte_count = UBInt64() actions = ListOfActions() def __init__(self, length=None, table_id=None, match=None, duration_sec=None, duration_nsec=None, priority=None, idle_timeout=None, hard_timeout=None, cookie=None, packet_count=None, byte_count=None, actions=None): """Create a FlowStats with the optional parameters below. Args: length (int): Length of this entry. table_id (int): ID of table flow came from. match (~pyof.v0x01.common.flow_match.Match): Description of fields. duration_sec (int): Time flow has been alive in seconds. duration_nsec (int): Time flow has been alive in nanoseconds in addition to duration_sec. priority (int): Priority of the entry. Only meaningful when this is not an exact-match entry. idle_timeout (int): Number of seconds idle before expiration. hard_timeout (int): Number of seconds before expiration. cookie (int): Opaque controller-issued identifier. packet_count (int): Number of packets in flow. byte_count (int): Number of bytes in flow. actions (:class:`~pyof.v0x01.common.actions.ListOfActions`): List of Actions. """ super().__init__() self.length = length self.table_id = table_id self.match = match self.duration_sec = duration_sec self.duration_nsec = duration_nsec self.priority = priority self.idle_timeout = idle_timeout self.hard_timeout = hard_timeout self.cookie = cookie self.packet_count = packet_count self.byte_count = byte_count self.actions = [] if actions is None else actions 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 FlowStatsRequest(GenericStruct): """Body for ofp_stats_request of type OFPST_FLOW.""" match = Match() table_id = UBInt8() #: Align to 32 bits. pad = Pad(1) out_port = UBInt16() def __init__(self, match=None, table_id=0xff, out_port=Port.OFPP_NONE): """Create a FlowStatsRequest with the optional parameters below. Args: match (:class:`~pyof.v0x01.common.flow_match.Match`): Fields to match. table_id (int): ID of table to read (from pyof_table_stats) 0xff for all tables or 0xfe for emergency. out_port (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): Require matching entries to include this as an output port. A value of :attr:`.Port.OFPP_NONE` indicates no restriction. """ super().__init__() self.match = Match() if match is None else match self.table_id = table_id self.out_port = out_port class PortStats(GenericStruct): """Body of reply to OFPST_PORT request. If a counter is unsupported, set the field to all ones. """ port_no = UBInt16() #: Align to 64-bits. pad = Pad(6) rx_packets = UBInt64() tx_packets = UBInt64() rx_bytes = UBInt64() tx_bytes = UBInt64() rx_dropped = UBInt64() tx_dropped = UBInt64() rx_errors = UBInt64() tx_errors = UBInt64() rx_frame_err = UBInt64() rx_over_err = UBInt64() rx_crc_err = UBInt64() collisions = UBInt64() def __init__(self, port_no=None, rx_packets=None, tx_packets=None, rx_bytes=None, tx_bytes=None, rx_dropped=None, tx_dropped=None, rx_errors=None, tx_errors=None, rx_frame_err=None, rx_over_err=None, rx_crc_err=None, collisions=None): """Create a PortStats with the optional parameters below. Args: port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): Port number. rx_packets (int): Number of received packets. tx_packets (int): Number of transmitted packets. rx_bytes (int): Number of received bytes. tx_bytes (int): Number of transmitted bytes. rx_dropped (int): Number of packets dropped by RX. tx_dropped (int): Number of packets dropped by TX. rx_errors (int): Number of receive errors. This is a super-set of more specific receive errors and should be greater than or equal to the sum of all rx_*_err values. tx_errors (int): Number of transmit errors. This is a super-set of more specific transmit errors and should be greater than or equal to the sum of all tx_*_err values (none currently defined). rx_frame_err (int): Number of frame alignment errors. rx_over_err (int): Number of packets with RX overrun. rx_crc_err (int): Number of CRC errors. collisions (int): Number of collisions. """ super().__init__() self.port_no = port_no self.rx_packets = rx_packets self.tx_packets = tx_packets self.rx_bytes = rx_bytes self.tx_bytes = tx_bytes self.rx_dropped = rx_dropped self.tx_dropped = tx_dropped self.rx_errors = rx_errors self.tx_errors = tx_errors self.rx_frame_err = rx_frame_err self.rx_over_err = rx_over_err self.rx_crc_err = rx_crc_err self.collisions = collisions 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): """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 QueueStats(GenericStruct): """Implements the reply body of a port_no.""" port_no = UBInt16() #: Align to 32-bits. pad = Pad(2) queue_id = UBInt32() tx_bytes = UBInt64() tx_packets = UBInt64() tx_errors = UBInt64() def __init__(self, port_no=None, queue_id=None, tx_bytes=None, tx_packets=None, tx_errors=None): """Create a QueueStats with the optional parameters below. Args: port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): Port Number. queue_id (int): Queue ID. tx_bytes (int): Number of transmitted bytes. tx_packets (int): Number of transmitted packets. tx_errors (int): Number of packets dropped due to overrun. """ super().__init__() self.port_no = port_no self.queue_id = queue_id self.tx_bytes = tx_bytes self.tx_packets = tx_packets self.tx_errors = tx_errors class QueueStatsRequest(GenericStruct): """Implements the request body of a ``port_no``.""" port_no = UBInt16() #: Align to 32-bits pad = Pad(2) queue_id = UBInt32() def __init__(self, port_no=None, queue_id=None): """Create a QueueStatsRequest with the optional parameters below. Args: port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): All ports if :attr:`.Port.OFPP_ALL`. queue_id (int): All queues if OFPQ_ALL (``0xfffffff``). """ super().__init__() self.port_no = port_no self.queue_id = queue_id class TableStats(GenericStruct): """Body of reply to OFPST_TABLE request.""" table_id = UBInt8() #: Align to 32-bits. pad = Pad(3) name = Char(length=OFP_MAX_TABLE_NAME_LEN) wildcards = UBInt32(enum_ref=FlowWildCards) max_entries = UBInt32() active_count = UBInt32() count_lookup = UBInt64() count_matched = UBInt64() def __init__(self, table_id=None, name=None, wildcards=None, max_entries=None, active_count=None, count_lookup=None, count_matched=None): """Create a TableStats with the optional parameters below. Args: table_id (int): Identifier of table. Lower numbered tables are consulted first. name (str): Table name. wildcards (:class:`~pyof.v0x01.common.flow_match.FlowWildCards`): Bitmap of OFPFW_* wildcards that are supported by the table. max_entries (int): Max number of entries supported. active_count (int): Number of active entries. count_lookup (int): Number of packets looked up in table. count_matched (int): Number of packets that hit table. """ super().__init__() self.table_id = table_id self.name = name self.wildcards = wildcards self.max_entries = max_entries self.active_count = active_count self.count_lookup = count_lookup self.count_matched = count_matched class VendorStats(GenericStruct): """Vendor extension.""" vendor = UBInt32() body = BinaryData() def __init__(self, vendor=None, body=b''): """Create instance attributes. Args: vendor (int): 32-bit vendor ID. body (bytes): Vendor-defined body """ super().__init__() self.vendor = vendor self.body = body VendorStatsRequest = VendorStats python-openflow-2019.2/pyof/v0x01/controller2switch/get_config_request.py0000644000175100001630000000053213577157232027343 0ustar runnerdocker00000000000000"""Defines Get Config Request classes and related items.""" from pyof.foundation.base import GenericMessage from pyof.v0x01.common.header import Header, Type __all__ = ('GetConfigRequest',) # Classe class GetConfigRequest(GenericMessage): """Get Config Request message.""" header = Header(message_type=Type.OFPT_GET_CONFIG_REQUEST) python-openflow-2019.2/pyof/v0x01/controller2switch/stats_request.py0000644000175100001630000000457613577157232026411 0ustar runnerdocker00000000000000"""Query the datapath about its current state.""" # System imports # Third-party imports from importlib import import_module from pyof.foundation.base import GenericMessage from pyof.foundation.basic_types import BinaryData, FixedTypeList, UBInt16 # Local imports from pyof.v0x01.common.header import Header, Type from pyof.v0x01.controller2switch.common import StatsType __all__ = ('StatsRequest',) class StatsRequest(GenericMessage): """Request statistics to switch.""" #: OpenFlow :class:`~pyof.v0x01.common.header.Header` header = Header(message_type=Type.OFPT_STATS_REQUEST) body_type = UBInt16(enum_ref=StatsType) flags = UBInt16() body = BinaryData() def __init__(self, xid=None, body_type=None, flags=0, body=b''): """Create a StatsRequest with the optional parameters below. Args: xid (int): xid to be used on the message header. body_type (StatsType): One of the OFPST_* constants. flags (int): OFPSF_REQ_* flags (none yet defined). body (BinaryData): Body of the request. """ super().__init__(xid) self.body_type = body_type self.flags = flags self.body = body def pack(self, value=None): """Pack according to :attr:`body_type`. Make `body` a binary pack before packing this object. Then, restore body. """ backup = self.body if not value: value = self.body if hasattr(value, 'pack'): self.body = value.pack() stats_request_packed = super().pack() self.body = backup return stats_request_packed def unpack(self, buff, offset=0): """Unpack according to :attr:`body_type`.""" super().unpack(buff) class_name = self._get_body_class() buff = self.body.value self.body = FixedTypeList(pyof_class=class_name) self.body.unpack(buff) def _get_body_class(self): if isinstance(self.body_type, (int, UBInt16)): self.body_type = self.body_type.enum_ref(self.body_type.value) module = import_module('pyof.v0x01.controller2switch.common') body_name = self.body_type.name.replace('OFPST_', '').title() for class_name in module.__all__: if 'Request' in class_name and body_name in class_name: return getattr(module, class_name) return None python-openflow-2019.2/pyof/v0x01/common/0000755000175100001630000000000013577157243020720 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/v0x01/common/phy_port.py0000644000175100001630000001537213577157232023144 0ustar runnerdocker00000000000000"""Defines physical port classes and related items.""" # System imports from enum import IntEnum # Local source tree imports from pyof.foundation.base import GenericBitMask, GenericStruct from pyof.foundation.basic_types import ( Char, FixedTypeList, HWAddress, UBInt16, UBInt32) from pyof.foundation.constants import OFP_MAX_PORT_NAME_LEN # Third-party imports __all__ = ('PhyPort', 'ListOfPhyPorts', 'Port', 'PortConfig', 'PortFeatures', 'PortState') class Port(IntEnum): """Port numbering. Physical ports are numbered starting from 1. Port number 0 is reserved by the specification and must not be used for a switch physical port. """ #: Maximum number of physical switch ports. OFPP_MAX = 0xff00 #: Send the packet out the input port. This virtual port must be explicitly #: used in order to send back out of the input port. OFPP_IN_PORT = 0xfff8 #: Perform actions in flow table. #: NB: This can only be the destination port for packet-out messages OFPP_TABLE = 0xfff9 #: Process with normal L2/L3 switching. OFPP_NORMAL = 0xfffa #: All physical ports except input port and those disabled by STP OFPP_FLOOD = 0xfffb #: All physical ports except input port OFPP_ALL = 0xfffc #: Send to controller OFPP_CONTROLLER = 0xfffd #: Local openflow "port" OFPP_LOCAL = 0xfffe #: Not associated with a physical port OFPP_NONE = 0xffff class PortConfig(GenericBitMask): """Flags to indicate behavior of the physical port. These flags are used in OFPPhyPort to describe the current configuration. They are used in the OFPPortMod message to configure the port's behavior. """ #: Port is administratively down. OFPPC_PORT_DOWN = 1 << 0 #: Disable 802.1D spanning tree on port. OFPPC_NO_STP = 1 << 1 #: Drop all packets except 802.1D spanning tree. OFPPC_NO_RECV = 1 << 2 #: Drop received 802.1D STP packets. OFPPC_NO_RECV_STP = 1 << 3 #: Do not include this port when flooding. OFPPC_FLOOD = 1 << 4 #: Drop packets forwarded to port. OFPPC_NO_FWD = 1 << 5 #: Do not send packet-in msgs for port. OFPPC_NO_PACKET_IN = 1 << 6 class PortFeatures(GenericBitMask): """Physical ports features. The :attr:`curr`, :attr:`advertised`, :attr:`supported`, and :attr:`peer` fields indicate link modes (10M to 10G full and half-duplex), link type (copper/fiber) and link features (autone-gotiation and pause). """ #: 10 Mb half-duplex rate support. OFPPF_10MB_HD = 1 << 0 #: 10 Mb full-duplex rate support. OFPPF_10MB_FD = 1 << 1 #: 100 Mb half-duplex rate support. OFPPF_100MB_HD = 1 << 2 #: 100 Mb full-duplex rate support. OFPPF_100MB_FD = 1 << 3 #: 1 Gb half-duplex rate support. OFPPF_1GB_HD = 1 << 4 #: 1 Gb full-duplex rate support. OFPPF_1GB_FD = 1 << 5 #: 10 Gb full-duplex rate support. OFPPF_10GB_FD = 1 << 6 #: Copper medium. OFPPF_COPPER = 1 << 7 #: Fiber medium. OFPPF_FIBER = 1 << 8 #: Auto-negotiation. OFPPF_AUTONEG = 1 << 9 #: Pause. OFPPF_PAUSE = 1 << 10 #: Asymmetric pause. OFPPF_PAUSE_ASYM = 1 << 11 class PortState(GenericBitMask): """Current state of the physical port. These are not configurable from the controller. The ``OFPPS_STP_*`` bits have no effect on switch operation. The controller must adjust :attr:`PortConfig.OFPPC_NO_RECV`, :attr:`~PortConfig.OFPPC_NO_FWD`, and :attr:`~PortConfig.OFPPC_NO_PACKET_IN` appropriately to fully implement an 802.1D spanning tree. """ #: Not learning or relaying frames. OFPPS_LINK_DOWN = 1 << 0 #: Not learning or relaying frames. OFPPS_STP_LISTEN = 0 << 8 #: Learning but not relaying frames. OFPPS_STP_LEARN = 1 << 8 #: Learning and relaying frames. OFPPS_STP_FORWARD = 2 << 8 #: Not part of spanning tree. OFPPS_STP_BLOCK = 3 << 8 # Classes class PhyPort(GenericStruct): """Description of a physical port. The port_no field is a value the datapath associates with a physical port. The hw_addr field typically is the MAC address for the port; :data:`OFP_ETH_ALEN` is 6. The name field is a null-terminated string containing a human-readable name for the interface. The value of :data:`OFP_MAX_PORT_NAME_LEN` is 16. :attr:`curr`, :attr:`advertised`, :attr:`supported` and :attr:`peer` are bitmaps of :class:`~pyof.v0x01.common.phy_port.PortFeatures` enum values that describe features. If unsupported or unavailable, set all bits to zero. """ port_no = UBInt16() hw_addr = HWAddress() name = Char(length=OFP_MAX_PORT_NAME_LEN) config = UBInt32(enum_ref=PortConfig) state = UBInt32(enum_ref=PortState) curr = UBInt32(enum_ref=PortFeatures) advertised = UBInt32(enum_ref=PortFeatures) supported = UBInt32(enum_ref=PortFeatures) peer = UBInt32(enum_ref=PortFeatures) def __init__(self, port_no=None, hw_addr=None, name=None, config=0, state=PortState.OFPPS_STP_LISTEN, curr=0, advertised=0, supported=0, peer=0): """Create a PhyPort with the optional parameters below. Args: port_no (int): Port number. hw_addr (HWAddress): Hardware address. name(str): Null-terminated name. config (~pyof.v0x01.common.phy_port.PortConfig): Bitmap of OFPPC* flags. state (~pyof.v0x01.common.phy_port.PortState): Bitmap of OFPPS* flags. curr (~pyof.v0x01.common.phy_port.PortFeatures): Current features. advertised (~pyof.v0x01.common.phy_port.PortFeatures): Features being advertised by the port. supported (~pyof.v0x01.common.phy_port.PortFeatures): Features supported by the port. peer (~pyof.v0x01.common.phy_port.PortFeatures): Features advertised by peer. """ super().__init__() self.port_no = port_no self.hw_addr = hw_addr self.name = name self.config = config self.state = state self.curr = curr self.advertised = advertised self.supported = supported self.peer = peer class ListOfPhyPorts(FixedTypeList): """List of PhyPorts. Represented by instances of PhyPort and used on :class:`pyof.v0x01.common.phy_port.FeaturesReply`/ :class:`pyof.v0x01.controller2switch.features_reply.SwitchFeatures` objects. """ def __init__(self, items=None): """Create a ListOfPhyPorts with the optional parameters below. Args: items (:class:`list`, :class:`PhyPort`): One :class:`PhyPort` instance or list. """ super().__init__(pyof_class=PhyPort, items=items) python-openflow-2019.2/pyof/v0x01/common/__init__.py0000644000175100001630000000006313577157232023026 0ustar runnerdocker00000000000000"""Common structures used on OpenFlow Protocol.""" python-openflow-2019.2/pyof/v0x01/common/flow_match.py0000644000175100001630000001645513577157232023426 0ustar runnerdocker00000000000000"""Defines flow statistics structures and related items.""" # System imports # Third-party imports # Local source tree imports from pyof.foundation.base import GenericBitMask, GenericStruct from pyof.foundation.basic_types import ( HWAddress, IPAddress, Pad, UBInt8, UBInt16, UBInt32) __all__ = ('Match', 'FlowWildCards') class FlowWildCards(GenericBitMask): """Wildcards used to identify flows. ``OFPFW_NW_SRC_*``: IP source address wildcard bit count. 0 is exact match, 1 ignores the LSB, 2 ignores the 2 least-significant bits, ..., 32 and higher wildcard the entire field. This is the *opposite* of the usual convention where e.g. /24 indicates that 8 bits (not 24 bits) are wildcarded. ``OFPFW_NW_DST_*``: IP destination address wildcard bit count. Same format as source. """ #: Switch input port. OFPFW_IN_PORT = 1 << 0 #: VLAN id. OFPFW_DL_VLAN = 1 << 1 #: Ethernet source address. OFPFW_DL_SRC = 1 << 2 #: Ethernet destination address. OFPFW_DL_DST = 1 << 3 #: Ethernet frame type. OFPFW_DL_TYPE = 1 << 4 #: IP protocol. OFPFW_NW_PROTO = 1 << 5 #: TCP/UDP source port. OFPFW_TP_SRC = 1 << 6 #: TCP/UDP destination port. OFPFW_TP_DST = 1 << 7 # See class docstring OFPFW_NW_SRC_SHIFT = 8 OFPFW_NW_SRC_BITS = 6 OFPFW_NW_SRC_MASK = ((1 << OFPFW_NW_SRC_BITS) - 1) << OFPFW_NW_SRC_SHIFT OFPFW_NW_SRC_ALL = 32 << OFPFW_NW_SRC_SHIFT # See class docstring OFPFW_NW_DST_SHIFT = 14 OFPFW_NW_DST_BITS = 6 OFPFW_NW_DST_MASK = ((1 << OFPFW_NW_DST_BITS) - 1) << OFPFW_NW_DST_SHIFT OFPFW_NW_DST_ALL = 32 << OFPFW_NW_DST_SHIFT OFPFW_DL_VLAN_PCP = 1 << 20 OFPFW_NW_TOS = 1 << 21 #: Wildcard all fields. OFPFW_ALL = ((1 << 22) - 1) # Classes class Match(GenericStruct): """Describes a flow entry. Fields to match against flows.""" #: Wildcards fields. wildcards = UBInt32(value=FlowWildCards.OFPFW_ALL, enum_ref=FlowWildCards) #: Input switch port. in_port = UBInt16(0) #: Ethernet source address. (default: '00:00:00:00:00:00') dl_src = HWAddress() #: Ethernet destination address. (default: '00:00:00:00:00:00') dl_dst = HWAddress() #: Input VLAN id. (default: 0) dl_vlan = UBInt16(0) #: Input VLAN priority. (default: 0) dl_vlan_pcp = UBInt8(0) #: Align to 64-bits. pad1 = Pad(1) #: Ethernet frame type. (default: 0) dl_type = UBInt16(0) #: IP ToS (actually DSCP field, 6 bits). (default: 0) nw_tos = UBInt8(0) #: IP protocol or lower 8 bits of ARP opcode. (default: 0) nw_proto = UBInt8(0) #: Align to 64-bits. pad2 = Pad(2) #: IP source address. (default: '0.0.0.0/0') nw_src = IPAddress('0.0.0.0/0') #: IP destination address. (default: '0.0.0.0/0') nw_dst = IPAddress('0.0.0.0/0') #: TCP/UDP source port. (default: 0) tp_src = UBInt16(0) #: TCP/UDP destination port. (default: 0) tp_dst = UBInt16(0) def __init__(self, **kwargs): """All the constructor parameters below are optional. Args: wildcards (FlowWildCards): Wildcards fields. (Default: OFPFW_ALL) in_port (int): Input switch port. (default: 0) dl_src (HWAddress): Ethernet source address. (default: '00:00:00:00:00:00') dl_dst (HWAddress): Ethernet destination address. (default: '00:00:00:00:00:00') dl_vlan (int): Input VLAN id. (default: 0) dl_vlan_pcp (int): Input VLAN priority. (default: 0) dl_type (int): Ethernet frame type. (default: 0) nw_tos (int): IP ToS (actually DSCP field, 6 bits). (default: 0) nw_proto (int): IP protocol or lower 8 bits of ARP opcode. (default: 0) nw_src (IPAddress): IP source address. (default: '0.0.0.0/0') nw_dst (IPAddress): IP destination address. (default: '0.0.0.0/0') tp_src (int): TCP/UDP source port. (default: 0) tp_dst (int): TCP/UDP destination port. (default: 0) """ super().__init__() for field, value in kwargs.items(): setattr(self, field, value) def __setattr__(self, name, value): # converts string ip_address to IPAddress if isinstance(getattr(Match, name), IPAddress) and \ not isinstance(value, IPAddress): if isinstance(value, list): value = ".".join(str(x) for x in value) value = IPAddress(value) # noqa # convertstring or list of hwaddress to HWAddress elif isinstance(getattr(Match, name), HWAddress) and \ not isinstance(value, HWAddress): if isinstance(value, list): values = ["{0:0{1}x}".format(x, 2) for x in value] value = ":".join(values) value = HWAddress(value) super().__setattr__(name, value) self.fill_wildcards(name, value) 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): Binary buffer. offset (int): Where to begin unpacking. Raises: :exc:`~.exceptions.UnpackException`: If unpack fails. """ super().unpack(buff, offset) self.wildcards = UBInt32(value=FlowWildCards.OFPFW_ALL, enum_ref=FlowWildCards) self.wildcards.unpack(buff, offset) def fill_wildcards(self, field=None, value=0): """Update wildcards attribute. This method update a wildcards considering the attributes of the current instance. Args: field (str): Name of the updated field. value (GenericType): New value used in the field. """ if field in [None, 'wildcards'] or isinstance(value, Pad): return default_value = getattr(Match, field) if isinstance(default_value, IPAddress): if field == 'nw_dst': shift = FlowWildCards.OFPFW_NW_DST_SHIFT base_mask = FlowWildCards.OFPFW_NW_DST_MASK else: shift = FlowWildCards.OFPFW_NW_SRC_SHIFT base_mask = FlowWildCards.OFPFW_NW_SRC_MASK # First we clear the nw_dst/nw_src mask related bits on the current # wildcard by setting 0 on all of them while we keep all other bits # as they are. self.wildcards &= FlowWildCards.OFPFW_ALL ^ base_mask # nw_dst and nw_src wildcard fields have 6 bits each. # "base_mask" is the 'all ones' for those 6 bits. # Once we know the netmask, we can calculate the these 6 bits # wildcard value and reverse them in order to insert them at the # correct position in self.wildcards wildcard = (value.max_prefix - value.netmask) << shift self.wildcards |= wildcard else: wildcard_field = "OFPFW_{}".format(field.upper()) wildcard = getattr(FlowWildCards, wildcard_field) if value == default_value and not (self.wildcards & wildcard) or \ value != default_value and (self.wildcards & wildcard): self.wildcards ^= wildcard python-openflow-2019.2/pyof/v0x01/common/queue.py0000644000175100001630000000706213577157232022421 0ustar runnerdocker00000000000000"""Defines OpenFlow queues structures and related items.""" # System imports from enum import IntEnum # Local source tree imports from pyof.foundation.base import GenericStruct from pyof.foundation.basic_types import FixedTypeList, Pad, UBInt16, UBInt32 # Third-party imports __all__ = ('QueuePropHeader', 'PacketQueue', 'QueuePropMinRate', 'QueueProperties', 'ListOfProperties', 'ListOfQueues') # Enums class QueueProperties(IntEnum): """Describe queue properties.""" #: No property defined for queue (default) OFPQT_NONE = 0 #: Minimum datarate guaranteed OFPQT_MIN_RATE = 1 # Classes class ListOfProperties(FixedTypeList): """List of properties. Represented by instances of :class:`QueuePropHeader` and used on :class:`PacketQueue` objects. """ def __init__(self, items=None): """Create a ListOfProperties with the optional parameters below. Args: items (:class:`list` of/or :class:`QueuePropHeader`): :class:`QueuePropHeader` instance or list of instances. """ super().__init__(pyof_class=QueuePropHeader, items=items) class QueuePropHeader(GenericStruct): """Describe the header of each queue property.""" queue_property = UBInt16(enum_ref=QueueProperties) length = UBInt16() #: 64-bit alignment pad = Pad(4) def __init__(self, queue_property=None, length=None): """Create a QueuePropHeader with the optional parameters below. Args: queue_property (~pyof.v0x01.common.queue.QueueProperties): The queue property. length (int): Length of property, including this header. """ super().__init__() self.queue_property = queue_property self.length = length class PacketQueue(GenericStruct): """Describe a queue.""" queue_id = UBInt32() length = UBInt16() #: 64-bit alignment. pad = Pad(2) properties = ListOfProperties() def __init__(self, queue_id=None, length=None, properties=None): """Create a PacketQueue with the optional parameters below. Args: queue_id (int): ID of the specific queue. length (int): Length in bytes of this queue desc. properties(~pyof.v0x01.common.queue.ListOfProperties): Queue's list of properties. Default is an empty list. """ super().__init__() self.queue_id = queue_id self.length = length self.properties = [] if properties is None else properties 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 ListOfQueues(FixedTypeList): """List of queues. Represented by instances of :class:`PacketQueue` and used on :class:`QueueGetConfigReply` objects. """ def __init__(self, items=None): """Create a ListOfQueues with the optional parameters below. Args: items (:class:`list` of/or :class:`PacketQueue`): :class:`PacketQueue` instance or list of instances. """ super().__init__(pyof_class=PacketQueue, items=items) python-openflow-2019.2/pyof/v0x01/common/utils.py0000644000175100001630000001155113577157232022433 0ustar runnerdocker00000000000000"""Helper python-openflow functions.""" # System imports # Third-party imports # Local source tree imports # Importing asynchronous messages from pyof.v0x01.asynchronous.error_msg import ErrorMsg from pyof.v0x01.asynchronous.flow_removed import FlowRemoved from pyof.v0x01.asynchronous.packet_in import PacketIn from pyof.v0x01.asynchronous.port_status import PortStatus # Importing controller2switch messages from pyof.v0x01.common.header import Header, Type from pyof.v0x01.controller2switch.barrier_reply import BarrierReply from pyof.v0x01.controller2switch.barrier_request import BarrierRequest from pyof.v0x01.controller2switch.features_reply import FeaturesReply from pyof.v0x01.controller2switch.features_request import FeaturesRequest from pyof.v0x01.controller2switch.flow_mod import FlowMod from pyof.v0x01.controller2switch.get_config_reply import GetConfigReply from pyof.v0x01.controller2switch.get_config_request import GetConfigRequest from pyof.v0x01.controller2switch.packet_out import PacketOut from pyof.v0x01.controller2switch.port_mod import PortMod from pyof.v0x01.controller2switch.queue_get_config_reply import ( QueueGetConfigReply) from pyof.v0x01.controller2switch.queue_get_config_request import ( QueueGetConfigRequest) from pyof.v0x01.controller2switch.set_config import SetConfig from pyof.v0x01.controller2switch.stats_reply import StatsReply from pyof.v0x01.controller2switch.stats_request import StatsRequest # Importing symmetric messages from pyof.v0x01.symmetric.echo_reply import EchoReply from pyof.v0x01.symmetric.echo_request import EchoRequest from pyof.v0x01.symmetric.hello import Hello from pyof.v0x01.symmetric.vendor_header import VendorHeader __all__ = ('MESSAGE_TYPES', 'new_message_from_header', 'new_message_from_message_type', 'unpack_message') MESSAGE_TYPES = { str(Type.OFPT_HELLO): Hello, str(Type.OFPT_ERROR): ErrorMsg, str(Type.OFPT_ECHO_REQUEST): EchoRequest, str(Type.OFPT_ECHO_REPLY): EchoReply, str(Type.OFPT_VENDOR): VendorHeader, str(Type.OFPT_FEATURES_REQUEST): FeaturesRequest, str(Type.OFPT_FEATURES_REPLY): FeaturesReply, str(Type.OFPT_GET_CONFIG_REQUEST): GetConfigRequest, str(Type.OFPT_GET_CONFIG_REPLY): GetConfigReply, str(Type.OFPT_SET_CONFIG): SetConfig, str(Type.OFPT_PACKET_IN): PacketIn, str(Type.OFPT_FLOW_REMOVED): FlowRemoved, str(Type.OFPT_PORT_STATUS): PortStatus, str(Type.OFPT_PACKET_OUT): PacketOut, str(Type.OFPT_FLOW_MOD): FlowMod, str(Type.OFPT_PORT_MOD): PortMod, str(Type.OFPT_STATS_REQUEST): StatsRequest, str(Type.OFPT_STATS_REPLY): StatsReply, str(Type.OFPT_BARRIER_REQUEST): BarrierRequest, str(Type.OFPT_BARRIER_REPLY): BarrierReply, str(Type.OFPT_QUEUE_GET_CONFIG_REQUEST): QueueGetConfigRequest, str(Type.OFPT_QUEUE_GET_CONFIG_REPLY): QueueGetConfigReply } def new_message_from_message_type(message_type): """Given an OpenFlow Message Type, return an empty message of that type. Args: messageType (:class:`~pyof.v0x01.common.header.Type`): Python-openflow message. Returns: Empty OpenFlow message of the requested message type. Raises: KytosUndefinedMessageType: Unkown Message_Type. """ message_type = str(message_type) if message_type not in MESSAGE_TYPES: raise ValueError('"{}" is not known.'.format(message_type)) message_class = MESSAGE_TYPES.get(message_type) message_instance = message_class() return message_instance def new_message_from_header(header): """Given an OF Header, return an empty message of header's message_type. Args: header (~pyof.v0x01.common.header.Header): Unpacked OpenFlow Header. Returns: Empty OpenFlow message of the same type of message_type attribute from the given header. The header attribute of the message will be populated. Raises: KytosUndefinedMessageType: Unkown Message_Type. """ message_type = header.message_type if not isinstance(message_type, Type): try: if isinstance(message_type, str): message_type = Type[message_type] elif isinstance(message_type, int): message_type = Type(message_type) except ValueError: raise ValueError message = new_message_from_message_type(message_type) message.header.xid = header.xid message.header.length = header.length return message 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 python-openflow-2019.2/pyof/v0x01/common/action.py0000644000175100001630000002541213577157232022551 0ustar runnerdocker00000000000000"""Defines actions that may be associated with flows packets.""" # System imports # Local source tree imports from pyof.foundation.base import GenericBitMask, GenericStruct from pyof.foundation.basic_types import ( FixedTypeList, HWAddress, Pad, UBInt8, UBInt16, UBInt32) from pyof.foundation.constants import UBINT16_MAX_VALUE # Third-party imports __all__ = ('ActionType', 'ActionHeader', 'ActionOutput', 'ActionStripVlan', 'ActionEnqueue', 'ActionVlanVid', 'ActionVlanPCP', 'ActionDLAddr', 'ActionNWAddr', 'ActionNWTos', 'ActionTPPort', 'ActionVendorHeader', 'ListOfActions') # Enums class ActionType(GenericBitMask): """Actions associated with flows and packets.""" #: Output to switch port. OFPAT_OUTPUT = 0 #: Set the 802.1q VLAN id. OFPAT_SET_VLAN_VID = 1 #: Set the 802.1q priority. OFPAT_SET_VLAN_PCP = 2 #: Strip the 802.1q header. OFPAT_STRIP_VLAN = 3 #: Ethernet source address. OFPAT_SET_DL_SRC = 4 #: Ethernet destination address. OFPAT_SET_DL_DST = 5 #: IP source address. OFPAT_SET_NW_SRC = 6 #: IP destination address. OFPAT_SET_NW_DST = 7 #: IP ToS (DSCP field, 6 bits). OFPAT_SET_NW_TOS = 8 #: TCP/UDP source port. OFPAT_SET_TP_SRC = 9 #: TCP/UDP destination port. OFPAT_SET_TP_DST = 10 #: Output to queue. OFPAT_ENQUEUE = 11 #: Vendor specific. OFPAT_VENDOR = 0xffff # Classes class ActionHeader(GenericStruct): """Defines the Header that is common to all actions.""" action_type = UBInt16(enum_ref=ActionType) length = UBInt16() # Pad for 64-bit alignment. # This attribute will not be implemented since not all subclasses from # this class will hold it on the same place and with the same size. # pad = Pad(4) _allowed_types = () def __init__(self, action_type=None, length=None): """Create an ActionHeader with the optional parameters below. Args: action_type (~pyof.v0x01.common.action.ActionType): The type of the action. length (int): Length of action, including this header. """ super().__init__() self.action_type = action_type self.length = length def unpack(self, buff, 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: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: Exception: If there is a struct unpacking error. """ self.action_type = UBInt16(enum_ref=ActionType) self.action_type.unpack(buff, offset) for cls in ActionHeader.__subclasses__(): if self.action_type.value in cls.get_allowed_types(): self.__class__ = cls break super().unpack(buff, offset) @classmethod def get_allowed_types(cls): """Return allowed types for the class.""" return cls._allowed_types class ActionOutput(ActionHeader): """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. """ port = UBInt16() max_length = UBInt16() _allowed_types = ActionType.OFPAT_OUTPUT, def __init__(self, port=None, max_length=UBINT16_MAX_VALUE): """Create an ActionOutput with the optional parameters below. Args: port (:class:`~pyof.v0x01.common.phy_port.Port` or :class:`int`): Output port. max_length (int): Max length to send to controller. """ super().__init__(action_type=ActionType.OFPAT_OUTPUT, length=8) self.port = port self.max_length = max_length class ActionStripVlan(ActionHeader): """Strips VLAN information from packets. Action defined for switches to remove the 802.1q VLAN information from packets. """ pad = Pad(4) _allowed_types = ActionType.OFPAT_STRIP_VLAN, def __init__(self): """Construct the ActionHeader with the appropriate ActionType. No parameters need to be specified. """ super().__init__(action_type=ActionType.OFPAT_STRIP_VLAN, length=8) class ActionEnqueue(ActionHeader): """Send packets to a queue's port. A switch may support only queues that are tied to specific PCP/TOS bits. In that case, we cannot map an arbitrary flow to a specific queue, therefore the action ENQUEUE is not supported. The user can still use these queues and map flows to them by setting the relevant fields (TOS, VLAN PCP). """ port = UBInt16() #: Pad for 64-bit alignment. pad = Pad(6) queue_id = UBInt32() _allowed_types = ActionType.OFPAT_ENQUEUE, def __init__(self, port=None, queue_id=None): """Create an ActionEnqueue with the optional parameters below. Args: port (physical port or :attr:`.Port.OFPP_IN_PORT`): Queue's port. queue_id (int): Where to enqueue the packets. """ super().__init__(action_type=ActionType.OFPAT_ENQUEUE, length=16) self.port = port self.queue_id = queue_id 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 """ vlan_id = UBInt16() #: Pad for bit alignment. pad2 = Pad(2) _allowed_types = ActionType.OFPAT_SET_VLAN_VID, def __init__(self, vlan_id=None): """Create an ActionVlanVid with the optional parameters below. Args: vlan_id (int): VLAN priority. """ super().__init__(action_type=ActionType.OFPAT_SET_VLAN_VID, length=8) self.vlan_id = vlan_id class ActionVlanPCP(ActionHeader): """Action structure for :attr:`ActionType.OFPAT_SET_VLAN_PCP`.""" vlan_pcp = UBInt8() #: Pad for bit alignment. pad = Pad(3) _allowed_types = ActionType.OFPAT_SET_VLAN_PCP, def __init__(self, vlan_pcp=None): """Create an ActionVlanPCP with the optional parameters below. Args: vlan_pcp (int): VLAN Priority. .. note:: The vlan_pcp field is 8 bits long, but only the lower 3 bits have meaning. """ super().__init__(action_type=ActionType.OFPAT_SET_VLAN_PCP, length=8) self.vlan_pcp = vlan_pcp class ActionDLAddr(ActionHeader): """Action structure for :attr:`ActionType.OFPAT_SET_DL_SRC` or _DST.""" dl_addr = HWAddress() #: Pad for bit alignment. pad = Pad(6) _allowed_types = (ActionType.OFPAT_SET_DL_SRC, ActionType.OFPAT_SET_DL_DST) def __init__(self, action_type=None, dl_addr=None): """Create an ActionDLAddr with the optional parameters below. Args: action_type (:class:`~pyof.v0x01.common.action.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__(action_type, length=16) self.dl_addr = dl_addr class ActionNWAddr(ActionHeader): """Action structure for :attr:`ActionType.OFPAT_SET_NW_SRC` or _DST.""" nw_addr = UBInt32() _allowed_types = (ActionType.OFPAT_SET_NW_SRC, ActionType.OFPAT_SET_NW_DST) def __init__(self, action_type=None, nw_addr=None): """Create an ActionNWAddr with the optional parameters below. Args: action_type (:class:`~pyof.v0x01.common.action.ActionType`): :attr:`~ActionType.OFPAT_SET_NW_SRC` or :attr:`~ActionType.OFPAT_SET_NW_DST`. nw_addr (int): IP Address. """ super().__init__(action_type, length=8) self.nw_addr = nw_addr class ActionNWTos(ActionHeader): """Action structure for :attr:`ActionType.OFPAT_SET_NW_TOS`. .. note:: The nw_tos field is the 6 upper bits of the ToS field to set, in the original bit positions (shifted to the left by 2). """ nw_tos = UBInt8() #: Pad for bit alignment. pad = Pad(3) _allowed_types = ActionType.OFPAT_SET_NW_TOS, def __init__(self, action_type=None, nw_tos=None): """Create an ActionNWTos with the optional parameters below. Args: action_type (:class:`~pyof.v0x01.common.action.ActionType`): :attr:`~ActionType.OFPAT_SET_NW_SRC` or :attr:`~ActionType.OFPAT_SET_NW_DST`. nw_tos (int): IP ToS (DSCP field, 6 bits). """ super().__init__(action_type, length=8) self.nw_tos = nw_tos class ActionTPPort(ActionHeader): """Action structure for :attr:`ActionType.OFPAT_SET_TP_SRC` or _DST.""" tp_port = UBInt16() #: Pad for bit alignment. pad = Pad(2) _allowed_types = (ActionType.OFPAT_SET_TP_SRC, ActionType.OFPAT_SET_TP_DST) def __init__(self, action_type=None, tp_port=None): """Create an ActionTPPort with the optional parameters below. Args: action_type (:class:`~pyof.v0x01.common.action.ActionType`): :attr:`~ActionType.OFPAT_SET_TP_SRC` or :attr:`~ActionType.OFPAT_SET_TP_DST`. tp_port (int): TCP/UDP/other port to set. """ super().__init__(action_type, length=8) self.tp_port = tp_port class ActionVendorHeader(ActionHeader): """Action header for :attr:`ActionType.OFPAT_VENDOR`. The rest of the body is vendor-defined. """ vendor = UBInt32() _allowed_types = ActionType.OFPAT_VENDOR, def __init__(self, length=None, vendor=None): """Create an ActionVendorHeader with the optional parameters below. Args: length (int): Length is a multiple of 8. vender (int): Vendor ID with the same form as in VendorHeader. Defaults to None. """ super().__init__(action_type=ActionType.OFPAT_VENDOR, length=length) self.vendor = vendor class ListOfActions(FixedTypeList): """List of actions. Represented by instances of ActionHeader and used on ActionHeader objects. """ def __init__(self, items=None): """Create a ListOfActions with the optional parameters below. Args: items (:class:`~pyof.v0x01.common.action.ActionHeader`): Instance or a list of instances. """ super().__init__(pyof_class=ActionHeader, items=items) python-openflow-2019.2/pyof/v0x01/common/constants.py0000644000175100001630000000027713577157232023312 0ustar runnerdocker00000000000000"""Here we have the constants related to v0x01 version.""" OFP_VERSION = 0x01 #: This value represents the constant -1. This value is used when no buffer is #: specified NO_BUFFER = 2**32-1 python-openflow-2019.2/pyof/v0x01/common/header.py0000644000175100001630000000471213577157232022524 0ustar runnerdocker00000000000000"""Defines Header classes and related items.""" # System imports from enum import IntEnum # Local source tree imports from pyof.foundation.base import GenericStruct from pyof.foundation.basic_types import UBInt8, UBInt16, UBInt32 from pyof.v0x01.common.constants import OFP_VERSION # Third-party imports __all__ = ('Header', 'Type') # Enums class Type(IntEnum): """Enumeration of Message Types.""" # Symetric/Immutable messages OFPT_HELLO = 0 OFPT_ERROR = 1 OFPT_ECHO_REQUEST = 2 OFPT_ECHO_REPLY = 3 OFPT_VENDOR = 4 # Switch configuration messages # Controller/Switch messages OFPT_FEATURES_REQUEST = 5 OFPT_FEATURES_REPLY = 6 OFPT_GET_CONFIG_REQUEST = 7 OFPT_GET_CONFIG_REPLY = 8 OFPT_SET_CONFIG = 9 # Async messages OFPT_PACKET_IN = 10 OFPT_FLOW_REMOVED = 11 OFPT_PORT_STATUS = 12 # Controller command messages # Controller/switch message OFPT_PACKET_OUT = 13 OFPT_FLOW_MOD = 14 OFPT_PORT_MOD = 15 # Statistics messages # Controller/Switch message OFPT_STATS_REQUEST = 16 OFPT_STATS_REPLY = 17 # Barrier messages # Controller/Switch message OFPT_BARRIER_REQUEST = 18 OFPT_BARRIER_REPLY = 19 # Queue Configuration messages # Controller/Switch message OFPT_QUEUE_GET_CONFIG_REQUEST = 20 OFPT_QUEUE_GET_CONFIG_REPLY = 21 # Classes class Header(GenericStruct): """Representation of an OpenFlow message Header.""" version = UBInt8(OFP_VERSION) message_type = UBInt8(enum_ref=Type) length = UBInt16() xid = UBInt32() def __init__(self, message_type=None, length=None, xid=None): """Create a Header with the optional parameters below. Args: message_type (~pyof.v0x01.common.header.Type): Type of the message. xid (int): ID of the message. Defaults to a random integer. length (int): Length of the message, including the header itself. """ super().__init__() self.message_type = message_type self.length = length self.xid = xid def __str__(self): """Get just the header type. Eg.: 'OFPT_SET_CONFIG'.""" return self.message_type.name def __repr__(self): """Show a full representation of the Header, including version.""" return "%s(message_type=%s, length=%r, xid=%r, version=%r)" \ % (self.__class__.__name__, self.message_type, self.length, self.xid, self.version) python-openflow-2019.2/pyof/foundation/0000755000175100001630000000000013577157243020720 5ustar runnerdocker00000000000000python-openflow-2019.2/pyof/foundation/exceptions.py0000644000175100001630000000326613577157232023460 0ustar runnerdocker00000000000000"""Exceptions raised by this library.""" class ValidationError(Exception): """Can be used directly or inherited by mpre specific validation errors.""" def __str__(self): return "Validation error: " + super().__str__() class MethodNotImplemented(Exception): """Exception to be raised when a method is not implemented.""" def __str__(self): return "Method not yet implemented: " + super().__str__() class BadValueException(Exception): """Attribute has an unexpected value.""" def __str__(self): return "BadValue error: " + super().__str__() class WrongListItemType(Exception): """When an item of a wrong type is inserted into a list. Exception used by :class:`.FixedTypeList` and :class:`.ConstantTypeList` when the user tries to insert an item that does not match the expected type. """ def __init__(self, item_class, expected_class): """Take the parameters to inform the user about the error. Args: item_class (:obj:`type`): The class of the item that was being inserted in the list when the exception was raised. expected_class (:obj:`type`): The expected type that didn't match against the item to be inserted. """ super().__init__() self.item_class = item_class self.expected_class = expected_class def __str__(self): return "'{}' is not an instance of {}".format(self.item_class, self.expected_class) class UnpackException(Exception): """Error while unpacking.""" pass class PackException(Exception): """Error while unpacking.""" pass python-openflow-2019.2/pyof/foundation/__init__.py0000644000175100001630000000016213577157232023026 0ustar runnerdocker00000000000000"""Foundation package is the base of python-openflow. It provides modules used throughout the whole library. """ python-openflow-2019.2/pyof/foundation/network_types.py0000644000175100001630000005724513577157232024222 0ustar runnerdocker00000000000000"""Basic Network packet types. Defines and Implements Basic Network packet types , such as Ethertnet and LLDP. """ # System imports from copy import deepcopy from enum import IntEnum # Local source tree imports from pyof.foundation.base import GenericStruct from pyof.foundation.basic_types import ( BinaryData, FixedTypeList, HWAddress, IPAddress, UBInt8, UBInt16) from pyof.foundation.exceptions import PackException, UnpackException __all__ = ('ARP', 'Ethernet', 'EtherType', 'GenericTLV', 'IPv4', 'VLAN', 'TLVWithSubType', 'LLDP') # NETWORK CONSTANTS AND ENUMS class EtherType(IntEnum): """Enumeration with IEEE Ethernet types. The items are being added as we need. If you need one EtherType that is not listed below, please, send us a Pull Request with the addition. Ref: http://standards-oui.ieee.org/ethertype/eth.txt """ #: Internet Protocol version 4 (IPv4) IPV4 = 0x0800 #: Address Resolution Protocol (ARP) ARP = 0x0806 #: Reverse Address Resolution Protocol RARP = 0x8035 #: VLAN-tagged frame (IEEE 802.1Q) and Shortest Path Bridging IEEE 802.1aq #: with NNI compatibility[8] #: IEEE Std 802.1Q - Customer VLAN Tag Type VLAN = 0x8100 #: Internet Protocol Version 6 (IPv6) IPV6 = 0x86DD #: Ethernet flow control ETHERNET_FLOW_CONTROL = 0x8808 #: MPLS (multiprotocol label switching) label stack - unicast #: reference: RFC 3032 URL: ftp://ftp.rfc-editor.org/in-notes/rfc3032.txt MPLS_UNICAST = 0x8847 #: MPLS (multiprotocol label switching) label stack - multicast #: reference: RFC 3032 URL: ftp://ftp.rfc-editor.org/in-notes/rfc3032.txt MPLS_MULTICAST = 0x8848 #: Link Layer Discovery Protocol (LLDP) LLDP = 0x88CC #: VLAN-tagged (IEEE 802.1Q) frame with double tagging #: Std 802.1Q Service VLAN tag identifier VLAN_QINQ = 0x88a8 class ARP(GenericStruct): """ARP packet "struct". Contains fields for an ARP packet's header and data. Designed for Ethernet and IPv4 only: needs to have some attributes changed for other HTYPE and PTYPE implementations. Must be encapsulated inside an Ethernet frame. """ htype = UBInt16() ptype = UBInt16() hlen = UBInt8() plen = UBInt8() oper = UBInt16() sha = HWAddress() spa = IPAddress() tha = HWAddress() tpa = IPAddress() def __init__(self, htype=1, ptype=EtherType.IPV4, hlen=6, plen=4, oper=1, sha='00:00:00:00:00:00', spa='0.0.0.0', tha="00:00:00:00:00:00", tpa='0.0.0.0'): """Create an ARP with the parameters below. Args: htype (int): Hardware protocol type. Defaults to 1 for Ethernet. ptype (int): Network protocol type. Defaults to 0x800 for IPv4. hlen (int): Length of the hardware address. Defaults to 6 for MAC addresses. plen (int): Length of the networking protocol address. Defaults to 4 for IPv4 addresses. oper (int): Determines the operation for this ARP packet. Must be 1 for ARP request or 2 for ARP reply. Defaults to 1. sha (str): Sender hardware address. Defaults to '00:00:00:00:00:00'. spa (str): Sender protocol address. Defaults to '0.0.0.0'. tha (str): Target hardware address. Defaults to '00:00:00:00:00:00'. tpa (str): Target protocol address. Defaults to '0.0.0.0'. """ super().__init__() self.htype = htype self.ptype = ptype self.hlen = hlen self.plen = plen self.oper = oper self.sha = sha self.spa = spa self.tha = tha self.tpa = tpa def is_valid(self): """Assure the ARP contains Ethernet and IPv4 information.""" return self.htype == 1 and self.ptype == EtherType.IPV4 def unpack(self, buff, offset=0): """Unpack a binary struct into this object's attributes. Return the values instead of the lib's basic types. Check if the protocols involved are Ethernet and IPv4. Other protocols are currently not supported. Args: buff (bytes): Binary buffer. offset (int): Where to begin unpacking. Raises: :exc:`~.exceptions.UnpackException`: If unpack fails. """ super().unpack(buff, offset) if not self.is_valid(): raise UnpackException("Unsupported protocols in ARP packet") class VLAN(GenericStruct): """802.1q VLAN header.""" #: tpid (:class:`UBInt16`): Tag Protocol Identifier tpid = UBInt16(EtherType.VLAN) #: _tci (:class:`UBInt16`): Tag Control Information - has the #: Priority Code Point, DEI/CFI bit and the VLAN ID _tci = UBInt16() def __init__(self, pcp=None, cfi=None, vid=None): """Create a VLAN with the parameters below. If no arguments are set for a particular instance, it is interpreted as abscence of VLAN information, and the pack() method will return an empty binary string. Args: tpid (int): Tag Protocol Identifier. Defaults to 0x8100 for 802.1q. pcp (int): 802.1p Priority Code Point. Defaults to 0 for Best Effort Queue. cfi (int): Canonical Format Indicator. Defaults to 0 for Ethernet. vid (int): VLAN ID. If no VLAN is specified, value is 0. """ super().__init__() self.tpid = EtherType.VLAN self.pcp = pcp self.cfi = cfi self.vid = vid def pack(self, value=None): """Pack the struct in a binary representation. Merge some fields to ensure correct packing. If no arguments are set for a particular instance, it is interpreted as abscence of VLAN information, and the pack() method will return an empty binary string. Returns: bytes: Binary representation of this instance. """ if isinstance(value, type(self)): return value.pack() if self.pcp is None and self.cfi is None and self.vid is None: return b'' self.pcp = self.pcp if self.pcp is not None else 0 self.cfi = self.cfi if self.cfi is not None else 0 self.vid = self.vid if self.vid is not None else 0 self._tci = self.pcp << 13 | self.cfi << 12 | self.vid return super().pack() def _validate(self): """Assure this is a valid VLAN header instance.""" if self.tpid.value not in (EtherType.VLAN, EtherType.VLAN_QINQ): raise UnpackException return def unpack(self, buff, offset=0): """Unpack a binary struct into this object's attributes. Return the values instead of the lib's basic types. After unpacking, the abscence of a `tpid` value causes the assignment of None to the field values to indicate that there is no VLAN information. Args: buff (bytes): Binary buffer. offset (int): Where to begin unpacking. Raises: :exc:`~.exceptions.UnpackException`: If unpack fails. """ super().unpack(buff, offset) if self.tpid.value: self._validate() self.tpid = self.tpid.value self.pcp = self._tci.value >> 13 self.cfi = (self._tci.value >> 12) & 1 self.vid = self._tci.value & 4095 else: self.tpid = EtherType.VLAN self.pcp = None self.cfi = None self.vid = None class ListOfVLAN(FixedTypeList): """List of VLAN tags. Represented by instances of VLAN. """ def __init__(self, items=None): """Create a ListOfVLAN with the optional parameters below. Args: items (:class:`~pyof.foundation.network_types.VLAN`): Instance or a list of instances. """ super().__init__(pyof_class=VLAN, items=items) class Ethernet(GenericStruct): """Ethernet "struct". Objects of this class represents an ethernet packet. It contains the 'Ethernet header', composed by destination (MAC), source (MAC), type (EtherType)[1] and the payload of the packet, as binary data. This class does not consider the Ethernet 'Preamble' or the 'CRC'. There is also a get_hash method, that hashes the binary representation of the object so we can have a unique representation of the ethernet packet, so we can keep a track of ethernet packets being flooded over the network. [1] EtherTypes: http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml#ieee-802-numbers-1 """ destination = HWAddress() source = HWAddress() vlans = ListOfVLAN() ether_type = UBInt16() data = BinaryData() def __init__(self, destination=None, source=None, vlans=None, ether_type=None, data=b''): """Create an instance and set its attributes. Args: destination (:class:`~pyof.foundation.basic_types.HWAddress`): The final destination MAC address. source (:class:`~pyof.foundation.basic_types.HWAddress`): The source Mac address of the packet. ether_type (:class:`~pyof.foundation.basic_types.UBInt16`): The EtherType of packet. data (:class:`~pyof.foundation.basic_types.BinaryData`): The content of the packet in binary format. """ super().__init__() self.destination = destination self.source = source self.vlans = ListOfVLAN() if vlans is None else vlans self.ether_type = ether_type self.data = data def get_hash(self): """Calculate a hash and returns it. Returns: int: Integer value that identifies this instance. """ return hash(self.pack()) @staticmethod def _get_vlan_length(buff): """Return the total length of VLAN tags in a given Ethernet buffer.""" length = 0 begin = 12 while(buff[begin:begin+2] in (EtherType.VLAN.to_bytes(2, 'big'), EtherType.VLAN_QINQ.to_bytes(2, 'big'))): length += 4 begin += 4 return length def unpack(self, buff, 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. Ethernet headers may have VLAN tags. If no VLAN tag is found, a 'wildcard VLAN tag' is inserted to assure correct unpacking. Args: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: UnpackException: If there is a struct unpacking error. """ begin = offset vlan_length = self._get_vlan_length(buff) for attribute_name, class_attribute in self.get_class_attributes(): attribute = deepcopy(class_attribute) if attribute_name == 'vlans': attribute.unpack(buff[begin:begin+vlan_length]) else: attribute.unpack(buff, begin) setattr(self, attribute_name, attribute) begin += attribute.get_size() class GenericTLV(GenericStruct): """TLV structure of LLDP packets. This is a Type, Length and Value (TLV) struct. The LLDP/TLV definition states that the Type field have 7 bits, while the length have 9 bits. The Value must be between 0-511 octets. Internally, on the instances of this class, the Type is a integer (0-127) and the Length is dynamically calculated based on the current type and value. """ def __init__(self, tlv_type=127, value=None): """Create an instance and set its attributes. Args: tlv_type (int): Type used by this class. Defaults to 127. value (:class:`~pyof.foundation.basic_types.BinaryData`): Value stored by GenericTLV. """ super().__init__() self.tlv_type = tlv_type self._value = BinaryData() if value is None else value @property def value(self): """Return the value stored by GenericTLV. Returns: :class:`~pyof.foundation.basic_types.BinaryData`: Value stored by GenericTLV. """ return self._value @property def length(self): """Return the length of value stored by GenericTLV. Returns: int: Value length in bytes. """ return len(self.value.pack()) @property 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 pack(self, value=None): """Pack the TLV in a binary representation. Returns: bytes: Binary representation of the struct object. Raises: :exc:`~.exceptions.ValidationError`: If validation fails. """ if value is None: output = self.header.pack() output += self.value.pack() return output elif isinstance(value, type(self)): return value.pack() else: msg = "{} is not an instance of {}".format(value, type(self).__name__) raise PackException(msg) def unpack(self, buff, 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: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: Exception: If there is a struct unpacking error. """ header = UBInt16() header.unpack(buff[offset:offset+2]) self.tlv_type = header.value >> 9 length = header.value & 511 begin, end = offset + 2, offset + 2 + length self._value = BinaryData(buff[begin:end]) def get_size(self, value=None): """Return struct size. Returns: int: Returns the struct size based on inner attributes. """ if isinstance(value, type(self)): return value.get_size() return 2 + self.length class IPv4(GenericStruct): """IPv4 packet "struct". Contains all fields of an IP version 4 packet header, plus the upper layer content as binary data. Some of the fields were merged together because of their size being inferior to 8 bits. They are represented as a single class attribute, but pack/unpack methods will take into account the values in individual instance attributes. """ #: _version_ihl (:class:`UBInt8`): IP protocol version + Internet Header #: Length (words) _version_ihl = UBInt8() #: _dscp_ecn (:class:`UBInt8`): Differentiated Services Code Point #: (ToS - Type of Service) + Explicit Congestion Notification _dscp_ecn = UBInt8() #: length (:class:`UBInt16`): IP packet length (bytes) length = UBInt16() #: identification (:class:`UBInt16`): Packet ID - common to all fragments identification = UBInt16() #: _flags_offset (:class:`UBInt16`): Fragmentation flags + fragmentation #: offset _flags_offset = UBInt16() #: ttl (:class:`UBInt8`): Packet time-to-live ttl = UBInt8() #: protocol (:class:`UBInt8`): Upper layer protocol number protocol = UBInt8() #: checksum (:class:`UBInt16`): Header checksum checksum = UBInt16() #: source (:class:`IPAddress`): Source IPv4 address source = IPAddress() #: destination (:class:`IPAddress`): Destination IPv4 address destination = IPAddress() #: options (:class:`BinaryData`): IP Options - up to 320 bits, always #: padded to 32 bits options = BinaryData() #: data (:class:`BinaryData`): Packet data data = BinaryData() def __init__(self, version=4, ihl=5, dscp=0, ecn=0, length=0, identification=0, flags=0, offset=0, ttl=255, protocol=0, checksum=0, source="0.0.0.0", destination="0.0.0.0", options=b'', data=b''): """Create an IPv4 with the parameters below. Args: version (int): IP protocol version. Defaults to 4. ihl (int): Internet Header Length. Default is 5. dscp (int): Differentiated Service Code Point. Defaults to 0. ecn (int): Explicit Congestion Notification. Defaults to 0. length (int): IP packet length in bytes. Defaults to 0. identification (int): Packet Id. Defaults to 0. flags (int): IPv4 Flags. Defults 0. offset (int): IPv4 offset. Defaults to 0. ttl (int): Packet time-to-live. Defaults to 255 protocol (int): Upper layer protocol number. Defaults to 0. checksum (int): Header checksum. Defaults to 0. source (str): Source IPv4 address. Defaults to "0.0.0.0" destination (str): Destination IPv4 address. Defaults to "0.0.0.0" options (bytes): IP options. Defaults to empty bytes. data (bytes): Packet data. Defaults to empty bytes. """ super().__init__() self.version = version self.ihl = ihl self.dscp = dscp self.ecn = ecn self.length = length self.identification = identification self.flags = flags self.offset = offset self.ttl = ttl self.protocol = protocol self.checksum = checksum self.source = source self.destination = destination self.options = options self.data = data def _update_checksum(self): """Update the packet checksum to enable integrity check.""" source_list = [int(octet) for octet in self.source.split(".")] destination_list = [int(octet) for octet in self.destination.split(".")] source_upper = (source_list[0] << 8) + source_list[1] source_lower = (source_list[2] << 8) + source_list[3] destination_upper = (destination_list[0] << 8) + destination_list[1] destination_lower = (destination_list[2] << 8) + destination_list[3] block_sum = ((self._version_ihl << 8 | self._dscp_ecn) + self.length + self.identification + self._flags_offset + (self.ttl << 8 | self.protocol) + source_upper + source_lower + destination_upper + destination_lower) while block_sum > 65535: carry = block_sum >> 16 block_sum = (block_sum & 65535) + carry self.checksum = ~block_sum & 65535 def pack(self, value=None): """Pack the struct in a binary representation. Merge some fields to ensure correct packing. Returns: bytes: Binary representation of this instance. """ # Set the correct IHL based on options size if self.options: self.ihl += int(len(self.options) / 4) # Set the correct packet length based on header length and data self.length = int(self.ihl * 4 + len(self.data)) self._version_ihl = self.version << 4 | self.ihl self._dscp_ecn = self.dscp << 2 | self.ecn self._flags_offset = self.flags << 13 | self.offset # Set the checksum field before packing self._update_checksum() return super().pack() def unpack(self, buff, offset=0): """Unpack a binary struct into this object's attributes. Return the values instead of the lib's basic types. Args: buff (bytes): Binary buffer. offset (int): Where to begin unpacking. Raises: :exc:`~.exceptions.UnpackException`: If unpack fails. """ super().unpack(buff, offset) self.version = self._version_ihl.value >> 4 self.ihl = self._version_ihl.value & 15 self.dscp = self._dscp_ecn.value >> 2 self.ecn = self._dscp_ecn.value & 3 self.length = self.length.value self.identification = self.identification.value self.flags = self._flags_offset.value >> 13 self.offset = self._flags_offset.value & 8191 self.ttl = self.ttl.value self.protocol = self.protocol.value self.checksum = self.checksum.value self.source = self.source.value self.destination = self.destination.value if self.ihl > 5: options_size = (self.ihl - 5) * 4 self.data = self.options.value[options_size:] self.options = self.options.value[:options_size] else: self.data = self.options.value self.options = b'' class TLVWithSubType(GenericTLV): """Modify the :class:`GenericTLV` to a Organization Specific TLV structure. Beyond the standard TLV (type, length, value), we can also have a more specific structure, with the :attr:`value` field being splitted into a :attr:`sub_type` field and a new :attr:`sub_value` field. """ def __init__(self, tlv_type=1, sub_type=7, sub_value=None): """Create an instance and set its attributes. Args: tlv_type (int): Type used by this class. Defaults to 1. sub_type (int): Sub type value used by this class. Defaults to 7. sub_value (:class:`~pyof.foundation.basic_types.BinaryData`): Data stored by TLVWithSubType. Defaults to empty BinaryData. """ super().__init__(tlv_type) self.sub_type = sub_type self.sub_value = BinaryData() if sub_value is None else sub_value @property def value(self): """Return sub type and sub value as binary data. Returns: :class:`~pyof.foundation.basic_types.BinaryData`: BinaryData calculated. """ binary = UBInt8(self.sub_type).pack() + self.sub_value.pack() return BinaryData(binary) def unpack(self, buff, 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: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: Exception: If there is a struct unpacking error. """ header = UBInt16() header.unpack(buff[offset:offset+2]) self.tlv_type = header.value >> 9 length = header.value & 511 begin, end = offset + 2, offset + 2 + length sub_type = UBInt8() sub_type.unpack(buff[begin:begin+1]) self.sub_type = sub_type.value self.sub_value = BinaryData(buff[begin+1:end]) class LLDP(GenericStruct): """LLDP class. Build a LLDP packet with TLVSubtype and Generic Subtypes. It contains a chassis_id TLV, a port_id TLV, a TTL (Time to live) and another TLV to represent the end of the LLDP Packet. """ #: chassis_id (:class:`~TLVWithSubType`) with tlv_type = 1 and sub_type = 7 chassis_id = TLVWithSubType(tlv_type=1, sub_type=7) #: port_id (:class:`TLVWithSubType`) with tlv = 2 and sub_type = 7 port_id = TLVWithSubType(tlv_type=2, sub_type=7) #: TTL (:class:`GenericTLV`) time is given in seconds, between 0 and 65535, #: with tlv_type = 3 ttl = GenericTLV(tlv_type=3, value=UBInt16(120)) # We are not using list of tlvs for now # tlvs = ListOfTLVs() #: end (:class:`GenericTLV`) with tlv_type = 0 end = GenericTLV(tlv_type=0) python-openflow-2019.2/pyof/foundation/basic_types.py0000644000175100001630000005475413577157232023614 0ustar runnerdocker00000000000000"""Basic types used in structures and messages.""" # System imports import struct from copy import deepcopy # Local source tree imports from pyof.foundation import exceptions from pyof.foundation.base import GenericStruct, GenericType # Third-party imports __all__ = ('BinaryData', 'Char', 'ConstantTypeList', 'FixedTypeList', 'IPAddress', 'DPID', 'HWAddress', 'Pad', 'UBInt8', 'UBInt16', 'UBInt32', 'UBInt64') class Pad(GenericType): """Class for padding attributes.""" _fmt = '' def __init__(self, length=0): """Pad up to ``length``, in bytes. Args: length (int): Total length, in bytes. """ super().__init__() self._length = length def __repr__(self): return "{}({})".format(type(self).__name__, self._length) def __str__(self): return '0' * self._length def get_size(self, value=None): """Return the type size in bytes. Args: value (int): In structs, the user can assign other value instead of this class' instance. Here, in such cases, ``self`` is a class attribute of the struct. Returns: int: Size in bytes. """ return self._length 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: Buffer where data is located. offset (int): Where data stream begins. """ pass def pack(self, value=None): """Pack the object. Args: value (int): In structs, the user can assign other value instead of this class' instance. Here, in such cases, ``self`` is a class attribute of the struct. Returns: bytes: the byte 0 (zero) *length* times. """ return b'\x00' * self._length def __deepcopy__(self, memo): """Improve deepcopy speed.""" return Pad(length=self._length) class UBInt8(GenericType): """Format character for an Unsigned Char. Class for an 8-bit (1-byte) Unsigned Integer. """ _fmt = "!B" class UBInt16(GenericType): """Format character for an Unsigned Short. Class for an 16-bit (2-byte) Unsigned Integer. """ _fmt = "!H" class UBInt32(GenericType): """Format character for an Unsigned Int. Class for an 32-bit (4-byte) Unsigned Integer. """ _fmt = "!I" class UBInt64(GenericType): """Format character for an Unsigned Long Long. Class for an 64-bit (8-byte) Unsigned Integer. """ _fmt = "!Q" class DPID(GenericType): """DataPath ID. Identifies a switch.""" _fmt = "!8B" def __init__(self, dpid=None): """Create an instance and optionally set its dpid value. Args: dpid (str): String with DPID value(e.g. `00:00:00:00:00:00:00:01`). """ super().__init__(value=dpid) def __str__(self): return self._value @property def value(self): """Return dpid value. Returns: str: DataPath ID stored by DPID class. """ return self._value def pack(self, value=None): """Pack the value as a binary representation. Returns: bytes: The binary representation. Raises: struct.error: If the value does not fit the binary format. """ if isinstance(value, type(self)): return value.pack() if value is None: value = self._value return struct.pack('!8B', *[int(v, 16) for v in value.split(':')]) def unpack(self, buff, 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: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: Exception: If there is a struct unpacking error. """ begin = offset hexas = [] while begin < offset + 8: number = struct.unpack("!B", buff[begin:begin+1])[0] hexas.append("%.2x" % number) begin += 1 self._value = ':'.join(hexas) def __deepcopy__(self, memo): """Improve deepcopy speed.""" return DPID(dpid=self._value) class Char(GenericType): """Build a double char type according to the length.""" def __init__(self, value=None, length=0): """Create a Char with the optional parameters below. Args: value: The character to be build. length (int): Character size. """ super().__init__(value) self.length = length self._fmt = '!{}{}'.format(self.length, 's') def pack(self, value=None): """Pack the value as a binary representation. Returns: bytes: The binary representation. Raises: struct.error: If the value does not fit the binary format. """ if isinstance(value, type(self)): return value.pack() try: if value is None: value = self.value packed = struct.pack(self._fmt, bytes(value, 'ascii')) return packed[:-1] + b'\0' # null-terminated except struct.error as err: msg = "Char Pack error. " msg += "Class: {}, struct error: {} ".format(type(value).__name__, err) raise exceptions.PackException(msg) def unpack(self, buff, 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: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: Exception: If there is a struct unpacking error. """ try: begin = offset end = begin + self.length unpacked_data = struct.unpack(self._fmt, buff[begin:end])[0] except struct.error: raise Exception("%s: %s" % (offset, buff)) self._value = unpacked_data.decode('ascii').rstrip('\0') def __deepcopy__(self, memo): """Improve deepcopy speed.""" return Char(value=self._value, length=self.length) class IPAddress(GenericType): """Defines a IP address.""" netmask = UBInt32() max_prefix = UBInt32(32) def __init__(self, address="0.0.0.0/32", netmask=None): """Create an IPAddress with the parameters below. Args: address (str): IP Address using ipv4. Defaults to '0.0.0.0/32' """ if '/' in address: address, netmask = address.split('/') else: netmask = 32 if netmask is None else netmask super().__init__(address) self.netmask = int(netmask) def pack(self, value=None): """Pack the value as a binary representation. If the value is None the self._value will be used to pack. Args: value (str): IP Address with ipv4 format. Returns: bytes: The binary representation. Raises: struct.error: If the value does not fit the binary format. """ if isinstance(value, type(self)): return value.pack() if value is None: value = self._value if value.find('/') >= 0: value = value.split('/')[0] try: value = value.split('.') return struct.pack('!4B', *[int(x) for x in value]) except struct.error as err: msg = "IPAddress error. " msg += "Class: {}, struct error: {} ".format(type(value).__name__, err) raise exceptions.PackException(msg) def unpack(self, buff, 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: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: Exception: If there is a struct unpacking error. """ try: unpacked_data = struct.unpack('!4B', buff[offset:offset+4]) self._value = '.'.join([str(x) for x in unpacked_data]) except struct.error as exception: raise exceptions.UnpackException('%s; %s: %s' % (exception, offset, buff)) def get_size(self, value=None): """Return the ip address size in bytes. Args: value: In structs, the user can assign other value instead of this class' instance. Here, in such cases, ``self`` is a class attribute of the struct. Returns: int: The address size in bytes. """ return 4 def __deepcopy__(self, memo): """Improve deepcopy speed.""" return IPAddress(address=self._value, netmask=self.netmask) class HWAddress(GenericType): """Defines a hardware address.""" # pylint: disable=useless-super-delegation def __init__(self, hw_address='00:00:00:00:00:00'): """Create a HWAddress with the parameters below. Args: hw_address (bytes): Hardware address. Defaults to '00:00:00:00:00:00'. """ super().__init__(hw_address) def pack(self, value=None): """Pack the value as a binary representation. If the passed value (or the self._value) is zero (int), then the pack will assume that the value to be packed is '00:00:00:00:00:00'. Returns bytes: The binary representation. Raises: struct.error: If the value does not fit the binary format. """ if isinstance(value, type(self)): return value.pack() if value is None: value = self._value if value == 0: value = '00:00:00:00:00:00' value = value.split(':') try: return struct.pack('!6B', *[int(x, 16) for x in value]) except struct.error as err: msg = "HWAddress error. " msg += "Class: {}, struct error: {} ".format(type(value).__name__, err) raise exceptions.PackException(msg) def unpack(self, buff, 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: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. Raises: Exception: If there is a struct unpacking error. """ def _int2hex(number): return "{0:0{1}x}".format(number, 2) try: unpacked_data = struct.unpack('!6B', buff[offset:offset+6]) except struct.error as exception: raise exceptions.UnpackException('%s; %s: %s' % (exception, offset, buff)) transformed_data = ':'.join([_int2hex(x) for x in unpacked_data]) self._value = transformed_data def get_size(self, value=None): """Return the address size in bytes. Args: value: In structs, the user can assign other value instead of this class' instance. Here, in such cases, ``self`` is a class attribute of the struct. Returns: int: The address size in bytes. """ return 6 def is_broadcast(self): """Return true if the value is a broadcast address. False otherwise.""" return self.value == 'ff:ff:ff:ff:ff:ff' def __deepcopy__(self, memo): """Improve deepcopy speed.""" return HWAddress(hw_address=self._value) class BinaryData(GenericType): """Class to create objects that represent binary data. This is used in the ``data`` attribute from :class:`~pyof.v0x01.asynchronous.packet_in.PacketIn` and :class:`~pyof.v0x01.controller2switch.packet_out.PacketOut` messages. Both the :meth:`pack` and :meth:`unpack` methods will return the binary data itself. :meth:`get_size` method will return the size of the instance using Python's :func:`len`. """ def __init__(self, value=None): # pylint: disable=useless-super-delegation """Initialize with a value (optional). Args: value (bytes): The binary data. Defaults to an empty value. """ super().__init__(value) def pack(self, value=None): """Pack the value as a binary representation. Returns: bytes: The binary representation. Raises: ValueError: If value can't be represented with bytes """ if value is None: value = self._value if hasattr(value, 'pack') and callable(value.pack): return value.pack() elif isinstance(value, bytes): return value elif value is None: return b'' else: raise ValueError(f"BinaryData can't be {type(value)} = '{value}'") def unpack(self, buff, 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. Since the *buff* is binary data, no conversion is done. Args: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. """ self._value = buff[offset:] def get_size(self, value=None): """Return the size in bytes. Args: value (bytes): In structs, the user can assign other value instead of this class' instance. Here, in such cases, ``self`` is a class attribute of the struct. Returns: int: The address size in bytes. """ if value is None: value = self._value if hasattr(value, 'get_size'): return value.get_size() return len(self.pack(value)) def __deepcopy__(self, memo): """Improve deepcopy speed.""" return BinaryData(value=self._value) class TypeList(list, GenericStruct): """Base class for lists that store objects of one single type.""" def __init__(self, items): """Initialize the list with one item or a list of items. Args: items (iterable, ``pyof_class``): Items to be stored. """ super().__init__() if isinstance(items, list): self.extend(items) elif items: self.append(items) def extend(self, items): """Extend the list by adding all items of ``items``. Args: items (iterable): Items to be added to the list. Raises: :exc:`~.exceptions.WrongListItemType`: If an item has an unexpected type. """ for item in items: self.append(item) def pack(self, value=None): """Pack the value as a binary representation. Returns: bytes: The binary representation. """ if isinstance(value, type(self)): return value.pack() if value is None: value = self else: container = type(self)(items=None) container.extend(value) value = container bin_message = b'' try: for item in value: bin_message += item.pack() return bin_message except exceptions.PackException as err: msg = "{} pack error: {}".format(type(self).__name__, err) raise exceptions.PackException(msg) # pylint: disable=arguments-differ def unpack(self, buff, item_class, offset=0): """Unpack the elements of the list. Args: buff (bytes): The binary data to be unpacked. item_class (:obj:`type`): Class of the expected items on this list. offset (int): If we need to shift the beginning of the data. """ begin = offset limit_buff = len(buff) while begin < limit_buff: item = item_class() item.unpack(buff, begin) self.append(item) begin += item.get_size() # pylint: enable=arguments-differ def get_size(self, value=None): """Return the size in bytes. Args: value: In structs, the user can assign other value instead of this class' instance. Here, in such cases, ``self`` is a class attribute of the struct. Returns: int: The size in bytes. """ if value is None: if not self: # If this is a empty list, then returns zero return 0 elif issubclass(type(self[0]), GenericType): # If the type of the elements is GenericType, then returns the # length of the list multiplied by the size of the GenericType. return len(self) * self[0].get_size() # Otherwise iter over the list accumulating the sizes. return sum(item.get_size() for item in self) return type(self)(value).get_size() def __str__(self): """Human-readable object representantion.""" return "{}".format([str(item) for item in self]) def __deepcopy__(self, memo): """Improve deepcopy speed.""" items = [deepcopy(item) for item in self] return TypeList(items=items) class FixedTypeList(TypeList): """A list that stores instances of one pyof class.""" _pyof_class = None def __init__(self, pyof_class, items=None): """Create a FixedTypeList with the parameters follows. Args: pyof_class (:obj:`type`): Class of the items to be stored. items (iterable, ``pyof_class``): Items to be stored. """ self._pyof_class = pyof_class super().__init__(items) def append(self, item): """Append one item to the list. Args: item: Item to be appended. Its type must match the one defined in the constructor. Raises: :exc:`~.exceptions.WrongListItemType`: If the item has a different type than the one specified in the constructor. """ if isinstance(item, list): self.extend(item) elif issubclass(item.__class__, self._pyof_class): list.append(self, item) else: raise exceptions.WrongListItemType(item.__class__.__name__, self._pyof_class.__name__) def insert(self, index, item): """Insert an item at the specified index. Args: index (int): Position to insert the item. item: Item to be inserted. It must have the type specified in the constructor. Raises: :exc:`~.exceptions.WrongListItemType`: If the item has a different type than the one specified in the constructor. """ if issubclass(item.__class__, self._pyof_class): list.insert(self, index, item) else: raise exceptions.WrongListItemType(item.__class__.__name__, self._pyof_class.__name__) def unpack(self, buff, offset=0): # pylint: disable=arguments-differ """Unpack the elements of the list. This unpack method considers that all elements have the same size. To use this class with a pyof_class that accepts elements with different sizes, you must reimplement the unpack method. Args: buff (bytes): The binary data to be unpacked. offset (int): If we need to shift the beginning of the data. """ super().unpack(buff, self._pyof_class, offset) def __deepcopy__(self, memo): """Improve deepcopy speed.""" items = [deepcopy(item) for item in self] return FixedTypeList(pyof_class=self._pyof_class, items=items) class ConstantTypeList(TypeList): """List that contains only objects of the same type (class). The types of all items are expected to be the same as the first item's. Otherwise, :exc:`~.exceptions.WrongListItemType` is raised in many list operations. """ # pylint: disable=useless-super-delegation def __init__(self, items=None): """Create a ConstantTypeList that can contain itens to be stored. Args: items (iterable, :class:`object`): Items to be stored. Raises: :exc:`~.exceptions.WrongListItemType`: If an item has a different type than the first item to be stored. """ super().__init__(items) def append(self, item): """Append one item to the list. Args: item: Item to be appended. Raises: :exc:`~.exceptions.WrongListItemType`: If an item has a different type than the first item to be stored. """ if isinstance(item, list): self.extend(item) elif not self: list.append(self, item) elif item.__class__ == self[0].__class__: list.append(self, item) else: raise exceptions.WrongListItemType(item.__class__.__name__, self[0].__class__.__name__) def insert(self, index, item): """Insert an item at the specified index. Args: index (int): Position to insert the item. item: Item to be inserted. Raises: :exc:`~.exceptions.WrongListItemType`: If an item has a different type than the first item to be stored. """ if not self: list.append(self, item) elif item.__class__ == self[0].__class__: list.insert(self, index, item) else: raise exceptions.WrongListItemType(item.__class__.__name__, self[0].__class__.__name__) def __deepcopy__(self, memo): """Improve deepcopy speed.""" items = [deepcopy(item) for item in self] return ConstantTypeList(items=items) python-openflow-2019.2/pyof/foundation/base.py0000644000175100001630000007660413577157232022217 0ustar runnerdocker00000000000000"""Base and fundamental classes used all over the library. Besides classes, several constants are defined here. We designed python-openflow in a manner to make it easy to create new messages and OpenFlow structs. You can realize that when you see a message class definition. A **struct** here is a group of basic attributes and/or struct attributes (i.e. :class:`~pyof.v0x01.common.header.Header`). A **message** here is like a struct, but all messages have a header attribute (i.e. :class:`~pyof.v0x01.asynchronous.packet_in.PacketIn`). The main classes of this module are :class:`GenericStruct`, :class:`GenericMessage`, :class:`GenericBitMask` and :class:`GenericType`. These classes are used in all parts of this library. """ # System imports import importlib import re import struct from collections import OrderedDict from copy import deepcopy from enum import Enum, IntEnum from random import randint from pyof.foundation.constants import UBINT32_MAX_VALUE as MAXID from pyof.foundation.exceptions import ( BadValueException, PackException, UnpackException, ValidationError) # Third-party imports # This will determine the order on sphinx documentation. __all__ = ('GenericStruct', 'GenericMessage', 'GenericType', 'GenericBitMask', 'MetaStruct', 'MetaBitMask') # Classes class GenericType: """Foundation class for all custom attributes. Base class for :class:`~.UBInt8`, :class:`~.Char` and others. """ _fmt = None def __init__(self, value=None, enum_ref=None): """Create a GenericType with the optional parameters below. Args: value: The type's value. enum_ref (type): If :attr:`value` is from an Enum, specify its type. """ self._value = value self.enum_ref = enum_ref def __deepcopy__(self, memo): """Improve deepcopy speed.""" return type(self)(value=self._value, enum_ref=self.enum_ref) def __repr__(self): return "{}({})".format(type(self).__name__, self._value) def __str__(self): return '{}'.format(str(self._value)) def __eq__(self, other): if isinstance(other, self.__class__): return self.pack() == other.pack() elif hasattr(other, 'value'): return self.value == other.value return self.value == other def __ne__(self, other): return self._value != other def __gt__(self, other): return self._value > other def __ge__(self, other): return self._value >= other def __lt__(self, other): return self._value < other def __le__(self, other): return self._value <= other def __add__(self, other): return self.value + other def __radd__(self, other): return self.value + other def __sub__(self, other): return self.value - other def __rsub__(self, other): return self.value - other def __or__(self, other): return self.value | other def __ror__(self, other): return self.value | other def __and__(self, other): return self.value & other def __rand__(self, other): return self.value & other def __xor__(self, other): return self.value ^ other def __rxor__(self, other): return self.value ^ other def __lshift__(self, shift): return self.value << shift def __rshift__(self, shift): return self.value >> shift def __len__(self): return self.get_size() @property def value(self): """Return this type's value. Returns: object: The value of an enum, bitmask, etc. """ if self.isenum(): if isinstance(self._value, self.enum_ref): return self._value.value return self._value elif self.is_bitmask(): return self._value.bitmask else: return self._value def pack(self, value=None): r"""Pack the value as a binary representation. Considering an example with UBInt8 class, that inherits from GenericType: >>> from pyof.foundation.basic_types import UBInt8 >>> objectA = UBInt8(1) >>> objectB = 5 >>> objectA.pack() b'\x01' >>> objectA.pack(objectB) b'\x05' Args: value: If the value is None, then we will pack the value of the current instance. Otherwise, if value is an instance of the same type as the current instance, then we call the pack of the value object. Otherwise, we will use the current instance pack method on the passed value. Returns: bytes: The binary representation. Raises: :exc:`~.exceptions.BadValueException`: If the value does not fit the binary format. """ if isinstance(value, type(self)): return value.pack() if value is None: value = self.value elif 'value' in dir(value): # if it is enum or bitmask gets only the 'int' value value = value.value try: return struct.pack(self._fmt, value) except struct.error: expected_type = type(self).__name__ actual_type = type(value).__name__ msg_args = expected_type, value, actual_type msg = 'Expected {}, found value "{}" of type {}'.format(*msg_args) raise PackException(msg) def unpack(self, buff, 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. """ try: self._value = struct.unpack_from(self._fmt, buff, offset)[0] if self.enum_ref: self._value = self.enum_ref(self._value) except (struct.error, TypeError, ValueError) as exception: msg = '{}; fmt = {}, buff = {}, offset = {}.'.format(exception, self._fmt, buff, offset) raise UnpackException(msg) def get_size(self, value=None): # pylint: disable=unused-argument """Return the size in bytes of this type. Returns: int: Size in bytes. """ return struct.calcsize(self._fmt) def is_valid(self): """Check whether the value fits the binary format. Assert that :func:`pack` succeeds. Returns: bool: Whether the value is valid for this type. """ try: self.pack() return True except BadValueException: return False def isenum(self): """Test whether it is an :class:`~enum.Enum`. Returns: bool: Whether it is an :class:`~enum.Enum`. """ return self.enum_ref and issubclass(self.enum_ref, (Enum, IntEnum)) def is_bitmask(self): """Test whether it is a :class:`GenericBitMask`. Returns: bool: Whether it is a :class:`GenericBitMask`. """ return self._value and issubclass(type(self._value), GenericBitMask) class MetaStruct(type): """MetaClass that dinamically handles openflow version of class attributes. See more about it at: https://github.com/kytos/python-openflow/wiki/Version-Inheritance You do not need to use this class. Inherit from :class:`GenericStruct` instead. """ # pylint: disable=unused-argument @classmethod def __prepare__(mcs, name, bases, **kwargs): return OrderedDict() def __new__(mcs, name, bases, classdict, **kwargs): """Inherit attributes from parent class and update their versions. Here is the moment that the new class is going to be created. During this process, two things may be done. Firstly, we will look if the type of any parent classes is this MetaStruct. We will inherit from the first parent class that fits this requirement. If any is found, then we will get all attributes from this class and place them as class attributes on the class being created. Secondly, for each class attribute being inherited, we will make sure that the pyof version of this attribute is the same as the version of the current class being created. If it is not, then we will find out which is the class and module of that attribute, look for a version that matches the version of the current class and replace that attribute with the correct version. See this link for more information on why this is being done: - https://github.com/kytos/python-openflow/wiki/Version-Inheritance """ #: Retrieving class attributes management markers removed_attributes = classdict.pop('_removed_attributes', []) # renamed_attributes = classdict.pop('_renamed_attributes', []) # reordered_attributes = classdict.pop('_reordered_attributes', {}) curr_module = classdict.get('__module__') curr_version = MetaStruct.get_pyof_version(curr_module) inherited_attributes = None #: looking for (kytos) class attributes defined on the bases #: classes so we can copy them into the current class being created #: so we can "inherit" them as class attributes for base in bases: #: Check if we are inheriting from one of our classes. if isinstance(base, MetaStruct): inherited_attributes = OrderedDict() for attr_name, obj in base.get_class_attributes(): #: Get an updated version of this attribute, #: considering the version of the current class being #: created. attr = MetaStruct.get_pyof_obj_new_version(attr_name, obj, curr_version) if attr_name == 'header': attr = mcs._header_message_type_update(obj, attr) inherited_attributes.update([attr]) #: We are going to inherit just from the 'closest parent' break #: If we have inherited something, then first we will remove the #: attributes marked to be removed on the 'removed_attributes' and #: after that we will update the inherited 'classdict' with the #: attributes from the current classdict. if inherited_attributes is not None: #: removing attributes set to be removed for attr_name in removed_attributes: inherited_attributes.pop(attr_name, None) #: Updating the inherited attributes with those defined on the #: body of the class being created. inherited_attributes.update(classdict) classdict = inherited_attributes return super().__new__(mcs, name, bases, classdict, **kwargs) @staticmethod def _header_message_type_update(obj, attr): """Update the message type on the header. Set the message_type of the header according to the message_type of the parent class. """ old_enum = obj.message_type new_header = attr[1] new_enum = new_header.__class__.message_type.enum_ref #: This 'if' will be removed on the future with an #: improved version of __init_subclass__ method of the #: GenericMessage class if old_enum: msg_type_name = old_enum.name new_type = new_enum[msg_type_name] new_header.message_type = new_type return (attr[0], new_header) @staticmethod def get_pyof_version(module_fullname): """Get the module pyof version based on the module fullname. Args: module_fullname (str): The fullname of the module (e.g.: pyof.v0x01.common.header) Returns: str: openflow version. The openflow version, on the format 'v0x0?' if any. Or None if there isn't a version on the fullname. """ ver_module_re = re.compile(r'(pyof\.)(v0x\d+)(\..*)') matched = ver_module_re.match(module_fullname) if matched: version = matched.group(2) return version return None @staticmethod def replace_pyof_version(module_fullname, version): """Replace the OF Version of a module fullname. Get's a module name (eg. 'pyof.v0x01.common.header') and returns it on a new 'version' (eg. 'pyof.v0x02.common.header'). Args: module_fullname (str): The fullname of the module (e.g.: pyof.v0x01.common.header) version (str): The version to be 'inserted' on the module fullname. Returns: str: module fullname The new module fullname, with the replaced version, on the format "pyof.v0x01.common.header". If the requested version is the same as the one of the module_fullname or if the module_fullname is not a 'OF version' specific module, returns None. """ module_version = MetaStruct.get_pyof_version(module_fullname) if not module_version or module_version == version: return None return module_fullname.replace(module_version, version) @staticmethod def get_pyof_obj_new_version(name, obj, new_version): r"""Return a class attribute on a different pyof version. This method receives the name of a class attribute, the class attribute itself (object) and an openflow version. The attribute will be evaluated and from it we will recover its class and the module where the class was defined. If the module is a "python-openflow version specific module" (starts with "pyof.v0"), then we will get it's version and if it is different from the 'new_version', then we will get the module on the 'new_version', look for the 'obj' class on the new module and return an instance of the new version of the 'obj'. Example: >>> from pyof.foundation.base import MetaStruct as ms >>> from pyof.v0x01.common.header import Header >>> name = 'header' >>> obj = Header() >>> new_version = 'v0x04' >>> header, obj2 = ms.get_pyof_obj_new_version(name, obj, new_version) >>> header 'header' >>> obj.version UBInt8(1) >>> obj2.version UBInt8(4) Args: name (str): the name of the class attribute being handled. obj (object): the class attribute itself new_version (string): the pyof version in which you want the object 'obj'. Return: (str, obj): Tuple with class name and object instance. A tuple in which the first item is the name of the class attribute (the same that was passed), and the second item is a instance of the passed class attribute. If the class attribute is not a pyof versioned attribute, then the same passed object is returned without any changes. Also, if the obj is a pyof versioned attribute, but it is already on the right version (same as new_version), then the passed obj is return. """ if new_version is None: return (name, obj) cls = obj.__class__ cls_name = cls.__name__ cls_mod = cls.__module__ #: If the module name does not starts with pyof.v0 then it is not a #: 'pyof versioned' module (OpenFlow specification defined), so we do #: not have anything to do with it. new_mod = MetaStruct.replace_pyof_version(cls_mod, new_version) if new_mod is not None: # Loads the module new_mod = importlib.import_module(new_mod) #: Get the class from the loaded module new_cls = getattr(new_mod, cls_name) #: return the tuple with the attribute name and the instance return (name, new_cls()) return (name, obj) class GenericStruct(object, metaclass=MetaStruct): """Class inherited by all OpenFlow structs. If you need to insert a method that will be used by all structs, this is the place to code it. .. note:: A struct on this library's context is like a struct in C. It has a list of attributes and theses attributes can be structs, too. """ def __init__(self): """Contructor takes no argument and stores attributes' deep copies.""" for name, value in self.get_class_attributes(): setattr(self, name, deepcopy(value)) def __eq__(self, other): """Check whether two structures have the same structure and values. Compare the binary representation of structs to decide whether they are equal or not. Args: other (GenericStruct): The struct to be compared with. Returns: bool: Returns the result of comparation. """ return self.pack() == other.pack() @staticmethod def _attr_fits_into_class(attr, cls): if not isinstance(attr, cls): try: struct.pack(cls._fmt, attr) # pylint: disable=protected-access except struct.error: return False return True @staticmethod def _is_pyof_attribute(obj): """Return True if the object is a kytos attribute. To be a kytos attribute the item must be an instance of either GenericType or GenericStruct. Returns: bool: Returns TRUE if the obj is a kytos attribute, otherwise False """ return isinstance(obj, (GenericType, GenericStruct)) def _validate_attributes_type(self): """Validate the type of each attribute.""" for _attr, _class in self._get_attributes(): if isinstance(_attr, _class): return True elif issubclass(_class, GenericType): if GenericStruct._attr_fits_into_class(_attr, _class): return True elif not isinstance(_attr, _class): return False return True @classmethod def get_class_attributes(cls): """Return a generator for class attributes' names and value. This method strict relies on the PEP 520 (Preserving Class Attribute Definition Order), implemented on Python 3.6. So, if this behaviour changes this whole lib can loose its functionality (since the attributes order are a strong requirement.) For the same reason, this lib will not work on python versions earlier than 3.6. .. code-block:: python3 for name, value in self.get_class_attributes(): print("attribute name: {}".format(name)) print("attribute type: {}".format(value)) Returns: generator: tuples with attribute name and value. """ #: see this method docstring for a important notice about the use of #: cls.__dict__ for name, value in cls.__dict__.items(): # gets only our (kytos) attributes. this ignores methods, dunder # methods and attributes, and common python type attributes. if GenericStruct._is_pyof_attribute(value): yield (name, value) def _get_instance_attributes(self): """Return a generator for instance attributes' name and value. .. code-block:: python3 for _name, _value in self._get_instance_attributes(): print("attribute name: {}".format(_name)) print("attribute value: {}".format(_value)) Returns: generator: tuples with attribute name and value. """ for name, value in self.__dict__.items(): if name in map((lambda x: x[0]), self.get_class_attributes()): yield (name, value) def _get_attributes(self): """Return a generator for instance and class attribute. .. code-block:: python3 for instance_attribute, class_attribute in self._get_attributes(): print("Instance Attribute: {}".format(instance_attribute)) print("Class Attribute: {}".format(class_attribute)) Returns: generator: Tuples with instance attribute and class attribute """ return map((lambda i, c: (i[1], c[1])), self._get_instance_attributes(), self.get_class_attributes()) def _get_named_attributes(self): """Return generator for attribute's name, instance and class values. Add attribute name to meth:`_get_attributes` for a better debugging message, so user can find the error easier. Returns: generator: Tuple with attribute's name, instance and class values. """ for cls, instance in zip(self.get_class_attributes(), self._get_instance_attributes()): attr_name, cls_value = cls instance_value = instance[1] yield attr_name, instance_value, cls_value def _unpack_attribute(self, name, obj, buff, begin): attribute = deepcopy(obj) setattr(self, name, attribute) if not buff: size = 0 else: try: attribute.unpack(buff, begin) size = attribute.get_size() except UnpackException as exception: child_cls = type(self).__name__ msg = '{}.{}; {}'.format(child_cls, name, exception) raise UnpackException(msg) return size def get_size(self, value=None): """Calculate the total struct size in bytes. For each struct attribute, sum the result of each one's ``get_size()`` method. Args: value: In structs, the user can assign other value instead of a class' instance. Returns: int: Total number of bytes used by the struct. Raises: Exception: If the struct is not valid. """ if value is None: return sum(cls_val.get_size(obj_val) for obj_val, cls_val in self._get_attributes()) elif isinstance(value, type(self)): return value.get_size() else: msg = "{} is not an instance of {}".format(value, type(self).__name__) raise PackException(msg) def pack(self, value=None): """Pack the struct in a binary representation. Iterate over the class attributes, according to the order of definition, and then convert each attribute to its byte representation using its own ``pack`` method. Returns: bytes: Binary representation of the struct object. Raises: :exc:`~.exceptions.ValidationError`: If validation fails. """ if value is None: if not self.is_valid(): error_msg = "Error on validation prior to pack() on class " error_msg += "{}.".format(type(self).__name__) raise ValidationError(error_msg) else: message = b'' # pylint: disable=no-member for attr_info in self._get_named_attributes(): name, instance_value, class_value = attr_info try: message += class_value.pack(instance_value) except PackException as pack_exception: cls = type(self).__name__ msg = f'{cls}.{name} - {pack_exception}' raise PackException(msg) return message elif isinstance(value, type(self)): return value.pack() else: msg = "{} is not an instance of {}".format(value, type(self).__name__) raise PackException(msg) def unpack(self, buff, offset=0): """Unpack a binary struct into this object's attributes. Update this object attributes based on the unpacked values of *buff*. It is an inplace method and it receives the binary data of the struct. Args: buff (bytes): Binary data package to be unpacked. offset (int): Where to begin unpacking. """ begin = offset for name, value in self.get_class_attributes(): size = self._unpack_attribute(name, value, buff, begin) begin += size def is_valid(self): """Check whether all struct attributes in are valid. This method will check whether all struct attributes have a proper value according to the OpenFlow specification. For instance, if you have a struct with an attribute of type :class:`~pyof.foundation.basic_types.UBInt8` and you assign a string value to it, this method will return False. Returns: bool: Whether the struct is valid. """ return True # pylint: disable=unreachable return self._validate_attributes_type() class GenericMessage(GenericStruct): """Base class that is the foundation for all OpenFlow messages. To add a method that will be used by all messages, write it here. .. note:: A Message on this library context is like a Struct but has a also a :attr:`header` attribute. """ header = None def __init__(self, xid=None): """Initialize header's xid.""" super().__init__() self.header.xid = randint(0, MAXID) if xid is None else xid def __repr__(self): """Show a full representation of the object.""" return "%s(xid=%r)" % (self.__class__.__name__, self.header.xid if self.header else None) def __init_subclass__(cls, **kwargs): if cls.header is None or cls.header.__class__.__name__ != 'Header': msg = "The header attribute must be implemented on the class " msg += cls.__name__ + "." raise NotImplementedError(msg) super().__init_subclass__(**kwargs) def _validate_message_length(self): return self.header.length == self.get_size() def is_valid(self): """Check whether a message is valid or not. This method will validate the Message content. During the validation process, we check whether the attributes' values are valid according to the OpenFlow specification. Call this method if you want to verify whether the message is ready to pack. Returns: bool: Whether the message is valid. """ return True # pylint: disable=unreachable return super().is_valid() and self._validate_message_length() def pack(self, value=None): """Pack the message into a binary data. One of the basic operations on a Message is the pack operation. During the packing process, we convert all message attributes to binary format. Since that this is usually used before sending the message to a switch, here we also call :meth:`update_header_length`. .. seealso:: This method call its parent's :meth:`GenericStruct.pack` after :meth:`update_header_length`. Returns: bytes: A binary data thats represents the Message. Raises: Exception: If there are validation errors. """ if value is None: self.update_header_length() return super().pack() elif isinstance(value, type(self)): return value.pack() else: msg = "{} is not an instance of {}".format(value, type(self).__name__) raise PackException(msg) def unpack(self, buff, 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. It is an inplace method and it receives the binary data of the message **without the header**. Args: buff (bytes): Binary data package to be unpacked, without the header. offset (int): Where to begin unpacking. """ begin = offset for name, value in self.get_class_attributes(): if type(value).__name__ != "Header": size = self._unpack_attribute(name, value, buff, begin) begin += size def update_header_length(self): """Update the header length attribute based on current message size. When sending an OpenFlow message we need to inform the message length on the header. This is mandatory. """ self.header.length = self.get_size() class MetaBitMask(type): """MetaClass to create a special BitMaskEnum type. You probably do not need to use this class. Inherit from :class:`GenericBitMask` instead. This metaclass converts the declared class attributes into elements of an enum. It also replaces the :meth:`__dir__` and :meth:`__getattr__` methods, so the resulting class will behave as an :class:`~Enum` class (you can access object.ELEMENT and recover either values or names). """ def __new__(mcs, name, bases, classdict): """Convert class attributes into enum elements.""" _enum = OrderedDict([(key, value) for key, value in classdict.items() if key[0] != '_' and not hasattr(value, '__call__') and not isinstance(value, property)]) if _enum: classdict = {key: value for key, value in classdict.items() if key[0] == '_' or hasattr(value, '__call__') or isinstance(value, property)} classdict['_enum'] = _enum return type.__new__(mcs, name, bases, classdict) def __getattr__(cls, name): return cls._enum[name] def __dir__(cls): res = dir(type(cls)) + list(cls.__dict__.keys()) if cls is not GenericBitMask: res.extend(cls._enum) return res class GenericBitMask(object, metaclass=MetaBitMask): """Base class for enums that use bitmask values.""" def __init__(self, bitmask=None): """Create a GenericBitMask with the optional parameter below. Args: bitmask: Bitmask value. """ self.bitmask = bitmask self._enum = {} def __str__(self): return "{}".format(self.bitmask) def __repr__(self): return "{}({})".format(type(self).__name__, self.bitmask) @property def names(self): """List of selected enum names. Returns: list: Enum names. """ result = [] for key, value in self.iteritems(): if value & self.bitmask: result.append(key) return result def iteritems(self): """Create a generator for attributes' name-value pairs. Returns: generator: Attributes' (name, value) tuples. """ for key, value in self._enum.items(): yield (key, value) python-openflow-2019.2/pyof/foundation/constants.py0000644000175100001630000000050013577157232023277 0ustar runnerdocker00000000000000"""General constants from python-openflow library.""" # Max values of each basic type UBINT8_MAX_VALUE = 255 UBINT16_MAX_VALUE = 65535 UBINT32_MAX_VALUE = 4294967295 UBINT64_MAX_VALUE = 18446744073709551615 OFP_ETH_ALEN = 6 OFP_MAX_PORT_NAME_LEN = 16 OFP_MAX_TABLE_NAME_LEN = 32 SERIAL_NUM_LEN = 32 DESC_STR_LEN = 256 python-openflow-2019.2/python_openflow.egg-info/0000755000175100001630000000000013577157243022521 5ustar runnerdocker00000000000000python-openflow-2019.2/python_openflow.egg-info/dependency_links.txt0000644000175100001630000000000113577157243026567 0ustar runnerdocker00000000000000 python-openflow-2019.2/python_openflow.egg-info/requires.txt0000644000175100001630000000005713577157243025123 0ustar runnerdocker00000000000000 [dev] pip-tools>=2.0 coverage pytest yala tox python-openflow-2019.2/python_openflow.egg-info/PKG-INFO0000644000175100001630000000103313577157243023613 0ustar runnerdocker00000000000000Metadata-Version: 2.1 Name: python-openflow Version: 2019.2 Summary: Library to parse and generate OpenFlow messages Home-page: http://github.com/kytos/python-openflow Author: Kytos Team Author-email: devel@lists.kytos.io License: MIT Description: UNKNOWN Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: System :: Networking Classifier: Topic :: Software Development :: Libraries Provides-Extra: dev python-openflow-2019.2/python_openflow.egg-info/SOURCES.txt0000644000175100001630000002114113577157243024404 0ustar runnerdocker00000000000000README.rst setup.cfg setup.py pyof/__init__.py pyof/utils.py pyof/foundation/__init__.py pyof/foundation/base.py pyof/foundation/basic_types.py pyof/foundation/constants.py pyof/foundation/exceptions.py pyof/foundation/network_types.py pyof/v0x01/__init__.py pyof/v0x01/asynchronous/__init__.py pyof/v0x01/asynchronous/error_msg.py pyof/v0x01/asynchronous/flow_removed.py pyof/v0x01/asynchronous/packet_in.py pyof/v0x01/asynchronous/port_status.py pyof/v0x01/common/__init__.py pyof/v0x01/common/action.py pyof/v0x01/common/constants.py pyof/v0x01/common/flow_match.py pyof/v0x01/common/header.py pyof/v0x01/common/phy_port.py pyof/v0x01/common/queue.py pyof/v0x01/common/utils.py pyof/v0x01/controller2switch/__init__.py pyof/v0x01/controller2switch/barrier_reply.py pyof/v0x01/controller2switch/barrier_request.py pyof/v0x01/controller2switch/common.py pyof/v0x01/controller2switch/features_reply.py pyof/v0x01/controller2switch/features_request.py pyof/v0x01/controller2switch/flow_mod.py pyof/v0x01/controller2switch/get_config_reply.py pyof/v0x01/controller2switch/get_config_request.py pyof/v0x01/controller2switch/packet_out.py pyof/v0x01/controller2switch/port_mod.py pyof/v0x01/controller2switch/queue_get_config_reply.py pyof/v0x01/controller2switch/queue_get_config_request.py pyof/v0x01/controller2switch/set_config.py pyof/v0x01/controller2switch/stats_reply.py pyof/v0x01/controller2switch/stats_request.py pyof/v0x01/symmetric/__init__.py pyof/v0x01/symmetric/echo_reply.py pyof/v0x01/symmetric/echo_request.py pyof/v0x01/symmetric/hello.py pyof/v0x01/symmetric/vendor_header.py pyof/v0x04/__init__.py pyof/v0x04/asynchronous/__init__.py pyof/v0x04/asynchronous/error_msg.py pyof/v0x04/asynchronous/flow_removed.py pyof/v0x04/asynchronous/packet_in.py pyof/v0x04/asynchronous/port_status.py pyof/v0x04/common/__init__.py pyof/v0x04/common/action.py pyof/v0x04/common/constants.py pyof/v0x04/common/flow_instructions.py pyof/v0x04/common/flow_match.py pyof/v0x04/common/header.py pyof/v0x04/common/port.py pyof/v0x04/common/queue.py pyof/v0x04/common/utils.py pyof/v0x04/controller2switch/__init__.py pyof/v0x04/controller2switch/barrier_reply.py pyof/v0x04/controller2switch/barrier_request.py pyof/v0x04/controller2switch/common.py pyof/v0x04/controller2switch/features_reply.py pyof/v0x04/controller2switch/features_request.py pyof/v0x04/controller2switch/flow_mod.py pyof/v0x04/controller2switch/get_async_reply.py pyof/v0x04/controller2switch/get_async_request.py pyof/v0x04/controller2switch/get_config_reply.py pyof/v0x04/controller2switch/get_config_request.py pyof/v0x04/controller2switch/group_mod.py pyof/v0x04/controller2switch/meter_mod.py pyof/v0x04/controller2switch/multipart_reply.py pyof/v0x04/controller2switch/multipart_request.py pyof/v0x04/controller2switch/packet_out.py pyof/v0x04/controller2switch/port_mod.py pyof/v0x04/controller2switch/queue_get_config_reply.py pyof/v0x04/controller2switch/queue_get_config_request.py pyof/v0x04/controller2switch/role_reply.py pyof/v0x04/controller2switch/role_request.py pyof/v0x04/controller2switch/set_async.py pyof/v0x04/controller2switch/set_config.py pyof/v0x04/controller2switch/table_mod.py pyof/v0x04/symmetric/__init__.py pyof/v0x04/symmetric/echo_reply.py pyof/v0x04/symmetric/echo_request.py pyof/v0x04/symmetric/experimenter.py pyof/v0x04/symmetric/hello.py python_openflow.egg-info/PKG-INFO python_openflow.egg-info/SOURCES.txt python_openflow.egg-info/dependency_links.txt python_openflow.egg-info/not-zip-safe python_openflow.egg-info/requires.txt python_openflow.egg-info/top_level.txt tests/test_class_inheritance/__init__.py tests/test_class_inheritance/test_inheritance.py tests/test_foundation/__init__.py tests/test_foundation/test_base.py tests/test_foundation/test_basic_types.py tests/test_foundation/test_network_types.py tests/v0x01/__init__.py tests/v0x01/test_asynchronous/__init__.py tests/v0x01/test_asynchronous/test_error_msg.py tests/v0x01/test_asynchronous/test_flow_removed.py tests/v0x01/test_asynchronous/test_packet_in.py tests/v0x01/test_asynchronous/test_port_status.py tests/v0x01/test_common/__init__.py tests/v0x01/test_common/test_action.py tests/v0x01/test_common/test_flow_match.py tests/v0x01/test_common/test_header.py tests/v0x01/test_common/test_phy_port.py tests/v0x01/test_common/test_queue.py tests/v0x01/test_controller2switch/__init__.py tests/v0x01/test_controller2switch/test_aggregate_stats_reply.py tests/v0x01/test_controller2switch/test_aggregate_stats_request.py tests/v0x01/test_controller2switch/test_barrier_reply.py tests/v0x01/test_controller2switch/test_barrier_request.py tests/v0x01/test_controller2switch/test_desc_stats.py tests/v0x01/test_controller2switch/test_features_reply.py tests/v0x01/test_controller2switch/test_features_request.py tests/v0x01/test_controller2switch/test_flow_mod.py tests/v0x01/test_controller2switch/test_flow_stats.py tests/v0x01/test_controller2switch/test_flow_stats_request.py tests/v0x01/test_controller2switch/test_get_config_reply.py tests/v0x01/test_controller2switch/test_get_config_request.py tests/v0x01/test_controller2switch/test_packet_out.py tests/v0x01/test_controller2switch/test_port_mod.py tests/v0x01/test_controller2switch/test_port_stats.py tests/v0x01/test_controller2switch/test_port_stats_request.py tests/v0x01/test_controller2switch/test_queue_get_config_reply.py tests/v0x01/test_controller2switch/test_queue_get_config_request.py tests/v0x01/test_controller2switch/test_queue_stats.py tests/v0x01/test_controller2switch/test_queue_stats_request.py tests/v0x01/test_controller2switch/test_set_config.py tests/v0x01/test_controller2switch/test_stats_reply.py tests/v0x01/test_controller2switch/test_stats_request.py tests/v0x01/test_controller2switch/test_table_stats.py tests/v0x01/test_controller2switch/test_vendor_stats.py tests/v0x01/test_symmetric/__init__.py tests/v0x01/test_symmetric/test_echo_reply.py tests/v0x01/test_symmetric/test_echo_request.py tests/v0x01/test_symmetric/test_hello.py tests/v0x01/test_symmetric/test_vendor_header.py tests/v0x04/__init__.py tests/v0x04/test_struct.py tests/v0x04/test_asynchronous/__init__.py tests/v0x04/test_asynchronous/test_error_msg.py tests/v0x04/test_asynchronous/test_flow_removed.py tests/v0x04/test_asynchronous/test_packet_in.py tests/v0x04/test_asynchronous/test_port_status.py tests/v0x04/test_common/__init__.py tests/v0x04/test_common/test_flow_match.py tests/v0x04/test_common/test_port.py tests/v0x04/test_common/test_queue.py tests/v0x04/test_controller2switch/__init__.py tests/v0x04/test_controller2switch/test_aggregate_stats.py tests/v0x04/test_controller2switch/test_aggregate_stats_request.py tests/v0x04/test_controller2switch/test_barrier_reply.py tests/v0x04/test_controller2switch/test_barrier_request.py tests/v0x04/test_controller2switch/test_features_reply.py tests/v0x04/test_controller2switch/test_features_request.py tests/v0x04/test_controller2switch/test_flow_mod.py tests/v0x04/test_controller2switch/test_flow_stats.py tests/v0x04/test_controller2switch/test_flow_stats_request.py tests/v0x04/test_controller2switch/test_get_async_reply.py tests/v0x04/test_controller2switch/test_get_async_request.py tests/v0x04/test_controller2switch/test_get_config_reply.py tests/v0x04/test_controller2switch/test_get_config_request.py tests/v0x04/test_controller2switch/test_group_mod.py tests/v0x04/test_controller2switch/test_group_stats.py tests/v0x04/test_controller2switch/test_group_stats_request.py tests/v0x04/test_controller2switch/test_meter_mod.py tests/v0x04/test_controller2switch/test_meter_multipart_request.py tests/v0x04/test_controller2switch/test_multipart_reply.py tests/v0x04/test_controller2switch/test_multipart_request.py tests/v0x04/test_controller2switch/test_packet_out.py tests/v0x04/test_controller2switch/test_port_desc.py tests/v0x04/test_controller2switch/test_port_mod.py tests/v0x04/test_controller2switch/test_port_stats.py tests/v0x04/test_controller2switch/test_port_stats_request.py tests/v0x04/test_controller2switch/test_queue_get_config_reply.py tests/v0x04/test_controller2switch/test_queue_get_config_request.py tests/v0x04/test_controller2switch/test_queue_stats.py tests/v0x04/test_controller2switch/test_queue_stats_request.py tests/v0x04/test_controller2switch/test_role_reply.py tests/v0x04/test_controller2switch/test_role_request.py tests/v0x04/test_controller2switch/test_set_async.py tests/v0x04/test_controller2switch/test_set_config.py tests/v0x04/test_controller2switch/test_table_mod.py tests/v0x04/test_controller2switch/test_table_stats.py tests/v0x04/test_symmetric/__init__.py tests/v0x04/test_symmetric/test_echo_reply.py tests/v0x04/test_symmetric/test_echo_request.py tests/v0x04/test_symmetric/test_hello.py tests/v0x04/test_symmetric/test_vendor_header.pypython-openflow-2019.2/python_openflow.egg-info/top_level.txt0000644000175100001630000000001313577157243025245 0ustar runnerdocker00000000000000pyof tests python-openflow-2019.2/python_openflow.egg-info/not-zip-safe0000644000175100001630000000000113577157243024747 0ustar runnerdocker00000000000000 python-openflow-2019.2/tests/0000755000175100001630000000000013577157243016737 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/test_class_inheritance/0000755000175100001630000000000013577157243023454 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/test_class_inheritance/__init__.py0000644000175100001630000000005413577157232025562 0ustar runnerdocker00000000000000"""Tests related to classes inheritance.""" python-openflow-2019.2/tests/test_class_inheritance/test_inheritance.py0000644000175100001630000000505013577157232027354 0ustar runnerdocker00000000000000"""Testing class inheritance attributes changes.""" import unittest from pyof.foundation.base import GenericStruct from pyof.foundation.basic_types import UBInt8, UBInt16, UBInt32, UBInt64 class TestInheritance(unittest.TestCase): """Testing GenericStruct class inheritance.""" def setUp(self): """Basic Test Setup.""" class MyClassA(GenericStruct): """Example class.""" a1 = UBInt8(1) a2 = UBInt16(2) a3 = UBInt8(3) a4 = UBInt16(4) a5 = UBInt32(5) class MyClassB(MyClassA): """Example class.""" a0 = UBInt32(0) a2 = UBInt64(2) b6 = UBInt8(6) _removed_attributes = ['a3'] # _rename_attributes = [('a4', 'b4')] # _insert_attributes_before = {'a0': 'a1'} self.MyClassA = MyClassA self.MyClassB = MyClassB self.a_expected_names = ['a1', 'a2', 'a3', 'a4', 'a5'] self.b_expected_names = ['a1', 'a2', 'a4', 'a5', 'a0', 'b6'] # self.b_expected_names = ['a0', 'a1', 'a2', 'b4', 'a5', 'b6'] def test_modifications(self): """[Foundation/Base/GenericStruct] - Attributes Modifications.""" m1 = self.MyClassA() m2 = self.MyClassB() # Checking keys (attributes names) and its ordering self.assertEqual([attr[0] for attr in m1.get_class_attributes()], self.a_expected_names) self.assertEqual([attr[0] for attr in m2.get_class_attributes()], self.b_expected_names) # Check if there is no shared attribute between instances self.assertIsNot(m1, m2) self.assertIsNot(m1.a1, m2.a1) self.assertIsNot(m1.a2, m2.a2) self.assertIsNot(m1.a3, m2.a4) self.assertIsNot(m1.a4, m2.a4) self.assertIsNot(m1.a5, m2.a5) # Check attributes types on MyClassA self.assertIsInstance(self.MyClassA.a1, UBInt8) self.assertIsInstance(self.MyClassA.a2, UBInt16) self.assertIsInstance(self.MyClassA.a3, UBInt8) self.assertIsInstance(self.MyClassA.a4, UBInt16) self.assertIsInstance(self.MyClassA.a5, UBInt32) # Check attributes types on MyClassA self.assertIsInstance(self.MyClassB.a1, UBInt8) self.assertIsInstance(self.MyClassB.a2, UBInt64) self.assertIsInstance(self.MyClassB.a4, UBInt16) self.assertIsInstance(self.MyClassB.a5, UBInt32) self.assertIsInstance(self.MyClassB.a0, UBInt32) self.assertIsInstance(self.MyClassB.b6, UBInt8) python-openflow-2019.2/tests/v0x04/0000755000175100001630000000000013577157243017620 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/v0x04/test_controller2switch/0000755000175100001630000000000013577157243024346 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/v0x04/test_controller2switch/test_table_stats.py0000644000175100001630000000076313577157232030270 0ustar runnerdocker00000000000000"""Table stats message.""" from pyof.v0x04.controller2switch.multipart_reply import TableStats from tests.test_struct import TestStruct class TestTableStats(TestStruct): """Table stats message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_table_stats') super().set_raw_dump_object(TableStats) super().set_minimum_size(24) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_role_request.py0000644000175100001630000000110713577157232030465 0ustar runnerdocker00000000000000"""RoleRequest message tests.""" from pyof.v0x04.controller2switch.role_request import RoleRequest from tests.test_struct import TestStruct class TestRoleRequest(TestStruct): """Test the RoleRequest message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_role_request') super().set_raw_dump_object(RoleRequest, xid=3, role=0, generation_id=0) super().set_minimum_size(24) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_group_mod.py0000644000175100001630000001377113577157232027761 0ustar runnerdocker00000000000000"""group_mod tests.""" from unittest import TestCase from pyof.v0x04.controller2switch.group_mod import GroupMod from pyof.v0x04.common.action import ( ActionExperimenter, ActionSetField, ListOfActions) from pyof.v0x04.common.flow_match import OxmClass, OxmOfbMatchField, OxmTLV from pyof.v0x04.common.port import PortNo from pyof.v0x04.controller2switch.common import Bucket from pyof.v0x04.controller2switch.group_mod import ListOfBuckets class TestGroupMod(TestCase): """group_mod tests.""" def test_min_size(self): """Test minimum struct size.""" self.assertEqual(16, GroupMod().get_size()) class TestBucket(TestCase): """bucket tests.""" def test_min_size(self): """Test minimum struct size.""" self.assertEqual(16, Bucket().get_size()) class TestListBuckets(TestCase): def setUp(self): """Configure raw file and its object in parent class (TestDump).""" super().setUp() self.oxmtlv1 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_METADATA, oxm_hasmask=False, oxm_value=b'\x00\x00\x00\x00\x00\x00\x00\x01') self.oxmtlv2 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_METADATA, oxm_hasmask=False, oxm_value=b'\x00\x00\x00\x00\x00\x00\x00\x02') self.action1 = ActionSetField(field=self.oxmtlv1) self.action2 = ActionSetField(field=self.oxmtlv2) self.action3 = ActionExperimenter(length=16, experimenter=0x00002320, body=b'\x00\x0e\xff\xf8\x28\x00\x00\x00') self.action4 = ActionExperimenter(length=16, experimenter=0x00001223, body=b'\x00\x0e\xff\xff\x28\x00\x00\x00') def test_bucket_list(self): bucket1 = Bucket(length=48, weight=1, watch_port=PortNo.OFPP_ANY, watch_group=PortNo.OFPP_ANY, actions=ListOfActions([self.action1, self.action2])) bucket2 = Bucket(length=80, weight=2, watch_port=PortNo.OFPP_ANY, watch_group=PortNo.OFPP_ANY, actions=ListOfActions([self.action1, self.action2, self.action3, self.action4])) bucket3 = Bucket(length=48, weight=3, watch_port=PortNo.OFPP_ANY, watch_group=PortNo.OFPP_ANY, actions=ListOfActions([self.action3, self.action4])) # Packing buckets buckets = ListOfBuckets([bucket1, bucket2, bucket3]) buff = packed_buff = buckets.pack() # Unpacking buckets bytes unpacked_buckets = ListOfBuckets() unpacked_buckets.unpack(buff) self.assertEqual(len(unpacked_buckets), 3) self.assertEqual(unpacked_buckets[0].length, 48) self.assertEqual(unpacked_buckets[0].weight, 1) self.assertEqual(len(unpacked_buckets[0].actions), 2) self.assertEqual(unpacked_buckets[0].actions[0].field.oxm_value, self.oxmtlv1.oxm_value) self.assertEqual(unpacked_buckets[0].actions[1].field.oxm_value, self.oxmtlv2.oxm_value) self.assertEqual(unpacked_buckets[1].length, 80) self.assertEqual(unpacked_buckets[1].weight, 2) self.assertEqual(len(unpacked_buckets[1].actions), 4) self.assertEqual(unpacked_buckets[1].actions[0].field.oxm_value, self.oxmtlv1.oxm_value) self.assertEqual(unpacked_buckets[1].actions[1].field.oxm_value, self.oxmtlv2.oxm_value) self.assertEqual(unpacked_buckets[1].actions[2].body, self.action3.body) self.assertEqual(unpacked_buckets[1].actions[3].body, self.action4.body) self.assertEqual(unpacked_buckets[2].length, 48) self.assertEqual(unpacked_buckets[2].weight, 3) self.assertEqual(len(unpacked_buckets[2].actions), 2) self.assertEqual(unpacked_buckets[2].actions[0].body, self.action3.body) self.assertEqual(unpacked_buckets[2].actions[1].body, self.action4.body) def test_buckets_one_item(self): bucket1 = Bucket(length=48, weight=1, watch_port=PortNo.OFPP_ANY, watch_group=PortNo.OFPP_ANY, actions=ListOfActions([self.action1, self.action2])) # Packing buckets buckets = ListOfBuckets([bucket1]) buff = packed_buff = buckets.pack() # Unpacking buckets bytes unpacked_buckets = ListOfBuckets() unpacked_buckets.unpack(buff) self.assertEqual(len(unpacked_buckets), 1) self.assertEqual(unpacked_buckets[0].length, 48) self.assertEqual(unpacked_buckets[0].weight, 1) self.assertEqual(len(unpacked_buckets[0].actions), 2) self.assertEqual(unpacked_buckets[0].actions[0].field.oxm_value, self.oxmtlv1.oxm_value) self.assertEqual(unpacked_buckets[0].actions[1].field.oxm_value, self.oxmtlv2.oxm_value) def test_buckets_no_action(self): bucket1 = Bucket(length=48, weight=1, watch_port=PortNo.OFPP_ANY, watch_group=PortNo.OFPP_ANY, actions=ListOfActions([self.action1])) # Packing buckets buckets = ListOfBuckets([bucket1]) buff = packed_buff = buckets.pack() # Unpacking buckets bytes unpacked_buckets = ListOfBuckets() unpacked_buckets.unpack(buff) self.assertEqual(len(unpacked_buckets), 1) self.assertEqual(unpacked_buckets[0].length, 48) self.assertEqual(unpacked_buckets[0].weight, 1) self.assertEqual(len(unpacked_buckets[0].actions), 1) self.assertEqual(unpacked_buckets[0].actions[0].field.oxm_value, self.oxmtlv1.oxm_value) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_role_reply.py0000644000175100001630000000107113577157232030130 0ustar runnerdocker00000000000000"""RoleReply message tests.""" from pyof.v0x04.controller2switch.role_reply import RoleReply from tests.test_struct import TestStruct class TestRoleReply(TestStruct): """Test the RoleReply message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_role_reply') super().set_raw_dump_object(RoleReply, xid=3, role=0, generation_id=0) super().set_minimum_size(24) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_get_async_reply.py0000644000175100001630000000140213577157232031141 0ustar runnerdocker00000000000000"""GetAsyncReply message tests.""" from pyof.v0x04.controller2switch.get_async_reply import GetAsyncReply from tests.test_struct import TestStruct class TestGetAsyncReply(TestStruct): """Test the GetAsyncReply message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_get_async_reply') super().set_raw_dump_object(GetAsyncReply, xid=3, packet_in_mask1=0, packet_in_mask2=0, port_status_mask1=0, port_status_mask2=0, flow_removed_mask1=0, flow_removed_mask2=0) super().set_minimum_size(32) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_set_config.py0000644000175100001630000000145413577157232030101 0ustar runnerdocker00000000000000"""Set Config message tests.""" from pyof.v0x04.common.action import ControllerMaxLen from pyof.v0x04.controller2switch.common import ConfigFlag from pyof.v0x04.controller2switch.set_config import SetConfig from tests.test_struct import TestStruct class TestSetConfig(TestStruct): """Test the Set Config message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" buffer = ControllerMaxLen.OFPCML_NO_BUFFER super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_set_config') super().set_raw_dump_object(SetConfig, xid=1201346349, flags=ConfigFlag.OFPC_FRAG_NORMAL, miss_send_len=buffer) super().set_minimum_size(12) python-openflow-2019.2/tests/v0x04/test_controller2switch/__init__.py0000644000175100001630000000004213577157232026451 0ustar runnerdocker00000000000000"""Controller-to-switch tests.""" python-openflow-2019.2/tests/v0x04/test_controller2switch/test_queue_get_config_request.py0000644000175100001630000000114513577157232033036 0ustar runnerdocker00000000000000"""Testing QueueGetConfigRequest message.""" from pyof.v0x04.controller2switch.queue_get_config_request import ( QueueGetConfigRequest) from tests.test_struct import TestStruct class TestQueueGetConfigRequest(TestStruct): """Test the QueueGetConfigRequest message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_queue_get_config_request') super().set_raw_dump_object(QueueGetConfigRequest, xid=1, port=1) super().set_minimum_size(16) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_queue_stats.py0000644000175100001630000000076313577157232030325 0ustar runnerdocker00000000000000"""Stats queue message.""" from pyof.v0x04.controller2switch.multipart_reply import QueueStats from tests.test_struct import TestStruct class TestQueueStats(TestStruct): """Stats queue message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_queue_stats') super().set_raw_dump_object(QueueStats) super().set_minimum_size(40) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_barrier_request.py0000644000175100001630000000107613577157232031157 0ustar runnerdocker00000000000000"""Barrier request message tests.""" from pyof.v0x04.controller2switch.barrier_request import BarrierRequest from tests.test_struct import TestStruct class TestBarrierRequest(TestStruct): """Barrier reply message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_barrier_request') super().set_raw_dump_object(BarrierRequest, xid=5) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_group_stats_request.py0000644000175100001630000000157213577157232032104 0ustar runnerdocker00000000000000"""Group stats request message.""" from pyof.v0x04.controller2switch.common import MultipartType from pyof.v0x04.controller2switch.multipart_request import ( GroupStatsRequest, MultipartRequest) from tests.test_struct import TestStruct class TestGroupStatsRequest(TestStruct): """Group stats request message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_group_stats_request') super().set_raw_dump_object(MultipartRequest, xid=1, multipart_type=MultipartType.OFPMP_GROUP, flags=0, body=_get_body()) super().set_minimum_size(16) def _get_body(): """Return the body used by MultipartRequest message.""" return GroupStatsRequest() python-openflow-2019.2/tests/v0x04/test_controller2switch/test_meter_mod.py0000644000175100001630000000227013577157232027731 0ustar runnerdocker00000000000000"""MeterMod tests.""" from unittest import TestCase from pyof.v0x04.controller2switch.meter_mod import ( MeterBandDrop, MeterBandDscpRemark, MeterBandExperimenter, MeterBandHeader, MeterMod) class TestMeterMod(TestCase): """MeterMod test.""" def test_min_size(self): """Test minimum message size.""" self.assertEqual(16, MeterMod().get_size()) class TestMeterBandHeader(TestCase): """MeterBandHeader test.""" def test_min_size(self): """Test minimum message size.""" self.assertEqual(12, MeterBandHeader().get_size()) class TestMeterBandDrop(TestCase): """MeterBandDrop test.""" def test_min_size(self): """Test minimum message size.""" self.assertEqual(16, MeterBandDrop().get_size()) class TestMeterBandDscpRemark(TestCase): """MeterBandDscpRemark test.""" def test_min_size(self): """Test minimum message size.""" self.assertEqual(16, MeterBandDscpRemark().get_size()) class TestMeterBandExperimenter(TestCase): """MeterBandExperimenter test.""" def test_min_size(self): """Test minimum message size.""" self.assertEqual(16, MeterBandExperimenter().get_size()) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_group_stats.py0000644000175100001630000000300313577157232030323 0ustar runnerdocker00000000000000"""Group stats message.""" from pyof.v0x04.common.action import ActionOutput, ListOfActions from pyof.v0x04.common.flow_instructions import ( InstructionApplyAction, ListOfInstruction) from pyof.v0x04.common.flow_match import ( Match, MatchType, OxmClass, OxmOfbMatchField, OxmTLV) from pyof.v0x04.common.port import PortNo from pyof.v0x04.controller2switch.common import ( BucketCounter, ListOfBucketCounter, MultipartType) from pyof.v0x04.controller2switch.multipart_reply import ( GroupStats, MultipartReply) from tests.test_struct import TestStruct class TestGroupStats(TestStruct): """Group stats message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_group_stats') super().set_raw_dump_object(MultipartReply, xid=1, multipart_type=MultipartType.OFPMP_GROUP, flags=0, body=_get_body()) super().set_minimum_size(16) def _get_body(): """Return the body used by MultipartReply message.""" bs = ListOfBucketCounter([BucketCounter(packet_count=0, byte_count=0), BucketCounter(packet_count=0, byte_count=0)]) return GroupStats(length=72, group_id=1, ref_count=0, packet_count=0, byte_count=0, duration_sec=14, duration_nsec=837000000, bucket_stats=bs) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_port_mod.py0000644000175100001630000000043213577157232027577 0ustar runnerdocker00000000000000"""PortMod tests.""" import unittest from pyof.v0x04.controller2switch.port_mod import PortMod class TestPortMod(unittest.TestCase): """PortMod test.""" def test_min_size(self): """Test minimum message size.""" self.assertEqual(40, PortMod().get_size()) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_table_mod.py0000644000175100001630000000075013577157232027705 0ustar runnerdocker00000000000000"""Table flow modification tests.""" import unittest from pyof.v0x04.common.header import Type from pyof.v0x04.controller2switch.table_mod import TableMod class TestTableMod(unittest.TestCase): """TableMod test.""" def test_min_size(self): """Test minimum message size.""" self.assertEqual(16, TableMod().get_size()) def test_header_type(self): """Test header type.""" self.assertEqual(Type.OFPT_TABLE_MOD, TableMod().header.message_type) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_set_async.py0000644000175100001630000000133513577157232027747 0ustar runnerdocker00000000000000"""SetAsync message tests.""" from pyof.v0x04.controller2switch.set_async import SetAsync from tests.test_struct import TestStruct class TestSetAsync(TestStruct): """Test the SetAsync message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_set_async') super().set_raw_dump_object(SetAsync, xid=3, packet_in_mask1=0, packet_in_mask2=0, port_status_mask1=0, port_status_mask2=0, flow_removed_mask1=0, flow_removed_mask2=0) super().set_minimum_size(32) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_queue_get_config_reply.py0000644000175100001630000000155513577157232032506 0ustar runnerdocker00000000000000"""Testing QueueGetConfigReply message.""" from pyof.v0x04.common.queue import ListOfQueues, PacketQueue from pyof.v0x04.controller2switch.queue_get_config_reply import ( QueueGetConfigReply) from tests.test_struct import TestStruct class TestQueueGetConfigReply(TestStruct): """Test the QueueGetConfigReply message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_queue_get_config_reply') super().set_raw_dump_object(QueueGetConfigReply, xid=1, port=1, queues=_new_list_of_queues()) super().set_minimum_size(16) def _new_list_of_queues(): """Crate new ListOfQueues.""" queue = PacketQueue(1, 2, 3) loq = ListOfQueues([queue, queue]) return loq python-openflow-2019.2/tests/v0x04/test_controller2switch/test_aggregate_stats_request.py0000644000175100001630000000175413577157232032700 0ustar runnerdocker00000000000000"""Aggregate stats request message.""" from pyof.v0x04.common.flow_match import Match from pyof.v0x04.controller2switch.common import MultipartType from pyof.v0x04.controller2switch.multipart_request import ( AggregateStatsRequest, MultipartRequest) from tests.test_struct import TestStruct class TestAggregateStatsRequest(TestStruct): """Aggregate stats request message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" mp_type = MultipartType.OFPMP_AGGREGATE super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_aggregate_stats_request') super().set_raw_dump_object(MultipartRequest, xid=1, multipart_type=mp_type, flags=0, body=_get_body()) super().set_minimum_size(16) def _get_body(): """Return the body used by MultipartRequest message.""" return AggregateStatsRequest(match=Match()) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_queue_stats_request.py0000644000175100001630000000103713577157232032070 0ustar runnerdocker00000000000000"""Queue Stat Request message.""" from pyof.v0x04.controller2switch.multipart_request import QueueStatsRequest from tests.test_struct import TestStruct class TestQueueStatsRequest(TestStruct): """Queue Stat Request message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_queue_stats_request') super().set_raw_dump_object(QueueStatsRequest) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_get_config_reply.py0000644000175100001630000000103213577157232031270 0ustar runnerdocker00000000000000"""Config Reply message tests.""" from pyof.v0x04.controller2switch.get_config_reply import GetConfigReply from tests.test_struct import TestStruct class TestGetConfigReply(TestStruct): """Config Reply message tests.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_get_config_reply') super().set_raw_dump_object(GetConfigReply, xid=1) super().set_minimum_size(12) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_packet_out.py0000644000175100001630000000602213577157232030113 0ustar runnerdocker00000000000000"""Packet out message tests.""" from pyof.foundation.exceptions import ValidationError from pyof.v0x04.common.action import ActionOutput from pyof.v0x04.common.constants import OFP_NO_BUFFER from pyof.v0x04.common.port import PortNo from pyof.v0x04.controller2switch.packet_out import PacketOut from tests.test_struct import TestStruct NO_RAW = 'No raw dump file found.' class TestPacketOut(TestStruct): """Packet out message tests (also those in :class:`.TestDump`). Attributes: message (PacketOut): The message configured in :meth:`setUpClass`. """ @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_packet_out') super().set_raw_dump_object(PacketOut, xid=2544740805, buffer_id=OFP_NO_BUFFER, in_port=PortNo.OFPP_CONTROLLER, actions=_get_actions(), data=_get_data()) super().set_minimum_size(24) def test_valid_virtual_in_ports(self): """Valid virtual ports as defined in 1.3.0 spec.""" virtual_ports = (PortNo.OFPP_LOCAL, PortNo.OFPP_CONTROLLER, PortNo.OFPP_ANY) for port in virtual_ports: with self.subTest(port=port): msg = PacketOut(in_port=port) self.assertTrue(msg.is_valid(), f'{port.name} should be a valid in_port') def test_invalid_virtual_in_ports(self): """Invalid virtual ports as defined in 1.3.0 spec.""" try: msg = self.get_raw_dump().read() except FileNotFoundError: raise self.skipTest(NO_RAW) else: invalid = (PortNo.OFPP_IN_PORT, PortNo.OFPP_TABLE, PortNo.OFPP_NORMAL, PortNo.OFPP_FLOOD, PortNo.OFPP_ALL) msg = self.get_raw_object() for in_port in invalid: msg.in_port = in_port self.assertFalse(msg.is_valid()) self.assertRaises(ValidationError, msg.validate) def test_valid_physical_in_ports(self): """Physical port limits from 1.3.0 spec.""" try: msg = self.get_raw_dump().read() except FileNotFoundError: raise self.skipTest(NO_RAW) else: max_valid = int(PortNo.OFPP_MAX.value) - 1 msg = self.get_raw_object() for in_port in (1, max_valid): msg.in_port = in_port self.assertTrue(msg.is_valid()) def _get_actions(): """Return a list of actions used by packetout instance.""" action = ActionOutput(port=1) return [action] def _get_data(): """Return a BinaryData used by packetout instance.""" data = b'\x01\x80\xc2\x00\x00\x0e\x4e\xbf\xca\x27\x8e\xca\x81\x00\x0e' data += b'\xd7\x88\xcc\x02\x09\x07\x00\x00\x00\x00\x00\x00\x00\x01\x04' data += b'\x05\x07\x00\x00\x00\x01\x06\x02\x00\x78\x00\x00' return data python-openflow-2019.2/tests/v0x04/test_controller2switch/test_barrier_reply.py0000644000175100001630000000106213577157232030615 0ustar runnerdocker00000000000000"""Barrier reply message tests.""" from pyof.v0x04.controller2switch.barrier_reply import BarrierReply from tests.test_struct import TestStruct class TestBarrierReply(TestStruct): """Barrier reply message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_barrier_reply') super().set_raw_dump_object(BarrierReply, xid=5) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_multipart_request.py0000644000175100001630000000507513577157232031555 0ustar runnerdocker00000000000000"""MultipartRequest message test.""" from pyof.v0x04.controller2switch.multipart_request import ( MultipartRequest, MultipartRequestFlags, MultipartType, PortStatsRequest, TableFeatures) from tests.v0x04.test_struct import TestStruct class TestMultipartRequest(TestStruct): """Test the MultipartRequest message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_message(MultipartRequest, xid=16, multipart_type=MultipartType.OFPMP_TABLE_FEATURES, flags=MultipartRequestFlags.OFPMPF_REQ_MORE, body=b'') super().set_minimum_size(16) @staticmethod def get_attributes(multipart_type=MultipartType.OFPMP_DESC, flags=MultipartRequestFlags.OFPMPF_REQ_MORE, body=b''): """Method used to return a dict with instance paramenters.""" return {'xid': 32, 'multipart_type': multipart_type, 'flags': flags, 'body': body} def test_pack_unpack_desc(self): """Testing multipart_type with OFPMP_DESC.""" options = TestMultipartRequest.get_attributes( multipart_type=MultipartType.OFPMP_DESC) self._test_pack_unpack(**options) def test_pack_unpack_table(self): """Testing multipart_type with OFPMP_TABLE.""" options = TestMultipartRequest.get_attributes( multipart_type=MultipartType.OFPMP_TABLE) self._test_pack_unpack(**options) def test_pack_unpack__port_stats_request(self): """Testing multipart_type with OFPMP_PORT_STATS.""" options = TestMultipartRequest.get_attributes( multipart_type=MultipartType.OFPMP_PORT_STATS, body=PortStatsRequest(port_no=33)) self._test_pack_unpack(**options) def test_pack_unpack_port_desc(self): """Testing multipart_type with OFPMP_PORT_DESC.""" options = TestMultipartRequest.get_attributes( multipart_type=MultipartType.OFPMP_PORT_DESC) self._test_pack_unpack(**options) def test_pack_unpack_table_features(self): """Testing multipart_type with OFPMP_TABLE_FEATURES.""" instance = [TableFeatures(table_id=2), TableFeatures(table_id=6), TableFeatures(table_id=4)] options = TestMultipartRequest.get_attributes( multipart_type=MultipartType.OFPMP_TABLE_FEATURES, body=instance) self._test_pack_unpack(**options) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_port_stats_request.py0000644000175100001630000000106513577157232031731 0ustar runnerdocker00000000000000"""Config Port Stats Request message tests.""" from pyof.v0x04.controller2switch.multipart_request import PortStatsRequest from tests.test_struct import TestStruct class TestPortStatsRequest(TestStruct): """Config Port Stats Request message tests.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_port_stats_request') super().set_raw_dump_object(PortStatsRequest) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_multipart_reply.py0000644000175100001630000000316313577157232031214 0ustar runnerdocker00000000000000"""MultipartReply message test.""" from pyof.v0x04.controller2switch.common import MultipartType from pyof.v0x04.controller2switch.multipart_reply import ( Desc, MultipartReply, MultipartReplyFlags) from tests.v0x04.test_struct import TestStruct class TestMultipartReply(TestStruct): """Test MultipartReply.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_message(MultipartReply, xid=16, multipart_type=MultipartType.OFPMP_METER_CONFIG, flags=MultipartReplyFlags.OFPMPF_REPLY_MORE, body=b'') super().set_minimum_size(16) @staticmethod def get_attributes(multipart_type=MultipartType.OFPMP_DESC, flags=MultipartReplyFlags.OFPMPF_REPLY_MORE, body=b''): """Method used to return a dict with instance paramenters.""" return {'xid': 32, 'multipart_type': multipart_type, 'flags': flags, 'body': body} def test_pack_unpack_desc(self): """Testing multipart_type with OFPMP_DESC.""" instances = Desc(mfr_desc="MANUFACTURER DESCRIPTION", hw_desc="HARDWARE DESCRIPTION", sw_desc="SOFTWARE DESCRIPTION", serial_num="SERIAL NUMBER", dp_desc="DATAPATH DESCRIPTION") options = TestMultipartReply.get_attributes( multipart_type=MultipartType.OFPMP_DESC, body=instances) self._test_pack_unpack(**options) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_get_async_request.py0000644000175100001630000000104713577157232031503 0ustar runnerdocker00000000000000"""GetAsyncRequest message tests.""" from pyof.v0x04.controller2switch.get_async_request import GetAsyncRequest from tests.test_struct import TestStruct class TestGetAsyncRequest(TestStruct): """Test the GetAsyncRequest message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_get_async_request') super().set_raw_dump_object(GetAsyncRequest, xid=3) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_meter_multipart_request.py0000644000175100001630000000157513577157232032752 0ustar runnerdocker00000000000000"""MultipartRequest message test.""" from pyof.v0x04.controller2switch.multipart_request import ( MeterMultipartRequest, MultipartRequest, MultipartType) from tests.test_struct import TestStruct class TestMeterMultipartRequest(TestStruct): """Test the MultipartRequest message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" mp_type = MultipartType.OFPMP_METER super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_meter_multipart_request') super().set_raw_dump_object(MultipartRequest, xid=1, multipart_type=mp_type, flags=0, body=_get_body()) super().set_minimum_size(16) def _get_body(): """Return the body used by MultipartRequest message.""" return MeterMultipartRequest() python-openflow-2019.2/tests/v0x04/test_controller2switch/test_port_desc.py0000644000175100001630000000443213577157232027742 0ustar runnerdocker00000000000000"""MultipartReply message test.""" from pyof.foundation.basic_types import HWAddress from pyof.v0x04.common.port import ( ListOfPorts, Port, PortConfig, PortFeatures, PortNo, PortState) from pyof.v0x04.controller2switch.common import MultipartType from pyof.v0x04.controller2switch.multipart_reply import MultipartReply from tests.test_struct import TestStruct class TestPortDesc(TestStruct): """Test PortDesc.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" mp_type = MultipartType.OFPMP_PORT_DESC super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_port_desc') super().set_raw_dump_object(MultipartReply, xid=1917225664, multipart_type=mp_type, flags=0, body=_get_body()) super().set_minimum_size(16) def _get_body(): """Return the body used by MultipartReply message.""" port1 = Port(port_no=PortNo.OFPP_LOCAL, hw_addr=HWAddress('5a:ee:a5:a0:62:4f'), name='s1', config=PortConfig.OFPPC_PORT_DOWN, state=PortState.OFPPS_LINK_DOWN, curr=0, advertised=0, supported=0, peer=0, curr_speed=0, max_speed=0) port2 = Port(port_no=1, hw_addr=HWAddress('4e:bf:ca:27:8e:ca'), name='s1-eth1', config=0, state=PortState.OFPPS_LIVE, curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER, advertised=0, supported=0, peer=0, curr_speed=10000000, max_speed=0) port3 = Port(port_no=2, hw_addr=HWAddress('26:1f:b9:5e:3c:c7'), name='s1-eth2', config=0, state=PortState.OFPPS_LIVE, curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER, advertised=0, supported=0, peer=0, curr_speed=10000000, max_speed=0) lop = ListOfPorts([port1, port2, port3]) return lop python-openflow-2019.2/tests/v0x04/test_controller2switch/test_port_stats.py0000644000175100001630000000101013577157232030147 0ustar runnerdocker00000000000000"""Config Port Stats message tests.""" from pyof.v0x04.controller2switch.multipart_reply import PortStats from tests.test_struct import TestStruct class TestPortStats(TestStruct): """Config Port Stats message tests.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_port_stats') super().set_raw_dump_object(PortStats) super().set_minimum_size(112) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_aggregate_stats.py0000644000175100001630000000173513577157232031127 0ustar runnerdocker00000000000000"""Aggregate stats request message.""" from pyof.v0x04.controller2switch.common import MultipartType from pyof.v0x04.controller2switch.multipart_reply import ( AggregateStatsReply, MultipartReply) from tests.test_struct import TestStruct class TestAggregateStats(TestStruct): """Aggregate stats message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" mp_type = MultipartType.OFPMP_AGGREGATE super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_aggregate_stats') super().set_raw_dump_object(MultipartReply, xid=1, multipart_type=mp_type, flags=0, body=_get_body()) super().set_minimum_size(16) def _get_body(): """Return the body used by MultipartReply message.""" return AggregateStatsReply(packet_count=2, byte_count=220, flow_count=2) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_flow_mod.py0000644000175100001630000000337513577157232027573 0ustar runnerdocker00000000000000"""FlowMod test.""" from pyof.v0x04.common.action import ActionOutput, ListOfActions from pyof.v0x04.common.flow_instructions import ( InstructionApplyAction, ListOfInstruction) from pyof.v0x04.common.flow_match import ( Match, MatchType, OxmClass, OxmOfbMatchField, OxmTLV) from pyof.v0x04.common.port import PortNo from pyof.v0x04.controller2switch.flow_mod import FlowMod, FlowModCommand from tests.test_struct import TestStruct class TestFlowMod(TestStruct): """FlowMod test.""" def test_min_size(self): """Test struct minimum size.""" super().set_raw_dump_file('v0x04', 'ofpt_flow_mod') super().set_raw_dump_object(FlowMod, xid=2219910763, command=FlowModCommand.OFPFC_ADD, priority=1000, match=_new_match(), instructions=_new_list_of_instructions()) super().set_minimum_size(56) def _new_match(): """Crate new Match instance.""" oxm_tlv1 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_ETH_TYPE, oxm_hasmask=False, oxm_value=b'\x88\xcc') oxmt_lv2 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_VLAN_VID, oxm_hasmask=False, oxm_value=b'\x1e\xd7') return Match(match_type=MatchType.OFPMT_OXM, oxm_match_fields=[oxm_tlv1, oxmt_lv2]) def _new_list_of_instructions(): """Crate new ListOfInstruction.""" output = ActionOutput(port=PortNo.OFPP_CONTROLLER) loa = ListOfActions([output]) instruction = InstructionApplyAction(loa) return ListOfInstruction([instruction]) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_get_config_request.py0000644000175100001630000000104013577157232031624 0ustar runnerdocker00000000000000"""Config Request message tests.""" from pyof.v0x04.controller2switch.get_config_request import GetConfigRequest from tests.test_struct import TestStruct class TestGetConfigRequest(TestStruct): """Config Request message tests.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_get_config_request') super().set_raw_dump_object(GetConfigRequest) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_features_request.py0000644000175100001630000000110513577157232031340 0ustar runnerdocker00000000000000"""Feature request message tests.""" from pyof.v0x04.controller2switch.features_request import FeaturesRequest from tests.test_struct import TestStruct class TestFeaturesRequest(TestStruct): """Feature request message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_features_request') super().set_raw_dump_object(FeaturesRequest, xid=3) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_features_reply.py0000644000175100001630000000154113577157232031007 0ustar runnerdocker00000000000000"""Echo request message tests.""" from pyof.foundation.basic_types import DPID from pyof.v0x04.controller2switch.features_reply import FeaturesReply from tests.test_struct import TestStruct class TestFeaturesReply(TestStruct): """Feature reply message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_features_reply') kwargs = _get_kwargs() super().set_raw_dump_object(FeaturesReply, **kwargs) super().set_minimum_size(32) def _get_kwargs(): return {'xid': 3, 'datapath_id': DPID('00:00:00:00:00:00:00:01'), 'n_buffers': 0, 'n_tables': 254, 'auxiliary_id': 0, 'capabilities': 0x0000004f, 'reserved': 0x00000000} python-openflow-2019.2/tests/v0x04/test_controller2switch/test_flow_stats_request.py0000644000175100001630000000166713577157232031724 0ustar runnerdocker00000000000000"""Flow stats request message.""" from pyof.v0x04.common.flow_match import Match from pyof.v0x04.controller2switch.common import MultipartType from pyof.v0x04.controller2switch.multipart_request import ( FlowStatsRequest, MultipartRequest) from tests.test_struct import TestStruct class TestFlowStatsRequest(TestStruct): """Flow stats request message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_flow_stats_request') super().set_raw_dump_object(MultipartRequest, xid=590715727, multipart_type=MultipartType.OFPMP_FLOW, flags=0, body=_get_body()) super().set_minimum_size(16) def _get_body(): """Return the body used by MultipartRequest message.""" return FlowStatsRequest(match=Match()) python-openflow-2019.2/tests/v0x04/test_controller2switch/test_flow_stats.py0000644000175100001630000000445713577157232030154 0ustar runnerdocker00000000000000"""Flow stats message.""" from pyof.v0x04.common.action import ActionOutput, ListOfActions from pyof.v0x04.common.flow_instructions import ( InstructionApplyAction, ListOfInstruction) from pyof.v0x04.common.flow_match import ( Match, MatchType, OxmClass, OxmOfbMatchField, OxmTLV) from pyof.v0x04.common.port import PortNo from pyof.v0x04.controller2switch.common import MultipartType from pyof.v0x04.controller2switch.multipart_reply import ( FlowStats, MultipartReply) from tests.test_struct import TestStruct class TestFlowStats(TestStruct): """Flow stats message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_flow_stats') super().set_raw_dump_object(MultipartReply, xid=2898845528, multipart_type=MultipartType.OFPMP_FLOW, flags=0, body=_get_body()) super().set_minimum_size(16) def _get_body(): """Return the body used by MultipartReply message.""" return FlowStats(length=88, table_id=0, duration_sec=56, duration_nsec=635000000, priority=1000, idle_timeout=0, hard_timeout=0, flags=0x00000001, cookie=0x0000000000000000, packet_count=18, byte_count=756, match=_new_match(), instructions=_new_list_of_instructions()) def _new_match(): """Crate new Match instance.""" oxmtlv1 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_ETH_TYPE, oxm_hasmask=False, oxm_value=b'\x88\xcc') oxmtlv2 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_VLAN_VID, oxm_hasmask=False, oxm_value=b'\x1e\xd7') return Match(match_type=MatchType.OFPMT_OXM, oxm_match_fields=[oxmtlv1, oxmtlv2]) def _new_list_of_instructions(): """Crate new ListOfInstruction.""" action_output = ActionOutput(port=PortNo.OFPP_CONTROLLER) loa = ListOfActions([action_output]) instruction = InstructionApplyAction(loa) return ListOfInstruction([instruction]) python-openflow-2019.2/tests/v0x04/test_struct.py0000644000175100001630000000752113577157232022560 0ustar runnerdocker00000000000000"""Automate struct tests.""" import unittest from pyof.v0x04.common.header import Header from pyof.v0x04.common.utils import new_message_from_header class TestStruct(unittest.TestCase): """Run tests related to struct packing and unpacking. Test the lib with raw dump files from an OpenFlow switch. We assume the raw files are valid according to the OF specs to check whether our pack and unpack implementations are correct. Also, check the minimum size of the struct by instantiating an object with no parameters. To run these tests, just extends this class and call 2 methods in the ``setUp`` method like the example. Example: .. code-block:: python3 class MyTest(TestStruct): @classmethod def setUpClass(cls): super().setUpClass() # Create BarrierReply(xid=5) super().set_message(BarrierReply, xid=5) # As in spec: ``OFP_ASSERT(sizeof(struct ...) == ...);`` super().set_minimum_size(8) To only test the minimum size and skip packing/unpacking: .. code-block:: python3 class MyTest(TestStruct): @classmethod def setUpClass(cls): super().set_message(BarrierReply) super().set_minimum_size(8) """ def __init__(self, *args, **kwargs): """The constructor will avoid that this class tests are executed. The tests in this class are executed through the child, so there's no no need for them to be executed once more through the parent. """ super().__init__(*args, **kwargs) # Override the run method, so it does nothing instead of running the # tests (again). if self.__class__ == TestStruct: self.run = lambda *args, **kwargs: None _msg_cls = None _msg_params = None _min_size = None @classmethod def set_message(cls, msg_cls, *args, **kwargs): """Set how to create the message object. Args: msg_class (:obj:`type`): The message class followed by its parameters to instantiate an object. Example: ``super().__init__(BarrierReply, xid=5)`` will create ``BarrierReply(xid=5)``. """ TestStruct._msg_cls = msg_cls cls._msg_params = (args, kwargs) @classmethod def set_minimum_size(cls, size): """Set the struct minimum size (from spec). The minimum size can be found in OF spec. For example, :class:`.PhyPort` minimum size is 48 because of ``OFP_ASSERT(sizeof(struct ofp_phy_port) == 48);`` (spec 1.0.0). Args: size (int): The minimum size of the struct, in bytes. """ cls._min_size = size def test_pack_unpack(self): """Pack the message, unpack and check whether they are the same.""" if self._msg_cls: args, kwargs = self._msg_params self._test_pack_unpack(*args, **kwargs) 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()) def test_minimum_size(self): """Test struct minimum size.""" if self._min_size is None: raise self.skipTest('minimum size was not set.') obj = TestStruct._msg_cls() self.assertEqual(obj.get_size(), self._min_size) python-openflow-2019.2/tests/v0x04/__init__.py0000644000175100001630000000002313577157232021722 0ustar runnerdocker00000000000000"""v0x04 tests.""" python-openflow-2019.2/tests/v0x04/test_symmetric/0000755000175100001630000000000013577157243022673 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/v0x04/test_symmetric/__init__.py0000644000175100001630000000004213577157232024776 0ustar runnerdocker00000000000000"""Testing symmetric messages.""" python-openflow-2019.2/tests/v0x04/test_symmetric/test_echo_request.py0000644000175100001630000000104313577157232026766 0ustar runnerdocker00000000000000"""Echo request message tests.""" from pyof.v0x04.symmetric.echo_request import EchoRequest from tests.test_struct import TestStruct class TestEchoRequest(TestStruct): """Echo request message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_echo_request') super().set_raw_dump_object(EchoRequest, xid=0) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x04/test_symmetric/test_hello.py0000644000175100001630000000164613577157232025414 0ustar runnerdocker00000000000000"""Hello message tests.""" from pyof.v0x04.symmetric.hello import ( Hello, HelloElemHeader, HelloElemType, ListOfHelloElements) from tests.test_struct import TestStruct class TestHello(TestStruct): """Hello message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_hello') super().set_raw_dump_object(Hello, xid=62, elements=_new_list_of_elements()) super().set_minimum_size(8) def _new_list_of_elements(): """Crate new ListOfHelloElements.""" hello_elem = HelloElemHeader(HelloElemType.OFPHET_VERSIONBITMAP, length=8, content=b'\x00\x00\x00\x10') elements = ListOfHelloElements() elements.append(hello_elem) return elements python-openflow-2019.2/tests/v0x04/test_symmetric/test_vendor_header.py0000644000175100001630000000116313577157232027110 0ustar runnerdocker00000000000000"""Experimenter message tests.""" from pyof.v0x04.symmetric.experimenter import ExperimenterHeader from tests.test_struct import TestStruct class TestExperimenter(TestStruct): """Experimenter message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_experimenter') super().set_raw_dump_object(ExperimenterHeader, xid=1, experimenter=1, exp_type=0) super().set_minimum_size(16) python-openflow-2019.2/tests/v0x04/test_symmetric/test_echo_reply.py0000644000175100001630000000102513577157232026431 0ustar runnerdocker00000000000000"""Echo reply message tests.""" from pyof.v0x04.symmetric.echo_reply import EchoReply from tests.test_struct import TestStruct class TestEchoReply(TestStruct): """Echo reply message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_echo_reply') super().set_raw_dump_object(EchoReply, xid=0) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x04/test_common/0000755000175100001630000000000013577157243022147 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/v0x04/test_common/__init__.py0000644000175100001630000000005013577157232024251 0ustar runnerdocker00000000000000"""Tests common strucutres of v0x04.""" python-openflow-2019.2/tests/v0x04/test_common/test_flow_match.py0000644000175100001630000001057313577157232025707 0ustar runnerdocker00000000000000"""Test OXM-related implementations.""" from unittest import TestCase from pyof.foundation.exceptions import PackException, UnpackException from pyof.v0x04.common.flow_match import ( Match, MatchType, OxmClass, OxmOfbMatchField, OxmTLV) class TestMatch(TestCase): """Test Match class.""" tlv1 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_IN_PHY_PORT, oxm_hasmask=True, oxm_value=b'abc') tlv2 = OxmTLV(oxm_class=OxmClass.OFPXMC_EXPERIMENTER, oxm_field=OxmOfbMatchField.OFPXMT_OFB_METADATA, oxm_hasmask=False, oxm_value=b'abcdef') match = Match(match_type=MatchType.OFPMT_OXM, oxm_match_fields=[tlv1, tlv2]) def test_unpacked_pack(self): """Pack and then unpack the result and check for equality. Use two TLVs to also test match-field list packing/unpacking. """ unpacked = Match() unpacked.unpack(self.match.pack()) self.assertEqual(self.match, unpacked) def test_pack_other_instance(self): """Test packing another Match instance by using the value argument.""" expected = self.match.pack() valued_pack = Match().pack(self.match) self.assertEqual(expected, valued_pack) class TestOxmTLV(TestCase): """Test OXM TLV pack and unpack.""" def setUp(self): """Instantiate an OXM TLV struct.""" self.tlv = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_IN_PHY_PORT, oxm_hasmask=False, oxm_value=b'') def test_different_class_types(self): """Pack, unpack the result and assert the values are equal.""" for oxm_class in (OxmClass.OFPXMC_OPENFLOW_BASIC, OxmClass.OFPXMC_EXPERIMENTER): self.tlv.oxm_class = oxm_class unpacked = self._create_from_pack() self.assertEqual(oxm_class, unpacked.oxm_class) def test_different_fields(self): """Pack, unpack the result and assert the values are equal.""" for oxm_field in (OxmOfbMatchField.OFPXMT_OFB_IN_PORT, OxmOfbMatchField.OFPXMT_OFB_IPV6_EXTHDR): self.tlv.oxm_field = oxm_field unpacked = self._create_from_pack() self.assertEqual(oxm_field, unpacked.oxm_field) def test_hasmask_bit(self): """Pack, unpack the result and assert the values are equal.""" for oxm_hasmask in True, False: self.tlv.oxm_hasmask = oxm_hasmask unpacked = self._create_from_pack() self.assertEqual(oxm_hasmask, unpacked.oxm_hasmask) def test_different_values(self): """Pack, unpack the result and assert the values are equal.""" for oxm_value in b'', b'abc': self.tlv.oxm_value = oxm_value unpacked = self._create_from_pack() self.assertEqual(oxm_value, unpacked.oxm_value) def _create_from_pack(self): """Return a new instance by unpacking self.tlv.pack().""" unpacked = OxmTLV() unpacked.unpack(self.tlv.pack()) return unpacked def test_pack_overflowed_field(self): """Raise PackException if field is bigger than 7 bit.""" self.tlv.oxm_class = OxmClass.OFPXMC_EXPERIMENTER self.tlv.oxm_field = 2**7 with self.assertRaises(PackException): self.tlv.pack() def test_pack_invalid_field(self): """Raise PackException if field is invalid for a class. Example: field 42 is invalid for oxm_class OFPXMC_OPENFLOW_BASIC. """ self.tlv.oxm_class = OxmClass.OFPXMC_OPENFLOW_BASIC self.tlv.oxm_field = 42 with self.assertRaises(PackException): self.tlv.pack() def test_unpack_invalid_field(self): """Raise UnpackException if field is invalid for a class. Example: field 42 is invalid for oxm_class OFPXMC_OPENFLOW_BASIC. """ field42 = b'\x80\x00T\x00' tlv = OxmTLV() with self.assertRaises(UnpackException): tlv.unpack(field42) def test_max_field_value(self): """Use all bits of oxm_field.""" self.tlv.oxm_class = OxmClass.OFPXMC_EXPERIMENTER self.tlv.oxm_field = 127 unpacked = OxmTLV() unpacked.unpack(self.tlv.pack()) self.assertEqual(self.tlv, unpacked) python-openflow-2019.2/tests/v0x04/test_common/test_queue.py0000644000175100001630000000442213577157232024704 0ustar runnerdocker00000000000000"""Test of v0x04 queue module.""" from pyof.v0x04.common.queue import ( PacketQueue, QueuePropExperimenter, QueuePropHeader, QueuePropMaxRate, QueuePropMinRate) from tests.test_struct import TestStruct class TestPacketQueue(TestStruct): """Packet Queue structure tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'packet_queue') super().set_raw_dump_object(PacketQueue) super().set_minimum_size(16) class TestQueuePropExperimenter(TestStruct): """QueuePropExperimenter tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'queue_prop_experimenter') super().set_raw_dump_object(QueuePropExperimenter) super().set_minimum_size(16) class TestQueuePropHeader(TestStruct): """QueuePropHeader structure tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'queue_prop_header') super().set_raw_dump_object(QueuePropHeader) super().set_minimum_size(8) class TestQueuePropMaxRate(TestStruct): """QueuePropMaxRate structure tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'queue_prop_max_rate') super().set_raw_dump_object(QueuePropMaxRate) super().set_minimum_size(16) class TestQueuePropMinRate(TestStruct): """QueuePropMinRate structure tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'queue_prop_min_rate') super().set_raw_dump_object(QueuePropMinRate) super().set_minimum_size(16) python-openflow-2019.2/tests/v0x04/test_common/test_port.py0000644000175100001630000000076513577157232024552 0ustar runnerdocker00000000000000"""Test of Port class from common module.""" from pyof.v0x04.common.port import Port from tests.test_struct import TestStruct class TestPort(TestStruct): """Port structure tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'port') super().set_raw_dump_object(Port) super().set_minimum_size(64) python-openflow-2019.2/tests/v0x04/test_asynchronous/0000755000175100001630000000000013577157243023412 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/v0x04/test_asynchronous/__init__.py0000644000175100001630000000007413577157232025522 0ustar runnerdocker00000000000000"""Tests for asynchronous package from v0x04 - OF 1.3.0.""" python-openflow-2019.2/tests/v0x04/test_asynchronous/test_port_status.py0000644000175100001630000000236213577157232027413 0ustar runnerdocker00000000000000"""Port Status message tests.""" from pyof.foundation.basic_types import HWAddress from pyof.v0x04.asynchronous.port_status import PortReason, PortStatus from pyof.v0x04.common.port import Port, PortFeatures, PortState from tests.test_struct import TestStruct class TestPortStatus(TestStruct): """Test the Port Status message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_port_status') super().set_raw_dump_object(_new_portstatus) super().set_minimum_size(80) def _new_portstatus(): """Crate new PortStatus and Port instances.""" desc_name = 's1-eth1' desc = Port(port_no=1, hw_addr=HWAddress('62:43:e5:db:35:0a'), name=desc_name, config=0, state=PortState.OFPPS_LIVE, curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER, advertised=0, supported=0, peer=0, curr_speed=10000000, max_speed=0) return PortStatus(xid=0, reason=PortReason.OFPPR_MODIFY, desc=desc) python-openflow-2019.2/tests/v0x04/test_asynchronous/test_error_msg.py0000644000175100001630000000244213577157232027022 0ustar runnerdocker00000000000000"""Testing v0x04 error message class.""" from pyof.foundation.exceptions import MethodNotImplemented from pyof.v0x04.asynchronous.error_msg import ( ErrorExperimenterMsg, ErrorMsg, ErrorType, MeterModFailedCode) from tests.test_struct import TestStruct class TestErrorMsg(TestStruct): """ErroMsg message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" error_type = ErrorType.OFPET_METER_MOD_FAILED code = MeterModFailedCode.OFPMMFC_UNKNOWN_METER super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_error') super().set_raw_dump_object(ErrorMsg, xid=1, error_type=error_type, code=code, data=_get_data()) super().set_minimum_size(12) @staticmethod def test_unpack(): """[Asynchronous/error_msg] - unpack ErrorExperimenterMsg.""" unpacked = ErrorExperimenterMsg() try: unpacked.unpack("pack") except MethodNotImplemented: pass def _get_data(): """Return data for ErrorMsg object.""" data = b'\x04\x12\x00\x18\x00\x00\x00\x01\x00\x0a\x00\x00\x00\x00\x00' data += b'\x00\x00\x00\x00\x01\x00\x00\x00\x00' return data python-openflow-2019.2/tests/v0x04/test_asynchronous/test_flow_removed.py0000644000175100001630000000314613577157232027515 0ustar runnerdocker00000000000000"""Testing v0x04 FlowRemoved message.""" from pyof.v0x04.asynchronous.flow_removed import FlowRemoved, FlowRemovedReason from pyof.v0x04.common.flow_match import ( Match, MatchType, OxmClass, OxmOfbMatchField, OxmTLV) from tests.test_struct import TestStruct class TestFlowRemovedMsg(TestStruct): """FlowRemoved message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_flow_removed') super().set_raw_dump_object(FlowRemoved, xid=0, cookie=0x0000000000000000, priority=1000, reason=FlowRemovedReason.OFPRR_DELETE, table_id=0, duration_sec=77, duration_nsec=559000000, idle_timeout=0, hard_timeout=0, packet_count=0, byte_count=0, match=_new_match()) super().set_minimum_size(56) def _new_match(): """Crate new Match instance.""" tlv1 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_ETH_TYPE, oxm_hasmask=False, oxm_value=b'\x88\xcc') tlv2 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_VLAN_VID, oxm_hasmask=False, oxm_value=b'\x1e\xd7') return Match(match_type=MatchType.OFPMT_OXM, oxm_match_fields=[tlv1, tlv2]) python-openflow-2019.2/tests/v0x04/test_asynchronous/test_packet_in.py0000644000175100001630000000445113577157232026762 0ustar runnerdocker00000000000000"""Packet in message tests.""" from pyof.v0x04.asynchronous.packet_in import PacketIn, PacketInReason from pyof.v0x04.common.constants import OFP_NO_BUFFER from pyof.v0x04.common.flow_match import ( Match, MatchType, OxmClass, OxmOfbMatchField, OxmTLV) from pyof.v0x04.common.port import PortNo from tests.test_struct import TestStruct class TestPacketInRaw(TestStruct): """Test PacketIn using a dump file.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x04', 'ofpt_packet_in') super().set_raw_dump_object(PacketIn, xid=0, buffer_id=OFP_NO_BUFFER, total_len=90, reason=PacketInReason.OFPR_ACTION, table_id=0, cookie=0x0000000000000000, match=_new_match(), data=_get_data()) super().set_minimum_size(34) def test_valid_physical_in_port(self): """Physical port limits from 1.3.0 spec.""" try: msg = self.get_raw_dump().read() except FileNotFoundError: raise self.skipTest('No raw dump file found.') else: max_valid = int(PortNo.OFPP_MAX.value) - 1 msg = self.get_raw_object() if msg.in_port in (1, max_valid): self.assertTrue(msg.is_valid()) def _new_match(): """Crate new Match instance.""" oxmtlv = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, oxm_field=OxmOfbMatchField.OFPXMT_OFB_IN_PORT, oxm_hasmask=False, oxm_value=b'\x00\x00\x00\x02') return Match(match_type=MatchType.OFPMT_OXM, oxm_match_fields=[oxmtlv]) def _get_data(): """Return data for PacketIn object.""" data = b'\x33\x33\x00\x00\x00\x16\x92\xfd\x3d\x2a\x06\x0c\x86\xdd\x60\x00' data += b'\x00\x00\x00\x24\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' data += b'\x00\x00\x00\x00\x00\x00\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00' data += b'\x00\x00\x00\x00\x00\x16\x3a\x00\x05\x02\x00\x00\x01\x00\x8f\x00' data += b'\x69\x54\x00\x00\x00\x01\x04\x00\x00\x00\xff\x02\x00\x00\x00\x00' data += b'\x00\x00\x00\x00\x00\x01\xff\x2a\x06\x0c' return data python-openflow-2019.2/tests/test_foundation/0000755000175100001630000000000013577157243022144 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/test_foundation/__init__.py0000644000175100001630000000006113577157232024250 0ustar runnerdocker00000000000000"""Test foundation module of Python-openflow.""" python-openflow-2019.2/tests/test_foundation/test_basic_types.py0000644000175100001630000001453513577157232026070 0ustar runnerdocker00000000000000"""Tests for Python-openflow BasicTypes.""" import unittest from pyof.foundation import basic_types from pyof.foundation.basic_types import BinaryData class TestUBInt8(unittest.TestCase): """Test of UBInt8 BasicType.""" def setUp(self): """Basic test setup.""" self.ubint8 = basic_types.UBInt8() def test_get_size(self): """[Foundation/BasicTypes/UBInt8] - size 1.""" self.assertEqual(self.ubint8.get_size(), 1) @unittest.skip('Not yet implemented') def test_pack(self): """[Foundation/BasicTypes/UBInt8] - packing.""" pass @unittest.skip('Not yet implemented') def test_unpack(self): """[Foundation/BasicTypes/UBInt8] - unpacking.""" pass class TestUBInt16(unittest.TestCase): """Test of UBInt16 BasicType.""" def setUp(self): """Basic test setup.""" self.ubint16 = basic_types.UBInt16() def test_get_size(self): """[Foundation/BasicTypes/UBInt16] - size 2.""" self.assertEqual(self.ubint16.get_size(), 2) @unittest.skip('Not yet implemented') def test_pack(self): """[Foundation/BasicTypes/UBInt16] - packing.""" pass @unittest.skip('Not yet implemented') def test_unpack(self): """[Foundation/BasicTypes/UBInt16] - unpacking.""" pass class TestUBInt32(unittest.TestCase): """Test of UBInt32 BasicType.""" def setUp(self): """Basic test setup.""" self.ubint32 = basic_types.UBInt32() def test_get_size(self): """[Foundation/BasicTypes/UBInt32] - size 4.""" self.assertEqual(self.ubint32.get_size(), 4) @unittest.skip('Not yet implemented') def test_pack(self): """[Foundation/BasicTypes/UBInt32] - packing.""" pass @unittest.skip('Not yet implemented') def test_unpack(self): """[Foundation/BasicTypes/UBInt32] - unpacking.""" pass class TestChar(unittest.TestCase): """Test of Char BasicType.""" def setUp(self): """Basic test setup.""" self.char1 = basic_types.Char('foo', length=3) self.char2 = basic_types.Char('foo', length=5) def test_get_size(self): """[Foundation/BasicTypes/Char] - get_size.""" self.assertEqual(self.char1.get_size(), 3) self.assertEqual(self.char2.get_size(), 5) def test_pack(self): """[Foundation/BasicTypes/Char] - packing.""" self.assertEqual(self.char1.pack(), b'fo\x00') self.assertEqual(self.char2.pack(), b'foo\x00\x00') def test_unpack(self): """[Foundation/BasicTypes/Char] - unpacking.""" char1 = basic_types.Char(length=3) char2 = basic_types.Char(length=5) char1.unpack(b'fo\x00') char2.unpack(b'foo\x00\x00') self.assertEqual(char1.value, 'fo') self.assertEqual(char2.value, 'foo') class TestHWaddress(unittest.TestCase): """Test of HWAddress BasicType.""" def test_unpack_packed(self): """Testing unpack of packed HWAddress.""" mac = '0a:d3:98:a5:30:47' hw_addr = basic_types.HWAddress(mac) packed = hw_addr.pack() unpacked = basic_types.HWAddress() unpacked.unpack(packed) self.assertEqual(mac, unpacked.value) def test_default_value(self): """Testing default_value for HWAddress.""" mac = '00:00:00:00:00:00' hw_addr = basic_types.HWAddress() packed = hw_addr.pack() unpacked = basic_types.HWAddress() unpacked.unpack(packed) self.assertEqual(mac, unpacked.value) class TestIPAddress(unittest.TestCase): """Test of IPAddress BasicType.""" def test_unpack_packed(self): """Test unpacking of packed IPAddress.""" ip_addr = basic_types.IPAddress('192.168.0.1') packed = ip_addr.pack() unpacked = basic_types.IPAddress() unpacked.unpack(packed) self.assertEqual(ip_addr.value, unpacked.value) def test_unpack_packed_with_netmask(self): """Testing unpack of packed IPAddress with netmask.""" ip_addr = basic_types.IPAddress('192.168.0.1/16') packed = ip_addr.pack() unpacked = basic_types.IPAddress() unpacked.unpack(packed) self.assertEqual(ip_addr.value, unpacked.value) def test_netmask(self): """Testing get netmask from IPAddress.""" ip_addr = basic_types.IPAddress('192.168.0.1/24') self.assertEqual(ip_addr.netmask, 24) ip_addr = basic_types.IPAddress('192.168.0.1/16') self.assertEqual(ip_addr.netmask, 16) ip_addr = basic_types.IPAddress('192.168.0.1') self.assertEqual(ip_addr.netmask, 32) def test_max_prefix(self): """Testing get max_prefix from IPAddress.""" ip_addr = basic_types.IPAddress() self.assertEqual(ip_addr.max_prefix, 32) ip_addr = basic_types.IPAddress('192.168.0.35/16') self.assertEqual(ip_addr.max_prefix, 32) def test_get_size(self): """Testing get_size from IPAddress.""" ip_addr = basic_types.IPAddress('192.168.0.1/24') self.assertEqual(ip_addr.get_size(), 4) class TestBinaryData(unittest.TestCase): """Test Binary data type.""" def test_default_value(self): """Default packed value should be an empty byte.""" expected = b'' actual = BinaryData().pack() self.assertEqual(expected, actual) def test_pack_none_value(self): """Test packing None value.""" expected = b'' actual = BinaryData(None).pack() self.assertEqual(expected, actual) def test_pack_bytes_value(self): """Test packing some bytes.""" expected = b'forty two' actual = BinaryData(expected).pack() self.assertEqual(expected, actual) def test_pack_empty_bytes(self): """Test packing empty bytes.""" expected = b'' actual = BinaryData(expected).pack() self.assertEqual(expected, actual) def test_pack_packable_value(self): """Test packing packable value.""" hw_addr = basic_types.HWAddress('0a:d3:98:a5:30:47') expected = hw_addr.pack() actual = BinaryData(hw_addr).pack() self.assertEqual(expected, actual) def test_unexpected_value_as_parameter(self): """Should raise ValueError if pack value is not bytes.""" data= BinaryData('Some string') self.assertRaises(ValueError, data.pack, "can't be string") python-openflow-2019.2/tests/test_foundation/test_network_types.py0000644000175100001630000001527413577157232026501 0ustar runnerdocker00000000000000"""Test Python-openflow network types.""" import unittest from pyof.foundation.basic_types import BinaryData from pyof.foundation.exceptions import UnpackException from pyof.foundation.network_types import ARP, VLAN, Ethernet, GenericTLV, IPv4 class TestARP(unittest.TestCase): """Test ARP packets, without Ethernet headers.""" def test_arp_pack(self): """Test pack method of ARP class.""" arp = ARP(oper=1, sha='00:15:af:d5:38:98', spa='172.16.0.10', tpa='172.16.10.20') packed = arp.pack() expected = b'\x00\x01\x08\x00\x06\x04\x00\x01\x00\x15\xaf\xd58\x98\xac' expected += b'\x10\x00\n\x00\x00\x00\x00\x00\x00\xac\x10\n\x14' self.assertEqual(packed, expected) def test_arp_unpack(self): """Test unpack method of ARP class.""" raw = b'\x00\x01\x08\x00\x06\x04\x00\x02\x00\x1f:>\x9a\xcf\xac\x10\n' raw += b'\x14\x00\x15\xaf\xd58\x98\xac\x10\x00\n' expected = ARP(oper=2, sha='00:1f:3a:3e:9a:cf', spa='172.16.10.20', tha='00:15:af:d5:38:98', tpa='172.16.0.10') unpacked = ARP() unpacked.unpack(raw) self.assertEqual(unpacked, expected) def test_unpack_invalid_htype(self): """Raise UnpackException when L2 protocol is not Ethernet.""" raw = b'\x01\x23\x08\x00\x06\x04\x00\x02\x00\x1f:>\x9a\xcf\xac\x10\n' raw += b'\x14\x00\x15\xaf\xd58\x98\xac\x10\x00\n' arp = ARP() with self.assertRaises(UnpackException): arp.unpack(raw) def test_unpack_invalid_ptype(self): """Raise UnpackException when L3 protocol is not IPv4.""" raw = b'\x00\x01\x08\x90\x06\x04\x00\x02\x00\x1f:>\x9a\xcf\xac\x10\n' raw += b'\x14\x00\x15\xaf\xd58\x98\xac\x10\x00\n' arp = ARP() with self.assertRaises(UnpackException): arp.unpack(raw) class TestNetworkTypes(unittest.TestCase): """Reproduce bugs found.""" def test_GenTLV_value_unpack(self): """Value attribute should be the same after unpacking.""" value = BinaryData(b'test') tlv = GenericTLV(value=value) tlv_unpacked = GenericTLV() tlv_unpacked.unpack(tlv.pack()) self.assertEqual(tlv.value.value, tlv_unpacked.value.value) class TestEthernet(unittest.TestCase): """Test Ethernet frames.""" def test_Ethernet_pack(self): """Test pack method of Ethernet class without VLAN tag.""" ethernet = Ethernet(destination='00:1f:3a:3e:9a:cf', source='00:15:af:d5:38:98', ether_type=0x800, data=b'testdata') packed = ethernet.pack() expected = b'\x00\x1f:>\x9a\xcf\x00\x15\xaf\xd58\x98\x08\x00testdata' self.assertEqual(packed, expected) def test_Ethernet_unpack(self): """Test pack method of Ethernet class without VLAN tag.""" raw = b'\x00\x15\xaf\xd58\x98\x00\x1f:>\x9a\xcf\x08\x00testdata' expected = Ethernet(destination='00:15:af:d5:38:98', source='00:1f:3a:3e:9a:cf', ether_type=0x800, data=b'testdata') expected.pack() unpacked = Ethernet() unpacked.unpack(raw) self.assertEqual(unpacked, expected) def test_Tagged_Ethernet_pack(self): """Test pack method of Ethernet class including VLAN tag.""" ethernet = Ethernet(destination='00:1f:3a:3e:9a:cf', source='00:15:af:d5:38:98', vlans=[VLAN(vid=200)], ether_type=0x800, data=b'testdata') packed = ethernet.pack() expected = b'\x00\x1f:>\x9a\xcf\x00\x15\xaf\xd58' expected += b'\x98\x81\x00\x00\xc8\x08\x00testdata' self.assertEqual(packed, expected) def test_Tagged_Ethernet_unpack(self): """Test pack method of Ethernet class including VLAN tag.""" raw = b'\x00\x15\xaf\xd58\x98\x00\x1f:>' raw += b'\x9a\xcf\x81\x00!^\x08\x00testdata' expected = Ethernet(destination='00:15:af:d5:38:98', source='00:1f:3a:3e:9a:cf', vlans=[VLAN(pcp=1, vid=350)], ether_type=0x800, data=b'testdata') expected.pack() unpacked = Ethernet() unpacked.unpack(raw) self.assertEqual(unpacked, expected) class TestVLAN(unittest.TestCase): """Test VLAN headers.""" def test_VLAN_pack(self): """Test pack method of VLAN class.""" vlan = VLAN(pcp=3, vid=20) packed = vlan.pack() expected = b'\x81\x00`\x14' self.assertEqual(packed, expected) def test_VLAN_unpack(self): """Test unpack method of VLAN class.""" raw = b'\x81\x00\xa0{' expected = VLAN(pcp=5, vid=123) unpacked = VLAN() unpacked.unpack(raw) self.assertEqual(unpacked, expected) def test_unpack_wrong_tpid(self): """Raise UnpackException if the tpid is not VLAN_TPID.""" raw = b'\x12\x34\xa0{' vlan = VLAN() with self.assertRaises(UnpackException): vlan.unpack(raw) class TestIPv4(unittest.TestCase): """Test IPv4 packets.""" def test_IPv4_pack(self): """Test pack/unpack of IPv4 class.""" packet = IPv4(dscp=10, ttl=64, protocol=17, source="192.168.0.10", destination="172.16.10.30", options=b'1000', data=b'testdata') packed = packet.pack() expected = b'F(\x00 \x00\x00\x00\x00@\x11\x02' expected += b'\xc5\xc0\xa8\x00\n\xac\x10\n\x1e1000testdata' self.assertEqual(packed, expected) def test_IPv4_unpack(self): """Test unpack of IPv4 binary packet.""" raw = b'FP\x00$\x00\x00\x00\x00\x80\x06W' raw += b'\xf4\n\x9aN\x81\xc0\xa8\xc7\xcc1000somemoredata' expected = IPv4(dscp=20, ttl=128, protocol=6, source="10.154.78.129", destination="192.168.199.204", options=b'1000', data=b'somemoredata') expected.pack() unpacked = IPv4() unpacked.unpack(raw) self.assertEqual(unpacked, expected) def test_IPv4_size(self): """Test Header size for IPv4 packet.""" packet = IPv4() packet.pack() self.assertEqual(20, packet.get_size()) self.assertEqual(20, packet.length) self.assertEqual(20, packet.ihl * 4) def test_IPv4_checksum(self): """Test if the IPv4 checksum is being calculated correclty.""" packet = IPv4(dscp=10, ttl=64, protocol=17, source="192.168.0.10", destination="172.16.10.30", options=b'1000', data=b'testdata') packet.pack() self.assertEqual(packet.checksum, 709) python-openflow-2019.2/tests/test_foundation/test_base.py0000644000175100001630000000553313577157232024473 0ustar runnerdocker00000000000000"""Test Base module of python-openflow.""" import unittest from pyof.foundation import base, basic_types class TestGenericStruct(unittest.TestCase): """Testing GenericStruct class.""" def setUp(self): """Basic Test Setup.""" class AttributeA(base.GenericStruct): """Example class.""" a1 = basic_types.UBInt8(1) a2 = basic_types.UBInt16(2) class AttributeC(base.GenericStruct): """Example class.""" c1 = basic_types.UBInt32(3) c2 = basic_types.UBInt64(4) class AttributeB(base.GenericStruct): """Example class.""" c = AttributeC() class Header(base.GenericStruct): """Mock Header class.""" version = basic_types.UBInt8(1) message_type = basic_types.UBInt8(2) length = basic_types.UBInt8(8) xid = basic_types.UBInt8(4) class MyMessage(base.GenericMessage): """Example class.""" header = Header() a = AttributeA() b = AttributeB() i = basic_types.UBInt32(5) def __init__(self): """Init method of example class.""" super().__init__(None) self.MyMessage = MyMessage def test_basic_attributes(self): """[Foundation/Base/GenericStruct] - Attributes Creation.""" message1 = self.MyMessage() message2 = self.MyMessage() self.assertIsNot(message1, message2) self.assertIsNot(message1.i, message2.i) self.assertIsNot(message1.a, message2.a) self.assertIsNot(message1.b, message2.b) self.assertIsNot(message1.a.a1, message2.a.a1) self.assertIsNot(message1.a.a2, message2.a.a2) self.assertIsNot(message1.b.c, message2.b.c) self.assertIsNot(message1.b.c.c1, message2.b.c.c1) self.assertIsNot(message1.b.c.c2, message2.b.c.c2) class TestGenericType(unittest.TestCase): """Testing GenericType class.""" def test_basic_operator(self): """[Foundation/Base/GenericType] - Basic Operators.""" a = basic_types.UBInt32(1) b = basic_types.UBInt32(2) self.assertEqual(a + 1, 2) self.assertEqual(1 + a, 2) self.assertEqual(b + 1, 3) self.assertEqual(1 + b, 3) self.assertEqual(a - 1, 0) self.assertEqual(1 - a, 0) self.assertEqual(b - 1, 1) self.assertEqual(1 - b, 1) self.assertEqual(a & 1, 1) self.assertEqual(1 & a, 1) self.assertEqual(b & 1, 0) self.assertEqual(1 & b, 0) self.assertEqual(a | 1, 1) self.assertEqual(1 | a, 1) self.assertEqual(b | 1, 3) self.assertEqual(1 | b, 3) self.assertEqual(a ^ 1, 0) self.assertEqual(1 ^ a, 0) self.assertEqual(b ^ 1, 3) self.assertEqual(1 ^ b, 3) python-openflow-2019.2/tests/v0x01/0000755000175100001630000000000013577157243017615 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/v0x01/test_controller2switch/0000755000175100001630000000000013577157243024343 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/v0x01/test_controller2switch/test_table_stats.py0000644000175100001630000000207313577157232030261 0ustar runnerdocker00000000000000"""Test TableStats message.""" from pyof.foundation.constants import OFP_MAX_TABLE_NAME_LEN from pyof.v0x01.common.flow_match import FlowWildCards from pyof.v0x01.controller2switch.common import StatsType, TableStats from pyof.v0x01.controller2switch.stats_reply import StatsReply from tests.test_struct import TestStruct class TestTableStats(TestStruct): """Test class for TableStats.""" @classmethod def setUpClass(cls): """[Controller2Switch/TableStats] - size 64.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_table_stats') super().set_raw_dump_object(StatsReply, xid=14, body_type=StatsType.OFPST_TABLE, flags=0, body=_get_table_stats()) super().set_minimum_size(12) def _get_table_stats(): return TableStats(table_id=1, name='X' * OFP_MAX_TABLE_NAME_LEN, wildcards=FlowWildCards.OFPFW_TP_DST, max_entries=1, active_count=10, count_lookup=10, count_matched=0) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_vendor_stats.py0000644000175100001630000000176113577157232030472 0ustar runnerdocker00000000000000"""Test VendorStats message.""" from pyof.v0x01.controller2switch.common import StatsType, VendorStats from pyof.v0x01.controller2switch.stats_request import StatsRequest from tests.test_struct import TestStruct class TestVendorStats(TestStruct): """Test class for TestVendorStats. The dump and unpacked data were provided by user bug report. """ @classmethod def setUpClass(cls): """[Controller2Switch/VendorStats] - size 1056.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_vendor_stats_reply') super().set_raw_dump_object(StatsRequest, xid=4, body_type=StatsType.OFPST_VENDOR, flags=0, body=_get_vendor_stats()) super().set_minimum_size(12) def _get_vendor_stats(): """Return vendor stats found in StatsReply.body.""" body = b'\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\xff\x00\x00\x00' return VendorStats(vendor=0x2320, body=body) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_desc_stats.py0000644000175100001630000000202013577157232030100 0ustar runnerdocker00000000000000"""Test DescStats message.""" from pyof.foundation.constants import DESC_STR_LEN from pyof.v0x01.controller2switch.common import DescStats, StatsType from pyof.v0x01.controller2switch.stats_reply import StatsReply from tests.test_struct import TestStruct class TestDescStats(TestStruct): """Test class for TestDescStats.""" @classmethod def setUpClass(cls): """[Controller2Switch/DescStats] - size 1056.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_desc_stats_reply') super().set_raw_dump_object(StatsReply, xid=14, body_type=StatsType.OFPST_DESC, flags=0, body=_get_desc_stats()) super().set_minimum_size(12) def _get_desc_stats(): """Function used to return desc_stat used by StatsReply instance.""" content = 'A' * DESC_STR_LEN return DescStats(mfr_desc=content, hw_desc=content, sw_desc=content, serial_num=content, dp_desc=content) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_aggregate_stats_reply.py0000644000175100001630000000162613577157232032336 0ustar runnerdocker00000000000000"""Test for AggregateStatsReply message.""" from pyof.v0x01.controller2switch.common import AggregateStatsReply, StatsType from pyof.v0x01.controller2switch.stats_reply import StatsReply from tests.test_struct import TestStruct class TestAggregateStatsReply(TestStruct): """Test for AggregateStatsReply message.""" @classmethod def setUpClass(cls): """[Controller2Switch/AggregateStatsReply] - size 24.""" aggregate_stats_reply = AggregateStatsReply(packet_count=5, byte_count=1, flow_count=8) super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_aggregate_stats_reply') super().set_raw_dump_object(StatsReply, xid=17, body_type=StatsType.OFPST_AGGREGATE, flags=0, body=aggregate_stats_reply) super().set_minimum_size(12) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_set_config.py0000644000175100001630000000126713577157232030100 0ustar runnerdocker00000000000000"""Set Config message tests.""" from pyof.v0x01.controller2switch.common import ConfigFlag from pyof.v0x01.controller2switch.set_config import SetConfig from tests.test_struct import TestStruct class TestSetConfig(TestStruct): """Test the Set Config message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_set_config') super().set_raw_dump_object(SetConfig, xid=3, flags=ConfigFlag.OFPC_FRAG_NORMAL, miss_send_len=128) super().set_minimum_size(12) python-openflow-2019.2/tests/v0x01/test_controller2switch/__init__.py0000644000175100001630000000005213577157232026447 0ustar runnerdocker00000000000000"""Testing Controller2Switch messages.""" python-openflow-2019.2/tests/v0x01/test_controller2switch/test_queue_get_config_request.py0000644000175100001630000000125713577157232033037 0ustar runnerdocker00000000000000"""Test for QueueGetConfigRequest message.""" from pyof.v0x01.common.phy_port import Port from pyof.v0x01.controller2switch import queue_get_config_request as request from tests.test_struct import TestStruct class TestQueueGetConfigRequest(TestStruct): """Test for QueueGetConfigRequest message.""" @classmethod def setUpClass(cls): """[Controller2Switch/QueueGetConfigRequest] - size 12.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_queue_get_config_request') super().set_raw_dump_object(request.QueueGetConfigRequest, xid=1, port=Port.OFPP_MAX) super().set_minimum_size(12) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_queue_stats.py0000644000175100001630000000156113577157232030317 0ustar runnerdocker00000000000000"""Test for QueueStats.""" from pyof.v0x01.controller2switch.common import QueueStats, StatsType from pyof.v0x01.controller2switch.stats_reply import StatsReply from tests.test_struct import TestStruct class TestQueueStats(TestStruct): """Test for QueueStats.""" @classmethod def setUpClass(cls): """[Controller2Switch/QueueStats] - size 32.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_queue_stats') super().set_raw_dump_object(StatsReply, xid=7, body_type=StatsType.OFPST_QUEUE, flags=0, body=_get_queue_stats()) super().set_minimum_size(12) def _get_queue_stats(): """Function used to return a QueueStats instance.""" return QueueStats(port_no=80, queue_id=5, tx_bytes=1, tx_packets=3, tx_errors=2) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_barrier_request.py0000644000175100001630000000107613577157232031154 0ustar runnerdocker00000000000000"""Barrier request message tests.""" from pyof.v0x01.controller2switch.barrier_request import BarrierRequest from tests.test_struct import TestStruct class TestBarrierRequest(TestStruct): """Barrier reply message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_barrier_request') super().set_raw_dump_object(BarrierRequest, xid=5) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_port_mod.py0000644000175100001630000000146213577157232027600 0ustar runnerdocker00000000000000"""Test PortMod message.""" from pyof.v0x01.common.phy_port import PortConfig, PortFeatures from pyof.v0x01.controller2switch.port_mod import PortMod from tests.test_struct import TestStruct class TestPortMod(TestStruct): """Test class for PortMod.""" @classmethod def setUpClass(cls): """[Controller2Switch/PortMod] - size 32.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_port_mod') super().set_raw_dump_object(PortMod, xid=3, port_no=80, hw_addr='aa:bb:cc:00:33:9f', config=PortConfig.OFPPC_PORT_DOWN, mask=PortConfig.OFPPC_NO_FWD, advertise=PortFeatures.OFPPF_FIBER) super().set_minimum_size(32) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_queue_get_config_reply.py0000644000175100001630000000244113577157232032476 0ustar runnerdocker00000000000000"""Test for QueueGetConfigReply message.""" from pyof.v0x01.common.phy_port import Port from pyof.v0x01.common.queue import ( PacketQueue, QueueProperties, QueuePropHeader) from pyof.v0x01.controller2switch import queue_get_config_reply from tests.test_struct import TestStruct class TestQueueGetConfigReply(TestStruct): """Test for QueueGetConfigReply message.""" @classmethod def setUpClass(cls): """[Controller2Switch/QueueGetConfigReply] - size 16.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_queue_get_config_reply') super().set_raw_dump_object(queue_get_config_reply.QueueGetConfigReply, xid=1, port=Port.OFPP_ALL, queues=_get_packet_queue()) super().set_minimum_size(16) def _get_packet_queue(): """Function used to return a PacketQueue instance.""" packets = [] packets.append(PacketQueue(queue_id=1, length=8, properties=_get_queue_properties())) return packets def _get_queue_properties(): """Function used to return a list of queue properties.""" properties = [] properties.append(QueuePropHeader( queue_property=QueueProperties.OFPQT_MIN_RATE, length=12)) return properties python-openflow-2019.2/tests/v0x01/test_controller2switch/test_aggregate_stats_request.py0000644000175100001630000000256513577157232032676 0ustar runnerdocker00000000000000"""Test AggregateStatsRequest message.""" from pyof.v0x01.common.flow_match import Match from pyof.v0x01.common.phy_port import Port from pyof.v0x01.controller2switch.common import ( AggregateStatsRequest, StatsType) from pyof.v0x01.controller2switch.stats_request import StatsRequest from tests.test_struct import TestStruct class TestAggregateStatsRequest(TestStruct): """Test class for TestAggregateStatsRequest.""" @classmethod def setUpClass(cls): """[Controller2Switch/AggregateStatsRequest] - size 44.""" request = AggregateStatsRequest(table_id=1, out_port=Port.OFPP_NONE, match=_get_match()) super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_aggregate_request') super().set_raw_dump_object(StatsRequest, xid=17, body_type=StatsType.OFPST_AGGREGATE, flags=0, body=request) super().set_minimum_size(12) def _get_match(): """Function used to built Match instance used by AggregateStatsRequest.""" return Match(in_port=80, dl_src="01:02:03:04:05:06", dl_dst="01:02:03:04:05:06", dl_vlan=1, dl_vlan_pcp=1, dl_type=1, nw_tos=1, nw_proto=1, nw_src='192.168.0.1', nw_dst='192.168.0.1', tp_src=80, tp_dst=80) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_queue_stats_request.py0000644000175100001630000000155213577157232032067 0ustar runnerdocker00000000000000"""Test for QueueStatsRequest message.""" from pyof.v0x01.controller2switch.common import QueueStatsRequest, StatsType from pyof.v0x01.controller2switch.stats_request import StatsRequest from tests.test_struct import TestStruct class TestQueueStatsRequest(TestStruct): """Test for QueueStatsRequest message.""" @classmethod def setUpClass(cls): """[Controller2Switch/QueueStatsRequest] - size 8.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_queue_stats_request') super().set_raw_dump_object(StatsRequest, xid=14, body_type=StatsType.OFPST_QUEUE, flags=0, body=QueueStatsRequest(port_no=80, queue_id=5)) super().set_minimum_size(12) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_get_config_reply.py0000644000175100001630000000131413577157232031270 0ustar runnerdocker00000000000000"""Test GetConfigReply message.""" from pyof.v0x01.controller2switch.common import ConfigFlag from pyof.v0x01.controller2switch.get_config_reply import GetConfigReply from tests.test_struct import TestStruct class TestGetConfigReply(TestStruct): """Test class for TestGetConfigReply.""" @classmethod def setUpClass(cls): """[Controller2Switch/GetConfigReply] - size 12.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_get_config_reply') super().set_raw_dump_object(GetConfigReply, xid=13, flags=ConfigFlag.OFPC_FRAG_REASM, miss_send_len=1024) super().set_minimum_size(12) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_packet_out.py0000644000175100001630000000544113577157232030114 0ustar runnerdocker00000000000000"""Packet out message tests.""" from pyof.foundation.exceptions import ValidationError from pyof.v0x01.common.action import ActionOutput from pyof.v0x01.common.phy_port import Port from pyof.v0x01.controller2switch.packet_out import PacketOut from tests.test_struct import TestStruct class TestPacketOut(TestStruct): """Packet out message tests (also those in :class:`.TestDump`). Attributes: message (PacketOut): The message configured in :meth:`setUpClass`. """ @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_packet_out') super().set_raw_dump_object(PacketOut, xid=8, buffer_id=4294967295, in_port=Port.OFPP_NONE, data=_get_data(), actions=_get_actions()) super().set_minimum_size(16) def setUp(self): """Run before every test.""" self.message = self.get_raw_object() def test_valid_virtual_in_ports(self): """Valid virtual ports as defined in 1.0.1 spec.""" valid = (Port.OFPP_LOCAL, Port.OFPP_CONTROLLER, Port.OFPP_NONE) for in_port in valid: self.message.in_port = in_port self.assertTrue(self.message.is_valid()) def test_invalid_virtual_in_ports(self): """Invalid virtual ports as defined in 1.0.1 spec.""" invalid = (Port.OFPP_IN_PORT, Port.OFPP_TABLE, Port.OFPP_NORMAL, Port.OFPP_FLOOD, Port.OFPP_ALL) for in_port in invalid: self.message.in_port = in_port self.assertFalse(self.message.is_valid()) self.assertRaises(ValidationError, self.message.validate) def test_valid_physical_in_ports(self): """Physical port limits from 1.0.0 spec.""" max_valid = int(Port.OFPP_MAX.value) for in_port in (1, max_valid): self.message.in_port = in_port self.assertTrue(self.message.is_valid()) def test_invalid_physical_in_port(self): """Physical port limits from 1.0.0 spec.""" max_valid = int(Port.OFPP_MAX.value) for in_port in (-1, 0, max_valid + 1, max_valid + 2): self.message.in_port = in_port self.assertFalse(self.message.is_valid()) self.assertRaises(ValidationError, self.message.validate) def _get_actions(): """Function used to return a list of actions used by packetout instance.""" action = ActionOutput(port=1, max_length=0) return [action] def _get_data(): """Function used to return a BinaryData used by packetout instance.""" data = b'\x01# \x00\x00\x01\xd2A\xc6.*@\x88\xcc\x02\x07\x07dpi' data += b'd:1\x04\x02\x021\x06\x02\x00x\x0c\x06dpid:1\x00\x00' return data python-openflow-2019.2/tests/v0x01/test_controller2switch/test_barrier_reply.py0000644000175100001630000000106213577157232030612 0ustar runnerdocker00000000000000"""Barrier reply message tests.""" from pyof.v0x01.controller2switch.barrier_reply import BarrierReply from tests.test_struct import TestStruct class TestBarrierReply(TestStruct): """Barrier reply message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_barrier_reply') super().set_raw_dump_object(BarrierReply, xid=5) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_stats_request.py0000644000175100001630000000126713577157232030666 0ustar runnerdocker00000000000000"""Test for StatsRequest message.""" from pyof.v0x01.controller2switch.common import StatsType from pyof.v0x01.controller2switch.stats_request import StatsRequest from tests.test_struct import TestStruct class TestStatsRequest(TestStruct): """Test for StatsRequest message.""" @classmethod def setUpClass(cls): """[Controller2Switch/StatsRequest] - size 12.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_stats_request') super().set_raw_dump_object(StatsRequest, xid=1, body_type=StatsType.OFPST_FLOW, flags=1, body=b'') super().set_minimum_size(12) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_port_stats_request.py0000644000175100001630000000134713577157232031731 0ustar runnerdocker00000000000000"""Test for PortStatsRequest.""" from pyof.v0x01.controller2switch.common import PortStatsRequest, StatsType from pyof.v0x01.controller2switch.stats_request import StatsRequest from tests.test_struct import TestStruct class TestPortStatsRequest(TestStruct): """Test for PortStatsRequest.""" @classmethod def setUpClass(cls): """[Controller2Switch/PortStatsRequest] - size 8.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_port_stats_request') super().set_raw_dump_object(StatsRequest, xid=17, body_type=StatsType.OFPST_PORT, flags=0, body=PortStatsRequest(port_no=80)) super().set_minimum_size(12) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_port_stats.py0000644000175100001630000000206113577157232030153 0ustar runnerdocker00000000000000"""Test for PortStats structure.""" from pyof.v0x01.controller2switch.common import PortStats, StatsType from pyof.v0x01.controller2switch.stats_reply import StatsReply from tests.test_struct import TestStruct class TestPortStats(TestStruct): """Test for PortStats structure.""" @classmethod def setUpClass(cls): """[Controller2Switch/PortStats] - size 104.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_port_stats') super().set_raw_dump_object(StatsReply, xid=13, body_type=StatsType.OFPST_PORT, flags=0, body=_get_port_stats()) super().set_minimum_size(12) def _get_port_stats(): """Function used to return a PortStats instance.""" return PortStats(port_no=80, rx_packets=5, tx_packets=10, rx_bytes=200, tx_bytes=400, rx_dropped=0, tx_dropped=0, rx_errors=0, tx_errors=0, rx_frame_err=0, rx_over_err=0, rx_crc_err=0, collisions=0) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_stats_reply.py0000644000175100001630000000125413577157232030325 0ustar runnerdocker00000000000000"""Test for StatsReply message.""" from pyof.v0x01.controller2switch.common import StatsType from pyof.v0x01.controller2switch.stats_reply import StatsReply from tests.test_struct import TestStruct class TestStatsReply(TestStruct): """Test for StatsReply message.""" @classmethod def setUpClass(cls): """[Controller2Switch/StatsReply] - size 12.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_stats_reply') super().set_raw_dump_object(StatsReply, xid=1, body_type=StatsType.OFPST_FLOW, flags=0x0001, body=b'') super().set_minimum_size(12) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_flow_mod.py0000644000175100001630000000361113577157232027561 0ustar runnerdocker00000000000000"""Flow modification (add/delete) message tests.""" from pyof.v0x01.common.action import ActionOutput from pyof.v0x01.common.flow_match import Match from pyof.v0x01.common.phy_port import Port from pyof.v0x01.controller2switch.flow_mod import FlowMod, FlowModCommand from tests.test_struct import TestStruct class TestFlowAdd(TestStruct): """Flow addition message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_flow_add') kwargs = _get_flowmod_kwargs(FlowModCommand.OFPFC_ADD) super().set_raw_dump_object(FlowMod, **kwargs) super().set_minimum_size(72) class TestFlowDelete(TestStruct): """Flow deletion message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_flow_delete') kwargs = _get_flowmod_kwargs(FlowModCommand.OFPFC_DELETE) super().set_raw_dump_object(FlowMod, **kwargs) # No need to test minimum size again. def _get_flowmod_kwargs(command): """Return parameters for FlowMod object.""" return {'xid': 4, 'command': command, 'match': _get_match(), 'cookie': 0, 'idle_timeout': 0, 'hard_timeout': 0, 'priority': 32768, 'buffer_id': 4294967295, 'out_port': Port.OFPP_NONE, 'flags': 0, 'actions': _get_actions()} def _get_match(): """Return a Match object.""" return Match() def _get_actions(): """Return a List of actions registered by flow object.""" action = ActionOutput(port=65533, max_length=65535) return [action] python-openflow-2019.2/tests/v0x01/test_controller2switch/test_get_config_request.py0000644000175100001630000000104013577157232031621 0ustar runnerdocker00000000000000"""Test GetConfigRequest message.""" from pyof.v0x01.controller2switch.get_config_request import GetConfigRequest from tests.test_struct import TestStruct class TestGetConfigRequest(TestStruct): """Test class for TestGetConfigRequest.""" @classmethod def setUpClass(cls): """[Controller2Switch/GetConfigRequest] - size 8.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_get_config_request') super().set_raw_dump_object(GetConfigRequest, xid=1) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_features_request.py0000644000175100001630000000110513577157232031335 0ustar runnerdocker00000000000000"""Feature request message tests.""" from pyof.v0x01.controller2switch.features_request import FeaturesRequest from tests.test_struct import TestStruct class TestFeaturesRequest(TestStruct): """Feature request message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_features_request') super().set_raw_dump_object(FeaturesRequest, xid=3) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_features_reply.py0000644000175100001630000000360113577157232031003 0ustar runnerdocker00000000000000"""Echo request message tests.""" from pyof.foundation.basic_types import DPID, HWAddress from pyof.v0x01.common.phy_port import PhyPort, PortConfig, PortState from pyof.v0x01.controller2switch.features_reply import FeaturesReply from tests.test_struct import TestStruct class TestFeaturesReply(TestStruct): """Feature reply message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_features_reply') kwargs = _get_kwargs() super().set_raw_dump_object(FeaturesReply, **kwargs) super().set_minimum_size(32) def _get_kwargs(): return {'xid': 2, 'datapath_id': DPID('00:00:00:00:00:00:00:01'), 'n_buffers': 256, 'n_tables': 254, 'capabilities': 0x000000c7, 'actions': 4095, 'ports': _get_ports()} def _get_ports(): return [ PhyPort(port_no=65534, hw_addr=HWAddress('0e:d3:98:a5:30:47'), name='s1', config=PortConfig.OFPPC_PORT_DOWN, state=PortState.OFPPS_LINK_DOWN, curr=0, advertised=0, supported=0, peer=0), PhyPort(port_no=1, hw_addr=HWAddress('0a:54:cf:fc:4e:6d'), name='s1-eth1', config=0, state=PortState.OFPPS_STP_LISTEN, curr=0x000000c0, advertised=0, supported=0, peer=0), PhyPort(port_no=2, hw_addr=HWAddress('f6:b6:ab:cc:f8:4f'), name='s1-eth2', config=0, state=PortState.OFPPS_STP_LISTEN, curr=0x000000c0, advertised=0, supported=0, peer=0) ] python-openflow-2019.2/tests/v0x01/test_controller2switch/test_flow_stats_request.py0000644000175100001630000000237313577157232031714 0ustar runnerdocker00000000000000"""Test FlowStatsRequest message.""" from pyof.v0x01.common.flow_match import Match from pyof.v0x01.controller2switch.common import FlowStatsRequest, StatsType from pyof.v0x01.controller2switch.stats_request import StatsRequest from tests.test_struct import TestStruct class TestFlowStatsRequest(TestStruct): """Test class for TestFlowStatsRequest.""" @classmethod def setUpClass(cls): """[Controller2Switch/FlowStatsRequest] - size 44.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_flow_stats_request') super().set_raw_dump_object(StatsRequest, xid=12, body_type=StatsType.OFPST_FLOW, flags=0, body=_get_flow_stats_request()) super().set_minimum_size(12) def _get_flow_stats_request(): return FlowStatsRequest(match=_get_match(), table_id=1, out_port=80) def _get_match(): """Function used to return a Match instance.""" return Match(in_port=80, dl_src='01:02:03:04:05:06', dl_dst='01:02:03:04:05:06', dl_vlan=1, dl_vlan_pcp=1, dl_type=1, nw_tos=1, nw_proto=1, nw_src='192.168.0.1', nw_dst='192.168.0.1', tp_src=80, tp_dst=80) python-openflow-2019.2/tests/v0x01/test_controller2switch/test_flow_stats.py0000644000175100001630000000270313577157232030141 0ustar runnerdocker00000000000000"""Test FlowStats message.""" from pyof.v0x01.common.flow_match import Match from pyof.v0x01.controller2switch.common import FlowStats, StatsType from pyof.v0x01.controller2switch.stats_reply import StatsReply from tests.test_struct import TestStruct class TestFlowStats(TestStruct): """Test class for TestFlowStats.""" @classmethod def setUpClass(cls): """[Controller2Switch/FlowStats] - size 88.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_flow_stats_reply') super().set_raw_dump_object(StatsReply, xid=12, body_type=StatsType.OFPST_FLOW, flags=0, body=_get_flow_stats()) super().set_minimum_size(12) def _get_flow_stats(): """Function used to return a FlowStats instance.""" return FlowStats(length=160, table_id=1, match=_get_match(), duration_sec=60, duration_nsec=10000, priority=1, idle_timeout=300, hard_timeout=6000, cookie=1, packet_count=1, byte_count=1) def _get_match(): """Function used to return a Match instance.""" return Match(in_port=80, dl_src='01:02:03:04:05:06', dl_dst='01:02:03:04:05:06', dl_vlan=1, dl_vlan_pcp=1, dl_type=1, nw_tos=1, nw_proto=1, nw_src='192.168.0.1', nw_dst='192.168.0.1', tp_src=80, tp_dst=80) python-openflow-2019.2/tests/v0x01/__init__.py0000644000175100001630000000005413577157232021723 0ustar runnerdocker00000000000000"""Module with tests for v0x01 (v1.0.0).""" python-openflow-2019.2/tests/v0x01/test_symmetric/0000755000175100001630000000000013577157243022670 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/v0x01/test_symmetric/__init__.py0000644000175100001630000000005513577157232024777 0ustar runnerdocker00000000000000"""Testing symmetric messages from v0x01.""" python-openflow-2019.2/tests/v0x01/test_symmetric/test_echo_request.py0000644000175100001630000000104313577157232026763 0ustar runnerdocker00000000000000"""Echo request message tests.""" from pyof.v0x01.symmetric.echo_request import EchoRequest from tests.test_struct import TestStruct class TestEchoRequest(TestStruct): """Echo request message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_echo_request') super().set_raw_dump_object(EchoRequest, xid=0) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x01/test_symmetric/test_hello.py0000644000175100001630000000076513577157232025412 0ustar runnerdocker00000000000000"""Hello message tests.""" from pyof.v0x01.symmetric.hello import Hello from tests.test_struct import TestStruct class TestHello(TestStruct): """Hello message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_hello') super().set_raw_dump_object(Hello, xid=1) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x01/test_symmetric/test_vendor_header.py0000644000175100001630000000114113577157232027101 0ustar runnerdocker00000000000000"""Testing VendorHeader message.""" from pyof.v0x01.symmetric.vendor_header import VendorHeader from tests.test_struct import TestStruct class TestVendorHeader(TestStruct): """Vendor message tests (also those in :class:`.TestDump`).""" def test_unpack(self): """Test unpack VendorHeader message.""" message = b'My custom vendor extra data.' vendor_header = VendorHeader(xid=4, vendor=128, data=message) data = b'\x01\x04\x00(\x00\x00\x00\x04\x00\x00\x00\x80' + message self._test_unpack(vendor_header, bytes2unpack=data) python-openflow-2019.2/tests/v0x01/test_symmetric/test_echo_reply.py0000644000175100001630000000102513577157232026426 0ustar runnerdocker00000000000000"""Echo reply message tests.""" from pyof.v0x01.symmetric.echo_reply import EchoReply from tests.test_struct import TestStruct class TestEchoReply(TestStruct): """Echo reply message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_echo_reply') super().set_raw_dump_object(EchoReply, xid=0) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x01/test_common/0000755000175100001630000000000013577157243022144 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/v0x01/test_common/test_phy_port.py0000644000175100001630000000352613577157232025425 0ustar runnerdocker00000000000000"""Testing PhyPort structure.""" import os from unittest import TestCase from pyof.foundation.basic_types import HWAddress from pyof.foundation.constants import OFP_MAX_PORT_NAME_LEN from pyof.v0x01.common.phy_port import ( PhyPort, PortConfig, PortFeatures, PortState) class TestPhyPort(TestCase): """Test PhyPort.""" def setUp(self): """Basic setup for test.""" self.message = PhyPort() self.message.port_no = 1 self.message.hw_addr = HWAddress('9a:da:11:8a:f4:0c') self.message.name = 's1-eth1' self.message.state = PortState.OFPPS_STP_LISTEN self.message.curr = (PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER) def test_get_size(self): """[Common/PhyPort] - size 48.""" self.assertEqual(self.message.get_size(), 48) def test_pack(self): """[Common/PhyPort] - packing.""" data = b'\x00\x01\x9a\xda\x11\x8a\xf4\x0cs1-eth1\x00\x00\x00\x00\x00' data += 15 * b'\x00' data += b'\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' self.assertEqual(self.message.pack(), data) def test_unpack(self): """[Common/PhyPort] - unpacking.""" filename = os.path.join(os.path.dirname(os.path.realpath('__file__')), 'raw/v0x01/ofpt_port_status.dat') f = open(filename, 'rb') f.seek(16, 1) self.message.unpack(f.read(48)) self.assertEqual(self.message.port_no, 1) self.assertEqual(self.message.hw_addr, '9a:da:11:8a:f4:0c') self.assertEqual(self.message.name, 's1-eth1') self.assertEqual(self.message.state, PortState.OFPPS_STP_LISTEN) self.assertEqual(self.message.curr, (PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER)) f.close() python-openflow-2019.2/tests/v0x01/test_common/__init__.py0000644000175100001630000000003313577157232024247 0ustar runnerdocker00000000000000"""Test common modules.""" python-openflow-2019.2/tests/v0x01/test_common/test_flow_match.py0000644000175100001630000000252313577157232025700 0ustar runnerdocker00000000000000"""Testing FlowMatch structure.""" import unittest from pyof.v0x01.common import flow_match class TestMatch(unittest.TestCase): """Test Match structure.""" def setUp(self): """Basic setup for test.""" self.message = flow_match.Match() self.message.in_port = 22 self.message.dl_src = [1, 2, 3, 4, 5, 6] self.message.dl_dst = [1, 2, 3, 4, 5, 6] self.message.dl_vlan = 1 self.message.dl_vlan_pcp = 1 self.message.dl_type = 1 self.message.nw_tos = 1 self.message.nw_proto = 1 self.message.nw_src = [192, 168, 0, 1] self.message.nw_dst = [192, 168, 0, 2] self.message.tp_src = 22 self.message.tp_dst = 22 def test_get_size(self): """[Common/FlowMatch] - size 40.""" self.assertEqual(self.message.get_size(), 40) def test_pack_unpack(self): """[Common/FlowMatch] - packing and unpacking.""" pack = self.message.pack() unpacked = flow_match.Match() unpacked.unpack(pack) self.assertEqual(self.message.pack(), unpacked.pack()) @unittest.skip('Not yet implemented') def test_pack(self): """[Common/FlowMatch] - packing.""" pass @unittest.skip('Not yet implemented') def test_unpack(self): """[Common/FlowMatch] - unpacking.""" pass python-openflow-2019.2/tests/v0x01/test_common/test_queue.py0000644000175100001630000000373013577157232024702 0ustar runnerdocker00000000000000"""Testing Queue structure.""" import unittest from pyof.v0x01.common import queue class TestQueuePropHeader(unittest.TestCase): """Test QueuePropHeader.""" def setUp(self): """Basic setup for test.""" self.message = queue.QueuePropHeader() self.message.queue_property = queue.QueueProperties.OFPQT_MIN_RATE self.message.length = 12 def test_get_size(self): """[Common/QueuePropHeader] - size 8.""" self.assertEqual(self.message.get_size(), 8) @unittest.skip('Not yet implemented') def test_pack(self): """[Common/QueuePropHeader] - packing.""" pass @unittest.skip('Not yet implemented') def test_unpack(self): """[Common/QueuePropHeader] - unpacking.""" pass class TestPacketQueue(unittest.TestCase): """TestPacketQueue.""" def setUp(self): """Basic setup for test.""" self.message = queue.PacketQueue() self.message.queue_id = 1 self.message.length = 8 def test_get_size(self): """[Common/PacketQueue] - size 8.""" self.assertEqual(self.message.get_size(), 8) @unittest.skip('Not yet implemented') def test_pack(self): """[Common/PacketQueue] - packing.""" pass @unittest.skip('Not yet implemented') def test_unpack(self): """[Common/PacketQueue] - unpacking.""" pass class TestQueuePropMinRate(unittest.TestCase): """Test QueuePropMinRate.""" def setUp(self): """Basic setup for test.""" self.message = queue.QueuePropMinRate() self.message.rate = 1000 def test_get_size(self): """[Common/PropMinRate] - size 16.""" self.assertEqual(self.message.get_size(), 16) @unittest.skip('Not yet implemented') def test_pack(self): """[Common/PropMinRate] - packing.""" pass @unittest.skip('Not yet implemented') def test_unpack(self): """[Common/PropMinRate] - unpacking.""" pass python-openflow-2019.2/tests/v0x01/test_common/test_action.py0000644000175100001630000001126113577157232025031 0ustar runnerdocker00000000000000"""Testing Port structures.""" from pyof.v0x01.common.action import ( ActionDLAddr, ActionEnqueue, ActionNWAddr, ActionNWTos, ActionOutput, ActionTPPort, ActionType, ActionVendorHeader, ActionVlanPCP, ActionVlanVid) from pyof.v0x01.common.phy_port import Port from tests.test_struct import TestStruct class TestActionOutput(TestStruct): """ActionOutput message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_action_output') super().set_raw_dump_object(ActionOutput, port=Port.OFPP_CONTROLLER, max_length=8) super().set_minimum_size(8) class TestActionEnqueue(TestStruct): """ActionEnqueue message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_action_enqueue') super().set_raw_dump_object(ActionEnqueue, port=Port.OFPP_CONTROLLER, queue_id=4) super().set_minimum_size(16) class TestActionVlanVid(TestStruct): """ActionVlanVid message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_action_vlan_vid') super().set_raw_dump_object(ActionVlanVid, vlan_id=5) super().set_minimum_size(8) class TestActionVlanPCP(TestStruct): """ActionVlanPCP message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_action_vlan_pcp') super().set_raw_dump_object(ActionVlanPCP, vlan_pcp=2) super().set_minimum_size(8) class TestActionDLAddr(TestStruct): """ActionDLAddr message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_action_dl_addr') super().set_raw_dump_object(ActionDLAddr, action_type=ActionType.OFPAT_SET_DL_SRC, dl_addr=[12, 12, 12, 12, 12, 12]) super().set_minimum_size(16) class TestActionNWAddr(TestStruct): """ActionNWAddr message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_action_nw_addr') super().set_raw_dump_object(ActionNWAddr, action_type=ActionType.OFPAT_SET_NW_SRC, nw_addr=[12, 12, 12, 12, 12, 12]) super().set_minimum_size(8) class TestActionNWTos(TestStruct): """ActionNWTos message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_action_nw_tos') super().set_raw_dump_object(ActionNWTos, action_type=ActionType.OFPAT_SET_NW_SRC, nw_tos=123456) super().set_minimum_size(8) class TestActionTPPort(TestStruct): """ActionTPPort message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_action_tp_port') super().set_raw_dump_object(ActionTPPort, action_type=ActionType.OFPAT_SET_TP_SRC, tp_port=8888) super().set_minimum_size(8) class TestActionVendorHeader(TestStruct): """ActionVendorHeader message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_action_vendor_header') super().set_raw_dump_object(ActionVendorHeader, length=16, vendor=1) super().set_minimum_size(8) python-openflow-2019.2/tests/v0x01/test_common/test_header.py0000644000175100001630000000271413577157232025007 0ustar runnerdocker00000000000000"""Testing Header structure.""" import os import unittest from unittest.mock import patch from pyof.v0x01.common.header import Header, Type 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'\x01\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/v0x01/ofpt_hello.dat') 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, 1) f.close() python-openflow-2019.2/tests/v0x01/test_asynchronous/0000755000175100001630000000000013577157243023407 5ustar runnerdocker00000000000000python-openflow-2019.2/tests/v0x01/test_asynchronous/__init__.py0000644000175100001630000000006013577157232025512 0ustar runnerdocker00000000000000"""Testing asynchronous messages from v0x01.""" python-openflow-2019.2/tests/v0x01/test_asynchronous/test_port_status.py0000644000175100001630000000214213577157232027404 0ustar runnerdocker00000000000000"""Port Status message tests.""" from pyof.foundation.basic_types import HWAddress from pyof.v0x01.asynchronous.port_status import PortReason, PortStatus from pyof.v0x01.common.phy_port import PhyPort, PortFeatures, PortState from tests.test_struct import TestStruct class TestPortStatus(TestStruct): """Test the Port Status message.""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_port_status') super().set_raw_dump_object(_new_portstatus) super().set_minimum_size(64) def _new_portstatus(): """Crate new PortStatus and PhyPort instances.""" desc_name = 's1-eth1' desc = PhyPort(port_no=1, hw_addr=HWAddress('9a:da:11:8a:f4:0c'), name=desc_name, state=PortState.OFPPS_STP_LISTEN, curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER) return PortStatus(xid=0, reason=PortReason.OFPPR_MODIFY, desc=desc) python-openflow-2019.2/tests/v0x01/test_asynchronous/test_error_msg.py0000644000175100001630000000225413577157232027020 0ustar runnerdocker00000000000000"""Testing Error Message.""" from pyof.v0x01.asynchronous.error_msg import ( BadRequestCode, ErrorMsg, ErrorType, FlowModFailedCode) from tests.test_struct import TestStruct class TestErrorMessage(TestStruct): """Test the Error Message.""" @classmethod def setUpClass(cls): """Setup TestStruct.""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_error_msg') super().set_raw_dump_object(ErrorMsg, xid=12, error_type=ErrorType.OFPET_BAD_REQUEST, code=BadRequestCode.OFPBRC_BAD_STAT, data=b'') super().set_minimum_size(12) def test_unpack_error_msg(self): """Test Unpack a sample ErrorMsg.""" expected = b'\x01\x01\x00\x1b\x00\x00\x00\x18\x00\x03\x00\x02FLOW' error_msg = ErrorMsg(xid=24, error_type=ErrorType.OFPET_FLOW_MOD_FAILED, code=FlowModFailedCode.OFPFMFC_EPERM, data=b'FLOW') actual = ErrorMsg(xid=24) actual.unpack(expected[8:]) self.assertEqual(actual, error_msg) python-openflow-2019.2/tests/v0x01/test_asynchronous/test_flow_removed.py0000644000175100001630000000242613577157232027512 0ustar runnerdocker00000000000000"""Testing FlowRemoved message.""" from pyof.foundation.basic_types import HWAddress, IPAddress from pyof.v0x01.asynchronous.flow_removed import FlowRemoved, FlowRemovedReason from pyof.v0x01.common.flow_match import Match from tests.test_struct import TestStruct class TestFlowRemoved(TestStruct): """Test the FlowRemoved message.""" @classmethod def setUpClass(cls): """Setup TestStruct.""" reason = FlowRemovedReason.OFPRR_IDLE_TIMEOUT match = Match(in_port=80, dl_vlan=1, dl_vlan_pcp=1, dl_type=1, nw_tos=1, nw_proto=1, tp_src=80, tp_dst=80, dl_src=HWAddress('00:00:00:00:00:00'), dl_dst=HWAddress('00:00:00:00:00:00'), nw_src=IPAddress('192.168.0.1'), nw_dst=IPAddress('192.168.0.2')) super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_flow_removed') super().set_raw_dump_object(FlowRemoved, xid=12, match=match, cookie=0, priority=1, reason=reason, duration_sec=4, duration_nsec=23, idle_timeout=9, packet_count=10, byte_count=4) super().set_minimum_size(88) python-openflow-2019.2/tests/v0x01/test_asynchronous/test_packet_in.py0000644000175100001630000000142713577157232026757 0ustar runnerdocker00000000000000"""Packet in message tests.""" from pyof.v0x01.asynchronous.packet_in import PacketIn, PacketInReason from tests.test_struct import TestStruct class TestPacketIn(TestStruct): """Packet in message tests (also those in :class:`.TestDump`).""" @classmethod def setUpClass(cls): """Configure raw file and its object in parent class (TestDump).""" super().setUpClass() super().set_raw_dump_file('v0x01', 'ofpt_packet_in') super().set_raw_dump_object(PacketIn, xid=15, buffer_id=1, total_len=1, in_port=1, reason=PacketInReason.OFPR_ACTION) # Different from the specification, the minimum size of this class is # 18, not 20. super().set_minimum_size(18) python-openflow-2019.2/setup.cfg0000644000175100001630000000054713577157243017424 0ustar runnerdocker00000000000000[aliases] test = pytest [pycodestyle] exclude = .eggs,ENV,build,docs/conf.py,venv [yala] radon mi args = --min C pylint args = --disable=too-many-arguments,too-many-locals,too-many-instance-attributes,too-few-public-methods [pydocstyle] add-ignore = D105 [isort] known_first_party = pyof,tests multi_line_output = 4 [egg_info] tag_build = tag_date = 0 python-openflow-2019.2/README.rst0000644000175100001630000001145413577157232017267 0ustar runnerdocker00000000000000 ######## Overview ######## |Experimental| |Openflow| |Tag| |Release| |License| |Build| |Coverage| |Quality| *python-openflow* is a low level library to parse and create OpenFlow messages. If you want to read an OpenFlow packet from an open socket or send a message to an OpenFlow switch, this is your best friend. The main features are: high performance, short learning curve and free software license. This library is part of `Kytos project `_, but feel free to use this simple and intuitive library in other projects. .. attention:: *python-openflow* does not perform I/O operations. To communicate with a switch, you must write your own controller using this library or use our `Kytos SDN Platform `_. A quick start follows for you to check whether this project fits your needs. For a more detailed documentation, please check the `python-openflow API Reference Manual `_. Quick Start *********** Installing ========== We use python3.6. So in order to use this software please install python3.6 into your environment beforehand. We are doing a huge effort to make Kytos and its components available on all common distros. So, we recommend you to download it from your distro repository. But if you are trying to test, develop or just want a more recent version of our software no problem: Download now, the latest release (it still a beta software), from our repository: First you need to clone `python-openflow` repository: .. code-block:: shell $ git clone https://github.com/kytos/python-openflow.git After cloning, the installation process is done by standard `setuptools` install procedure: .. code-block:: shell $ cd python-openflow $ sudo python3.6 setup.py install Alternatively, if you are a developer and want to install in develop mode: .. code-block:: shell $ cd python-openflow $ pip3.6 install -r requirements/dev.txt Basic Usage Example =================== See how it is easy to create a feature request message with this library. You can use ipython3 to get the advantages of autocompletion: .. code-block:: python >>> from pyof.v0x01.controller2switch.features_request import FeaturesRequest >>> request = FeaturesRequest() >>> print(request.header.message_type) Type.OFPT_FEATURES_REQUEST If you need to send this message via socket, call the ``pack()`` method to get its binary representation to be sent through the network: .. code:: python >>> binary_msg = request.pack() >>> print(binary_msg) b"\x01\x05\x00\x08\x14\xad'\x8d" >>> # Use a controller (e.g. Kytos SDN controller) to send "binary_msg" To parse a message, use ``unpack_message()``: .. code:: python >>> from pyof.v0x01.common.utils import unpack_message >>> binary_msg = b"\x01\x05\x00\x08\x14\xad'\x8d" >>> msg = unpack_message(binary_msg) >>> print(msg.header.message_type) Type.OFPT_FEATURES_REQUEST Please, note that this library do not send or receive messages via socket. You have to create your own server to receive messages from switches. This library only helps you to handle OpenFlow messages in a more pythonic way. Authors ******* For a complete list of authors, please open ``AUTHORS.rst`` file. Contributing ************ If you want to contribute to this project, please read `Kytos Documentation `__ website. License ******* This software is under *MIT-License*. For more information please read ``LICENSE`` file. .. |Experimental| image:: https://img.shields.io/badge/stability-experimental-orange.svg .. |Openflow| image:: https://img.shields.io/badge/Openflow-1.3-brightgreen.svg :target: https://www.opennetworking.org/images/stories/downloads/sdn-resources/onf-specifications/openflow/openflow-switch-v1.3.5.pdf .. |Tag| image:: https://img.shields.io/github/tag/kytos/python-openflow.svg :target: https://github.com/kytos/python-openflow/tags .. |Release| image:: https://img.shields.io/github/release/kytos/python-openflow.svg :target: https://github.com/kytos/python-openflow/releases .. |License| image:: https://img.shields.io/github/license/kytos/python-openflow.svg :target: https://github.com/kytos/python-openflow/blob/master/LICENSE .. |Build| image:: https://scrutinizer-ci.com/g/kytos/python-openflow/badges/build.png?b=master :alt: Build status :target: https://scrutinizer-ci.com/g/kytos/python-openflow/?branch=master .. |Coverage| image:: https://scrutinizer-ci.com/g/kytos/python-openflow/badges/coverage.png?b=master :alt: Code coverage :target: https://scrutinizer-ci.com/g/kytos/python-openflow/?branch=master .. |Quality| image:: https://scrutinizer-ci.com/g/kytos/python-openflow/badges/quality-score.png?b=master :alt: Code-quality score :target: https://scrutinizer-ci.com/g/kytos/python-openflow/?branch=master