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.')
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 []
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()
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)