backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / lib / packet / bmp.py
1 # Copyright (C) 2014 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 BGP Monitoring Protocol draft-ietf-grow-bmp-07
18 """
19
20 import struct
21
22 import six
23
24 from ryu.lib import addrconv
25 from ryu.lib.packet import packet_base
26 from ryu.lib.packet import stream_parser
27 from ryu.lib.packet.bgp import BGPMessage
28 from ryu.lib.type_desc import TypeDisp
29
30 VERSION = 3
31
32 BMP_MSG_ROUTE_MONITORING = 0
33 BMP_MSG_STATISTICS_REPORT = 1
34 BMP_MSG_PEER_DOWN_NOTIFICATION = 2
35 BMP_MSG_PEER_UP_NOTIFICATION = 3
36 BMP_MSG_INITIATION = 4
37 BMP_MSG_TERMINATION = 5
38
39 BMP_PEER_TYPE_GLOBAL = 0
40 BMP_PEER_TYPE_L3VPN = 1
41
42 BMP_INIT_TYPE_STRING = 0
43 BMP_INIT_TYPE_SYSDESCR = 1
44 BMP_INIT_TYPE_SYSNAME = 2
45
46 BMP_TERM_TYPE_STRING = 0
47 BMP_TERM_TYPE_REASON = 1
48
49 BMP_TERM_REASON_ADMIN = 0
50 BMP_TERM_REASON_UNSPEC = 1
51 BMP_TERM_REASON_OUT_OF_RESOURCE = 2
52 BMP_TERM_REASON_REDUNDANT_CONNECTION = 3
53
54 BMP_STAT_TYPE_REJECTED = 0
55 BMP_STAT_TYPE_DUPLICATE_PREFIX = 1
56 BMP_STAT_TYPE_DUPLICATE_WITHDRAW = 2
57 BMP_STAT_TYPE_INV_UPDATE_DUE_TO_CLUSTER_LIST_LOOP = 3
58 BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_PATH_LOOP = 4
59 BMP_STAT_TYPE_INV_UPDATE_DUE_TO_ORIGINATOR_ID = 5
60 BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_CONFED_LOOP = 6
61 BMP_STAT_TYPE_ADJ_RIB_IN = 7
62 BMP_STAT_TYPE_LOC_RIB = 8
63
64 BMP_PEER_DOWN_REASON_UNKNOWN = 0
65 BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION = 1
66 BMP_PEER_DOWN_REASON_LOCAL_NO_NOTIFICATION = 2
67 BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION = 3
68 BMP_PEER_DOWN_REASON_REMOTE_NO_NOTIFICATION = 4
69
70
71 class BMPMessage(packet_base.PacketBase, TypeDisp):
72     r"""Base class for BGP Monitoring Protocol messages.
73
74     An instance has the following attributes at least.
75     Most of them are same to the on-wire counterparts but in host byte
76     order.
77     __init__ takes the corresponding args in this order.
78
79     ========================== ===============================================
80     Attribute                  Description
81     ========================== ===============================================
82     version                    Version. this packet lib defines BMP ver. 3
83     len                        Length field.  Ignored when encoding.
84     type                       Type field.  one of BMP\_MSG\_ constants.
85     ========================== ===============================================
86     """
87
88     _HDR_PACK_STR = '!BIB'  # version, padding, len, type, padding
89     _HDR_LEN = struct.calcsize(_HDR_PACK_STR)
90
91     def __init__(self, type_, len_=None, version=VERSION):
92         self.version = version
93         self.len = len_
94         self.type = type_
95
96     @classmethod
97     def parse_header(cls, buf):
98         if len(buf) < cls._HDR_LEN:
99             raise stream_parser.StreamParser.TooSmallException(
100                 '%d < %d' % (len(buf), cls._HDR_LEN))
101         (version, len_, type_) = struct.unpack_from(cls._HDR_PACK_STR,
102                                                     six.binary_type(buf))
103
104         return version, len_, type_
105
106     @classmethod
107     def parser(cls, buf):
108         version, msglen, type_ = cls.parse_header(buf)
109
110         if version != VERSION:
111             raise ValueError("not supportted bmp version: %d" % version)
112
113         if len(buf) < msglen:
114             raise stream_parser.StreamParser.TooSmallException(
115                 '%d < %d' % (len(buf), msglen))
116
117         binmsg = buf[cls._HDR_LEN:msglen]
118         rest = buf[msglen:]
119         subcls = cls._lookup_type(type_)
120
121         if subcls == cls._UNKNOWN_TYPE:
122             raise ValueError("unknown bmp type: %d" % type_)
123
124         kwargs = subcls.parser(binmsg)
125         return subcls(len_=msglen,
126                       type_=type_, version=version, **kwargs), rest
127
128     def serialize(self):
129         # fixup
130         tail = self.serialize_tail()
131         self.len = self._HDR_LEN + len(tail)
132
133         hdr = bytearray(struct.pack(self._HDR_PACK_STR, self.version,
134                                     self.len, self.type))
135         return hdr + tail
136
137     def __len__(self):
138         # XXX destructive
139         buf = self.serialize()
140         return len(buf)
141
142
143 class BMPPeerMessage(BMPMessage):
144     r"""BMP Message with Per Peer Header
145
146     Following BMP Messages contain Per Peer Header after Common BMP Header.
147
148     - BMP_MSG_TYPE_ROUTE_MONITRING
149     - BMP_MSG_TYPE_STATISTICS_REPORT
150     - BMP_MSG_PEER_UP_NOTIFICATION
151
152     ========================== ===============================================
153     Attribute                  Description
154     ========================== ===============================================
155     version                    Version. this packet lib defines BMP ver. 3
156     len                        Length field.  Ignored when encoding.
157     type                       Type field.  one of BMP\_MSG\_ constants.
158     peer_type                  The type of the peer.
159     is_post_policy             Indicate the message reflects the post-policy
160                                Adj-RIB-In
161     peer_distinguisher         Use for L3VPN router which can have multiple
162                                instance.
163     peer_address               The remote IP address associated with the TCP
164                                session.
165     peer_as                    The Autonomous System number of the peer.
166     peer_bgp_id                The BGP Identifier of the peer
167     timestamp                  The time when the encapsulated routes were
168                                received.
169     ========================== ===============================================
170     """
171
172     _PEER_HDR_PACK_STR = '!BBQ16sI4sII'
173     _TYPE = {
174         'ascii': [
175             'peer_address',
176             'peer_bgp_id'
177         ]
178     }
179
180     def __init__(self, peer_type, is_post_policy, peer_distinguisher,
181                  peer_address, peer_as, peer_bgp_id, timestamp,
182                  version=VERSION, type_=None, len_=None):
183         super(BMPPeerMessage, self).__init__(version=version,
184                                              len_=len_,
185                                              type_=type_)
186         self.peer_type = peer_type
187         self.is_post_policy = is_post_policy
188         self.peer_distinguisher = peer_distinguisher
189         self.peer_address = peer_address
190         self.peer_as = peer_as
191         self.peer_bgp_id = peer_bgp_id
192         self.timestamp = timestamp
193
194     @classmethod
195     def parser(cls, buf):
196         (peer_type, peer_flags, peer_distinguisher,
197          peer_address, peer_as, peer_bgp_id,
198          timestamp1, timestamp2) = struct.unpack_from(cls._PEER_HDR_PACK_STR,
199                                                       six.binary_type(buf))
200
201         rest = buf[struct.calcsize(cls._PEER_HDR_PACK_STR):]
202
203         if peer_flags & (1 << 6):
204             is_post_policy = True
205         else:
206             is_post_policy = False
207
208         if peer_flags & (1 << 7):
209             peer_address = addrconv.ipv6.bin_to_text(peer_address)
210         else:
211             peer_address = addrconv.ipv4.bin_to_text(peer_address[-4:])
212
213         peer_bgp_id = addrconv.ipv4.bin_to_text(peer_bgp_id)
214
215         timestamp = float(timestamp1) + timestamp2 * (10 ** -6)
216
217         return {
218             "peer_type": peer_type,
219             "is_post_policy": is_post_policy,
220             "peer_distinguisher": peer_distinguisher,
221             "peer_address": peer_address,
222             "peer_as": peer_as,
223             "peer_bgp_id": peer_bgp_id,
224             "timestamp": timestamp
225         }, rest
226
227     def serialize_tail(self):
228         flags = 0
229
230         if self.is_post_policy:
231             flags |= (1 << 6)
232
233         if ':' in self.peer_address:
234             flags |= (1 << 7)
235             peer_address = addrconv.ipv6.text_to_bin(self.peer_address)
236         else:
237             peer_address = struct.pack(
238                 '!12x4s', addrconv.ipv4.text_to_bin(self.peer_address))
239
240         peer_bgp_id = addrconv.ipv4.text_to_bin(self.peer_bgp_id)
241
242         t1, t2 = [int(t) for t in ("%.6f" % self.timestamp).split('.')]
243
244         msg = bytearray(struct.pack(self._PEER_HDR_PACK_STR, self.peer_type,
245                                     flags, self.peer_distinguisher,
246                                     peer_address, self.peer_as,
247                                     peer_bgp_id, t1, t2))
248         return msg
249
250
251 @BMPMessage.register_type(BMP_MSG_ROUTE_MONITORING)
252 class BMPRouteMonitoring(BMPPeerMessage):
253     r"""BMP Route Monitoring Message
254
255     ========================== ===============================================
256     Attribute                  Description
257     ========================== ===============================================
258     version                    Version. this packet lib defines BMP ver. 3
259     len                        Length field.  Ignored when encoding.
260     type                       Type field.  one of BMP\_MSG\_ constants.
261     peer_type                  The type of the peer.
262     peer_flags                 Provide more information about the peer.
263     peer_distinguisher         Use for L3VPN router which can have multiple
264                                instance.
265     peer_address               The remote IP address associated with the TCP
266                                session.
267     peer_as                    The Autonomous System number of the peer.
268     peer_bgp_id                The BGP Identifier of the peer
269     timestamp                  The time when the encapsulated routes were
270                                received.
271     bgp_update                 BGP Update PDU
272     ========================== ===============================================
273     """
274
275     def __init__(self, bgp_update, peer_type, is_post_policy,
276                  peer_distinguisher, peer_address, peer_as, peer_bgp_id,
277                  timestamp, version=VERSION, type_=BMP_MSG_ROUTE_MONITORING,
278                  len_=None):
279         super(BMPRouteMonitoring,
280               self).__init__(peer_type=peer_type,
281                              is_post_policy=is_post_policy,
282                              peer_distinguisher=peer_distinguisher,
283                              peer_address=peer_address,
284                              peer_as=peer_as,
285                              peer_bgp_id=peer_bgp_id,
286                              timestamp=timestamp,
287                              len_=len_,
288                              type_=type_,
289                              version=version)
290         self.bgp_update = bgp_update
291
292     @classmethod
293     def parser(cls, buf):
294         kwargs, buf = super(BMPRouteMonitoring, cls).parser(buf)
295
296         bgp_update, _, buf = BGPMessage.parser(buf)
297
298         kwargs['bgp_update'] = bgp_update
299
300         return kwargs
301
302     def serialize_tail(self):
303         msg = super(BMPRouteMonitoring, self).serialize_tail()
304         msg += self.bgp_update.serialize()
305
306         return msg
307
308
309 @BMPMessage.register_type(BMP_MSG_STATISTICS_REPORT)
310 class BMPStatisticsReport(BMPPeerMessage):
311     r"""BMP Statistics Report Message
312
313     ========================== ===============================================
314     Attribute                  Description
315     ========================== ===============================================
316     version                    Version. this packet lib defines BMP ver. 3
317     len                        Length field.  Ignored when encoding.
318     type                       Type field.  one of BMP\_MSG\_ constants.
319     peer_type                  The type of the peer.
320     peer_flags                 Provide more information about the peer.
321     peer_distinguisher         Use for L3VPN router which can have multiple
322                                instance.
323     peer_address               The remote IP address associated with the TCP
324                                session.
325     peer_as                    The Autonomous System number of the peer.
326     peer_bgp_id                The BGP Identifier of the peer
327     timestamp                  The time when the encapsulated routes were
328                                received.
329     stats                      Statistics (one or more stats encoded as a TLV)
330     ========================== ===============================================
331     """
332
333     _TLV_PACK_STR = '!HH'
334     _MIN_LEN = struct.calcsize(_TLV_PACK_STR)
335
336     def __init__(self, stats, peer_type, is_post_policy, peer_distinguisher,
337                  peer_address, peer_as, peer_bgp_id, timestamp,
338                  version=VERSION, type_=BMP_MSG_STATISTICS_REPORT, len_=None):
339         super(BMPStatisticsReport,
340               self).__init__(peer_type=peer_type,
341                              is_post_policy=is_post_policy,
342                              peer_distinguisher=peer_distinguisher,
343                              peer_address=peer_address,
344                              peer_as=peer_as,
345                              peer_bgp_id=peer_bgp_id,
346                              timestamp=timestamp,
347                              len_=len_,
348                              type_=type_,
349                              version=version)
350         self.stats = stats
351
352     @classmethod
353     def parser(cls, buf):
354         kwargs, rest = super(BMPStatisticsReport, cls).parser(buf)
355
356         stats_count, = struct.unpack_from('!I', six.binary_type(rest))
357
358         buf = rest[struct.calcsize('!I'):]
359
360         stats = []
361
362         while len(buf):
363             if len(buf) < cls._MIN_LEN:
364                 raise stream_parser.StreamParser.TooSmallException(
365                     '%d < %d' % (len(buf), cls._MIN_LEN))
366             (type_, len_) = struct.unpack_from(cls._TLV_PACK_STR,
367                                                six.binary_type(buf))
368
369             if len(buf) < (cls._MIN_LEN + len_):
370                 raise stream_parser.StreamParser.TooSmallException(
371                     '%d < %d' % (len(buf), cls._MIN_LEN + len_))
372
373             value = buf[cls._MIN_LEN:cls._MIN_LEN + len_]
374
375             if type_ == BMP_STAT_TYPE_REJECTED or \
376                type_ == BMP_STAT_TYPE_DUPLICATE_PREFIX or \
377                type_ == BMP_STAT_TYPE_DUPLICATE_WITHDRAW or \
378                type_ == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_CLUSTER_LIST_LOOP or \
379                type_ == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_PATH_LOOP or \
380                type_ == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_ORIGINATOR_ID or \
381                type_ == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_CONFED_LOOP:
382                 value, = struct.unpack_from('!I', six.binary_type(value))
383             elif type_ == BMP_STAT_TYPE_ADJ_RIB_IN or \
384                     type_ == BMP_STAT_TYPE_LOC_RIB:
385                 value, = struct.unpack_from('!Q', six.binary_type(value))
386
387             buf = buf[cls._MIN_LEN + len_:]
388
389             stats.append({'type': type_, 'len': len_, 'value': value})
390
391         kwargs['stats'] = stats
392
393         return kwargs
394
395     def serialize_tail(self):
396         msg = super(BMPStatisticsReport, self).serialize_tail()
397
398         stats_count = len(self.stats)
399
400         msg += bytearray(struct.pack('!I', stats_count))
401
402         for v in self.stats:
403             t = v['type']
404             if t == BMP_STAT_TYPE_REJECTED or \
405                t == BMP_STAT_TYPE_DUPLICATE_PREFIX or \
406                t == BMP_STAT_TYPE_DUPLICATE_WITHDRAW or \
407                t == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_CLUSTER_LIST_LOOP or \
408                t == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_PATH_LOOP or \
409                t == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_ORIGINATOR_ID or \
410                t == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_CONFED_LOOP:
411                 valuepackstr = 'I'
412             elif t == BMP_STAT_TYPE_ADJ_RIB_IN or \
413                     t == BMP_STAT_TYPE_LOC_RIB:
414                 valuepackstr = 'Q'
415             else:
416                 continue
417
418             v['len'] = struct.calcsize(valuepackstr)
419             msg += bytearray(struct.pack(self._TLV_PACK_STR + valuepackstr,
420                                          t, v['len'], v['value']))
421
422         return msg
423
424
425 @BMPMessage.register_type(BMP_MSG_PEER_DOWN_NOTIFICATION)
426 class BMPPeerDownNotification(BMPPeerMessage):
427     r"""BMP Peer Down Notification Message
428
429     ========================== ===============================================
430     Attribute                  Description
431     ========================== ===============================================
432     version                    Version. this packet lib defines BMP ver. 3
433     len                        Length field.  Ignored when encoding.
434     type                       Type field.  one of BMP\_MSG\_ constants.
435     reason                     Reason indicates why the session was closed.
436     data                       vary by the reason.
437     ========================== ===============================================
438     """
439
440     def __init__(self, reason, data, peer_type, is_post_policy,
441                  peer_distinguisher, peer_address, peer_as, peer_bgp_id,
442                  timestamp, version=VERSION,
443                  type_=BMP_MSG_PEER_DOWN_NOTIFICATION, len_=None):
444
445         super(BMPPeerDownNotification,
446               self).__init__(peer_type=peer_type,
447                              is_post_policy=is_post_policy,
448                              peer_distinguisher=peer_distinguisher,
449                              peer_address=peer_address,
450                              peer_as=peer_as,
451                              peer_bgp_id=peer_bgp_id,
452                              timestamp=timestamp,
453                              len_=len_,
454                              type_=type_,
455                              version=version)
456
457         self.reason = reason
458         self.data = data
459
460     @classmethod
461     def parser(cls, buf):
462         kwargs, buf = super(BMPPeerDownNotification, cls).parser(buf)
463         reason, = struct.unpack_from('!B', six.binary_type(buf))
464         buf = buf[struct.calcsize('!B'):]
465
466         if reason == BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION:
467             data, _, rest = BGPMessage.parser(buf)
468         elif reason == BMP_PEER_DOWN_REASON_LOCAL_NO_NOTIFICATION:
469             data = struct.unpack_from('!H', six.binary_type(buf))
470         elif reason == BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION:
471             data, _, rest = BGPMessage.parser(buf)
472         elif reason == BMP_PEER_DOWN_REASON_REMOTE_NO_NOTIFICATION:
473             data = None
474         else:
475             reason = BMP_PEER_DOWN_REASON_UNKNOWN
476             data = buf
477
478         kwargs['reason'] = reason
479         kwargs['data'] = data
480
481         return kwargs
482
483     def serialize_tail(self):
484         msg = super(BMPPeerDownNotification, self).serialize_tail()
485         msg += struct.pack('!B', self.reason)
486
487         if self.reason == BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION:
488             msg += self.data.serialize()
489         elif self.reason == BMP_PEER_DOWN_REASON_LOCAL_NO_NOTIFICATION:
490             msg += struct.pack('!H', self.data)
491         elif self.reason == BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION:
492             msg += self.data.serialize()
493         elif self.reason == BMP_PEER_DOWN_REASON_UNKNOWN:
494             msg += str(self.data)
495
496         return msg
497
498
499 @BMPMessage.register_type(BMP_MSG_PEER_UP_NOTIFICATION)
500 class BMPPeerUpNotification(BMPPeerMessage):
501     r"""BMP Peer Up Notification Message
502
503     ========================== ===============================================
504     Attribute                  Description
505     ========================== ===============================================
506     version                    Version. this packet lib defines BMP ver. 3
507     len                        Length field.  Ignored when encoding.
508     type                       Type field.  one of BMP\_MSG\_ constants.
509     peer_type                  The type of the peer.
510     peer_flags                 Provide more information about the peer.
511     peer_distinguisher         Use for L3VPN router which can have multiple
512                                instance.
513     peer_address               The remote IP address associated with the TCP
514                                session.
515     peer_as                    The Autonomous System number of the peer.
516     peer_bgp_id                The BGP Identifier of the peer
517     timestamp                  The time when the encapsulated routes were
518                                received.
519     local_address              The local IP address associated with the
520                                peering TCP session.
521     local_port                 The local port number associated with the
522                                peering TCP session.
523     remote_port                The remote port number associated with the
524                                peering TCP session.
525     sent_open_message          The full OPEN message transmitted by the
526                                monitored router to its peer.
527     received_open_message      The full OPEN message received by the monitored
528                                router from its peer.
529     ========================== ===============================================
530     """
531
532     _PACK_STR = '!16sHH'
533     _MIN_LEN = struct.calcsize(_PACK_STR)
534
535     def __init__(self, local_address, local_port, remote_port,
536                  sent_open_message, received_open_message,
537                  peer_type, is_post_policy, peer_distinguisher,
538                  peer_address, peer_as, peer_bgp_id, timestamp,
539                  version=VERSION, type_=BMP_MSG_PEER_UP_NOTIFICATION,
540                  len_=None):
541         super(BMPPeerUpNotification,
542               self).__init__(peer_type=peer_type,
543                              is_post_policy=is_post_policy,
544                              peer_distinguisher=peer_distinguisher,
545                              peer_address=peer_address,
546                              peer_as=peer_as,
547                              peer_bgp_id=peer_bgp_id,
548                              timestamp=timestamp,
549                              len_=len_,
550                              type_=type_,
551                              version=version)
552         self.local_address = local_address
553         self.local_port = local_port
554         self.remote_port = remote_port
555         self.sent_open_message = sent_open_message
556         self.received_open_message = received_open_message
557
558     @classmethod
559     def parser(cls, buf):
560         kwargs, rest = super(BMPPeerUpNotification, cls).parser(buf)
561
562         (local_address, local_port,
563          remote_port) = struct.unpack_from(cls._PACK_STR, six.binary_type(rest))
564
565         if '.' in kwargs['peer_address']:
566             local_address = addrconv.ipv4.bin_to_text(local_address[-4:])
567         elif ':' in kwargs['peer_address']:
568             local_address = addrconv.ipv6.bin_to_text(local_address)
569         else:
570             raise ValueError("invalid local_address: %s" % local_address)
571
572         kwargs['local_address'] = local_address
573         kwargs['local_port'] = local_port
574         kwargs['remote_port'] = remote_port
575
576         rest = rest[cls._MIN_LEN:]
577
578         sent_open_msg, _, rest = BGPMessage.parser(rest)
579         received_open_msg, _, rest = BGPMessage.parser(rest)
580
581         kwargs['sent_open_message'] = sent_open_msg
582         kwargs['received_open_message'] = received_open_msg
583
584         return kwargs
585
586     def serialize_tail(self):
587         msg = super(BMPPeerUpNotification, self).serialize_tail()
588
589         if '.' in self.local_address:
590             local_address = struct.pack(
591                 '!12x4s', addrconv.ipv4.text_to_bin(self.local_address))
592         elif ':' in self.local_address:
593             local_address = addrconv.ipv6.text_to_bin(self.local_address)
594         else:
595             raise ValueError("invalid local_address: %s" % self.local_address)
596
597         msg += struct.pack(self._PACK_STR, local_address,
598                            self.local_port, self.remote_port)
599
600         msg += self.sent_open_message.serialize()
601         msg += self.received_open_message.serialize()
602
603         return msg
604
605
606 @BMPMessage.register_type(BMP_MSG_INITIATION)
607 class BMPInitiation(BMPMessage):
608     r"""BMP Initiation Message
609
610     ========================== ===============================================
611     Attribute                  Description
612     ========================== ===============================================
613     version                    Version. this packet lib defines BMP ver. 3
614     len                        Length field.  Ignored when encoding.
615     type                       Type field.  one of BMP\_MSG\_ constants.
616     info                       One or more piece of information encoded as a
617                                TLV
618     ========================== ===============================================
619     """
620
621     _TLV_PACK_STR = '!HH'
622     _MIN_LEN = struct.calcsize(_TLV_PACK_STR)
623
624     def __init__(self, info, type_=BMP_MSG_INITIATION, len_=None,
625                  version=VERSION):
626         super(BMPInitiation, self).__init__(type_, len_, version)
627         self.info = info
628
629     @classmethod
630     def parser(cls, buf):
631         info = []
632         while len(buf):
633             if len(buf) < cls._MIN_LEN:
634                 raise stream_parser.StreamParser.TooSmallException(
635                     '%d < %d' % (len(buf), cls._MIN_LEN))
636             (type_, len_) = struct.unpack_from(cls._TLV_PACK_STR,
637                                                six.binary_type(buf))
638
639             if len(buf) < (cls._MIN_LEN + len_):
640                 raise stream_parser.StreamParser.TooSmallException(
641                     '%d < %d' % (len(buf), cls._MIN_LEN + len_))
642
643             value = buf[cls._MIN_LEN:cls._MIN_LEN + len_]
644
645             if type_ == BMP_INIT_TYPE_STRING:
646                 value = value.decode('utf-8')
647
648             buf = buf[cls._MIN_LEN + len_:]
649
650             info.append({'type': type_, 'len': len_, 'value': value})
651
652         return {'info': info}
653
654     def serialize_tail(self):
655         msg = bytearray()
656
657         for v in self.info:
658             if v['type'] == BMP_INIT_TYPE_STRING:
659                 value = v['value'].encode('utf-8')
660             else:
661                 value = v['value']
662
663             v['len'] = len(value)
664             msg += struct.pack(self._TLV_PACK_STR, v['type'], v['len'])
665             msg += value
666
667         return msg
668
669
670 @BMPMessage.register_type(BMP_MSG_TERMINATION)
671 class BMPTermination(BMPMessage):
672     r"""BMP Termination Message
673
674     ========================== ===============================================
675     Attribute                  Description
676     ========================== ===============================================
677     version                    Version. this packet lib defines BMP ver. 3
678     len                        Length field.  Ignored when encoding.
679     type                       Type field.  one of BMP\_MSG\_ constants.
680     info                       One or more piece of information encoded as a
681                                TLV
682     ========================== ===============================================
683     """
684
685     _TLV_PACK_STR = '!HH'
686     _MIN_LEN = struct.calcsize(_TLV_PACK_STR)
687
688     def __init__(self, info, type_=BMP_MSG_TERMINATION, len_=None,
689                  version=VERSION):
690         super(BMPTermination, self).__init__(type_, len_, version)
691         self.info = info
692
693     @classmethod
694     def parser(cls, buf):
695         info = []
696         while len(buf):
697             if len(buf) < cls._MIN_LEN:
698                 raise stream_parser.StreamParser.TooSmallException(
699                     '%d < %d' % (len(buf), cls._MIN_LEN))
700             (type_, len_) = struct.unpack_from(cls._TLV_PACK_STR,
701                                                six.binary_type(buf))
702
703             if len(buf) < (cls._MIN_LEN + len_):
704                 raise stream_parser.StreamParser.TooSmallException(
705                     '%d < %d' % (len(buf), cls._MIN_LEN + len_))
706
707             value = buf[cls._MIN_LEN:cls._MIN_LEN + len_]
708             if type_ == BMP_TERM_TYPE_STRING:
709                 value = value.decode('utf-8')
710             elif type_ == BMP_TERM_TYPE_REASON:
711                 value, = struct.unpack_from('!H', six.binary_type(value))
712
713             buf = buf[cls._MIN_LEN + len_:]
714
715             info.append({'type': type_, 'len': len_, 'value': value})
716
717         return {'info': info}
718
719     def serialize_tail(self):
720         msg = bytearray()
721
722         for v in self.info:
723             if v['type'] == BMP_TERM_TYPE_STRING:
724                 value = v['value'].encode('utf-8')
725             elif v['type'] == BMP_TERM_TYPE_REASON:
726                 value = struct.pack('!H', v['value'])
727             v['len'] = len(value)
728             msg += struct.pack(self._TLV_PACK_STR, v['type'], v['len'])
729             msg += value
730
731         return msg