backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / lib / mrtlib.py
1 # Copyright (C) 2016 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 Library for reading/writing MRT (Multi-Threaded Routing Toolkit) Routing
18 Information Export Format [RFC6396].
19 """
20
21 import abc
22 import logging
23 import struct
24 import time
25
26 import netaddr
27 import six
28
29 from ryu.lib import addrconv
30 from ryu.lib import ip
31 from ryu.lib import stringify
32 from ryu.lib import type_desc
33 from ryu.lib.packet import bgp
34 from ryu.lib.packet import ospf
35
36
37 LOG = logging.getLogger(__name__)
38
39
40 @six.add_metaclass(abc.ABCMeta)
41 class MrtRecord(stringify.StringifyMixin, type_desc.TypeDisp):
42     """
43     MRT record.
44     """
45     _HEADER_FMT = '!IHHI'  # the same as MRT Common Header
46     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
47     MESSAGE_CLS = None  # parser class for message field
48
49     # MRT Types
50     TYPE_OSPFv2 = 11
51     TYPE_TABLE_DUMP = 12
52     TYPE_TABLE_DUMP_V2 = 13
53     TYPE_BGP4MP = 16
54     TYPE_BGP4MP_ET = 17
55     TYPE_ISIS = 32
56     TYPE_ISIS_ET = 33
57     TYPE_OSPFv3 = 48
58     TYPE_OSPFv3_ET = 49
59
60     # List of MRT type using Extended Timestamp MRT Header
61     _EXT_TS_TYPES = [TYPE_BGP4MP_ET, TYPE_ISIS_ET, TYPE_OSPFv3_ET]
62
63     def __init__(self, message, timestamp=None, type_=None, subtype=None,
64                  length=None):
65         assert issubclass(message.__class__, MrtMessage)
66         self.message = message
67         self.timestamp = timestamp
68         if type_ is None:
69             type_ = self._rev_lookup_type(self.__class__)
70         self.type = type_
71         if subtype is None:
72             subtype = self.MESSAGE_CLS._rev_lookup_type(message.__class__)
73         self.subtype = subtype
74         self.length = length
75
76     @classmethod
77     def parse_common_header(cls, buf):
78         header_fields = struct.unpack_from(
79             cls._HEADER_FMT, buf)
80
81         return list(header_fields), buf[cls.HEADER_SIZE:]
82
83     @classmethod
84     def parse_extended_header(cls, buf):
85         # If extended header field exist, override this in subclass.
86         return [], buf
87
88     @classmethod
89     def parse_pre(cls, buf):
90         buf = six.binary_type(buf)  # for convenience
91
92         header_fields, _ = cls.parse_common_header(buf)
93         # timestamp = header_fields[0]
94         type_ = header_fields[1]
95         # subtype = header_fields[2]
96         length = header_fields[3]
97         if type_ in cls._EXT_TS_TYPES:
98             header_cls = ExtendedTimestampMrtRecord
99         else:
100             header_cls = MrtCommonRecord
101
102         required_len = header_cls.HEADER_SIZE + length
103
104         return required_len
105
106     @classmethod
107     def parse(cls, buf):
108         buf = six.binary_type(buf)  # for convenience
109
110         header_fields, rest = cls.parse_common_header(buf)
111         # timestamp = header_fields[0]
112         type_ = header_fields[1]
113         subtype = header_fields[2]
114         length = header_fields[3]
115
116         sub_cls = MrtRecord._lookup_type(type_)
117         extended_headers, rest = sub_cls.parse_extended_header(rest)
118         header_fields.extend(extended_headers)
119
120         msg_cls = sub_cls.MESSAGE_CLS._lookup_type(subtype)
121         message_bin = rest[:length]
122         message = msg_cls.parse(message_bin)
123
124         return sub_cls(message, *header_fields), rest[length:]
125
126     @abc.abstractmethod
127     def serialize_header(self):
128         pass
129
130     def serialize(self):
131         if self.timestamp is None:
132             self.timestamp = int(time.time())
133
134         buf = self.message.serialize()
135
136         self.length = len(buf)  # fixup
137
138         return self.serialize_header() + buf
139
140
141 class MrtCommonRecord(MrtRecord):
142     """
143     MRT record using MRT Common Header.
144     """
145     #  0                   1                   2                   3
146     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
147     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
148     # |                           Timestamp                           |
149     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
150     # |             Type              |            Subtype            |
151     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
152     # |                             Length                            |
153     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
154     # |                      Message... (variable)                    |
155     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
156     _HEADER_FMT = '!IHHI'
157     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
158
159     def serialize_header(self):
160         return struct.pack(self._HEADER_FMT,
161                            self.timestamp,
162                            self.type, self.subtype,
163                            self.length)
164
165
166 class ExtendedTimestampMrtRecord(MrtRecord):
167     """
168     MRT record using Extended Timestamp MRT Header.
169     """
170     #  0                   1                   2                   3
171     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
172     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
173     # |                           Timestamp                           |
174     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
175     # |             Type              |            Subtype            |
176     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
177     # |                             Length                            |
178     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
179     # |                      Microsecond Timestamp                    |
180     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
181     # |                      Message... (variable)                    |
182     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
183     _HEADER_FMT = '!IHHII'
184     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
185     _EXT_HEADER_FMT = '!I'
186     EXT_HEADER_SIZE = struct.calcsize(_EXT_HEADER_FMT)
187
188     def __init__(self, message, timestamp=None, type_=None, subtype=None,
189                  ms_timestamp=None, length=None):
190         super(ExtendedTimestampMrtRecord, self).__init__(
191             message, timestamp, type_, subtype, length)
192         self.ms_timestamp = ms_timestamp
193
194     @classmethod
195     def parse_extended_header(cls, buf):
196         (ms_timestamp,) = struct.unpack_from(cls._EXT_HEADER_FMT, buf)
197
198         return [ms_timestamp], buf[cls.EXT_HEADER_SIZE:]
199
200     def serialize_header(self):
201         return struct.pack(self._HEADER_FMT,
202                            self.timestamp,
203                            self.type, self.subtype,
204                            self.length,
205                            self.ms_timestamp)
206
207
208 @six.add_metaclass(abc.ABCMeta)
209 class MrtMessage(stringify.StringifyMixin, type_desc.TypeDisp):
210     """
211     MRT Message in record.
212     """
213
214     @classmethod
215     @abc.abstractmethod
216     def parse(cls, buf):
217         pass
218
219     @abc.abstractmethod
220     def serialize(self):
221         pass
222
223
224 class UnknownMrtMessage(MrtMessage):
225     """
226     MRT Message for the UNKNOWN Type.
227     """
228
229     def __init__(self, buf):
230         self.buf = buf
231
232     @classmethod
233     def parse(cls, buf):
234         return cls(buf)
235
236     def serialize(self):
237         return self.buf
238
239
240 # Registers self to unknown(default) type
241 UnknownMrtMessage._UNKNOWN_TYPE = UnknownMrtMessage
242
243
244 @MrtRecord.register_unknown_type()
245 class UnknownMrtRecord(MrtCommonRecord):
246     """
247     MRT record for the UNKNOWN Type.
248     """
249     MESSAGE_CLS = UnknownMrtMessage
250
251
252 class Ospf2MrtMessage(MrtMessage):
253     """
254     MRT Message for the OSPFv2 Type.
255     """
256     #  0                   1                   2                   3
257     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
258     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
259     # |                        Remote IP Address                      |
260     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
261     # |                         Local IP Address                      |
262     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
263     # |                  OSPF Message Contents (variable)             |
264     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
265     _HEADER_FMT = '!4s4s'
266     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
267     _TYPE = {
268         'ascii': [
269             'remote_ip',
270             'local_ip',
271         ],
272     }
273
274     def __init__(self, remote_ip, local_ip, ospf_message):
275         self.remote_ip = remote_ip
276         self.local_ip = local_ip
277         assert isinstance(ospf_message, ospf.OSPFMessage)
278         self.ospf_message = ospf_message
279
280     @classmethod
281     def parse(cls, buf):
282         (remote_ip, local_ip) = struct.unpack_from(cls._HEADER_FMT, buf)
283         remote_ip = addrconv.ipv4.bin_to_text(remote_ip)
284         local_ip = addrconv.ipv4.bin_to_text(local_ip)
285         ospf_message, _, _ = ospf.OSPFMessage.parser(buf[cls.HEADER_SIZE:])
286
287         return cls(remote_ip, local_ip, ospf_message)
288
289     def serialize(self):
290         return (addrconv.ipv4.text_to_bin(self.remote_ip)
291                 + addrconv.ipv4.text_to_bin(self.local_ip)
292                 + self.ospf_message.serialize())
293
294
295 @MrtRecord.register_type(MrtRecord.TYPE_OSPFv2)
296 class Ospf2MrtRecord(MrtCommonRecord):
297     """
298     MRT Record for the OSPFv2 Type.
299     """
300     MESSAGE_CLS = Ospf2MrtMessage
301
302     def __init__(self, message, timestamp=None, type_=None, subtype=0,
303                  length=None):
304         super(Ospf2MrtRecord, self).__init__(
305             message=message, timestamp=timestamp, type_=type_,
306             subtype=subtype, length=length)
307
308
309 # Registers self to unknown(default) type
310 Ospf2MrtMessage._UNKNOWN_TYPE = Ospf2MrtMessage
311
312
313 @six.add_metaclass(abc.ABCMeta)
314 class TableDumpMrtMessage(MrtMessage):
315     """
316     MRT Message for the TABLE_DUMP Type.
317     """
318     #  0                   1                   2                   3
319     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
320     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
321     # |         View Number           |       Sequence Number         |
322     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
323     # |                        Prefix (variable)                      |
324     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
325     # | Prefix Length |    Status     |
326     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
327     # |                         Originated Time                       |
328     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
329     # |                    Peer IP Address (variable)                 |
330     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
331     # |           Peer AS             |       Attribute Length        |
332     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
333     # |                   BGP Attribute... (variable)
334     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
335     _HEADER_FMT = ''  # should be defined in subclass
336     HEADER_SIZE = 0
337     _TYPE = {
338         'ascii': [
339             'prefix',
340             'peer_ip',
341         ],
342     }
343
344     def __init__(self, view_num, seq_num, prefix, prefix_len, status,
345                  originated_time, peer_ip, peer_as, bgp_attributes,
346                  attr_len=None):
347         self.view_num = view_num
348         self.seq_num = seq_num
349         self.prefix = prefix
350         self.prefix_len = prefix_len
351         # Status in the TABLE_DUMP Type SHOULD be set to 1
352         assert status == 1
353         self.status = status
354         self.originated_time = originated_time
355         self.peer_ip = peer_ip
356         self.peer_as = peer_as
357         self.attr_len = attr_len
358         assert isinstance(bgp_attributes, (list, tuple))
359         for attr in bgp_attributes:
360             assert isinstance(attr, bgp._PathAttribute)
361         self.bgp_attributes = bgp_attributes
362
363     @classmethod
364     def parse(cls, buf):
365         (view_num, seq_num, prefix, prefix_len, status, originated_time,
366          peer_ip, peer_as, attr_len) = struct.unpack_from(cls._HEADER_FMT, buf)
367         prefix = ip.bin_to_text(prefix)
368         peer_ip = ip.bin_to_text(peer_ip)
369
370         bgp_attr_bin = buf[cls.HEADER_SIZE:cls.HEADER_SIZE + attr_len]
371         bgp_attributes = []
372         while bgp_attr_bin:
373             attr, bgp_attr_bin = bgp._PathAttribute.parser(bgp_attr_bin)
374             bgp_attributes.append(attr)
375
376         return cls(view_num, seq_num, prefix, prefix_len, status,
377                    originated_time, peer_ip, peer_as, bgp_attributes,
378                    attr_len)
379
380     def serialize(self):
381         bgp_attrs_bin = bytearray()
382         for attr in self.bgp_attributes:
383             bgp_attrs_bin += attr.serialize()
384         self.attr_len = len(bgp_attrs_bin)  # fixup
385
386         prefix = ip.text_to_bin(self.prefix)
387         peer_ip = ip.text_to_bin(self.peer_ip)
388
389         return struct.pack(self._HEADER_FMT,
390                            self.view_num, self.seq_num,
391                            prefix,
392                            self.prefix_len, self.status,
393                            self.originated_time,
394                            peer_ip,
395                            self.peer_as, self.attr_len) + bgp_attrs_bin
396
397
398 @MrtRecord.register_type(MrtRecord.TYPE_TABLE_DUMP)
399 class TableDumpMrtRecord(MrtCommonRecord):
400     """
401     MRT Record for the TABLE_DUMP Type.
402     """
403     MESSAGE_CLS = TableDumpMrtMessage
404
405     # MRT Subtype
406     SUBTYPE_AFI_IPv4 = 1
407     SUBTYPE_AFI_IPv6 = 2
408
409
410 @TableDumpMrtMessage.register_type(TableDumpMrtRecord.SUBTYPE_AFI_IPv4)
411 class TableDumpAfiIPv4MrtMessage(TableDumpMrtMessage):
412     """
413     MRT Message for the TABLE_DUMP Type and the AFI_IPv4 subtype.
414     """
415     _HEADER_FMT = '!HH4sBBI4sHH'
416     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
417
418
419 @TableDumpMrtMessage.register_type(TableDumpMrtRecord.SUBTYPE_AFI_IPv6)
420 class TableDumpAfiIPv6MrtMessage(TableDumpMrtMessage):
421     """
422     MRT Message for the TABLE_DUMP Type and the AFI_IPv6 subtype.
423     """
424     _HEADER_FMT = '!HH16sBBI16sHH'
425     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
426
427
428 @six.add_metaclass(abc.ABCMeta)
429 class TableDump2MrtMessage(MrtMessage):
430     """
431     MRT Message for the TABLE_DUMP_V2 Type.
432     """
433
434
435 @MrtRecord.register_type(MrtRecord.TYPE_TABLE_DUMP_V2)
436 class TableDump2MrtRecord(MrtCommonRecord):
437     MESSAGE_CLS = TableDump2MrtMessage
438
439     # MRT Subtype
440     SUBTYPE_PEER_INDEX_TABLE = 1
441     SUBTYPE_RIB_IPV4_UNICAST = 2
442     SUBTYPE_RIB_IPV4_MULTICAST = 3
443     SUBTYPE_RIB_IPV6_UNICAST = 4
444     SUBTYPE_RIB_IPV6_MULTICAST = 5
445     SUBTYPE_RIB_GENERIC = 6
446     SUBTYPE_RIB_IPV4_UNICAST_ADDPATH = 8
447     SUBTYPE_RIB_IPV4_MULTICAST_ADDPATH = 9
448     SUBTYPE_RIB_IPV6_UNICAST_ADDPATH = 10
449     SUBTYPE_RIB_IPV6_MULTICAST_ADDPATH = 11
450     SUBTYPE_RIB_GENERIC_ADDPATH = 12
451
452
453 @TableDump2MrtMessage.register_type(
454     TableDump2MrtRecord.SUBTYPE_PEER_INDEX_TABLE)
455 class TableDump2PeerIndexTableMrtMessage(TableDump2MrtMessage):
456     """
457     MRT Message for the TABLE_DUMP_V2 Type and the PEER_INDEX_TABLE subtype.
458     """
459     #  0                   1                   2                   3
460     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
461     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
462     # |                      Collector BGP ID                         |
463     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
464     # |       View Name Length        |     View Name (variable)      |
465     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
466     # |          Peer Count           |    Peer Entries (variable)
467     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
468     _HEADER_FMT = '!4sH'
469     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
470     _PEER_COUNT_FMT = '!H'
471     PEER_COUNT_SIZE = struct.calcsize(_PEER_COUNT_FMT)
472     _TYPE = {
473         'ascii': [
474             'bgp_id',
475         ],
476     }
477
478     def __init__(self, bgp_id, peer_entries,
479                  view_name='', view_name_len=None, peer_count=None):
480         self.bgp_id = bgp_id
481         assert isinstance(peer_entries, (list, tuple))
482         for p in peer_entries:
483             assert isinstance(p, MrtPeer)
484         self.peer_entries = peer_entries
485         assert isinstance(view_name, str)
486         self.view_name = view_name
487         self.view_name_len = view_name_len
488         self.peer_count = peer_count
489
490     @classmethod
491     def parse(cls, buf):
492         (bgp_id, view_name_len) = struct.unpack_from(cls._HEADER_FMT, buf)
493         bgp_id = addrconv.ipv4.bin_to_text(bgp_id)
494         offset = cls.HEADER_SIZE
495
496         (view_name,) = struct.unpack_from('!%ds' % view_name_len, buf, offset)
497         view_name = str(view_name.decode('utf-8'))
498         offset += view_name_len
499
500         (peer_count,) = struct.unpack_from(cls._PEER_COUNT_FMT, buf, offset)
501         offset += cls.PEER_COUNT_SIZE
502
503         rest = buf[offset:]
504         peer_entries = []
505         for i in range(peer_count):
506             p, rest = MrtPeer.parse(rest)
507             peer_entries.insert(i, p)
508
509         return cls(bgp_id, peer_entries, view_name, view_name_len, peer_count)
510
511     def serialize(self):
512         view_name = self.view_name.encode('utf-8')
513         self.view_name_len = len(view_name)  # fixup
514
515         self.peer_count = len(self.peer_entries)  # fixup
516
517         buf = struct.pack(self._HEADER_FMT,
518                           addrconv.ipv4.text_to_bin(self.bgp_id),
519                           self.view_name_len) + view_name
520
521         buf += struct.pack(self._PEER_COUNT_FMT,
522                            self.peer_count)
523
524         for p in self.peer_entries:
525             buf += p.serialize()
526
527         return buf
528
529
530 class MrtPeer(stringify.StringifyMixin):
531     """
532     MRT Peer.
533     """
534     #  0                   1                   2                   3
535     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
536     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
537     # |   Peer Type   |
538     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
539     # |                         Peer BGP ID                           |
540     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
541     # |                   Peer IP Address (variable)                  |
542     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
543     # |                        Peer AS (variable)                     |
544     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
545     _HEADER_FMT = '!B4s'
546     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
547
548     # Peer Type field:
549     #
550     #  0 1 2 3 4 5 6 7
551     # +-+-+-+-+-+-+-+-+
552     # | | | | | | |A|I|
553     # +-+-+-+-+-+-+-+-+
554     #
555     #  Bit 6: Peer AS number size:  0 = 2 bytes, 1 = 4 bytes
556     #  Bit 7: Peer IP Address family:  0 = IPv4(4 bytes),  1 = IPv6(16 bytes)
557     IP_ADDR_FAMILY_BIT = 1 << 0
558     AS_NUMBER_SIZE_BIT = 1 << 1
559
560     _TYPE = {
561         'ascii': [
562             'bgp_id',
563             'ip_addr',
564         ],
565     }
566
567     def __init__(self, bgp_id, ip_addr, as_num, type_=0):
568         self.type = type_
569         self.bgp_id = bgp_id
570         self.ip_addr = ip_addr
571         self.as_num = as_num
572
573     @classmethod
574     def parse(cls, buf):
575         (type_, bgp_id) = struct.unpack_from(cls._HEADER_FMT, buf)
576         bgp_id = addrconv.ipv4.bin_to_text(bgp_id)
577         offset = cls.HEADER_SIZE
578
579         if type_ & cls.IP_ADDR_FAMILY_BIT:
580             # IPv6 address family
581             ip_addr_len = 16
582         else:
583             # IPv4 address family
584             ip_addr_len = 4
585         ip_addr = ip.bin_to_text(buf[offset:offset + ip_addr_len])
586         offset += ip_addr_len
587
588         if type_ & cls.AS_NUMBER_SIZE_BIT:
589             # Four octet AS number
590             (as_num,) = struct.unpack_from('!I', buf, offset)
591             offset += 4
592         else:
593             # Two octet AS number
594             (as_num,) = struct.unpack_from('!H', buf, offset)
595             offset += 2
596
597         return cls(bgp_id, ip_addr, as_num, type_), buf[offset:]
598
599     def serialize(self):
600         if ip.valid_ipv6(self.ip_addr):
601             # Sets Peer IP Address family bit to IPv6
602             self.type |= self.IP_ADDR_FAMILY_BIT
603         ip_addr = ip.text_to_bin(self.ip_addr)
604
605         if self.type & self.AS_NUMBER_SIZE_BIT or self.as_num > 0xffff:
606             # Four octet AS number
607             self.type |= self.AS_NUMBER_SIZE_BIT
608             as_num = struct.pack('!I', self.as_num)
609         else:
610             # Two octet AS number
611             as_num = struct.pack('!H', self.as_num)
612
613         buf = struct.pack(self._HEADER_FMT,
614                           self.type,
615                           addrconv.ipv4.text_to_bin(self.bgp_id))
616
617         return buf + ip_addr + as_num
618
619
620 @six.add_metaclass(abc.ABCMeta)
621 class TableDump2AfiSafiSpecificRibMrtMessage(TableDump2MrtMessage):
622     """
623     MRT Message for the TABLE_DUMP_V2 Type and the AFI/SAFI-specific
624     RIB subtypes.
625
626     The AFI/SAFI-specific RIB subtypes consist of the RIB_IPV4_UNICAST,
627     RIB_IPV4_MULTICAST, RIB_IPV6_UNICAST, RIB_IPV6_MULTICAST and their
628     additional-path version subtypes.
629     """
630     #  0                   1                   2                   3
631     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
632     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
633     # |                         Sequence Number                       |
634     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
635     # | Prefix Length |
636     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
637     # |                        Prefix (variable)                      |
638     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
639     # |         Entry Count           |  RIB Entries (variable)
640     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
641     _HEADER_FMT = '!I'
642     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
643
644     # Parser class to parse the Prefix field
645     _PREFIX_CLS = None  # should be defined in subclass
646
647     # Is additional-path version?
648     _IS_ADDPATH = False
649
650     def __init__(self, seq_num, prefix, rib_entries, entry_count=None):
651         self.seq_num = seq_num
652         assert isinstance(prefix, self._PREFIX_CLS)
653         self.prefix = prefix
654         self.entry_count = entry_count
655         assert isinstance(rib_entries, (list, tuple))
656         for rib_entry in rib_entries:
657             assert isinstance(rib_entry, MrtRibEntry)
658         self.rib_entries = rib_entries
659
660     @classmethod
661     def parse_rib_entries(cls, buf):
662         (entry_count,) = struct.unpack_from('!H', buf)
663
664         rest = buf[2:]
665         rib_entries = []
666         for i in range(entry_count):
667             r, rest = MrtRibEntry.parse(rest, is_addpath=cls._IS_ADDPATH)
668             rib_entries.insert(i, r)
669
670         return entry_count, rib_entries, rest
671
672     @classmethod
673     def parse(cls, buf):
674         (seq_num,) = struct.unpack_from(cls._HEADER_FMT, buf)
675         rest = buf[cls.HEADER_SIZE:]
676
677         prefix, rest = cls._PREFIX_CLS.parser(rest)
678
679         entry_count, rib_entries, _ = cls.parse_rib_entries(rest)
680
681         return cls(seq_num, prefix, rib_entries, entry_count)
682
683     def serialize_rib_entries(self):
684         self.entry_count = len(self.rib_entries)  # fixup
685
686         rib_entries_bin = bytearray()
687         for r in self.rib_entries:
688             rib_entries_bin += r.serialize()
689
690         return struct.pack('!H', self.entry_count) + rib_entries_bin
691
692     def serialize(self):
693         prefix_bin = self.prefix.serialize()
694
695         rib_bin = self.serialize_rib_entries()  # entry_count + rib_entries
696
697         return struct.pack(self._HEADER_FMT,
698                            self.seq_num) + prefix_bin + rib_bin
699
700
701 @TableDump2MrtMessage.register_type(
702     TableDump2MrtRecord.SUBTYPE_RIB_IPV4_UNICAST)
703 class TableDump2RibIPv4UnicastMrtMessage(
704         TableDump2AfiSafiSpecificRibMrtMessage):
705     """
706     MRT Message for the TABLE_DUMP_V2 Type and the
707     SUBTYPE_RIB_IPV4_UNICAST subtype.
708     """
709     _PREFIX_CLS = bgp.IPAddrPrefix
710
711
712 @TableDump2MrtMessage.register_type(
713     TableDump2MrtRecord.SUBTYPE_RIB_IPV4_MULTICAST)
714 class TableDump2RibIPv4MulticastMrtMessage(
715         TableDump2AfiSafiSpecificRibMrtMessage):
716     """
717     MRT Message for the TABLE_DUMP_V2 Type and the
718     SUBTYPE_RIB_IPV4_MULTICAST subtype.
719     """
720     _PREFIX_CLS = bgp.IPAddrPrefix
721
722
723 @TableDump2MrtMessage.register_type(
724     TableDump2MrtRecord.SUBTYPE_RIB_IPV6_UNICAST)
725 class TableDump2RibIPv6UnicastMrtMessage(
726         TableDump2AfiSafiSpecificRibMrtMessage):
727     """
728     MRT Message for the TABLE_DUMP_V2 Type and the
729     SUBTYPE_RIB_IPV6_MULTICAST subtype.
730     """
731     _PREFIX_CLS = bgp.IP6AddrPrefix
732
733
734 @TableDump2MrtMessage.register_type(
735     TableDump2MrtRecord.SUBTYPE_RIB_IPV6_MULTICAST)
736 class TableDump2RibIPv6MulticastMrtMessage(
737         TableDump2AfiSafiSpecificRibMrtMessage):
738     """
739     MRT Message for the TABLE_DUMP_V2 Type and the
740     SUBTYPE_RIB_IPV6_MULTICAST subtype.
741     """
742     _PREFIX_CLS = bgp.IP6AddrPrefix
743
744
745 @TableDump2MrtMessage.register_type(
746     TableDump2MrtRecord.SUBTYPE_RIB_IPV4_UNICAST_ADDPATH)
747 class TableDump2RibIPv4UnicastAddPathMrtMessage(
748         TableDump2AfiSafiSpecificRibMrtMessage):
749     """
750     MRT Message for the TABLE_DUMP_V2 Type and the
751     SUBTYPE_RIB_IPV4_UNICAST_ADDPATH subtype.
752     """
753     _PREFIX_CLS = bgp.IPAddrPrefix
754     _IS_ADDPATH = True
755
756
757 @TableDump2MrtMessage.register_type(
758     TableDump2MrtRecord.SUBTYPE_RIB_IPV4_MULTICAST_ADDPATH)
759 class TableDump2RibIPv4MulticastAddPathMrtMessage(
760         TableDump2AfiSafiSpecificRibMrtMessage):
761     """
762     MRT Message for the TABLE_DUMP_V2 Type and the
763     SUBTYPE_RIB_IPV4_MULTICAST_ADDPATH subtype.
764     """
765     _PREFIX_CLS = bgp.IPAddrPrefix
766     _IS_ADDPATH = True
767
768
769 @TableDump2MrtMessage.register_type(
770     TableDump2MrtRecord.SUBTYPE_RIB_IPV6_UNICAST_ADDPATH)
771 class TableDump2RibIPv6UnicastAddPathMrtMessage(
772         TableDump2AfiSafiSpecificRibMrtMessage):
773     """
774     MRT Message for the TABLE_DUMP_V2 Type and the
775     SUBTYPE_RIB_IPV6_UNICAST_ADDPATH subtype.
776     """
777     _PREFIX_CLS = bgp.IP6AddrPrefix
778     _IS_ADDPATH = True
779
780
781 @TableDump2MrtMessage.register_type(
782     TableDump2MrtRecord.SUBTYPE_RIB_IPV6_MULTICAST_ADDPATH)
783 class TableDump2RibIPv6MulticastAddPathMrtMessage(
784         TableDump2AfiSafiSpecificRibMrtMessage):
785     """
786     MRT Message for the TABLE_DUMP_V2 Type and the
787     SUBTYPE_RIB_IPV6_MULTICAST_ADDPATH subtype.
788     """
789     _PREFIX_CLS = bgp.IP6AddrPrefix
790     _IS_ADDPATH = True
791
792
793 @TableDump2MrtMessage.register_type(
794     TableDump2MrtRecord.SUBTYPE_RIB_GENERIC)
795 class TableDump2RibGenericMrtMessage(TableDump2MrtMessage):
796     """
797     MRT Message for the TABLE_DUMP_V2 Type and the generic RIB subtypes.
798
799     The generic RIB subtypes consist of the RIB_GENERIC and
800     RIB_GENERIC_ADDPATH subtypes.
801     """
802     #  0                   1                   2                   3
803     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
804     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
805     # |                         Sequence Number                       |
806     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
807     # |    Address Family Identifier  |Subsequent AFI |
808     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
809     # |     Network Layer Reachability Information (variable)         |
810     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
811     # |         Entry Count           |  RIB Entries (variable)
812     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
813     _HEADER_FMT = '!IHB'
814     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
815
816     # Is additional-path version?
817     _IS_ADDPATH = False
818
819     def __init__(self, seq_num, afi, safi, nlri, rib_entries,
820                  entry_count=None):
821         self.seq_num = seq_num
822         self.afi = afi
823         self.safi = safi
824         assert isinstance(nlri, bgp._AddrPrefix)
825         self.nlri = nlri
826         self.entry_count = entry_count
827         assert isinstance(rib_entries, (list, tuple))
828         for rib_entry in rib_entries:
829             assert isinstance(rib_entry, MrtRibEntry)
830         self.rib_entries = rib_entries
831
832     @classmethod
833     def parse_rib_entries(cls, buf):
834         (entry_count,) = struct.unpack_from('!H', buf)
835
836         rest = buf[2:]
837         rib_entries = []
838         for i in range(entry_count):
839             r, rest = MrtRibEntry.parse(rest, is_addpath=cls._IS_ADDPATH)
840             rib_entries.insert(i, r)
841
842         return entry_count, rib_entries, rest
843
844     @classmethod
845     def parse(cls, buf):
846         (seq_num, afi, safi) = struct.unpack_from(cls._HEADER_FMT, buf)
847         rest = buf[cls.HEADER_SIZE:]
848
849         nlri, rest = bgp.BGPNLRI.parser(rest)
850
851         entry_count, rib_entries, _ = cls.parse_rib_entries(rest)
852
853         return cls(seq_num, afi, safi, nlri, rib_entries, entry_count)
854
855     def serialize_rib_entries(self):
856         self.entry_count = len(self.rib_entries)  # fixup
857
858         rib_entries_bin = bytearray()
859         for r in self.rib_entries:
860             rib_entries_bin += r.serialize()
861
862         return struct.pack('!H', self.entry_count) + rib_entries_bin
863
864     def serialize(self):
865         nlri_bin = self.nlri.serialize()
866
867         rib_bin = self.serialize_rib_entries()  # entry_count + rib_entries
868
869         return struct.pack(self._HEADER_FMT,
870                            self.seq_num,
871                            self.afi, self.safi) + nlri_bin + rib_bin
872
873
874 @TableDump2MrtMessage.register_type(
875     TableDump2MrtRecord.SUBTYPE_RIB_GENERIC_ADDPATH)
876 class TableDump2RibGenericAddPathMrtMessage(TableDump2RibGenericMrtMessage):
877     """
878     MRT Message for the TABLE_DUMP_V2 Type and the RIB_GENERIC_ADDPATH
879     subtype.
880     """
881     _IS_ADDPATH = True
882
883
884 class MrtRibEntry(stringify.StringifyMixin):
885     """
886     MRT RIB Entry.
887     """
888     #  0                   1                   2                   3
889     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
890     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
891     # |         Peer Index            |
892     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
893     # |                         Originated Time                       |
894     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
895     # |                        (Path Identifier)                      |
896     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
897     # |      Attribute Length         |
898     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
899     # |                    BGP Attributes... (variable)
900     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
901     # peer_index, originated_time, attr_len
902     _HEADER_FMT = '!HIH'
903     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
904     # peer_index, originated_time, path_id, attr_len
905     _HEADER_FMT_ADDPATH = '!HIIH'
906     HEADER_SIZE_ADDPATH = struct.calcsize(_HEADER_FMT_ADDPATH)
907
908     def __init__(self, peer_index, originated_time, bgp_attributes,
909                  attr_len=None, path_id=None):
910         self.peer_index = peer_index
911         self.originated_time = originated_time
912         assert isinstance(bgp_attributes, (list, tuple))
913         for attr in bgp_attributes:
914             assert isinstance(attr, bgp._PathAttribute)
915         self.bgp_attributes = bgp_attributes
916         self.attr_len = attr_len
917         self.path_id = path_id
918
919     @classmethod
920     def parse(cls, buf, is_addpath=False):
921         path_id = None
922         if not is_addpath:
923             (peer_index, originated_time,
924              attr_len) = struct.unpack_from(cls._HEADER_FMT, buf)
925             _header_size = cls.HEADER_SIZE
926         else:
927             (peer_index, originated_time, path_id,
928              attr_len) = struct.unpack_from(cls._HEADER_FMT_ADDPATH, buf)
929             _header_size = cls.HEADER_SIZE_ADDPATH
930
931         bgp_attr_bin = buf[_header_size:_header_size + attr_len]
932         bgp_attributes = []
933         while bgp_attr_bin:
934             attr, bgp_attr_bin = bgp._PathAttribute.parser(bgp_attr_bin)
935             bgp_attributes.append(attr)
936
937         return cls(peer_index, originated_time, bgp_attributes,
938                    attr_len, path_id), buf[_header_size + attr_len:]
939
940     def serialize(self):
941         bgp_attrs_bin = bytearray()
942         for attr in self.bgp_attributes:
943             bgp_attrs_bin += attr.serialize()
944         self.attr_len = len(bgp_attrs_bin)  # fixup
945
946         if self.path_id is None:
947             return struct.pack(self._HEADER_FMT,
948                                self.peer_index,
949                                self.originated_time,
950                                self.attr_len) + bgp_attrs_bin
951         else:
952             return struct.pack(self._HEADER_FMT_ADDPATH,
953                                self.peer_index,
954                                self.originated_time,
955                                self.path_id,
956                                self.attr_len) + bgp_attrs_bin
957
958
959 @six.add_metaclass(abc.ABCMeta)
960 class Bgp4MpMrtMessage(MrtMessage):
961     """
962     MRT Message for the BGP4MP Type.
963     """
964     _TYPE = {
965         'ascii': [
966             'peer_ip',
967             'local_ip',
968         ],
969     }
970
971
972 @MrtRecord.register_type(MrtRecord.TYPE_BGP4MP)
973 class Bgp4MpMrtRecord(MrtCommonRecord):
974     MESSAGE_CLS = Bgp4MpMrtMessage
975
976     # MRT Subtype
977     SUBTYPE_BGP4MP_STATE_CHANGE = 0
978     SUBTYPE_BGP4MP_MESSAGE = 1
979     SUBTYPE_BGP4MP_MESSAGE_AS4 = 4
980     SUBTYPE_BGP4MP_STATE_CHANGE_AS4 = 5
981     SUBTYPE_BGP4MP_MESSAGE_LOCAL = 6
982     SUBTYPE_BGP4MP_MESSAGE_AS4_LOCAL = 7
983     SUBTYPE_BGP4MP_MESSAGE_ADDPATH = 8
984     SUBTYPE_BGP4MP_MESSAGE_AS4_ADDPATH = 9
985     SUBTYPE_BGP4MP_MESSAGE_LOCAL_ADDPATH = 10
986     SUBTYPE_BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH = 11
987
988
989 @MrtRecord.register_type(MrtRecord.TYPE_BGP4MP_ET)
990 class Bgp4MpEtMrtRecord(ExtendedTimestampMrtRecord):
991     MESSAGE_CLS = Bgp4MpMrtMessage
992
993     # MRT Subtype
994     SUBTYPE_BGP4MP_STATE_CHANGE = 0
995     SUBTYPE_BGP4MP_MESSAGE = 1
996     SUBTYPE_BGP4MP_MESSAGE_AS4 = 4
997     SUBTYPE_BGP4MP_STATE_CHANGE_AS4 = 5
998     SUBTYPE_BGP4MP_MESSAGE_LOCAL = 6
999     SUBTYPE_BGP4MP_MESSAGE_AS4_LOCAL = 7
1000     SUBTYPE_BGP4MP_MESSAGE_ADDPATH = 8
1001     SUBTYPE_BGP4MP_MESSAGE_AS4_ADDPATH = 9
1002     SUBTYPE_BGP4MP_MESSAGE_LOCAL_ADDPATH = 10
1003     SUBTYPE_BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH = 11
1004
1005
1006 @Bgp4MpMrtMessage.register_type(
1007     Bgp4MpMrtRecord.SUBTYPE_BGP4MP_STATE_CHANGE)
1008 class Bgp4MpStateChangeMrtMessage(Bgp4MpMrtMessage):
1009     """
1010     MRT Message for the BGP4MP Type and the BGP4MP_STATE_CHANGE subtype.
1011     """
1012     #  0                   1                   2                   3
1013     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1014     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1015     # |         Peer AS Number        |        Local AS Number        |
1016     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1017     # |        Interface Index        |        Address Family         |
1018     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1019     # |                      Peer IP Address (variable)               |
1020     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1021     # |                      Local IP Address (variable)              |
1022     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1023     # |            Old State          |          New State            |
1024     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1025     _HEADER_FMT = '!HHHH'
1026     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
1027     _ADDRS_FMT = '!%ds%ds'
1028     _STATES_FMT = '!HH'
1029     STATES_SIZE = struct.calcsize(_STATES_FMT)
1030
1031     # FSM states
1032     STATE_IDLE = 1
1033     STATE_CONNECT = 2
1034     STATE_ACTIVE = 3
1035     STATE_OPEN_SENT = 4
1036     STATE_OPEN_CONFIRM = 5
1037     STATE_ESTABLISHED = 6
1038
1039     # Address Family types
1040     AFI_IPv4 = 1
1041     AFI_IPv6 = 2
1042
1043     def __init__(self, peer_as, local_as, if_index,
1044                  peer_ip, local_ip, old_state, new_state, afi=None):
1045         self.peer_as = peer_as
1046         self.local_as = local_as
1047         self.if_index = if_index
1048         self.afi = afi
1049         self.peer_ip = peer_ip
1050         self.local_ip = local_ip
1051         self.old_state = old_state
1052         self.new_state = new_state
1053
1054     @classmethod
1055     def parse(cls, buf):
1056         (peer_as, local_as, if_index, afi) = struct.unpack_from(
1057             cls._HEADER_FMT, buf)
1058         offset = cls.HEADER_SIZE
1059
1060         if afi == cls.AFI_IPv4:
1061             # IPv4 Address
1062             addrs_fmt = cls._ADDRS_FMT % (4, 4)
1063         elif afi == cls.AFI_IPv6:
1064             # IPv6 Address
1065             addrs_fmt = cls._ADDRS_FMT % (16, 16)
1066         else:
1067             raise struct.error('Unsupported address family: %d' % afi)
1068
1069         (peer_ip, local_ip) = struct.unpack_from(addrs_fmt, buf, offset)
1070         peer_ip = ip.bin_to_text(peer_ip)
1071         local_ip = ip.bin_to_text(local_ip)
1072         offset += struct.calcsize(addrs_fmt)
1073
1074         (old_state, new_state) = struct.unpack_from(
1075             cls._STATES_FMT, buf, offset)
1076
1077         return cls(peer_as, local_as, if_index,
1078                    peer_ip, local_ip, old_state, new_state, afi)
1079
1080     def serialize(self):
1081         # fixup
1082         if ip.valid_ipv4(self.peer_ip) and ip.valid_ipv4(self.local_ip):
1083             self.afi = self.AFI_IPv4
1084         elif ip.valid_ipv6(self.peer_ip) and ip.valid_ipv6(self.local_ip):
1085             self.afi = self.AFI_IPv6
1086         else:
1087             raise ValueError(
1088                 'peer_ip and local_ip must be the same address family: '
1089                 'peer_ip=%s, local_ip=%s' % (self.peer_ip, self.local_ip))
1090
1091         buf = struct.pack(self._HEADER_FMT,
1092                           self.peer_as, self.local_as,
1093                           self.if_index, self.afi)
1094
1095         buf += ip.text_to_bin(self.peer_ip)
1096         buf += ip.text_to_bin(self.local_ip)
1097
1098         buf += struct.pack(self._STATES_FMT,
1099                            self.old_state, self.new_state)
1100
1101         return buf
1102
1103
1104 @Bgp4MpMrtMessage.register_type(
1105     Bgp4MpMrtRecord.SUBTYPE_BGP4MP_MESSAGE)
1106 class Bgp4MpMessageMrtMessage(Bgp4MpMrtMessage):
1107     """
1108     MRT Message for the BGP4MP Type and the BGP4MP_MESSAGE subtype.
1109     """
1110     #  0                   1                   2                   3
1111     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1112     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1113     # |         Peer AS Number        |        Local AS Number        |
1114     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1115     # |        Interface Index        |        Address Family         |
1116     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1117     # |                      Peer IP Address (variable)               |
1118     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1119     # |                      Local IP Address (variable)              |
1120     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1121     # |                    BGP Message... (variable)
1122     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1123     _HEADER_FMT = '!HHHH'
1124     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
1125     _ADDRS_FMT = '!%ds%ds'
1126
1127     # Address Family types
1128     AFI_IPv4 = 1
1129     AFI_IPv6 = 2
1130
1131     def __init__(self, peer_as, local_as, if_index,
1132                  peer_ip, local_ip, bgp_message, afi=None):
1133         self.peer_as = peer_as
1134         self.local_as = local_as
1135         self.if_index = if_index
1136         self.peer_ip = peer_ip
1137         self.local_ip = local_ip
1138         assert isinstance(bgp_message, bgp.BGPMessage)
1139         self.bgp_message = bgp_message
1140         self.afi = afi
1141
1142     @classmethod
1143     def parse(cls, buf):
1144         (peer_as, local_as, if_index, afi) = struct.unpack_from(
1145             cls._HEADER_FMT, buf)
1146         offset = cls.HEADER_SIZE
1147
1148         if afi == cls.AFI_IPv4:
1149             # IPv4 Address
1150             addrs_fmt = cls._ADDRS_FMT % (4, 4)
1151         elif afi == cls.AFI_IPv6:
1152             # IPv6 Address
1153             addrs_fmt = cls._ADDRS_FMT % (16, 16)
1154         else:
1155             raise struct.error('Unsupported address family: %d' % afi)
1156
1157         (peer_ip, local_ip) = struct.unpack_from(addrs_fmt, buf, offset)
1158         peer_ip = ip.bin_to_text(peer_ip)
1159         local_ip = ip.bin_to_text(local_ip)
1160         offset += struct.calcsize(addrs_fmt)
1161
1162         rest = buf[offset:]
1163         bgp_message, _, _ = bgp.BGPMessage.parser(rest)
1164
1165         return cls(peer_as, local_as, if_index,
1166                    peer_ip, local_ip, bgp_message, afi)
1167
1168     def serialize(self):
1169         # fixup
1170         if ip.valid_ipv4(self.peer_ip) and ip.valid_ipv4(self.local_ip):
1171             self.afi = self.AFI_IPv4
1172         elif ip.valid_ipv6(self.peer_ip) and ip.valid_ipv6(self.local_ip):
1173             self.afi = self.AFI_IPv6
1174         else:
1175             raise ValueError(
1176                 'peer_ip and local_ip must be the same address family: '
1177                 'peer_ip=%s, local_ip=%s' % (self.peer_ip, self.local_ip))
1178
1179         buf = struct.pack(self._HEADER_FMT,
1180                           self.peer_as, self.local_as,
1181                           self.if_index, self.afi)
1182
1183         buf += ip.text_to_bin(self.peer_ip)
1184         buf += ip.text_to_bin(self.local_ip)
1185
1186         buf += self.bgp_message.serialize()
1187
1188         return buf
1189
1190
1191 @Bgp4MpMrtMessage.register_type(
1192     Bgp4MpMrtRecord.SUBTYPE_BGP4MP_MESSAGE_AS4)
1193 class Bgp4MpMessageAs4MrtMessage(Bgp4MpMessageMrtMessage):
1194     """
1195     MRT Message for the BGP4MP Type and the BGP4MP_MESSAGE_AS4 subtype.
1196     """
1197     #  0                   1                   2                   3
1198     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1199     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1200     # |                         Peer AS Number                        |
1201     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1202     # |                         Local AS Number                       |
1203     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1204     # |        Interface Index        |        Address Family         |
1205     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1206     # |                      Peer IP Address (variable)               |
1207     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1208     # |                      Local IP Address (variable)              |
1209     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1210     # |                    BGP Message... (variable)
1211     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1212     _HEADER_FMT = '!IIHH'
1213     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
1214
1215
1216 @Bgp4MpMrtMessage.register_type(
1217     Bgp4MpMrtRecord.SUBTYPE_BGP4MP_STATE_CHANGE_AS4)
1218 class Bgp4MpStateChangeAs4MrtMessage(Bgp4MpStateChangeMrtMessage):
1219     """
1220     MRT Message for the BGP4MP Type and the BGP4MP_STATE_CHANGE_AS4 subtype.
1221     """
1222     #  0                   1                   2                   3
1223     #  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1224     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1225     # |                         Peer AS Number                        |
1226     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1227     # |                         Local AS Number                       |
1228     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1229     # |        Interface Index        |        Address Family         |
1230     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1231     # |                      Peer IP Address (variable)               |
1232     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1233     # |                      Local IP Address (variable)              |
1234     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1235     # |            Old State          |          New State            |
1236     # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1237     _HEADER_FMT = '!IIHH'
1238     HEADER_SIZE = struct.calcsize(_HEADER_FMT)
1239
1240
1241 @Bgp4MpMrtMessage.register_type(
1242     Bgp4MpMrtRecord.SUBTYPE_BGP4MP_MESSAGE_LOCAL)
1243 class Bgp4MpMessageLocalMrtMessage(Bgp4MpMessageMrtMessage):
1244     """
1245     MRT Message for the BGP4MP Type and the BGP4MP_MESSAGE_LOCAL subtype.
1246     """
1247
1248
1249 @Bgp4MpMrtMessage.register_type(
1250     Bgp4MpMrtRecord.SUBTYPE_BGP4MP_MESSAGE_AS4_LOCAL)
1251 class Bgp4MpMessageAs4LocalMrtMessage(Bgp4MpMessageAs4MrtMessage):
1252     """
1253     MRT Message for the BGP4MP Type and the BGP4MP_MESSAGE_AS4_LOCAL subtype.
1254     """
1255
1256
1257 # TODO:
1258 # Currently, Ryu does not provide the packet library for ISIS protocol.
1259 # Implement parser for ISIS MRT message.
1260 # class IsisMrtRecord(MrtCommonRecord):
1261 # class IsisMrtMessage(MrtMessage):
1262
1263
1264 # TODO:
1265 # Currently, Ryu does not provide the packet library for OSPFv3 protocol.
1266 # Implement the parser for OSPFv3 MRT message.
1267 # class Ospf3MrtRecord(MrtCommonRecord):
1268 # class Ospf3MrtMessage(MrtMessage):
1269
1270
1271 class Reader(object):
1272     """
1273     MRT format file reader.
1274
1275     ========= ================================================
1276     Argument  Description
1277     ========= ================================================
1278     f         File object which reading MRT format file
1279               in binary mode.
1280     ========= ================================================
1281
1282     Example of Usage::
1283
1284         import bz2
1285         from ryu.lib import mrtlib
1286
1287         count = 0
1288         for record in mrtlib.Reader(
1289                 bz2.BZ2File('rib.YYYYMMDD.hhmm.bz2', 'rb')):
1290             print("%d, %s" % (count, record))
1291             count += 1
1292     """
1293
1294     def __init__(self, f):
1295         self._f = f
1296
1297     def __iter__(self):
1298         return self
1299
1300     def next(self):
1301         header_buf = self._f.read(MrtRecord.HEADER_SIZE)
1302         if len(header_buf) < MrtRecord.HEADER_SIZE:
1303             raise StopIteration()
1304
1305         # Hack to avoid eating memory up
1306         self._f.seek(-MrtRecord.HEADER_SIZE, 1)
1307         required_len = MrtRecord.parse_pre(header_buf)
1308         buf = self._f.read(required_len)
1309         record, _ = MrtRecord.parse(buf)
1310
1311         return record
1312
1313     # for Python 3 compatible
1314     __next__ = next
1315
1316     def close(self):
1317         self._f.close()
1318
1319     def __del__(self):
1320         self.close()
1321
1322
1323 class Writer(object):
1324     """
1325     MRT format file writer.
1326
1327     ========= ================================================
1328     Argument  Description
1329     ========= ================================================
1330     f         File object which writing MRT format file
1331               in binary mode.
1332     ========= ================================================
1333
1334     Example of usage::
1335
1336         import bz2
1337         import time
1338         from ryu.lib import mrtlib
1339         from ryu.lib.packet import bgp
1340
1341         mrt_writer = mrtlib.Writer(
1342             bz2.BZ2File('rib.YYYYMMDD.hhmm.bz2', 'wb'))
1343
1344         prefix = bgp.IPAddrPrefix(24, '10.0.0.0')
1345
1346         rib_entry = mrtlib.MrtRibEntry(
1347             peer_index=0,
1348             originated_time=int(time.time()),
1349             bgp_attributes=[bgp.BGPPathAttributeOrigin(0)])
1350
1351         message = mrtlib.TableDump2RibIPv4UnicastMrtMessage(
1352             seq_num=0,
1353             prefix=prefix,
1354             rib_entries=[rib_entry])
1355
1356         record = mrtlib.TableDump2MrtRecord(
1357             message=message)
1358
1359         mrt_writer.write(record)
1360     """
1361
1362     def __init__(self, f):
1363         self._f = f
1364
1365     def write(self, record):
1366         if not isinstance(record, MrtRecord):
1367             raise ValueError(
1368                 'record should be an instance of MrtRecord subclass')
1369
1370         self._f.write(record.serialize())
1371
1372     def close(self):
1373         self._f.close()
1374
1375     def __del__(self):
1376         self.close()