1# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12# implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16 17""" 18Bridge Protocol Data Unit(BPDU, IEEE 802.1D) parser/serializer 19http://standards.ieee.org/getieee802/download/802.1D-2004.pdf 20 21 22Configuration BPDUs format 23 24 +----------------------------------------------+---------+ 25 | Structure | Octet | 26 +==============================================+=========+ 27 | Protocol Identifier = 0000 0000 0000 0000 | 1 - 2 | 28 | | | 29 +----------------------------------------------+---------+ 30 | Protocol Version Identifier = 0000 0000 | 3 | 31 +----------------------------------------------+---------+ 32 | BPDU Type = 0000 0000 | 4 | 33 +----------------------------------------------+---------+ 34 | Flags | 5 | 35 +----------------------------------------------+---------+ 36 | Root Identifier | 6 - 13 | 37 | include - priority | | 38 | system ID extension | | 39 | MAC address | | 40 +----------------------------------------------+---------+ 41 | Root Path Cost | 14 - 17 | 42 | | | 43 +----------------------------------------------+---------+ 44 | Bridge Identifier | 18 - 25 | 45 | include - priority | | 46 | system ID extension | | 47 | MAC address | | 48 +----------------------------------------------+---------+ 49 | Port Identifier | 26 - 27 | 50 | include - priority | | 51 | port number | | 52 +----------------------------------------------+---------+ 53 | Message Age | 28 - 29 | 54 | | | 55 +----------------------------------------------+---------+ 56 | Max Age | 30 - 31 | 57 | | | 58 +----------------------------------------------+---------+ 59 | Hello Time | 32 - 33 | 60 | | | 61 +----------------------------------------------+---------+ 62 | Forward Delay | 34 - 35 | 63 | | | 64 +----------------------------------------------+---------+ 65 66 67Topology Change NotificationBPDUs format 68 69 +----------------------------------------------+---------+ 70 | Structure | Octet | 71 +==============================================+=========+ 72 | Protocol Identifier = 0000 0000 0000 0000 | 1 - 2 | 73 | | | 74 +----------------------------------------------+---------+ 75 | Protocol Version Identifier = 0000 0000 | 3 | 76 +----------------------------------------------+---------+ 77 | BPDU Type = 1000 0000 | 4 | 78 +----------------------------------------------+---------+ 79 80 81Rapid Spanning Tree BPDUs(RST BPDUs) format 82 83 +----------------------------------------------+---------+ 84 | Structure | Octet | 85 +==============================================+=========+ 86 | Protocol Identifier = 0000 0000 0000 0000 | 1 - 2 | 87 | | | 88 +----------------------------------------------+---------+ 89 | Protocol Version Identifier = 0000 0010 | 3 | 90 +----------------------------------------------+---------+ 91 | BPDU Type = 0000 0010 | 4 | 92 +----------------------------------------------+---------+ 93 | Flags | 5 | 94 +----------------------------------------------+---------+ 95 | Root Identifier | 6 - 13 | 96 | include - priority | | 97 | system ID extension | | 98 | MAC address | | 99 +----------------------------------------------+---------+ 100 | Root Path Cost | 14 - 17 | 101 | | | 102 +----------------------------------------------+---------+ 103 | Bridge Identifier | 18 - 25 | 104 | include - priority | | 105 | system ID extension | | 106 | MAC address | | 107 +----------------------------------------------+---------+ 108 | Port Identifier | 26 - 27 | 109 | include - priority | | 110 | port number | | 111 +----------------------------------------------+---------+ 112 | Message Age | 28 - 29 | 113 | | | 114 +----------------------------------------------+---------+ 115 | Max Age | 30 - 31 | 116 | | | 117 +----------------------------------------------+---------+ 118 | Hello Time | 32 - 33 | 119 | | | 120 +----------------------------------------------+---------+ 121 | Forward Delay | 34 - 35 | 122 | | | 123 +----------------------------------------------+---------+ 124 | Version 1 Length = 0000 0000 | 36 | 125 +----------------------------------------------+---------+ 126 127""" 128 129 130import binascii 131import struct 132from . import packet_base 133from ryu.lib import addrconv 134 135 136# BPDU destination 137BRIDGE_GROUP_ADDRESS = '01:80:c2:00:00:00' 138 139 140PROTOCOL_IDENTIFIER = 0 141PROTOCOLVERSION_ID_BPDU = 0 142PROTOCOLVERSION_ID_RSTBPDU = 2 143TYPE_CONFIG_BPDU = 0 144TYPE_TOPOLOGY_CHANGE_BPDU = 128 145TYPE_RSTBPDU = 2 146DEFAULT_BRIDGE_PRIORITY = 32768 147DEFAULT_PORT_PRIORITY = 128 148PORT_PATH_COST_100KB = 200000000 149PORT_PATH_COST_1MB = 20000000 150PORT_PATH_COST_10MB = 2000000 151PORT_PATH_COST_100MB = 200000 152PORT_PATH_COST_1GB = 20000 153PORT_PATH_COST_10GB = 2000 154PORT_PATH_COST_100GB = 200 155PORT_PATH_COST_1TB = 20 156PORT_PATH_COST_10TB = 2 157DEFAULT_MAX_AGE = 20 158DEFAULT_HELLO_TIME = 2 159DEFAULT_FORWARD_DELAY = 15 160VERSION_1_LENGTH = 0 161 162 163class bpdu(packet_base.PacketBase): 164 """Bridge Protocol Data Unit(BPDU) header encoder/decoder base class. 165 """ 166 _PACK_STR = '!HBB' 167 _PACK_LEN = struct.calcsize(_PACK_STR) 168 _BPDU_TYPES = {} 169 170 _MIN_LEN = _PACK_LEN 171 172 @staticmethod 173 def register_bpdu_type(sub_cls): 174 bpdu._BPDU_TYPES.setdefault(sub_cls.VERSION_ID, {}) 175 bpdu._BPDU_TYPES[sub_cls.VERSION_ID][sub_cls.BPDU_TYPE] = sub_cls 176 return sub_cls 177 178 def __init__(self): 179 super(bpdu, self).__init__() 180 181 assert hasattr(self, 'VERSION_ID') 182 assert hasattr(self, 'BPDU_TYPE') 183 184 self._protocol_id = PROTOCOL_IDENTIFIER 185 self._version_id = self.VERSION_ID 186 self._bpdu_type = self.BPDU_TYPE 187 188 if hasattr(self, 'check_parameters'): 189 self.check_parameters() 190 191 @classmethod 192 def parser(cls, buf): 193 assert len(buf) >= cls._PACK_LEN 194 (protocol_id, version_id, 195 bpdu_type) = struct.unpack_from(cls._PACK_STR, buf) 196 assert protocol_id == PROTOCOL_IDENTIFIER 197 198 if (version_id in cls._BPDU_TYPES 199 and bpdu_type in cls._BPDU_TYPES[version_id]): 200 bpdu_cls = cls._BPDU_TYPES[version_id][bpdu_type] 201 assert len(buf[cls._PACK_LEN:]) >= bpdu_cls.PACK_LEN 202 return bpdu_cls.parser(buf[cls._PACK_LEN:]) 203 else: 204 # Unknown bpdu version/type. 205 return buf, None, None 206 207 def serialize(self, payload, prev): 208 return struct.pack(bpdu._PACK_STR, self._protocol_id, 209 self._version_id, self._bpdu_type) 210 211 212@bpdu.register_bpdu_type 213class ConfigurationBPDUs(bpdu): 214 """Configuration BPDUs(IEEE 802.1D) header encoder/decoder class. 215 216 An instance has the following attributes at least. 217 Most of them are same to the on-wire counterparts but in host byte 218 order. 219 __init__ takes the corresponding args in this order. 220 221 ========================== =============================================== 222 Attribute Description 223 ========================== =============================================== 224 flags | Bit 1: Topology Change flag 225 | Bits 2 through 7: unused and take the value 0 226 | Bit 8: Topology Change Acknowledgment flag 227 root_priority Root Identifier priority \ 228 set 0-61440 in steps of 4096 229 root_system_id_extension Root Identifier system ID extension 230 root_mac_address Root Identifier MAC address 231 root_path_cost Root Path Cost 232 bridge_priority Bridge Identifier priority \ 233 set 0-61440 in steps of 4096 234 bridge_system_id_extension Bridge Identifier system ID extension 235 bridge_mac_address Bridge Identifier MAC address 236 port_priority Port Identifier priority \ 237 set 0-240 in steps of 16 238 port_number Port Identifier number 239 message_age Message Age timer value 240 max_age Max Age timer value 241 hello_time Hello Time timer value 242 forward_delay Forward Delay timer value 243 ========================== =============================================== 244 """ 245 246 VERSION_ID = PROTOCOLVERSION_ID_BPDU 247 BPDU_TYPE = TYPE_CONFIG_BPDU 248 _PACK_STR = '!BQIQHHHHH' 249 PACK_LEN = struct.calcsize(_PACK_STR) 250 _TYPE = { 251 'ascii': [ 252 'root_mac_address', "bridge_mac_address" 253 ] 254 } 255 256 _BRIDGE_PRIORITY_STEP = 4096 257 _PORT_PRIORITY_STEP = 16 258 _TIMER_STEP = float(1) / 256 259 260 def __init__(self, flags=0, root_priority=DEFAULT_BRIDGE_PRIORITY, 261 root_system_id_extension=0, 262 root_mac_address='00:00:00:00:00:00', 263 root_path_cost=0, bridge_priority=DEFAULT_BRIDGE_PRIORITY, 264 bridge_system_id_extension=0, 265 bridge_mac_address='00:00:00:00:00:00', 266 port_priority=DEFAULT_PORT_PRIORITY, port_number=0, 267 message_age=0, max_age=DEFAULT_MAX_AGE, 268 hello_time=DEFAULT_HELLO_TIME, 269 forward_delay=DEFAULT_FORWARD_DELAY): 270 self.flags = flags 271 self.root_priority = root_priority 272 self.root_system_id_extension = root_system_id_extension 273 self.root_mac_address = root_mac_address 274 self.root_path_cost = root_path_cost 275 self.bridge_priority = bridge_priority 276 self.bridge_system_id_extension = bridge_system_id_extension 277 self.bridge_mac_address = bridge_mac_address 278 self.port_priority = port_priority 279 self.port_number = port_number 280 self.message_age = message_age 281 self.max_age = max_age 282 self.hello_time = hello_time 283 self.forward_delay = forward_delay 284 285 super(ConfigurationBPDUs, self).__init__() 286 287 def check_parameters(self): 288 assert (self.flags >> 1 & 0b111111) == 0 289 assert self.root_priority % self._BRIDGE_PRIORITY_STEP == 0 290 assert self.bridge_priority % self._BRIDGE_PRIORITY_STEP == 0 291 assert self.port_priority % self._PORT_PRIORITY_STEP == 0 292 assert self.message_age % self._TIMER_STEP == 0 293 assert self.max_age % self._TIMER_STEP == 0 294 assert self.hello_time % self._TIMER_STEP == 0 295 assert self.forward_delay % self._TIMER_STEP == 0 296 297 @classmethod 298 def parser(cls, buf): 299 (flags, root_id, root_path_cost, bridge_id, 300 port_id, message_age, max_age, hello_time, 301 forward_delay) = struct.unpack_from(ConfigurationBPDUs._PACK_STR, buf) 302 303 (root_priority, 304 root_system_id_extension, 305 root_mac_address) = cls._decode_bridge_id(root_id) 306 (bridge_priority, 307 bridge_system_id_extension, 308 bridge_mac_address) = cls._decode_bridge_id(bridge_id) 309 (port_priority, 310 port_number) = cls._decode_port_id(port_id) 311 312 return (cls(flags, root_priority, root_system_id_extension, 313 root_mac_address, root_path_cost, 314 bridge_priority, bridge_system_id_extension, 315 bridge_mac_address, port_priority, port_number, 316 cls._decode_timer(message_age), 317 cls._decode_timer(max_age), 318 cls._decode_timer(hello_time), 319 cls._decode_timer(forward_delay)), 320 None, buf[ConfigurationBPDUs.PACK_LEN:]) 321 322 def serialize(self, payload, prev): 323 base = super(ConfigurationBPDUs, self).serialize(payload, prev) 324 325 root_id = self.encode_bridge_id(self.root_priority, 326 self.root_system_id_extension, 327 self.root_mac_address) 328 bridge_id = self.encode_bridge_id(self.bridge_priority, 329 self.bridge_system_id_extension, 330 self.bridge_mac_address) 331 port_id = self.encode_port_id(self.port_priority, 332 self.port_number) 333 sub = struct.pack(ConfigurationBPDUs._PACK_STR, 334 self.flags, 335 root_id, 336 self.root_path_cost, 337 bridge_id, 338 port_id, 339 self._encode_timer(self.message_age), 340 self._encode_timer(self.max_age), 341 self._encode_timer(self.hello_time), 342 self._encode_timer(self.forward_delay)) 343 344 return base + sub 345 346 @staticmethod 347 def _decode_bridge_id(bridge_id): 348 priority = (bridge_id >> 48) & 0xf000 349 system_id_extension = (bridge_id >> 48) & 0xfff 350 mac_addr = bridge_id & 0xffffffffffff 351 352 mac_addr_list = [format((mac_addr >> (8 * i)) & 0xff, '02x') 353 for i in range(0, 6)] 354 mac_addr_list.reverse() 355 mac_address_bin = binascii.a2b_hex(''.join(mac_addr_list)) 356 mac_address = addrconv.mac.bin_to_text(mac_address_bin) 357 358 return priority, system_id_extension, mac_address 359 360 @staticmethod 361 def encode_bridge_id(priority, system_id_extension, mac_address): 362 mac_addr = int(binascii.hexlify(addrconv.mac.text_to_bin(mac_address)), 363 16) 364 return ((priority + system_id_extension) << 48) + mac_addr 365 366 @staticmethod 367 def _decode_port_id(port_id): 368 priority = port_id >> 8 & 0xf0 369 port_number = port_id & 0xfff 370 return priority, port_number 371 372 @staticmethod 373 def encode_port_id(priority, port_number): 374 return (priority << 8) + port_number 375 376 @staticmethod 377 def _decode_timer(timer): 378 return timer / float(0x100) 379 380 @staticmethod 381 def _encode_timer(timer): 382 return int(timer) * 0x100 383 384 385@bpdu.register_bpdu_type 386class TopologyChangeNotificationBPDUs(bpdu): 387 """Topology Change Notification BPDUs(IEEE 802.1D) 388 header encoder/decoder class. 389 """ 390 391 VERSION_ID = PROTOCOLVERSION_ID_BPDU 392 BPDU_TYPE = TYPE_TOPOLOGY_CHANGE_BPDU 393 _PACK_STR = '' 394 PACK_LEN = struct.calcsize(_PACK_STR) 395 396 def __init__(self): 397 super(TopologyChangeNotificationBPDUs, self).__init__() 398 399 @classmethod 400 def parser(cls, buf): 401 return cls(), None, buf[bpdu._PACK_LEN:] 402 403 404@bpdu.register_bpdu_type 405class RstBPDUs(ConfigurationBPDUs): 406 """Rapid Spanning Tree BPDUs(RST BPDUs, IEEE 802.1D) 407 header encoder/decoder class. 408 409 An instance has the following attributes at least. 410 Most of them are same to the on-wire counterparts but in host byte 411 order. 412 __init__ takes the corresponding args in this order. 413 414 ========================== =========================================== 415 Attribute Description 416 ========================== =========================================== 417 flags | Bit 1: Topology Change flag 418 | Bit 2: Proposal flag 419 | Bits 3 and 4: Port Role 420 | Bit 5: Learning flag 421 | Bit 6: Forwarding flag 422 | Bit 7: Agreement flag 423 | Bit 8: Topology Change Acknowledgment flag 424 root_priority Root Identifier priority \ 425 set 0-61440 in steps of 4096 426 root_system_id_extension Root Identifier system ID extension 427 root_mac_address Root Identifier MAC address 428 root_path_cost Root Path Cost 429 bridge_priority Bridge Identifier priority \ 430 set 0-61440 in steps of 4096 431 bridge_system_id_extension Bridge Identifier system ID extension 432 bridge_mac_address Bridge Identifier MAC address 433 port_priority Port Identifier priority \ 434 set 0-240 in steps of 16 435 port_number Port Identifier number 436 message_age Message Age timer value 437 max_age Max Age timer value 438 hello_time Hello Time timer value 439 forward_delay Forward Delay timer value 440 ========================== =========================================== 441 """ 442 443 VERSION_ID = PROTOCOLVERSION_ID_RSTBPDU 444 BPDU_TYPE = TYPE_RSTBPDU 445 _PACK_STR = '!B' 446 PACK_LEN = struct.calcsize(_PACK_STR) 447 448 def __init__(self, flags=0, root_priority=DEFAULT_BRIDGE_PRIORITY, 449 root_system_id_extension=0, 450 root_mac_address='00:00:00:00:00:00', 451 root_path_cost=0, bridge_priority=DEFAULT_BRIDGE_PRIORITY, 452 bridge_system_id_extension=0, 453 bridge_mac_address='00:00:00:00:00:00', 454 port_priority=DEFAULT_PORT_PRIORITY, port_number=0, 455 message_age=0, max_age=DEFAULT_MAX_AGE, 456 hello_time=DEFAULT_HELLO_TIME, 457 forward_delay=DEFAULT_FORWARD_DELAY): 458 self._version_1_length = VERSION_1_LENGTH 459 460 super(RstBPDUs, self).__init__(flags, root_priority, 461 root_system_id_extension, 462 root_mac_address, root_path_cost, 463 bridge_priority, 464 bridge_system_id_extension, 465 bridge_mac_address, 466 port_priority, port_number, 467 message_age, max_age, 468 hello_time, forward_delay) 469 470 def check_parameters(self): 471 assert self.root_priority % self._BRIDGE_PRIORITY_STEP == 0 472 assert self.bridge_priority % self._BRIDGE_PRIORITY_STEP == 0 473 assert self.port_priority % self._PORT_PRIORITY_STEP == 0 474 assert self.message_age % self._TIMER_STEP == 0 475 assert self.max_age % self._TIMER_STEP == 0 476 assert self.hello_time % self._TIMER_STEP == 0 477 assert self.forward_delay % self._TIMER_STEP == 0 478 479 @classmethod 480 def parser(cls, buf): 481 get_cls, next_type, buf = super(RstBPDUs, cls).parser(buf) 482 483 (version_1_length,) = struct.unpack_from(RstBPDUs._PACK_STR, buf) 484 assert version_1_length == VERSION_1_LENGTH 485 486 return get_cls, next_type, buf[RstBPDUs.PACK_LEN:] 487 488 def serialize(self, payload, prev): 489 base = super(RstBPDUs, self).serialize(payload, prev) 490 sub = struct.pack(RstBPDUs._PACK_STR, self._version_1_length) 491 return base + sub 492