1 | """Defines common structures and enums for controller2switch.""" |
||
2 | |||
3 | # System imports |
||
4 | from enum import IntEnum |
||
5 | |||
6 | from pyof.foundation.base import GenericMessage, GenericStruct |
||
7 | from pyof.foundation.basic_types import ( |
||
8 | BinaryData, Char, Pad, UBInt8, UBInt16, UBInt32, UBInt64) |
||
9 | from pyof.foundation.constants import ( |
||
10 | DESC_STR_LEN, OFP_MAX_TABLE_NAME_LEN, SERIAL_NUM_LEN) |
||
11 | # Local source tree imports |
||
12 | from pyof.v0x01.common.action import ListOfActions |
||
13 | from pyof.v0x01.common.flow_match import FlowWildCards, Match |
||
14 | from pyof.v0x01.common.header import Header |
||
15 | from pyof.v0x01.common.phy_port import Port |
||
16 | |||
17 | # Third-party imports |
||
18 | |||
19 | __all__ = ('ConfigFlag', 'StatsType', 'AggregateStatsReply', |
||
20 | 'AggregateStatsRequest', 'DescStats', 'FlowStats', |
||
21 | 'FlowStatsRequest', 'PortStats', 'PortStatsRequest', 'QueueStats', |
||
22 | 'QueueStatsRequest', 'TableStats', 'VendorStats', |
||
23 | 'VendorStatsRequest') |
||
24 | |||
25 | # Enums |
||
26 | |||
27 | |||
28 | class ConfigFlag(IntEnum): |
||
29 | """Configuration Flags. Handling of IP Fragments.""" |
||
30 | |||
31 | #: No special handling for fragments |
||
32 | OFPC_FRAG_NORMAL = 0 |
||
33 | #: Drop fragments |
||
34 | OFPC_FRAG_DROP = 1 |
||
35 | #: Reassemble (only if OFPC_IP_REASM set) |
||
36 | OFPC_FRAG_REASM = 2 |
||
37 | OFPC_FRAG_MASK = 3 |
||
38 | |||
39 | |||
40 | class StatsType(IntEnum): |
||
41 | """Type field to be used both in both request and reply. |
||
42 | |||
43 | It specifies the kind of information being passed and determines how the |
||
44 | body field is interpreted. |
||
45 | """ |
||
46 | |||
47 | #: Description of this OpenFlow switch. The request body is empty. |
||
48 | OFPST_DESC = 0 |
||
49 | #: Individual flow statistics. The request body is struct |
||
50 | #: ofp_flow_stats_request. |
||
51 | OFPST_FLOW = 1 |
||
52 | #: Aggregate flow statistics. The request body is struct |
||
53 | #: ofp_aggregate_stats_request. |
||
54 | OFPST_AGGREGATE = 2 |
||
55 | #: Flow table statistics. The request body is empty. |
||
56 | OFPST_TABLE = 3 |
||
57 | #: Physical port statistics. The request body is empty. |
||
58 | OFPST_PORT = 4 |
||
59 | #: Queue statistics for a port. The request body defines the port |
||
60 | OFPST_QUEUE = 5 |
||
61 | #: Vendor extension. The request and reply bodies begin with a 32-bit |
||
62 | #: vendor ID |
||
63 | OFPST_VENDOR = 0xffff |
||
64 | |||
65 | |||
66 | # Classes |
||
67 | |||
68 | |||
69 | class SwitchConfig(GenericMessage): |
||
70 | """Used as base class for SET_CONFIG and GET_CONFIG_REPLY messages.""" |
||
71 | |||
72 | header = Header() |
||
73 | flags = UBInt16(enum_ref=ConfigFlag) |
||
74 | miss_send_len = UBInt16() |
||
75 | |||
76 | def __init__(self, xid=None, flags=None, miss_send_len=None): |
||
77 | """Create a SwitchConfig with the optional parameters below. |
||
78 | |||
79 | Args: |
||
80 | xid (int): xid to be used on the message header. |
||
81 | flags (ConfigFlag): OFPC_* flags. |
||
82 | miss_send_len (int): UBInt16 max bytes of new flow that the |
||
83 | datapath should send to the controller. |
||
84 | """ |
||
85 | super().__init__(xid) |
||
86 | self.flags = flags |
||
87 | self.miss_send_len = miss_send_len |
||
88 | |||
89 | def __repr__(self): |
||
90 | """Show a full representation of the object.""" |
||
91 | return "%s(xid=%r, flags=%s, miss_send_len=%r)" \ |
||
92 | % (self.__class__.__name__, self.header.xid, self.flags, |
||
93 | self.miss_send_len) |
||
94 | |||
95 | |||
96 | class AggregateStatsReply(GenericStruct): |
||
97 | """Body of reply to OFPST_AGGREGATE request.""" |
||
98 | |||
99 | packet_count = UBInt64() |
||
100 | byte_count = UBInt64() |
||
101 | flow_count = UBInt32() |
||
102 | #: Align to 64 bits |
||
103 | pad = Pad(4) |
||
104 | |||
105 | def __init__(self, packet_count=None, byte_count=None, flow_count=None): |
||
106 | """Create a AggregateStatsReply with the optional parameters below. |
||
107 | |||
108 | Args: |
||
109 | packet_count (int): Number of packets in flows |
||
110 | byte_count (int): Number of bytes in flows |
||
111 | flow_count (int): Number of flows |
||
112 | """ |
||
113 | super().__init__() |
||
114 | self.packet_count = packet_count |
||
115 | self.byte_count = byte_count |
||
116 | self.flow_count = flow_count |
||
117 | |||
118 | |||
119 | class AggregateStatsRequest(GenericStruct): |
||
120 | """Body for ofp_stats_request of type OFPST_AGGREGATE.""" |
||
121 | |||
122 | match = Match() |
||
123 | table_id = UBInt8() |
||
124 | #: Align to 32 bits |
||
125 | pad = Pad(1) |
||
126 | out_port = UBInt16() |
||
127 | |||
128 | def __init__(self, match=Match(), table_id=0xff, out_port=Port.OFPP_NONE): |
||
129 | """Create a AggregateStatsRequest with the optional parameters below. |
||
130 | |||
131 | Args: |
||
132 | match (~pyof.v0x01.common.flow_match.Match): Fields to match. |
||
133 | table_id (int): ID of table to read (from pyof_table_stats) 0xff |
||
134 | for all tables or 0xfe for emergency. |
||
135 | out_port (int): Require matching entries to include this as an |
||
136 | output port. A value of OFPP_NONE indicates no restriction. |
||
137 | """ |
||
138 | super().__init__() |
||
139 | self.match = match |
||
140 | self.table_id = table_id |
||
141 | self.out_port = out_port |
||
142 | |||
143 | |||
144 | View Code Duplication | class DescStats(GenericStruct): |
|
0 ignored issues
–
show
Duplication
introduced
by
![]() |
|||
145 | """Information available from the OFPST_DESC stats request. |
||
146 | |||
147 | Information about the switch manufacturer, hardware revision, software |
||
148 | revision, serial number and a description field. |
||
149 | """ |
||
150 | |||
151 | mfr_desc = Char(length=DESC_STR_LEN) |
||
152 | hw_desc = Char(length=DESC_STR_LEN) |
||
153 | sw_desc = Char(length=DESC_STR_LEN) |
||
154 | serial_num = Char(length=SERIAL_NUM_LEN) |
||
155 | dp_desc = Char(length=DESC_STR_LEN) |
||
156 | |||
157 | def __init__(self, mfr_desc=None, hw_desc=None, sw_desc=None, |
||
158 | serial_num=None, dp_desc=None): |
||
159 | """Create a DescStats with the optional parameters below. |
||
160 | |||
161 | Args: |
||
162 | mfr_desc (str): Manufacturer description |
||
163 | hw_desc (str): Hardware description |
||
164 | sw_desc (str): Software description |
||
165 | serial_num (str): Serial number |
||
166 | dp_desc (str): Human readable description of datapath |
||
167 | """ |
||
168 | super().__init__() |
||
169 | self.mfr_desc = mfr_desc |
||
170 | self.hw_desc = hw_desc |
||
171 | self.sw_desc = sw_desc |
||
172 | self.serial_num = serial_num |
||
173 | self.dp_desc = dp_desc |
||
174 | |||
175 | |||
176 | View Code Duplication | class FlowStats(GenericStruct): |
|
0 ignored issues
–
show
|
|||
177 | """Body of reply to OFPST_FLOW request.""" |
||
178 | |||
179 | length = UBInt16() |
||
180 | table_id = UBInt8() |
||
181 | #: Align to 32 bits. |
||
182 | pad = Pad(1) |
||
183 | match = Match() |
||
184 | duration_sec = UBInt32() |
||
185 | duration_nsec = UBInt32() |
||
186 | priority = UBInt16() |
||
187 | idle_timeout = UBInt16() |
||
188 | hard_timeout = UBInt16() |
||
189 | #: Align to 64-bits |
||
190 | pad2 = Pad(6) |
||
191 | cookie = UBInt64() |
||
192 | packet_count = UBInt64() |
||
193 | byte_count = UBInt64() |
||
194 | actions = ListOfActions() |
||
195 | |||
196 | def __init__(self, length=None, table_id=None, match=None, |
||
197 | duration_sec=None, duration_nsec=None, priority=None, |
||
198 | idle_timeout=None, hard_timeout=None, cookie=None, |
||
199 | packet_count=None, byte_count=None, actions=None): |
||
200 | """Create a FlowStats with the optional parameters below. |
||
201 | |||
202 | Args: |
||
203 | length (int): Length of this entry. |
||
204 | table_id (int): ID of table flow came from. |
||
205 | match (~pyof.v0x01.common.flow_match.Match): Description of fields. |
||
206 | duration_sec (int): Time flow has been alive in seconds. |
||
207 | duration_nsec (int): Time flow has been alive in nanoseconds in |
||
208 | addition to duration_sec. |
||
209 | priority (int): Priority of the entry. Only meaningful when this |
||
210 | is not an exact-match entry. |
||
211 | idle_timeout (int): Number of seconds idle before expiration. |
||
212 | hard_timeout (int): Number of seconds before expiration. |
||
213 | cookie (int): Opaque controller-issued identifier. |
||
214 | packet_count (int): Number of packets in flow. |
||
215 | byte_count (int): Number of bytes in flow. |
||
216 | actions (:class:`~pyof.v0x01.common.actions.ListOfActions`): |
||
217 | List of Actions. |
||
218 | """ |
||
219 | super().__init__() |
||
220 | self.length = length |
||
221 | self.table_id = table_id |
||
222 | self.match = match |
||
223 | self.duration_sec = duration_sec |
||
224 | self.duration_nsec = duration_nsec |
||
225 | self.priority = priority |
||
226 | self.idle_timeout = idle_timeout |
||
227 | self.hard_timeout = hard_timeout |
||
228 | self.cookie = cookie |
||
229 | self.packet_count = packet_count |
||
230 | self.byte_count = byte_count |
||
231 | self.actions = [] if actions is None else actions |
||
232 | |||
233 | def unpack(self, buff, offset=0): |
||
234 | """Unpack *buff* into this object. |
||
235 | |||
236 | Do nothing, since the _length is already defined and it is just a Pad. |
||
237 | Keep buff and offset just for compability with other unpack methods. |
||
238 | |||
239 | Args: |
||
240 | buff (bytes): Buffer where data is located. |
||
241 | offset (int): Where data stream begins. |
||
242 | """ |
||
243 | self.length = UBInt16() |
||
244 | self.length.unpack(buff, offset) |
||
245 | max_length = offset + self.length.value |
||
246 | super().unpack(buff[:max_length], offset) |
||
247 | |||
248 | |||
249 | class FlowStatsRequest(GenericStruct): |
||
250 | """Body for ofp_stats_request of type OFPST_FLOW.""" |
||
251 | |||
252 | match = Match() |
||
253 | table_id = UBInt8() |
||
254 | #: Align to 32 bits. |
||
255 | pad = Pad(1) |
||
256 | out_port = UBInt16() |
||
257 | |||
258 | def __init__(self, match=None, table_id=0xff, out_port=Port.OFPP_NONE): |
||
259 | """Create a FlowStatsRequest with the optional parameters below. |
||
260 | |||
261 | Args: |
||
262 | match (:class:`~pyof.v0x01.common.flow_match.Match`): |
||
263 | Fields to match. |
||
264 | table_id (int): ID of table to read (from pyof_table_stats) |
||
265 | 0xff for all tables or 0xfe for emergency. |
||
266 | out_port (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): |
||
267 | Require matching entries to include this as an output port. |
||
268 | A value of :attr:`.Port.OFPP_NONE` indicates no restriction. |
||
269 | """ |
||
270 | super().__init__() |
||
271 | self.match = Match() if match is None else match |
||
272 | self.table_id = table_id |
||
273 | self.out_port = out_port |
||
274 | |||
275 | |||
276 | class PortStats(GenericStruct): |
||
277 | """Body of reply to OFPST_PORT request. |
||
278 | |||
279 | If a counter is unsupported, set the field to all ones. |
||
280 | """ |
||
281 | |||
282 | port_no = UBInt16() |
||
283 | #: Align to 64-bits. |
||
284 | pad = Pad(6) |
||
285 | rx_packets = UBInt64() |
||
286 | tx_packets = UBInt64() |
||
287 | rx_bytes = UBInt64() |
||
288 | tx_bytes = UBInt64() |
||
289 | rx_dropped = UBInt64() |
||
290 | tx_dropped = UBInt64() |
||
291 | rx_errors = UBInt64() |
||
292 | tx_errors = UBInt64() |
||
293 | rx_frame_err = UBInt64() |
||
294 | rx_over_err = UBInt64() |
||
295 | rx_crc_err = UBInt64() |
||
296 | collisions = UBInt64() |
||
297 | |||
298 | def __init__(self, port_no=None, rx_packets=None, |
||
299 | tx_packets=None, rx_bytes=None, tx_bytes=None, |
||
300 | rx_dropped=None, tx_dropped=None, rx_errors=None, |
||
301 | tx_errors=None, rx_frame_err=None, rx_over_err=None, |
||
302 | rx_crc_err=None, collisions=None): |
||
303 | """Create a PortStats with the optional parameters below. |
||
304 | |||
305 | Args: |
||
306 | port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): |
||
307 | Port number. |
||
308 | rx_packets (int): Number of received packets. |
||
309 | tx_packets (int): Number of transmitted packets. |
||
310 | rx_bytes (int): Number of received bytes. |
||
311 | tx_bytes (int): Number of transmitted bytes. |
||
312 | rx_dropped (int): Number of packets dropped by RX. |
||
313 | tx_dropped (int): Number of packets dropped by TX. |
||
314 | rx_errors (int): Number of receive errors. This is a super-set of |
||
315 | more specific receive errors and should be greater than or |
||
316 | equal to the sum of all rx_*_err values. |
||
317 | tx_errors (int): Number of transmit errors. This is a super-set of |
||
318 | more specific transmit errors and should be greater than or |
||
319 | equal to the sum of all tx_*_err values (none currently |
||
320 | defined). |
||
321 | rx_frame_err (int): Number of frame alignment errors. |
||
322 | rx_over_err (int): Number of packets with RX overrun. |
||
323 | rx_crc_err (int): Number of CRC errors. |
||
324 | collisions (int): Number of collisions. |
||
325 | """ |
||
326 | super().__init__() |
||
327 | self.port_no = port_no |
||
328 | self.rx_packets = rx_packets |
||
329 | self.tx_packets = tx_packets |
||
330 | self.rx_bytes = rx_bytes |
||
331 | self.tx_bytes = tx_bytes |
||
332 | self.rx_dropped = rx_dropped |
||
333 | self.tx_dropped = tx_dropped |
||
334 | self.rx_errors = rx_errors |
||
335 | self.tx_errors = tx_errors |
||
336 | self.rx_frame_err = rx_frame_err |
||
337 | self.rx_over_err = rx_over_err |
||
338 | self.rx_crc_err = rx_crc_err |
||
339 | self.collisions = collisions |
||
340 | |||
341 | |||
342 | class PortStatsRequest(GenericStruct): |
||
343 | """Body for ofp_stats_request of type OFPST_PORT.""" |
||
344 | |||
345 | port_no = UBInt16() |
||
346 | #: Align to 64-bits. |
||
347 | pad = Pad(6) |
||
348 | |||
349 | def __init__(self, port_no=Port.OFPP_NONE): |
||
350 | """Create a PortStatsRequest with the optional parameters below. |
||
351 | |||
352 | Args: |
||
353 | port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): |
||
354 | OFPST_PORT message must request statistics either for a single |
||
355 | port (specified in ``port_no``) or for all ports |
||
356 | (if ``port_no`` == :attr:`.Port.OFPP_NONE`). |
||
357 | """ |
||
358 | super().__init__() |
||
359 | self.port_no = port_no |
||
360 | |||
361 | |||
362 | class QueueStats(GenericStruct): |
||
363 | """Implements the reply body of a port_no.""" |
||
364 | |||
365 | port_no = UBInt16() |
||
366 | #: Align to 32-bits. |
||
367 | pad = Pad(2) |
||
368 | queue_id = UBInt32() |
||
369 | tx_bytes = UBInt64() |
||
370 | tx_packets = UBInt64() |
||
371 | tx_errors = UBInt64() |
||
372 | |||
373 | def __init__(self, port_no=None, queue_id=None, tx_bytes=None, |
||
374 | tx_packets=None, tx_errors=None): |
||
375 | """Create a QueueStats with the optional parameters below. |
||
376 | |||
377 | Args: |
||
378 | port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): |
||
379 | Port Number. |
||
380 | queue_id (int): Queue ID. |
||
381 | tx_bytes (int): Number of transmitted bytes. |
||
382 | tx_packets (int): Number of transmitted packets. |
||
383 | tx_errors (int): Number of packets dropped due to overrun. |
||
384 | """ |
||
385 | super().__init__() |
||
386 | self.port_no = port_no |
||
387 | self.queue_id = queue_id |
||
388 | self.tx_bytes = tx_bytes |
||
389 | self.tx_packets = tx_packets |
||
390 | self.tx_errors = tx_errors |
||
391 | |||
392 | |||
393 | class QueueStatsRequest(GenericStruct): |
||
394 | """Implements the request body of a ``port_no``.""" |
||
395 | |||
396 | port_no = UBInt16() |
||
397 | #: Align to 32-bits |
||
398 | pad = Pad(2) |
||
399 | queue_id = UBInt32() |
||
400 | |||
401 | def __init__(self, port_no=None, queue_id=None): |
||
402 | """Create a QueueStatsRequest with the optional parameters below. |
||
403 | |||
404 | Args: |
||
405 | port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): |
||
406 | All ports if :attr:`.Port.OFPP_ALL`. |
||
407 | queue_id (int): All queues if OFPQ_ALL (``0xfffffff``). |
||
408 | """ |
||
409 | super().__init__() |
||
410 | self.port_no = port_no |
||
411 | self.queue_id = queue_id |
||
412 | |||
413 | |||
414 | class TableStats(GenericStruct): |
||
415 | """Body of reply to OFPST_TABLE request.""" |
||
416 | |||
417 | table_id = UBInt8() |
||
418 | #: Align to 32-bits. |
||
419 | pad = Pad(3) |
||
420 | name = Char(length=OFP_MAX_TABLE_NAME_LEN) |
||
421 | wildcards = UBInt32(enum_ref=FlowWildCards) |
||
422 | max_entries = UBInt32() |
||
423 | active_count = UBInt32() |
||
424 | count_lookup = UBInt64() |
||
425 | count_matched = UBInt64() |
||
426 | |||
427 | def __init__(self, table_id=None, name=None, wildcards=None, |
||
428 | max_entries=None, active_count=None, count_lookup=None, |
||
429 | count_matched=None): |
||
430 | """Create a TableStats with the optional parameters below. |
||
431 | |||
432 | Args: |
||
433 | table_id (int): Identifier of table. Lower numbered tables are |
||
434 | consulted first. |
||
435 | name (str): Table name. |
||
436 | wildcards (:class:`~pyof.v0x01.common.flow_match.FlowWildCards`): |
||
437 | Bitmap of OFPFW_* wildcards that are supported by the table. |
||
438 | max_entries (int): Max number of entries supported. |
||
439 | active_count (int): Number of active entries. |
||
440 | count_lookup (int): Number of packets looked up in table. |
||
441 | count_matched (int): Number of packets that hit table. |
||
442 | """ |
||
443 | super().__init__() |
||
444 | self.table_id = table_id |
||
445 | self.name = name |
||
446 | self.wildcards = wildcards |
||
447 | self.max_entries = max_entries |
||
448 | self.active_count = active_count |
||
449 | self.count_lookup = count_lookup |
||
450 | self.count_matched = count_matched |
||
451 | |||
452 | |||
453 | class VendorStats(GenericStruct): |
||
454 | """Vendor extension.""" |
||
455 | |||
456 | vendor = UBInt32() |
||
457 | body = BinaryData() |
||
458 | |||
459 | def __init__(self, vendor=None, body=b''): |
||
460 | """Create instance attributes. |
||
461 | |||
462 | Args: |
||
463 | vendor (int): 32-bit vendor ID. |
||
464 | body (bytes): Vendor-defined body |
||
465 | """ |
||
466 | super().__init__() |
||
467 | self.vendor = vendor |
||
468 | self.body = body |
||
469 | |||
470 | |||
471 | VendorStatsRequest = VendorStats |
||
472 |