1
|
|
|
"""For the controller to send a packet out through the datapath.""" |
2
|
1 |
|
from copy import deepcopy |
3
|
|
|
|
4
|
1 |
|
from pyof.foundation.base import GenericMessage |
5
|
1 |
|
from pyof.foundation.basic_types import BinaryData, Pad, UBInt16, UBInt32 |
6
|
1 |
|
from pyof.foundation.constants import UBINT32_MAX_VALUE |
7
|
1 |
|
from pyof.foundation.exceptions import PackException, ValidationError |
8
|
1 |
|
from pyof.v0x04.common.action import ListOfActions |
9
|
1 |
|
from pyof.v0x04.common.header import Header, Type |
10
|
1 |
|
from pyof.v0x04.common.port import PortNo |
11
|
|
|
|
12
|
1 |
|
__all__ = ('PacketOut',) |
13
|
|
|
|
14
|
|
|
# Classes |
15
|
|
|
|
16
|
|
|
#: in_port valid virtual port values, for validation |
17
|
1 |
|
_VIRT_IN_PORTS = (PortNo.OFPP_LOCAL, PortNo.OFPP_CONTROLLER, PortNo.OFPP_ANY) |
18
|
|
|
|
19
|
|
|
|
20
|
1 |
View Code Duplication |
class PacketOut(GenericMessage): |
|
|
|
|
21
|
|
|
"""Send packet (controller -> datapath).""" |
22
|
|
|
|
23
|
|
|
#: Openflow :class:`~pyof.v0x04.common.header.Header` |
24
|
1 |
|
header = Header(message_type=Type.OFPT_PACKET_OUT) |
25
|
|
|
#: ID assigned by datapath (OFP_NO_BUFFER if none). |
26
|
1 |
|
buffer_id = UBInt32() |
27
|
|
|
#: Packet’s input port or OFPP_CONTROLLER. |
28
|
1 |
|
in_port = UBInt32() |
29
|
|
|
#: Size of action array in bytes. |
30
|
1 |
|
actions_len = UBInt16() |
31
|
|
|
#: Padding |
32
|
1 |
|
pad = Pad(6) |
33
|
|
|
#: Action List. |
34
|
1 |
|
actions = ListOfActions() |
35
|
|
|
#: Packet data. The length is inferred from the length field in the header. |
36
|
|
|
#: (Only meaningful if buffer_id == -1.) |
37
|
1 |
|
data = BinaryData() |
38
|
|
|
|
39
|
1 |
|
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
|
1 |
|
super().__init__(xid) |
58
|
1 |
|
self.buffer_id = buffer_id |
59
|
1 |
|
self.in_port = in_port |
60
|
1 |
|
self.actions = [] if actions is None else actions |
61
|
1 |
|
self.data = data |
62
|
|
|
|
63
|
1 |
|
def validate(self): |
64
|
|
|
"""Validate the entire message.""" |
65
|
1 |
|
if not super().is_valid(): |
66
|
|
|
raise ValidationError() |
67
|
1 |
|
self._validate_in_port() |
68
|
|
|
|
69
|
1 |
|
def is_valid(self): |
70
|
|
|
"""Answer if this message is valid.""" |
71
|
1 |
|
try: |
72
|
1 |
|
self.validate() |
73
|
1 |
|
return True |
74
|
|
|
except ValidationError: |
75
|
|
|
return False |
76
|
|
|
|
77
|
1 |
|
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
|
1 |
|
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
|
1 |
|
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
|
1 |
|
def _validate_in_port(self): |
125
|
1 |
|
is_valid_range = self.in_port > 0 and self.in_port <= PortNo.OFPP_MAX |
126
|
1 |
|
is_valid_virtual_in_ports = self.in_port in _VIRT_IN_PORTS |
127
|
|
|
|
128
|
1 |
|
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
|
|
|
|