@@ 20-129 (lines=110) @@ | ||
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 | if isinstance(value, type(self)): |
|
73 | return value.pack() |
|
74 | msg = "{} is not an instance of {}".format(value, type(self).__name__) |
|
75 | raise PackException(msg) |
|
76 | ||
77 | def unpack(self, buff, offset=0): |
|
78 | """Unpack a binary message into this object's attributes. |
|
79 | ||
80 | Unpack the binary value *buff* and update this object attributes based |
|
81 | on the results. It is an inplace method and it receives the binary data |
|
82 | of the message **without the header**. |
|
83 | ||
84 | This class' unpack method is like the :meth:`.GenericMessage.unpack` |
|
85 | one, except for the ``actions`` attribute which has a length determined |
|
86 | by the ``actions_len`` attribute. |
|
87 | ||
88 | Args: |
|
89 | buff (bytes): Binary data package to be unpacked, without the |
|
90 | header. |
|
91 | offset (int): Where to begin unpacking. |
|
92 | """ |
|
93 | begin = offset |
|
94 | for attribute_name, class_attribute in self.get_class_attributes(): |
|
95 | if type(class_attribute).__name__ != "Header": |
|
96 | attribute = deepcopy(class_attribute) |
|
97 | if attribute_name == 'actions': |
|
98 | length = self.actions_len.value |
|
99 | attribute.unpack(buff[begin:begin+length]) |
|
100 | else: |
|
101 | attribute.unpack(buff, begin) |
|
102 | setattr(self, attribute_name, attribute) |
|
103 | begin += attribute.get_size() |
|
104 | ||
105 | def _update_actions_len(self): |
|
106 | """Update the actions_len field based on actions value.""" |
|
107 | if isinstance(self.actions, ListOfActions): |
|
108 | self.actions_len = self.actions.get_size() |
|
109 | else: |
|
110 | self.actions_len = ListOfActions(self.actions).get_size() |
|
111 | ||
112 | def _validate_in_port(self): |
|
113 | """Validate in_port attribute. |
|
114 | ||
115 | A valid port is either: |
|
116 | ||
117 | * Greater than 0 and less than or equals to Port.OFPP_MAX |
|
118 | * One of the valid virtual ports: Port.OFPP_LOCAL, |
|
119 | Port.OFPP_CONTROLLER or Port.OFPP_NONE |
|
120 | ||
121 | Raises: |
|
122 | ValidationError: If in_port is an invalid port. |
|
123 | ||
124 | """ |
|
125 | is_valid_range = self.in_port > 0 and self.in_port <= Port.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 |
@@ 20-127 (lines=108) @@ | ||
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 | if isinstance(value, type(self)): |
|
83 | return value.pack() |
|
84 | msg = "{} is not an instance of {}".format(value, type(self).__name__) |
|
85 | raise PackException(msg) |
|
86 | ||
87 | def unpack(self, buff, offset=0): |
|
88 | """Unpack a binary message into this object's attributes. |
|
89 | ||
90 | Unpack the binary value *buff* and update this object attributes based |
|
91 | on the results. It is an inplace method and it receives the binary data |
|
92 | of the message **without the header**. |
|
93 | ||
94 | This class' unpack method is like the :meth:`.GenericMessage.unpack` |
|
95 | one, except for the ``actions`` attribute which has a length determined |
|
96 | by the ``actions_len`` attribute. |
|
97 | ||
98 | Args: |
|
99 | buff (bytes): Binary data package to be unpacked, without the |
|
100 | header. |
|
101 | offset (int): Where to begin unpacking. |
|
102 | """ |
|
103 | begin = offset |
|
104 | for attribute_name, class_attribute in self.get_class_attributes(): |
|
105 | if type(class_attribute).__name__ != "Header": |
|
106 | attribute = deepcopy(class_attribute) |
|
107 | if attribute_name == 'actions': |
|
108 | length = self.actions_len.value |
|
109 | attribute.unpack(buff[begin:begin+length]) |
|
110 | else: |
|
111 | attribute.unpack(buff, begin) |
|
112 | setattr(self, attribute_name, attribute) |
|
113 | begin += attribute.get_size() |
|
114 | ||
115 | def _update_actions_len(self): |
|
116 | """Update the actions_len field based on actions value.""" |
|
117 | if isinstance(self.actions, ListOfActions): |
|
118 | self.actions_len = self.actions.get_size() |
|
119 | else: |
|
120 | self.actions_len = ListOfActions(self.actions).get_size() |
|
121 | ||
122 | def _validate_in_port(self): |
|
123 | is_valid_range = self.in_port > 0 and self.in_port <= PortNo.OFPP_MAX |
|
124 | is_valid_virtual_in_ports = self.in_port in _VIRT_IN_PORTS |
|
125 | ||
126 | if (is_valid_range or is_valid_virtual_in_ports) is False: |
|
127 | raise ValidationError(f'{self.in_port} is not a valid input port.') |
|
128 |