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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
|
"""Defines common structures and enums for controller2switch."""
# System imports
from enum import IntEnum
from pyof.foundation.base import GenericMessage, GenericStruct
from pyof.foundation.basic_types import (
Char, FixedTypeList, Pad, UBInt8, UBInt16, UBInt32, UBInt64)
from pyof.foundation.constants import OFP_MAX_TABLE_NAME_LEN
from pyof.v0x04.asynchronous.flow_removed import FlowRemovedReason
from pyof.v0x04.asynchronous.packet_in import PacketInReason
from pyof.v0x04.asynchronous.port_status import PortReason
# Local source tree imports
from pyof.v0x04.common.action import (
ActionHeader, ControllerMaxLen, ListOfActions)
from pyof.v0x04.common.flow_instructions import ListOfInstruction
from pyof.v0x04.common.flow_match import ListOfOxmHeader
from pyof.v0x04.common.header import Header
from pyof.v0x04.controller2switch.table_mod import Table
__all__ = ('ConfigFlag', 'ControllerRole', 'TableFeaturePropType',
'MultipartType', 'Bucket', 'BucketCounter', 'ListOfBucketCounter',
'ExperimenterMultipartHeader', 'Property', 'InstructionsProperty',
'NextTablesProperty', 'ActionsProperty', 'OxmProperty',
'ListOfProperty', 'TableFeatures')
# Enum
class ConfigFlag(IntEnum):
"""Handling of IP fragments."""
#: No special handling for fragments.
OFPC_FRAG_NORMAL = 0
#: Drop fragments.
OFPC_FRAG_DROP = 1
#: Reassemble (only if OFPC_IP_REASM set).
OFPC_FRAG_REASM = 2
OFPC_FRAG_MASK = 3
class ControllerRole(IntEnum):
"""Controller roles."""
#: Don’t change current role.
OFPCR_ROLE_NOCHANGE = 0
#: Default role, full access.
OFPCR_ROLE_EQUAL = 1
#: Full access, at most one master.
OFPCR_ROLE_MASTER = 2
#: Read-only access.
OFPCR_ROLE_SLAVE = 3
class TableFeaturePropType(IntEnum):
"""Table Property Types.
Low order bit cleared indicates a property for a regular Flow Entry.
Low order bit set indicates a property for the Table-Miss Flow Entry.
"""
# Instructions property.
OFPTFPT_INSTRUCTIONS = 0
# Instructions for table-miss.
OFPTFPT_INSTRUCTIONS_MISS = 1
# Next Table property.
OFPTFPT_NEXT_TABLES = 2
# Next Table for table-miss.
OFPTFPT_NEXT_TABLES_MISS = 3
# Write Actions property.
OFPTFPT_WRITE_ACTIONS = 4
# Write Actions for table-miss.
OFPTFPT_WRITE_ACTIONS_MISS = 5
# Apply Actions property.
OFPTFPT_APPLY_ACTIONS = 6
# Apply Actions for table-miss.
OFPTFPT_APPLY_ACTIONS_MISS = 7
# Match property.
OFPTFPT_MATCH = 8
# Wildcards property.
OFPTFPT_WILDCARDS = 10
# Write Set-Field property.
OFPTFPT_WRITE_SETFIELD = 12
# Write Set-Field for table-miss.
OFPTFPT_WRITE_SETFIELD_MISS = 13
# Apply Set-Field property.
OFPTFPT_APPLY_SETFIELD = 14
# Apply Set-Field for table-miss.
OFPTFPT_APPLY_SETFIELD_MISS = 15
# Experimenter property.
OFPTFPT_EXPERIMENTER = 0xFFFE
# Experimenter for table-miss.
OFPTFPT_EXPERIMENTER_MISS = 0xFFFF
# pylint: disable=comparison-with-callable
def find_class(self):
"""Return a class related with this type."""
if self.value <= 1:
return InstructionsProperty
if self.value <= 3:
return NextTablesProperty
if self.value <= 7:
return ActionsProperty
return OxmProperty
class MultipartType(IntEnum):
"""Types of Multipart Messages, both Request and Reply."""
#: Description of this OpenFlow switch.
#: The request body is empty.
#: The reply body is struct ofp_desc.
OFPMP_DESC = 0
#: Individual flow statistics.
#: The request body is struct ofp_flow_stats_request.
#: The reply body is an array of struct ofp_flow_stats.
OFPMP_FLOW = 1
#: Aggregate flow statistics.
#: The request body is struct ofp_aggregate_stats_request.
#: The reply body is struct ofp_aggregate_stats_reply.
OFPMP_AGGREGATE = 2
#: Flow table statistics.
#: The request body is empty.
#: The reply body is an array of struct ofp_table_stats.
OFPMP_TABLE = 3
#: Port statistics.
#: The request body is struct ofp_port_stats_request.
#: The reply body is an array of struct ofp_port_stats.
OFPMP_PORT_STATS = 4
#: Queue statistics for a port.
#: The request body is struct ofp_queue_stats_request.
#: The reply body is an array of struct ofp_queue_stats.
OFPMP_QUEUE = 5
#: Group counter statistics.
#: The request body is struct ofp_group_stats_request.
#: The reply is an array of struct ofp_group_stats.
OFPMP_GROUP = 6
#: Group description.
#: The request body is empty.
#: The reply body is an array of struct ofp_group_desc_stats.
OFPMP_GROUP_DESC = 7
#: Group features.
#: The request body is empty.
#: The reply body is struct ofp_group_features.
OFPMP_GROUP_FEATURES = 8
#: Meter statistics.
#: The request body is struct ofp_meter_multipart_requests.
#: The reply body is an array of struct ofp_meter_stats.
OFPMP_METER = 9
#: Meter configuration.
#: The request body is struct ofp_meter_multipart_requests.
#: The reply body is an array of struct ofp_meter_config.
OFPMP_METER_CONFIG = 10
#: Meter features.
#: The request body is empty.
#: The reply body is struct ofp_meter_features.
OFPMP_METER_FEATURES = 11
#: Table features.
#: The request body is either empty or contains an array of
#: struct ofp_table_features containing the controller’s desired view of
#: the switch. If the switch is unable to set the specified view an error
#: is returned.
#: The reply body is an array of struct ofp_table_features.
OFPMP_TABLE_FEATURES = 12
#: Port description.
#: The request body is empty.
#: The reply body is an array of struct ofp_port.
OFPMP_PORT_DESC = 13
#: Experimenter extension.
#: The request and reply bodies begin with
#: struct ofp_experimenter_multipart_header.
#: The request and reply bodies are otherwise experimenter-defined.
OFPMP_EXPERIMENTER = 0xffff
# Classes
class Bucket(GenericStruct):
"""Bucket for use in groups."""
length = UBInt16()
weight = UBInt16()
watch_port = UBInt32()
watch_group = UBInt32()
pad = Pad(4)
actions = FixedTypeList(ActionHeader)
def __init__(self, length=None, weight=None, watch_port=None,
watch_group=None, actions=None):
"""Initialize all instance variables.
Args:
length (int): Length the bucket in bytes, including this header and
any padding to make it 64-bit aligned.
weight (int): Relative weight of bucket. Only defined for select
groups.
watch_port (int): Port whose state affects whether this bucket is
live. Only required for fast failover groups.
watch_group (int): Group whose state affects whether this bucket is
live. Only required for fast failover groups.
actions (~pyof.v0x04.common.action.ListOfActions): The action
length is inferred from the length field in the header.
"""
super().__init__()
self.length = length
self.weight = weight
self.watch_port = watch_port
self.watch_group = watch_group
self.actions = actions
def __repr__(self):
return f"{type(self).__name__}(actions={self.actions!r})"
def unpack(self, buff, offset=0):
"""Unpack bucket.
Bucket has a dynamic content with length as first field.
The length is needed to compute the total buffer offset.
"""
length = UBInt16()
length.unpack(buff, offset=offset)
super().unpack(buff[:offset + length.value], offset=offset)
def get_size(self, value=None):
"""
Return the Bucket length.
If the object length is None, returns the minimum size.
"""
if self.length is None:
return super().get_size()
return self.length
class BucketCounter(GenericStruct):
"""Used in group stats replies."""
#: Number of packets processed by bucket.
packet_count = UBInt64()
#: Number of bytes processed by bucket.
byte_count = UBInt64()
def __init__(self, packet_count=None, byte_count=None):
"""Create BucketCounter with the optional parameters below.
Args:
packet_count (int): Number of packets processed by bucket.
byte_count (int): Number of bytes processed by bucket.
"""
super().__init__()
self.packet_count = packet_count
self.byte_count = byte_count
class ListOfBucketCounter(FixedTypeList):
"""List of BucketCounter.
Represented by instances of BucketCounter.
"""
def __init__(self, items=None):
"""Create a ListOfBucketCounter with the optional parameters below.
Args:
items (|BucketCounter_v0x04|): Instance or a list of instances.
"""
super().__init__(pyof_class=BucketCounter, items=items)
# Base Classes for other messages - not meant to be directly used, so, because
# of that, they will not be inserted on the __all__ attribute.
class AsyncConfig(GenericMessage):
"""Asynchronous message configuration base class.
Common structure for SetAsync and GetAsyncReply messages.
AsyncConfig contains three 2-element arrays. Each array controls whether
the controller receives asynchronous messages with a specific
:class:`~pyof.v0x04.common.header.Type`. Within each array, element
0 specifies messages of interest when the controller has a OFPCR_ROLE_EQUAL
or OFPCR_ROLE_MASTER role; element 1, when the controller has a
OFPCR_ROLE_SLAVE role. Each array element is a bit-mask in which a 0-bit
disables receiving a message sent with the reason code corresponding to the
bit index and a 1-bit enables receiving it.
"""
#: OpenFlow :class:`~pyof.v0x04.common.header.Header`
#: OFPT_GET_ASYNC_REPLY or OFPT_SET_ASYNC.
header = Header()
packet_in_mask1 = UBInt32(enum_ref=PacketInReason)
packet_in_mask2 = UBInt32(enum_ref=PacketInReason)
port_status_mask1 = UBInt32(enum_ref=PortReason)
port_status_mask2 = UBInt32(enum_ref=PortReason)
flow_removed_mask1 = UBInt32(enum_ref=FlowRemovedReason)
flow_removed_mask2 = UBInt32(enum_ref=FlowRemovedReason)
def __init__(self, xid=None, packet_in_mask1=None, packet_in_mask2=None,
port_status_mask1=None, port_status_mask2=None,
flow_removed_mask1=None, flow_removed_mask2=None):
"""Create a AsyncConfig with the optional parameters below.
Args:
xid (int): xid to be used on the message header.
packet_in_mask1
(~pyof.v0x04.asynchronous.packet_in.PacketInReason):
A instance of PacketInReason
packet_in_mask2
(~pyof.v0x04.asynchronous.packet_in.PacketInReason):
A instance of PacketInReason
port_status_mask1
(~pyof.v0x04.asynchronous.port_status.PortReason):
A instance of PortReason
port_status_mask2
(~pyof.v0x04.asynchronous.port_status.PortReason):
A instance of PortReason
flow_removed_mask1
(~pyof.v0x04.asynchronous.flow_removed.FlowRemoved):
A instance of FlowRemoved.
flow_removed_mask2
(~pyof.v0x04.asynchronous.flow_removed.FlowRemoved):
A instance of FlowRemoved.
"""
super().__init__(xid)
self.packet_in_mask1 = packet_in_mask1
self.packet_in_mask2 = packet_in_mask2
self.port_status_mask1 = port_status_mask1
self.port_status_mask2 = port_status_mask2
self.flow_removed_mask1 = flow_removed_mask1
self.flow_removed_mask2 = flow_removed_mask2
class RoleBaseMessage(GenericMessage):
"""Role basic structure for RoleRequest and RoleReply messages."""
#: :class:`~pyof.v0x04.common.header.Header`
#: Type OFPT_ROLE_REQUEST/OFPT_ROLE_REPLY.
header = Header()
#: One of NX_ROLE_*. (:class:`~.controller2switch.common.ControllerRole`)
role = UBInt32(enum_ref=ControllerRole)
#: Align to 64 bits.
pad = Pad(4)
#: Master Election Generation Id.
generation_id = UBInt64()
def __init__(self, xid=None, role=None, generation_id=None):
"""Create a RoleBaseMessage with the optional parameters below.
Args:
xid (int): OpenFlow xid to the header.
role (:class:`~.controller2switch.common.ControllerRole`): .
generation_id (int): Master Election Generation Id.
"""
super().__init__(xid)
self.role = role
self.generation_id = generation_id
class SwitchConfig(GenericMessage):
"""Used as base class for SET_CONFIG and GET_CONFIG_REPLY messages."""
#: OpenFlow :class:`~pyof.v0x04.common.header.Header`
header = Header()
flags = UBInt16(enum_ref=ConfigFlag)
miss_send_len = UBInt16()
def __init__(self, xid=None, flags=ConfigFlag.OFPC_FRAG_NORMAL,
miss_send_len=ControllerMaxLen.OFPCML_NO_BUFFER):
"""Create a SwitchConfig with the optional parameters below.
Args:
xid (int): xid to be used on the message header.
flags (ConfigFlag): OFPC_* flags.
miss_send_len (int): UBInt16 max bytes of new flow that the
datapath should send to the controller.
"""
super().__init__(xid)
self.flags = flags
self.miss_send_len = miss_send_len
# Multipart body
class ExperimenterMultipartHeader(GenericStruct):
"""Body for ofp_multipart_request/reply of type OFPMP_EXPERIMENTER."""
experimenter = UBInt32()
exp_type = UBInt32()
#: Followed by experimenter-defined arbitrary data.
def __init__(self, experimenter=None, exp_type=None):
"""Create a ExperimenterMultipartHeader with the parameters below.
Args:
experimenter: Experimenter ID which takes the same form as in
struct ofp_experimenter_header (
:class:`~pyof.v0x04.symmetric.experimenter.ExperimenterHeader`)
exp_type: Experimenter defined.
"""
super().__init__()
self.experimenter = experimenter
self.exp_type = exp_type
class Property(GenericStruct):
"""Table Property class.
This class represents a Table Property generic structure.
"""
property_type = UBInt16(enum_ref=TableFeaturePropType)
length = UBInt16(4)
def __init__(self, property_type=None):
"""Create a Property with the optional parameters below.
Args:
type(|TableFeaturePropType_v0x04|):
Property Type value of this instance.
"""
super().__init__()
self.property_type = property_type
def pack(self, value=None):
"""Pack method used to update the length of instance and packing.
Args:
value: Structure to be packed.
"""
self.update_length()
return super().pack(value)
def unpack(self, buff=None, offset=0):
"""Unpack *buff* into this object.
This method will convert a binary data into a readable value according
to the attribute format.
Args:
buff (bytes): Binary buffer.
offset (int): Where to begin unpacking.
Raises:
:exc:`~.exceptions.UnpackException`: If unpack fails.
"""
property_type = UBInt16(enum_ref=TableFeaturePropType)
property_type.unpack(buff, offset)
self.__class__ = TableFeaturePropType(property_type.value).find_class()
length = UBInt16()
length.unpack(buff, offset=offset+2)
super().unpack(buff[:offset+length.value], offset=offset)
def update_length(self):
"""Update the length of current instance."""
self.length = self.get_size()
class InstructionsProperty(Property):
"""Instructions property.
This class represents Property with the following types:
OFPTFPT_INSTRUCTIONS
OFPTFPT_INSTRUCTIONS_MISS
"""
instruction_ids = ListOfInstruction()
def __init__(self, property_type=TableFeaturePropType.OFPTFPT_INSTRUCTIONS,
instruction_ids=None):
"""Create a InstructionsProperty with the optional parameters below.
Args:
type(|TableFeaturePropType_v0x04|):
Property Type value of this instance.
next_table_ids(|ListOfInstruction_v0x04|):
List of InstructionGotoTable instances.
"""
super().__init__(property_type=property_type)
self.instruction_ids = instruction_ids if instruction_ids else []
self.update_length()
class NextTablesProperty(Property):
"""Next Tables Property.
This class represents Property with the following types:
OFPTFPT_NEXT_TABLES
OFPTFPT_NEXT_TABLES_MISS
"""
next_table_ids = ListOfInstruction()
def __init__(self, property_type=TableFeaturePropType.OFPTFPT_NEXT_TABLES,
next_table_ids=None):
"""Create a NextTablesProperty with the optional parameters below.
Args:
type(|TableFeaturePropType_v0x04|):
Property Type value of this instance.
next_table_ids (|ListOfInstruction_v0x04|):
List of InstructionGotoTable instances.
"""
super().__init__(property_type)
self.next_table_ids = (ListOfInstruction() if next_table_ids is None
else next_table_ids)
self.update_length()
class ActionsProperty(Property):
"""Actions Property.
This class represents Property with the following type:
OFPTFPT_WRITE_ACTIONS
OFPTFPT_WRITE_ACTIONS_MISS
OFPTFPT_APPLY_ACTIONS
OFPTFPT_APPLY_ACTIONS_MISS
"""
action_ids = ListOfActions()
def __init__(self,
property_type=TableFeaturePropType.OFPTFPT_WRITE_ACTIONS,
action_ids=None):
"""Create a ActionsProperty with the optional parameters below.
Args:
type(|TableFeaturePropType_v0x04|):
Property Type value of this instance.
action_ids(|ListOfActions_v0x04|):
List of Action instances.
"""
super().__init__(property_type)
self.action_ids = action_ids if action_ids else ListOfActions()
self.update_length()
class OxmProperty(Property):
"""Match, Wildcard or Set-Field property.
This class represents Property with the following types:
OFPTFPT_MATCH
OFPTFPT_WILDCARDS
OFPTFPT_WRITE_SETFIELD
OFPTFPT_WRITE_SETFIELD_MISS
OFPTFPT_APPLY_SETFIELD
OFPTFPT_APPLY_SETFIELD_MISS
"""
oxm_ids = ListOfOxmHeader()
def __init__(self, property_type=TableFeaturePropType.OFPTFPT_MATCH,
oxm_ids=None):
"""Create an OxmProperty with the optional parameters below.
Args:
type(|TableFeaturePropType_v0x04|):
Property Type value of this instance.
oxm_ids(|ListOfOxmHeader_v0x04|):
List of OxmHeader instances.
"""
super().__init__(property_type)
self.oxm_ids = ListOfOxmHeader() if oxm_ids is None else oxm_ids
self.update_length()
class ListOfProperty(FixedTypeList):
"""List of Table Property.
Represented by instances of Property.
"""
def __init__(self, items=None):
"""Create a ListOfProperty with the optional parameters below.
Args:
items (|Property_v0x04|): Instance or a list of instances.
"""
super().__init__(pyof_class=Property, items=items)
class TableFeatures(GenericStruct):
"""Abstraction of common class Table Features.
Body for MultipartRequest of type OFPMP_TABLE_FEATURES.
Body of reply to OFPMP_TABLE_FEATURES request.
"""
length = UBInt16()
# /* Identifier of table. Lower numbered tables are consulted first. */
table_id = UBInt8()
# /* Align to 64-bits. */
pad = Pad(5)
name = Char(length=OFP_MAX_TABLE_NAME_LEN)
# /* Bits of metadata table can match. */
metadata_match = UBInt64()
# /* Bits of metadata table can write. */
metadata_write = UBInt64()
# /* Bitmap of OFPTC_* values */
config = UBInt32()
# /* Max number of entries supported. */
max_entries = UBInt32()
# /* Table Feature Property list */
properties = ListOfProperty()
def __init__(self, table_id=Table.OFPTT_ALL, name="",
metadata_match=0xFFFFFFFFFFFFFFFF,
metadata_write=0xFFFFFFFFFFFFFFFF,
config=0,
max_entries=0,
properties=None):
"""Create a TableFeatures with the optional parameters below.
Args:
table_id(int): Indetifier of table.The default value
OFPTT_ALL(``0xff``) will apply the configuration to all tables
in the switch.
name(Char): Characters representing the table name.
metadata_match(int): Indicate the bits of the metadata field that
the table can match on.The default value ``0xFFFFFFFFFFFFFFFF``
indicates that the table can match the full metadata field.
metadata_write(int): Indicates the bits of the metadata field that
the table can write using the OFPIT_WRITE_METADATA instruction.
The default value ``0xFFFFFFFFFFFFFFFF`` indicates that the
table can write the full metadata field.
config(int): Field reseved for future use.
max_entries(int): Describe the maximum number of flow entries that
can be inserted into that table.
properties(~pyof.v0x04.controller2switch.common.ListOfProperty):
List of Property intances.
"""
super().__init__()
self.table_id = table_id
self.name = name
self.metadata_match = metadata_match
self.metadata_write = metadata_write
self.config = config
self.max_entries = max_entries
self.properties = (ListOfProperty() if properties is None else
properties)
self.update_length()
def pack(self, value=None):
"""Pack method used to update the length of instance and packing.
Args:
value: Structure to be packed.
"""
self.update_length()
return super().pack(value)
def update_length(self):
"""Update the length of current instance."""
self.length = self.get_size()
def unpack(self, buff=None, offset=0):
"""Unpack *buff* into this object.
This method will convert a binary data into a readable value according
to the attribute format.
Args:
buff (bytes): Binary buffer.
offset (int): Where to begin unpacking.
Raises:
:exc:`~.exceptions.UnpackException`: If unpack fails.
"""
length = UBInt16()
length.unpack(buff, offset)
super().unpack(buff[:offset+length.value], offset)
|