1 # Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
18 Bridge Protocol Data Unit(BPDU, IEEE 802.1D) parser/serializer
19 http://standards.ieee.org/getieee802/download/802.1D-2004.pdf
22 Configuration BPDUs format
24 +----------------------------------------------+---------+
26 +==============================================+=========+
27 | Protocol Identifier = 0000 0000 0000 0000 | 1 - 2 |
29 +----------------------------------------------+---------+
30 | Protocol Version Identifier = 0000 0000 | 3 |
31 +----------------------------------------------+---------+
32 | BPDU Type = 0000 0000 | 4 |
33 +----------------------------------------------+---------+
35 +----------------------------------------------+---------+
36 | Root Identifier | 6 - 13 |
37 | include - priority | |
38 | system ID extension | |
40 +----------------------------------------------+---------+
41 | Root Path Cost | 14 - 17 |
43 +----------------------------------------------+---------+
44 | Bridge Identifier | 18 - 25 |
45 | include - priority | |
46 | system ID extension | |
48 +----------------------------------------------+---------+
49 | Port Identifier | 26 - 27 |
50 | include - priority | |
52 +----------------------------------------------+---------+
53 | Message Age | 28 - 29 |
55 +----------------------------------------------+---------+
58 +----------------------------------------------+---------+
59 | Hello Time | 32 - 33 |
61 +----------------------------------------------+---------+
62 | Forward Delay | 34 - 35 |
64 +----------------------------------------------+---------+
67 Topology Change NotificationBPDUs format
69 +----------------------------------------------+---------+
71 +==============================================+=========+
72 | Protocol Identifier = 0000 0000 0000 0000 | 1 - 2 |
74 +----------------------------------------------+---------+
75 | Protocol Version Identifier = 0000 0000 | 3 |
76 +----------------------------------------------+---------+
77 | BPDU Type = 1000 0000 | 4 |
78 +----------------------------------------------+---------+
81 Rapid Spanning Tree BPDUs(RST BPDUs) format
83 +----------------------------------------------+---------+
85 +==============================================+=========+
86 | Protocol Identifier = 0000 0000 0000 0000 | 1 - 2 |
88 +----------------------------------------------+---------+
89 | Protocol Version Identifier = 0000 0010 | 3 |
90 +----------------------------------------------+---------+
91 | BPDU Type = 0000 0010 | 4 |
92 +----------------------------------------------+---------+
94 +----------------------------------------------+---------+
95 | Root Identifier | 6 - 13 |
96 | include - priority | |
97 | system ID extension | |
99 +----------------------------------------------+---------+
100 | Root Path Cost | 14 - 17 |
102 +----------------------------------------------+---------+
103 | Bridge Identifier | 18 - 25 |
104 | include - priority | |
105 | system ID extension | |
107 +----------------------------------------------+---------+
108 | Port Identifier | 26 - 27 |
109 | include - priority | |
111 +----------------------------------------------+---------+
112 | Message Age | 28 - 29 |
114 +----------------------------------------------+---------+
115 | Max Age | 30 - 31 |
117 +----------------------------------------------+---------+
118 | Hello Time | 32 - 33 |
120 +----------------------------------------------+---------+
121 | Forward Delay | 34 - 35 |
123 +----------------------------------------------+---------+
124 | Version 1 Length = 0000 0000 | 36 |
125 +----------------------------------------------+---------+
132 from . import packet_base
133 from ryu.lib import addrconv
137 BRIDGE_GROUP_ADDRESS = '01:80:c2:00:00:00'
140 PROTOCOL_IDENTIFIER = 0
141 PROTOCOLVERSION_ID_BPDU = 0
142 PROTOCOLVERSION_ID_RSTBPDU = 2
144 TYPE_TOPOLOGY_CHANGE_BPDU = 128
146 DEFAULT_BRIDGE_PRIORITY = 32768
147 DEFAULT_PORT_PRIORITY = 128
148 PORT_PATH_COST_100KB = 200000000
149 PORT_PATH_COST_1MB = 20000000
150 PORT_PATH_COST_10MB = 2000000
151 PORT_PATH_COST_100MB = 200000
152 PORT_PATH_COST_1GB = 20000
153 PORT_PATH_COST_10GB = 2000
154 PORT_PATH_COST_100GB = 200
155 PORT_PATH_COST_1TB = 20
156 PORT_PATH_COST_10TB = 2
158 DEFAULT_HELLO_TIME = 2
159 DEFAULT_FORWARD_DELAY = 15
163 class bpdu(packet_base.PacketBase):
164 """Bridge Protocol Data Unit(BPDU) header encoder/decoder base class.
167 _PACK_LEN = struct.calcsize(_PACK_STR)
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
179 super(bpdu, self).__init__()
181 assert hasattr(self, 'VERSION_ID')
182 assert hasattr(self, 'BPDU_TYPE')
184 self._protocol_id = PROTOCOL_IDENTIFIER
185 self._version_id = self.VERSION_ID
186 self._bpdu_type = self.BPDU_TYPE
188 if hasattr(self, 'check_parameters'):
189 self.check_parameters()
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
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:])
204 # Unknown bpdu version/type.
205 return buf, None, None
207 def serialize(self, payload, prev):
208 return struct.pack(bpdu._PACK_STR, self._protocol_id,
209 self._version_id, self._bpdu_type)
212 @bpdu.register_bpdu_type
213 class ConfigurationBPDUs(bpdu):
214 """Configuration BPDUs(IEEE 802.1D) header encoder/decoder class.
216 An instance has the following attributes at least.
217 Most of them are same to the on-wire counterparts but in host byte
219 __init__ takes the corresponding args in this order.
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 ========================== ===============================================
246 VERSION_ID = PROTOCOLVERSION_ID_BPDU
247 BPDU_TYPE = TYPE_CONFIG_BPDU
248 _PACK_STR = '!BQIQHHHHH'
249 PACK_LEN = struct.calcsize(_PACK_STR)
252 'root_mac_address', "bridge_mac_address"
256 _BRIDGE_PRIORITY_STEP = 4096
257 _PORT_PRIORITY_STEP = 16
258 _TIMER_STEP = float(1) / 256
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):
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
285 super(ConfigurationBPDUs, self).__init__()
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
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)
304 root_system_id_extension,
305 root_mac_address) = cls._decode_bridge_id(root_id)
307 bridge_system_id_extension,
308 bridge_mac_address) = cls._decode_bridge_id(bridge_id)
310 port_number) = cls._decode_port_id(port_id)
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:])
322 def serialize(self, payload, prev):
323 base = super(ConfigurationBPDUs, self).serialize(payload, prev)
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,
333 sub = struct.pack(ConfigurationBPDUs._PACK_STR,
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))
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
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)
358 return priority, system_id_extension, mac_address
361 def encode_bridge_id(priority, system_id_extension, mac_address):
362 mac_addr = int(binascii.hexlify(addrconv.mac.text_to_bin(mac_address)),
364 return ((priority + system_id_extension) << 48) + mac_addr
367 def _decode_port_id(port_id):
368 priority = port_id >> 8 & 0xf0
369 port_number = port_id & 0xfff
370 return priority, port_number
373 def encode_port_id(priority, port_number):
374 return (priority << 8) + port_number
377 def _decode_timer(timer):
378 return timer / float(0x100)
381 def _encode_timer(timer):
382 return int(timer) * 0x100
385 @bpdu.register_bpdu_type
386 class TopologyChangeNotificationBPDUs(bpdu):
387 """Topology Change Notification BPDUs(IEEE 802.1D)
388 header encoder/decoder class.
391 VERSION_ID = PROTOCOLVERSION_ID_BPDU
392 BPDU_TYPE = TYPE_TOPOLOGY_CHANGE_BPDU
394 PACK_LEN = struct.calcsize(_PACK_STR)
397 super(TopologyChangeNotificationBPDUs, self).__init__()
400 def parser(cls, buf):
401 return cls(), None, buf[bpdu._PACK_LEN:]
404 @bpdu.register_bpdu_type
405 class RstBPDUs(ConfigurationBPDUs):
406 """Rapid Spanning Tree BPDUs(RST BPDUs, IEEE 802.1D)
407 header encoder/decoder class.
409 An instance has the following attributes at least.
410 Most of them are same to the on-wire counterparts but in host byte
412 __init__ takes the corresponding args in this order.
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 ========================== ===========================================
443 VERSION_ID = PROTOCOLVERSION_ID_RSTBPDU
444 BPDU_TYPE = TYPE_RSTBPDU
446 PACK_LEN = struct.calcsize(_PACK_STR)
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
460 super(RstBPDUs, self).__init__(flags, root_priority,
461 root_system_id_extension,
462 root_mac_address, root_path_cost,
464 bridge_system_id_extension,
466 port_priority, port_number,
467 message_age, max_age,
468 hello_time, forward_delay)
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
480 def parser(cls, buf):
481 get_cls, next_type, buf = super(RstBPDUs, cls).parser(buf)
483 (version_1_length,) = struct.unpack_from(RstBPDUs._PACK_STR, buf)
484 assert version_1_length == VERSION_1_LENGTH
486 return get_cls, next_type, buf[RstBPDUs.PACK_LEN:]
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)