| @@ 20-131 (lines=112) @@ | ||
| 17 | _VIRT_IN_PORTS = (Port.OFPP_LOCAL, Port.OFPP_CONTROLLER, Port.OFPP_NONE) |
|
| 18 | ||
| 19 | ||
| 20 | class PacketOut(GenericMessage): |
|
| 21 | """Send packet (controller -> datapath).""" |
|
| 22 | ||
| 23 | #: :class:`~pyof.v0x01.common.header.Header` |
|
| 24 | header = Header(message_type=Type.OFPT_PACKET_OUT) |
|
| 25 | buffer_id = UBInt32() |
|
| 26 | in_port = UBInt16() |
|
| 27 | actions_len = UBInt16() |
|
| 28 | actions = ListOfActions() |
|
| 29 | data = BinaryData() |
|
| 30 | ||
| 31 | def __init__(self, xid=None, buffer_id=NO_BUFFER, in_port=Port.OFPP_NONE, |
|
| 32 | actions=None, data=b''): |
|
| 33 | """Create a PacketOut with the optional parameters below. |
|
| 34 | ||
| 35 | Args: |
|
| 36 | xid (int): xid of the message header. |
|
| 37 | buffer_id (int): ID assigned by datapath (-1 if none). |
|
| 38 | in_port (:class:`int` / :class:`~pyof.v0x01.common.phy_port.Port`): |
|
| 39 | Packet's input port (:attr:`.Port.OFPP_NONE` if none). Virtual |
|
| 40 | ports :attr:`Port.OFPP_IN_PORT`, :attr:`Port.OFPP_TABLE`, |
|
| 41 | :attr:`Port.OFPP_NORMAL`, :attr:`Port.OFPP_FLOOD`, and |
|
| 42 | :attr:`Port.OFPP_ALL` cannot be used as input port. |
|
| 43 | actions (~pyof.v0x01.common.action.ListOfActions): List of Actions. |
|
| 44 | data (bytes): Packet data. The length is inferred from the length |
|
| 45 | field in the header. (Only meaningful if ``buffer_id`` == -1). |
|
| 46 | """ |
|
| 47 | super().__init__(xid) |
|
| 48 | self.buffer_id = buffer_id |
|
| 49 | self.in_port = in_port |
|
| 50 | self.actions = [] if actions is None else actions |
|
| 51 | self.data = data |
|
| 52 | ||
| 53 | def validate(self): |
|
| 54 | """Validate the entire message.""" |
|
| 55 | if not super().is_valid(): |
|
| 56 | raise ValidationError() |
|
| 57 | self._validate_in_port() |
|
| 58 | ||
| 59 | def is_valid(self): |
|
| 60 | """Answer if this message is valid.""" |
|
| 61 | try: |
|
| 62 | self.validate() |
|
| 63 | return True |
|
| 64 | except ValidationError: |
|
| 65 | return False |
|
| 66 | ||
| 67 | def pack(self, value=None): |
|
| 68 | """Update the action_len attribute and call super's pack.""" |
|
| 69 | if value is None: |
|
| 70 | self._update_actions_len() |
|
| 71 | return super().pack() |
|
| 72 | elif isinstance(value, type(self)): |
|
| 73 | return value.pack() |
|
| 74 | else: |
|
| 75 | msg = "{} is not an instance of {}".format(value, |
|
| 76 | type(self).__name__) |
|
| 77 | raise PackException(msg) |
|
| 78 | ||
| 79 | def unpack(self, buff, offset=0): |
|
| 80 | """Unpack a binary message into this object's attributes. |
|
| 81 | ||
| 82 | Unpack the binary value *buff* and update this object attributes based |
|
| 83 | on the results. It is an inplace method and it receives the binary data |
|
| 84 | of the message **without the header**. |
|
| 85 | ||
| 86 | This class' unpack method is like the :meth:`.GenericMessage.unpack` |
|
| 87 | one, except for the ``actions`` attribute which has a length determined |
|
| 88 | by the ``actions_len`` attribute. |
|
| 89 | ||
| 90 | Args: |
|
| 91 | buff (bytes): Binary data package to be unpacked, without the |
|
| 92 | header. |
|
| 93 | offset (int): Where to begin unpacking. |
|
| 94 | """ |
|
| 95 | begin = offset |
|
| 96 | for attribute_name, class_attribute in self.get_class_attributes(): |
|
| 97 | if type(class_attribute).__name__ != "Header": |
|
| 98 | attribute = deepcopy(class_attribute) |
|
| 99 | if attribute_name == 'actions': |
|
| 100 | length = self.actions_len.value |
|
| 101 | attribute.unpack(buff[begin:begin+length]) |
|
| 102 | else: |
|
| 103 | attribute.unpack(buff, begin) |
|
| 104 | setattr(self, attribute_name, attribute) |
|
| 105 | begin += attribute.get_size() |
|
| 106 | ||
| 107 | def _update_actions_len(self): |
|
| 108 | """Update the actions_len field based on actions value.""" |
|
| 109 | if isinstance(self.actions, ListOfActions): |
|
| 110 | self.actions_len = self.actions.get_size() |
|
| 111 | else: |
|
| 112 | self.actions_len = ListOfActions(self.actions).get_size() |
|
| 113 | ||
| 114 | def _validate_in_port(self): |
|
| 115 | """Validate in_port attribute. |
|
| 116 | ||
| 117 | A valid port is either: |
|
| 118 | ||
| 119 | * Greater than 0 and less than or equals to Port.OFPP_MAX |
|
| 120 | * One of the valid virtual ports: Port.OFPP_LOCAL, |
|
| 121 | Port.OFPP_CONTROLLER or Port.OFPP_NONE |
|
| 122 | ||
| 123 | Raises: |
|
| 124 | ValidationError: If in_port is an invalid port. |
|
| 125 | ||
| 126 | """ |
|
| 127 | is_valid_range = self.in_port > 0 and self.in_port <= Port.OFPP_MAX |
|
| 128 | is_valid_virtual_in_ports = self.in_port in _VIRT_IN_PORTS |
|
| 129 | ||
| 130 | if (is_valid_range or is_valid_virtual_in_ports) is False: |
|
| 131 | raise ValidationError(f'{self.in_port} is not a valid input port.') |
|
| 132 | ||
| @@ 20-129 (lines=110) @@ | ||
| 17 | _VIRT_IN_PORTS = (PortNo.OFPP_LOCAL, PortNo.OFPP_CONTROLLER, PortNo.OFPP_ANY) |
|
| 18 | ||
| 19 | ||
| 20 | class PacketOut(GenericMessage): |
|
| 21 | """Send packet (controller -> datapath).""" |
|
| 22 | ||
| 23 | #: Openflow :class:`~pyof.v0x04.common.header.Header` |
|
| 24 | header = Header(message_type=Type.OFPT_PACKET_OUT) |
|
| 25 | #: ID assigned by datapath (OFP_NO_BUFFER if none). |
|
| 26 | buffer_id = UBInt32() |
|
| 27 | #: Packet’s input port or OFPP_CONTROLLER. |
|
| 28 | in_port = UBInt32() |
|
| 29 | #: Size of action array in bytes. |
|
| 30 | actions_len = UBInt16() |
|
| 31 | #: Padding |
|
| 32 | pad = Pad(6) |
|
| 33 | #: Action List. |
|
| 34 | actions = ListOfActions() |
|
| 35 | #: Packet data. The length is inferred from the length field in the header. |
|
| 36 | #: (Only meaningful if buffer_id == -1.) |
|
| 37 | data = BinaryData() |
|
| 38 | ||
| 39 | def __init__(self, xid=None, buffer_id=UBINT32_MAX_VALUE, |
|
| 40 | in_port=PortNo.OFPP_CONTROLLER, actions=None, |
|
| 41 | data=b''): |
|
| 42 | """Create a PacketOut with the optional parameters below. |
|
| 43 | ||
| 44 | Args: |
|
| 45 | xid (int): xid of the message header. |
|
| 46 | buffer_id (int): ID assigned by datapath (-1 if none). In this case |
|
| 47 | UBINT32_MAX_VALUE is -1 for the field. |
|
| 48 | in_port (:class:`int`, :class:`~pyof.v0x04.common.port.Port`): |
|
| 49 | Packet's input port (:attr:`Port.OFPP_NONE` if none). |
|
| 50 | Virtual ports OFPP_IN_PORT, OFPP_TABLE, OFPP_NORMAL, |
|
| 51 | OFPP_FLOOD, and OFPP_ALL cannot be used as input port. |
|
| 52 | actions (:class:`~pyof.v0x04.common.action.ListOfActions`): |
|
| 53 | List of Action instances. |
|
| 54 | data (bytes): Packet data. The length is inferred from the length |
|
| 55 | field in the header. (Only meaningful if ``buffer_id`` == -1). |
|
| 56 | """ |
|
| 57 | super().__init__(xid) |
|
| 58 | self.buffer_id = buffer_id |
|
| 59 | self.in_port = in_port |
|
| 60 | self.actions = [] if actions is None else actions |
|
| 61 | self.data = data |
|
| 62 | ||
| 63 | def validate(self): |
|
| 64 | """Validate the entire message.""" |
|
| 65 | if not super().is_valid(): |
|
| 66 | raise ValidationError() |
|
| 67 | self._validate_in_port() |
|
| 68 | ||
| 69 | def is_valid(self): |
|
| 70 | """Answer if this message is valid.""" |
|
| 71 | try: |
|
| 72 | self.validate() |
|
| 73 | return True |
|
| 74 | except ValidationError: |
|
| 75 | return False |
|
| 76 | ||
| 77 | def pack(self, value=None): |
|
| 78 | """Update the action_len attribute and call super's pack.""" |
|
| 79 | if value is None: |
|
| 80 | self._update_actions_len() |
|
| 81 | return super().pack() |
|
| 82 | elif isinstance(value, type(self)): |
|
| 83 | return value.pack() |
|
| 84 | else: |
|
| 85 | msg = "{} is not an instance of {}".format(value, |
|
| 86 | type(self).__name__) |
|
| 87 | raise PackException(msg) |
|
| 88 | ||
| 89 | def unpack(self, buff, offset=0): |
|
| 90 | """Unpack a binary message into this object's attributes. |
|
| 91 | ||
| 92 | Unpack the binary value *buff* and update this object attributes based |
|
| 93 | on the results. It is an inplace method and it receives the binary data |
|
| 94 | of the message **without the header**. |
|
| 95 | ||
| 96 | This class' unpack method is like the :meth:`.GenericMessage.unpack` |
|
| 97 | one, except for the ``actions`` attribute which has a length determined |
|
| 98 | by the ``actions_len`` attribute. |
|
| 99 | ||
| 100 | Args: |
|
| 101 | buff (bytes): Binary data package to be unpacked, without the |
|
| 102 | header. |
|
| 103 | offset (int): Where to begin unpacking. |
|
| 104 | """ |
|
| 105 | begin = offset |
|
| 106 | for attribute_name, class_attribute in self.get_class_attributes(): |
|
| 107 | if type(class_attribute).__name__ != "Header": |
|
| 108 | attribute = deepcopy(class_attribute) |
|
| 109 | if attribute_name == 'actions': |
|
| 110 | length = self.actions_len.value |
|
| 111 | attribute.unpack(buff[begin:begin+length]) |
|
| 112 | else: |
|
| 113 | attribute.unpack(buff, begin) |
|
| 114 | setattr(self, attribute_name, attribute) |
|
| 115 | begin += attribute.get_size() |
|
| 116 | ||
| 117 | def _update_actions_len(self): |
|
| 118 | """Update the actions_len field based on actions value.""" |
|
| 119 | if isinstance(self.actions, ListOfActions): |
|
| 120 | self.actions_len = self.actions.get_size() |
|
| 121 | else: |
|
| 122 | self.actions_len = ListOfActions(self.actions).get_size() |
|
| 123 | ||
| 124 | def _validate_in_port(self): |
|
| 125 | is_valid_range = self.in_port > 0 and self.in_port <= PortNo.OFPP_MAX |
|
| 126 | is_valid_virtual_in_ports = self.in_port in _VIRT_IN_PORTS |
|
| 127 | ||
| 128 | if (is_valid_range or is_valid_virtual_in_ports) is False: |
|
| 129 | raise ValidationError(f'{self.in_port} is not a valid input port.') |
|
| 130 | ||