second try
[vsorcdistro/.git] / ryu / ryu / lib / packet / bpdu.py
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 """
18 Bridge Protocol Data Unit(BPDU, IEEE 802.1D) parser/serializer
19 http://standards.ieee.org/getieee802/download/802.1D-2004.pdf
20
21
22 Configuration 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
67 Topology 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
81 Rapid 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
130 import binascii
131 import struct
132 from . import packet_base
133 from ryu.lib import addrconv
134
135
136 # BPDU destination
137 BRIDGE_GROUP_ADDRESS = '01:80:c2:00:00:00'
138
139
140 PROTOCOL_IDENTIFIER = 0
141 PROTOCOLVERSION_ID_BPDU = 0
142 PROTOCOLVERSION_ID_RSTBPDU = 2
143 TYPE_CONFIG_BPDU = 0
144 TYPE_TOPOLOGY_CHANGE_BPDU = 128
145 TYPE_RSTBPDU = 2
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
157 DEFAULT_MAX_AGE = 20
158 DEFAULT_HELLO_TIME = 2
159 DEFAULT_FORWARD_DELAY = 15
160 VERSION_1_LENGTH = 0
161
162
163 class 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
213 class 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
386 class 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
405 class 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