summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ryu/lib/packet/bpdu.py473
1 files changed, 473 insertions, 0 deletions
diff --git a/ryu/lib/packet/bpdu.py b/ryu/lib/packet/bpdu.py
new file mode 100644
index 00000000..8e31771b
--- /dev/null
+++ b/ryu/lib/packet/bpdu.py
@@ -0,0 +1,473 @@
+# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""
+Bridge Protocol Data Unit(BPDU, IEEE 802.1D) parser/serializer
+http://standards.ieee.org/getieee802/download/802.1D-2004.pdf
+
+
+Configuration BPDUs format
+
+ +----------------------------------------------+---------+
+ | Structure | Octet |
+ +==============================================+=========+
+ | Protocol Identifier = 0000 0000 0000 0000 | 1 - 2 |
+ | | |
+ +----------------------------------------------+---------+
+ | Protocol Version Identifier = 0000 0000 | 3 |
+ +----------------------------------------------+---------+
+ | BPDU Type = 0000 0000 | 4 |
+ +----------------------------------------------+---------+
+ | Flags | 5 |
+ +----------------------------------------------+---------+
+ | Root Identifier | 6 - 13 |
+ | include - priority | |
+ | system ID extension | |
+ | MAC address | |
+ +----------------------------------------------+---------+
+ | Root Path Cost | 14 - 17 |
+ | | |
+ +----------------------------------------------+---------+
+ | Bridge Identifier | 18 - 25 |
+ | include - priority | |
+ | system ID extension | |
+ | MAC address | |
+ +----------------------------------------------+---------+
+ | Port Identifier | 26 - 27 |
+ | include - priority | |
+ | port number | |
+ +----------------------------------------------+---------+
+ | Message Age | 28 - 29 |
+ | | |
+ +----------------------------------------------+---------+
+ | Max Age | 30 - 31 |
+ | | |
+ +----------------------------------------------+---------+
+ | Hello Time | 32 - 33 |
+ | | |
+ +----------------------------------------------+---------+
+ | Forward Delay | 34 - 35 |
+ | | |
+ +----------------------------------------------+---------+
+
+
+Topology Change NotificationBPDUs format
+
+ +----------------------------------------------+---------+
+ | Structure | Octet |
+ +==============================================+=========+
+ | Protocol Identifier = 0000 0000 0000 0000 | 1 - 2 |
+ | | |
+ +----------------------------------------------+---------+
+ | Protocol Version Identifier = 0000 0000 | 3 |
+ +----------------------------------------------+---------+
+ | BPDU Type = 1000 0000 | 4 |
+ +----------------------------------------------+---------+
+
+
+Rapid Spanning Tree BPDUs(RST BPDUs) format
+
+ +----------------------------------------------+---------+
+ | Structure | Octet |
+ +==============================================+=========+
+ | Protocol Identifier = 0000 0000 0000 0000 | 1 - 2 |
+ | | |
+ +----------------------------------------------+---------+
+ | Protocol Version Identifier = 0000 0010 | 3 |
+ +----------------------------------------------+---------+
+ | BPDU Type = 0000 0010 | 4 |
+ +----------------------------------------------+---------+
+ | Flags | 5 |
+ +----------------------------------------------+---------+
+ | Root Identifier | 6 - 13 |
+ | include - priority | |
+ | system ID extension | |
+ | MAC address | |
+ +----------------------------------------------+---------+
+ | Root Path Cost | 14 - 17 |
+ | | |
+ +----------------------------------------------+---------+
+ | Bridge Identifier | 18 - 25 |
+ | include - priority | |
+ | system ID extension | |
+ | MAC address | |
+ +----------------------------------------------+---------+
+ | Port Identifier | 26 - 27 |
+ | include - priority | |
+ | port number | |
+ +----------------------------------------------+---------+
+ | Message Age | 28 - 29 |
+ | | |
+ +----------------------------------------------+---------+
+ | Max Age | 30 - 31 |
+ | | |
+ +----------------------------------------------+---------+
+ | Hello Time | 32 - 33 |
+ | | |
+ +----------------------------------------------+---------+
+ | Forward Delay | 34 - 35 |
+ | | |
+ +----------------------------------------------+---------+
+ | Version 1 Length = 0000 0000 | 36 |
+ +----------------------------------------------+---------+
+
+"""
+
+
+import binascii
+import struct
+from . import packet_base
+from ryu.lib.mac import haddr_to_bin
+
+
+# BPDU destination
+BRIDGE_GROUP_ADDRESS = '\x01\x80\xc2\x00\x00\x00'
+
+
+PROTOCOL_IDENTIFIER = 0
+PROTOCOLVERSION_ID_BPDU = 0
+PROTOCOLVERSION_ID_RSTBPDU = 2
+TYPE_CONFIG_BPDU = 0
+TYPE_TOPOLOGY_CHANGE_BPDU = 128
+TYPE_RSTBPDU = 2
+DEFAULT_BRIDGE_PRIORITY = 32768
+DEFAULT_PORT_PRIORITY = 128
+DEFAULT_MAX_AGE = 20
+DEFAULT_HELLO_TIME = 2
+DEFAULT_FORWARD_DELAY = 15
+VERSION_1_LENGTH = 0
+
+
+class bpdu(packet_base.PacketBase):
+ """Bridge Protocol Data Unit(BPDU) header encoder/decoder base class.
+ """
+ _PACK_STR = '!HBB'
+ _PACK_LEN = struct.calcsize(_PACK_STR)
+ _BPDU_TYPES = {}
+
+ @staticmethod
+ def register_bpdu_type(sub_cls):
+ bpdu._BPDU_TYPES[sub_cls.BPDU_TYPE] = sub_cls
+ return sub_cls
+
+ def __init__(self):
+ super(bpdu, self).__init__()
+
+ assert hasattr(self, 'VERSION_ID')
+ assert hasattr(self, 'BPDU_TYPE')
+
+ self.protocol_id = PROTOCOL_IDENTIFIER
+ self.version_id = self.VERSION_ID
+ self.bpdu_type = self.BPDU_TYPE
+
+ if hasattr(self, 'check_parameters'):
+ self.check_parameters()
+
+ @classmethod
+ def parser(cls, buf):
+ assert len(buf) >= cls._PACK_LEN
+ (protocol_id, version_id,
+ bpdu_type) = struct.unpack_from(cls._PACK_STR, buf)
+ assert protocol_id == PROTOCOL_IDENTIFIER
+
+ bpdu_cls = cls._BPDU_TYPES.get(bpdu_type, None)
+
+ if bpdu_cls:
+ assert version_id == bpdu_cls.VERSION_ID
+ assert len(buf[cls._PACK_LEN:]) >= bpdu_cls.PACK_LEN
+ return bpdu_cls.parser(buf[cls._PACK_LEN:])
+ else:
+ # Unknown bdpu type.
+ return buf, None, None
+
+ def serialize(self, payload, prev):
+ return struct.pack(bpdu._PACK_STR, self.protocol_id,
+ self.version_id, self.bpdu_type)
+
+
+@bpdu.register_bpdu_type
+class ConfigurationBPDUs(bpdu):
+ """Configuration BPDUs(IEEE 802.1D) header encoder/decoder class.
+
+ An instance has the following attributes at least.
+ Most of them are same to the on-wire counterparts but in host byte
+ order.
+ __init__ takes the correspondig args in this order.
+
+ ========================== ===============================================
+ Attribute Description
+ ========================== ===============================================
+ flags | Bit 1: Topology Change flag
+ | Bits 2 through 7: unused and take the value 0
+ | Bit 8: Topology Change Acknowledgment flag
+ root_priority Root Identifier priority \
+ set 0-61440 in steps of 4096
+ root_system_id_extension Root Identifier system ID extension
+ root_mac_address Root Identifier MAC address
+ root_path_cost Root Path Cost
+ bridge_priority Bridge Identifier priority \
+ set 0-61440 in steps of 4096
+ bridge_system_id_extension Bridge Identifier system ID extension
+ bridge_mac_address Bridge Identifier MAC address
+ port_priority Port Identifier priority \
+ set 0-240 in steps of 16
+ port_number Port Identifier number
+ message_age Message Age timer value
+ max_age Max Age timer value
+ hello_time Hello Time timer value
+ forward_delay Forward Delay timer value
+ ========================== ===============================================
+ """
+
+ VERSION_ID = PROTOCOLVERSION_ID_BPDU
+ BPDU_TYPE = TYPE_CONFIG_BPDU
+ _PACK_STR = '!BQIQHHHHH'
+ PACK_LEN = struct.calcsize(_PACK_STR)
+
+ _BRIDGE_PRIORITY_STEP = 4096
+ _PORT_PRIORITY_STEP = 16
+ _TIMER_STEP = float(1)/256
+
+ def __init__(self, flags=0, root_priority=DEFAULT_BRIDGE_PRIORITY,
+ root_system_id_extension=0,
+ root_mac_address=haddr_to_bin('00:00:00:00:00:00'),
+ root_path_cost=0, bridge_priority=DEFAULT_BRIDGE_PRIORITY,
+ bridge_system_id_extension=0,
+ bridge_mac_address=haddr_to_bin('00:00:00:00:00:00'),
+ port_priority=DEFAULT_PORT_PRIORITY, port_number=0,
+ message_age=0, max_age=DEFAULT_MAX_AGE,
+ hello_time=DEFAULT_HELLO_TIME,
+ forward_delay=DEFAULT_FORWARD_DELAY):
+ self.flags = flags
+ self.root_priority = root_priority
+ self.root_system_id_extension = root_system_id_extension
+ self.root_mac_address = root_mac_address
+ self.root_path_cost = root_path_cost
+ self.bridge_priority = bridge_priority
+ self.bridge_system_id_extension = bridge_system_id_extension
+ self.bridge_mac_address = bridge_mac_address
+ self.port_priority = port_priority
+ self.port_number = port_number
+ self.message_age = message_age
+ self.max_age = max_age
+ self.hello_time = hello_time
+ self.forward_delay = forward_delay
+
+ super(ConfigurationBPDUs, self).__init__()
+
+ def check_parameters(self):
+ assert (self.flags >> 1 & 0b111111) == 0
+ assert self.root_priority % self._BRIDGE_PRIORITY_STEP == 0
+ assert self.bridge_priority % self._BRIDGE_PRIORITY_STEP == 0
+ assert self.port_priority % self._PORT_PRIORITY_STEP == 0
+ assert self.message_age % self._TIMER_STEP == 0
+ assert self.max_age % self._TIMER_STEP == 0
+ assert self.hello_time % self._TIMER_STEP == 0
+ assert self.forward_delay % self._TIMER_STEP == 0
+
+ @classmethod
+ def parser(cls, buf):
+ (flags, root_id, root_path_cost, bridge_id,
+ port_id, message_age, max_age, hello_time,
+ forward_delay) = struct.unpack_from(ConfigurationBPDUs._PACK_STR, buf)
+
+ (root_priority,
+ root_system_id_extension,
+ root_mac_address) = cls._decode_bridge_id(root_id)
+ (bridge_priority,
+ bridge_system_id_extension,
+ bridge_mac_address) = cls._decode_bridge_id(bridge_id)
+ (port_priority,
+ port_number) = cls._decode_port_id(port_id)
+
+ return (cls(flags, root_priority, root_system_id_extension,
+ root_mac_address, root_path_cost,
+ bridge_priority, bridge_system_id_extension,
+ bridge_mac_address, port_priority, port_number,
+ cls._decode_timer(message_age),
+ cls._decode_timer(max_age),
+ cls._decode_timer(hello_time),
+ cls._decode_timer(forward_delay)),
+ None, buf[ConfigurationBPDUs.PACK_LEN:])
+
+ def serialize(self, payload, prev):
+ base = super(ConfigurationBPDUs, self).serialize(payload, prev)
+
+ root_id = self.encode_bridge_id(self.root_priority,
+ self.root_system_id_extension,
+ self.root_mac_address)
+ bridge_id = self.encode_bridge_id(self.bridge_priority,
+ self.bridge_system_id_extension,
+ self.bridge_mac_address)
+ port_id = self.encode_port_id(self.port_priority,
+ self.port_number)
+ sub = struct.pack(ConfigurationBPDUs._PACK_STR,
+ self.flags,
+ root_id,
+ self.root_path_cost,
+ bridge_id,
+ port_id,
+ self._encode_timer(self.message_age),
+ self._encode_timer(self.max_age),
+ self._encode_timer(self.hello_time),
+ self._encode_timer(self.forward_delay))
+
+ return base + sub
+
+ @staticmethod
+ def _decode_bridge_id(bridge_id):
+ priority = (bridge_id >> 48) & 0xf000
+ system_id_extension = (bridge_id >> 48) & 0xfff
+ mac_addr = bridge_id & 0xffffffffffff
+
+ mac_addr_list = [format((mac_addr >> (8 * i)) & 0xff, '02x')
+ for i in range(0, 6)]
+ mac_addr_list.reverse()
+ mac_address = binascii.a2b_hex(''.join(mac_addr_list))
+
+ return priority, system_id_extension, mac_address
+
+ @staticmethod
+ def encode_bridge_id(priority, system_id_extension, mac_address):
+ mac_addr = int(binascii.hexlify(mac_address), 16)
+ return ((priority + system_id_extension) << 48) + mac_addr
+
+ @staticmethod
+ def _decode_port_id(port_id):
+ priority = port_id >> 8 & 0xf0
+ port_number = port_id & 0xfff
+ return priority, port_number
+
+ @staticmethod
+ def encode_port_id(priority, port_number):
+ return (priority << 8) + port_number
+
+ @staticmethod
+ def _decode_timer(timer):
+ return timer / float(0x100)
+
+ @staticmethod
+ def _encode_timer(timer):
+ return timer * 0x100
+
+
+@bpdu.register_bpdu_type
+class TopologyChangeNotificationBPDUs(bpdu):
+ """Topology Change Notification BPDUs(IEEE 802.1D)
+ header encoder/decoder class.
+ """
+
+ VERSION_ID = PROTOCOLVERSION_ID_BPDU
+ BPDU_TYPE = TYPE_TOPOLOGY_CHANGE_BPDU
+ _PACK_STR = ''
+ PACK_LEN = struct.calcsize(_PACK_STR)
+
+ def __init__(self):
+ super(TopologyChangeNotificationBPDUs, self).__init__()
+
+ @classmethod
+ def parser(cls, buf):
+ return cls(), None, buf[bpdu._PACK_LEN:]
+
+
+@bpdu.register_bpdu_type
+class RstBPDUs(ConfigurationBPDUs):
+ """Rapid Spanning Tree BPDUs(RST BPDUs, IEEE 802.1D)
+ header encoder/decoder class.
+
+ An instance has the following attributes at least.
+ Most of them are same to the on-wire counterparts but in host byte
+ order.
+ __init__ takes the correspondig args in this order.
+
+ ========================== ===========================================
+ Attribute Description
+ ========================== ===========================================
+ flags | Bit 1: Topology Change flag
+ | Bit 2: Proposal flag
+ | Bits 3 and 4: Port Role
+ | Bit 5: Learning flag
+ | Bit 6: Forwarding flag
+ | Bit 7: Agreement flag
+ | Bit 8: Topology Change Acknowledgment flag
+ root_priority Root Identifier priority \
+ set 0-61440 in steps of 4096
+ root_system_id_extension Root Identifier system ID extension
+ root_mac_address Root Identifier MAC address
+ root_path_cost Root Path Cost
+ bridge_priority Bridge Identifier priority \
+ set 0-61440 in steps of 4096
+ bridge_system_id_extension Bridge Identifier system ID extension
+ bridge_mac_address Bridge Identifier MAC address
+ port_priority Port Identifier priority \
+ set 0-240 in steps of 16
+ port_number Port Identifier number
+ message_age Message Age timer value
+ max_age Max Age timer value
+ hello_time Hello Time timer value
+ forward_delay Forward Delay timer value
+ ========================== ===========================================
+ """
+
+ VERSION_ID = PROTOCOLVERSION_ID_RSTBPDU
+ BPDU_TYPE = TYPE_RSTBPDU
+ _PACK_STR = '!B'
+ PACK_LEN = struct.calcsize(_PACK_STR)
+
+ def __init__(self, flags=0, root_priority=DEFAULT_BRIDGE_PRIORITY,
+ root_system_id_extension=0,
+ root_mac_address=haddr_to_bin('00:00:00:00:00:00'),
+ root_path_cost=0, bridge_priority=DEFAULT_BRIDGE_PRIORITY,
+ bridge_system_id_extension=0,
+ bridge_mac_address=haddr_to_bin('00:00:00:00:00:00'),
+ port_priority=DEFAULT_PORT_PRIORITY, port_number=0,
+ message_age=0, max_age=DEFAULT_MAX_AGE,
+ hello_time=DEFAULT_HELLO_TIME,
+ forward_delay=DEFAULT_FORWARD_DELAY):
+ self.version_1_length = VERSION_1_LENGTH
+
+ super(RstBPDUs, self).__init__(flags, root_priority,
+ root_system_id_extension,
+ root_mac_address, root_path_cost,
+ bridge_priority,
+ bridge_system_id_extension,
+ bridge_mac_address,
+ port_priority, port_number,
+ message_age, max_age,
+ hello_time, forward_delay)
+
+ def check_parameters(self):
+ assert self.root_priority % self._BRIDGE_PRIORITY_STEP == 0
+ assert self.bridge_priority % self._BRIDGE_PRIORITY_STEP == 0
+ assert self.port_priority % self._PORT_PRIORITY_STEP == 0
+ assert self.message_age % self._TIMER_STEP == 0
+ assert self.max_age % self._TIMER_STEP == 0
+ assert self.hello_time % self._TIMER_STEP == 0
+ assert self.forward_delay % self._TIMER_STEP == 0
+
+ @classmethod
+ def parser(cls, buf):
+ get_cls, next_type, buf = super(RstBPDUs, cls).parser(buf)
+
+ (version_1_length,) = struct.unpack_from(RstBPDUs._PACK_STR, buf)
+ assert version_1_length == VERSION_1_LENGTH
+
+ return get_cls, next_type, buf[RstBPDUs.PACK_LEN:]
+
+ def serialize(self, payload, prev):
+ base = super(RstBPDUs, self).serialize(payload, prev)
+ sub = struct.pack(RstBPDUs._PACK_STR, self.version_1_length)
+ return base + sub