1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
"""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()
if isinstance(value, type(self)):
return value.pack()
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.')
|