diff options
author | ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp> | 2014-07-30 16:03:53 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2014-07-30 17:41:37 +0900 |
commit | 7d3b02cfabc30c382e59051f78536b0043040a6b (patch) | |
tree | 55e4c1215f047a1c48d61e8116d4f3ab31f9a952 | |
parent | 40a70a9336bfad9c9953bb5fe4314f7b1bc5806b (diff) |
packet lib: add bmp(BGP Monitoring Protocol)
implements I-D grow-bmp-07
Signed-off-by: ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | ryu/lib/packet/bmp.py | 746 |
1 files changed, 746 insertions, 0 deletions
diff --git a/ryu/lib/packet/bmp.py b/ryu/lib/packet/bmp.py new file mode 100644 index 00000000..207ded84 --- /dev/null +++ b/ryu/lib/packet/bmp.py @@ -0,0 +1,746 @@ +# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +BGP Monitoring Protocol draft-ietf-grow-bmp-07 +""" + +from ryu.lib.packet import packet_base +from ryu.lib.packet import stream_parser +from ryu.lib.packet.bgp import BGPMessage +from ryu.lib import addrconv +import struct + +VERSION = 3 + +BMP_MSG_ROUTE_MONITORING = 0 +BMP_MSG_STATISTICS_REPORT = 1 +BMP_MSG_PEER_DOWN_NOTIFICATION = 2 +BMP_MSG_PEER_UP_NOTIFICATION = 3 +BMP_MSG_INITIATION = 4 +BMP_MSG_TERMINATION = 5 + +BMP_PEER_TYPE_GLOBAL = 0 +BMP_PEER_TYPE_L3VPN = 1 + +BMP_INIT_TYPE_STRING = 0 +BMP_INIT_TYPE_SYSDESCR = 1 +BMP_INIT_TYPE_SYSNAME = 2 + +BMP_TERM_TYPE_STRING = 0 +BMP_TERM_TYPE_REASON = 1 + +BMP_TERM_REASON_ADMIN = 0 +BMP_TERM_REASON_UNSPEC = 1 +BMP_TERM_REASON_OUT_OF_RESOURCE = 2 +BMP_TERM_REASON_REDUNDANT_CONNECTION = 3 + +BMP_STAT_TYPE_REJECTED = 0 +BMP_STAT_TYPE_DUPLICATE_PREFIX = 1 +BMP_STAT_TYPE_DUPLICATE_WITHDRAW = 2 +BMP_STAT_TYPE_INV_UPDATE_DUE_TO_CLUSTER_LIST_LOOP = 3 +BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_PATH_LOOP = 4 +BMP_STAT_TYPE_INV_UPDATE_DUE_TO_ORIGINATOR_ID = 5 +BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_CONFED_LOOP = 6 +BMP_STAT_TYPE_ADJ_RIB_IN = 7 +BMP_STAT_TYPE_LOC_RIB = 8 + +BMP_PEER_DOWN_REASON_UNKNOWN = 0 +BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION = 1 +BMP_PEER_DOWN_REASON_LOCAL_NO_NOTIFICATION = 2 +BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION = 3 +BMP_PEER_DOWN_REASON_REMOTE_NO_NOTIFICATION = 3 + + +class _TypeDisp(object): + _TYPES = {} + _REV_TYPES = None + _UNKNOWN_TYPE = None + + @classmethod + def register_unknown_type(cls): + def _register_type(subcls): + cls._UNKNOWN_TYPE = subcls + return subcls + return _register_type + + @classmethod + def register_type(cls, type_): + cls._TYPES = cls._TYPES.copy() + + def _register_type(subcls): + cls._TYPES[type_] = subcls + cls._REV_TYPES = None + return subcls + return _register_type + + @classmethod + def _lookup_type(cls, type_): + try: + return cls._TYPES[type_] + except KeyError: + return cls._UNKNOWN_TYPE + + @classmethod + def _rev_lookup_type(cls, targ_cls): + if cls._REV_TYPES is None: + rev = dict((v, k) for k, v in cls._TYPES.iteritems()) + cls._REV_TYPES = rev + return cls._REV_TYPES[targ_cls] + + +class BMPMessage(packet_base.PacketBase, _TypeDisp): + """Base class for BGP Monitoring Protocol messages. + + An instance has the following attributes at least. + Most of them are same to the on-wire counterparts but in host byte + order. + __init__ takes the corresponding args in this order. + + ========================== =============================================== + Attribute Description + ========================== =============================================== + version Version. this packet lib defines BMP ver. 3 + len Length field. Ignored when encoding. + type Type field. one of BMP\_MSG\_ constants. + ========================== =============================================== + """ + + _HDR_PACK_STR = '!BIB' # version, padding, len, type, padding + _HDR_LEN = struct.calcsize(_HDR_PACK_STR) + + def __init__(self, type_, len_=None, version=VERSION): + self.version = version + self.len = len_ + self.type = type_ + + @classmethod + def parse_header(cls, buf): + if len(buf) < cls._HDR_LEN: + raise stream_parser.StreamParser.TooSmallException( + '%d < %d' % (len(buf), cls._HDR_LEN)) + (version, len_, type_) = struct.unpack_from(cls._HDR_PACK_STR, + buffer(buf)) + + return version, len_, type_ + + @classmethod + def parser(cls, buf): + version, msglen, type_ = cls.parse_header(buf) + + if version != VERSION: + raise ValueError("not supportted bmp version: %d" % version) + + if len(buf) < msglen: + raise stream_parser.StreamParser.TooSmallException( + '%d < %d' % (len(buf), msglen)) + + binmsg = buf[cls._HDR_LEN:msglen] + rest = buf[msglen:] + subcls = cls._lookup_type(type_) + + if subcls == cls._UNKNOWN_TYPE: + raise ValueError("unknown bmp type: %d" % type_) + + kwargs = subcls.parser(binmsg) + return subcls(len_=msglen, + type_=type_, version=version, **kwargs), rest + + def serialize(self): + # fixup + tail = self.serialize_tail() + self.len = self._HDR_LEN + len(tail) + + hdr = bytearray(struct.pack(self._HDR_PACK_STR, self.version, + self.len, self.type)) + return hdr + tail + + def __len__(self): + # XXX destructive + buf = self.serialize() + return len(buf) + + +class BMPPeerMessage(BMPMessage): + """BMP Message with Per Peer Header + + Following BMP Messages contain Per Peer Header after Common BMP Header. + + - BMP_MSG_TYPE_ROUTE_MONITRING + - BMP_MSG_TYPE_STATISTICS_REPORT + - BMP_MSG_PEER_UP_NOTIFICATION + + ========================== =============================================== + Attribute Description + ========================== =============================================== + version Version. this packet lib defines BMP ver. 3 + len Length field. Ignored when encoding. + type Type field. one of BMP\_MSG\_ constants. + peer_type The type of the peer. + is_post_policy Indicate the message reflects the post-policy + Adj-RIB-In + peer_distinguisher Use for L3VPN router which can have multiple + instance. + peer_address The remote IP address associated with the TCP + session. + peer_as The Autonomous System number of the peer. + peer_bgp_id The BGP Identifier of the peer + timestamp The time when the encapsulated routes were + received. + ========================== =============================================== + """ + + _PEER_HDR_PACK_STR = '!BBQ16sI4sII' + _TYPE = { + 'ascii': [ + 'peer_address', + 'peer_bgp_id' + ] + } + + def __init__(self, peer_type, is_post_policy, peer_distinguisher, + peer_address, peer_as, peer_bgp_id, timestamp, + version=VERSION, type_=None, len_=None): + super(BMPPeerMessage, self).__init__(version=version, + len_=len_, + type_=type_) + self.peer_type = peer_type + self.is_post_policy = is_post_policy + self.peer_distinguisher = peer_distinguisher + self.peer_address = peer_address + self.peer_as = peer_as + self.peer_bgp_id = peer_bgp_id + self.timestamp = timestamp + + @classmethod + def parser(cls, buf): + (peer_type, peer_flags, peer_distinguisher, + peer_address, peer_as, peer_bgp_id, + timestamp1, timestamp2) = struct.unpack_from(cls._PEER_HDR_PACK_STR, + buffer(buf)) + + rest = buf[struct.calcsize(cls._PEER_HDR_PACK_STR):] + + if peer_flags & (1 << 6): + is_post_policy = True + else: + is_post_policy = False + + if peer_flags & (1 << 7): + peer_address = addrconv.ipv6.bin_to_text(buffer(peer_address)) + else: + peer_address = addrconv.ipv4.bin_to_text(buffer(peer_address[:4])) + + peer_bgp_id = addrconv.ipv4.bin_to_text(buffer(peer_bgp_id)) + + timestamp = float(timestamp1) + timestamp2 * (10 ** -6) + + return { + "peer_type": peer_type, + "is_post_policy": is_post_policy, + "peer_distinguisher": peer_distinguisher, + "peer_address": peer_address, + "peer_as": peer_as, + "peer_bgp_id": peer_bgp_id, + "timestamp": timestamp + }, rest + + def serialize_tail(self): + flags = 0 + + if self.is_post_policy: + flags |= (1 << 6) + + if ':' in self.peer_address: + flags |= (1 << 7) + peer_address = addrconv.ipv6.text_to_bin(self.peer_address) + else: + peer_address = addrconv.ipv4.text_to_bin(self.peer_address) + + peer_bgp_id = addrconv.ipv4.text_to_bin(self.peer_bgp_id) + + t1, t2 = [int(t) for t in ("%.6f" % self.timestamp).split('.')] + + msg = bytearray(struct.pack(self._PEER_HDR_PACK_STR, self.peer_type, + flags, self.peer_distinguisher, + peer_address, self.peer_as, + peer_bgp_id, t1, t2)) + return msg + + +@BMPMessage.register_type(BMP_MSG_ROUTE_MONITORING) +class BMPRouteMonitoring(BMPPeerMessage): + """BMP Route Monitoring Message + + ========================== =============================================== + Attribute Description + ========================== =============================================== + version Version. this packet lib defines BMP ver. 3 + len Length field. Ignored when encoding. + type Type field. one of BMP\_MSG\_ constants. + peer_type The type of the peer. + peer_flags Provide more information about the peer. + peer_distinguisher Use for L3VPN router which can have multiple + instance. + peer_address The remote IP address associated with the TCP + session. + peer_as The Autonomous System number of the peer. + peer_bgp_id The BGP Identifier of the peer + timestamp The time when the encapsulated routes were + received. + bgp_update BGP Update PDU + ========================== =============================================== + """ + + def __init__(self, bgp_update, peer_type, is_post_policy, + peer_distinguisher, peer_address, peer_as, peer_bgp_id, + timestamp, version=VERSION, type_=BMP_MSG_ROUTE_MONITORING, + len_=None): + super(BMPRouteMonitoring, + self).__init__(peer_type=peer_type, + is_post_policy=is_post_policy, + peer_distinguisher=peer_distinguisher, + peer_address=peer_address, + peer_as=peer_as, + peer_bgp_id=peer_bgp_id, + timestamp=timestamp, + len_=len_, + type_=type_, + version=version) + self.bgp_update = bgp_update + + @classmethod + def parser(cls, buf): + kwargs, buf = super(BMPRouteMonitoring, cls).parser(buf) + + bgp_update, buf = BGPMessage.parser(buf) + + kwargs['bgp_update'] = bgp_update + + return kwargs + + def serialize_tail(self): + msg = super(BMPRouteMonitoring, self).serialize_tail() + msg += self.bgp_update.serialize() + + return msg + + +@BMPMessage.register_type(BMP_MSG_STATISTICS_REPORT) +class BMPStatisticsReport(BMPPeerMessage): + """BMP Statistics Report Message + + ========================== =============================================== + Attribute Description + ========================== =============================================== + version Version. this packet lib defines BMP ver. 3 + len Length field. Ignored when encoding. + type Type field. one of BMP\_MSG\_ constants. + peer_type The type of the peer. + peer_flags Provide more information about the peer. + peer_distinguisher Use for L3VPN router which can have multiple + instance. + peer_address The remote IP address associated with the TCP + session. + peer_as The Autonomous System number of the peer. + peer_bgp_id The BGP Identifier of the peer + timestamp The time when the encapsulated routes were + received. + stats Statistics (one or more stats encoded as a TLV) + ========================== =============================================== + """ + + _TLV_PACK_STR = '!HH' + _MIN_LEN = struct.calcsize(_TLV_PACK_STR) + + def __init__(self, stats, peer_type, is_post_policy, peer_distinguisher, + peer_address, peer_as, peer_bgp_id, timestamp, + version=VERSION, type_=BMP_MSG_STATISTICS_REPORT, len_=None): + super(BMPStatisticsReport, + self).__init__(peer_type=peer_type, + is_post_policy=is_post_policy, + peer_distinguisher=peer_distinguisher, + peer_address=peer_address, + peer_as=peer_as, + peer_bgp_id=peer_bgp_id, + timestamp=timestamp, + len_=len_, + type_=type_, + version=version) + self.stats = stats + + @classmethod + def parser(cls, buf): + kwargs, rest = super(BMPStatisticsReport, cls).parser(buf) + + stats_count, = struct.unpack_from('!I', buffer(rest)) + + buf = rest[struct.calcsize('!I'):] + + stats = [] + + while len(buf): + if len(buf) < cls._MIN_LEN: + raise stream_parser.StreamParser.TooSmallException( + '%d < %d' % (len(buf), cls._MIN_LEN)) + (type_, len_) = struct.unpack_from(cls._TLV_PACK_STR, buffer(buf)) + + if len(buf) < (cls._MIN_LEN + len_): + raise stream_parser.StreamParser.TooSmallException( + '%d < %d' % (len(buf), cls._MIN_LEN + len_)) + + value = buf[cls._MIN_LEN:cls._MIN_LEN + len_] + + if type_ == BMP_STAT_TYPE_REJECTED or \ + type_ == BMP_STAT_TYPE_DUPLICATE_PREFIX or \ + type_ == BMP_STAT_TYPE_DUPLICATE_WITHDRAW or \ + type_ == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_CLUSTER_LIST_LOOP or \ + type_ == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_PATH_LOOP or \ + type_ == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_ORIGINATOR_ID or \ + type_ == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_CONFED_LOOP: + value, = struct.unpack_from('!I', buffer(value)) + elif type_ == BMP_STAT_TYPE_ADJ_RIB_IN or \ + type_ == BMP_STAT_TYPE_LOC_RIB: + value, = struct.unpack_from('!Q', buffer(value)) + + buf = buf[cls._MIN_LEN + len_:] + + stats.append({'type': type_, 'len': len_, 'value': value}) + + kwargs['stats'] = stats + + return kwargs + + def serialize_tail(self): + msg = super(BMPStatisticsReport, self).serialize_tail() + + stats_count = len(self.stats) + + msg += bytearray(struct.pack('!I', stats_count)) + + for v in self.stats: + t = v['type'] + if t == BMP_STAT_TYPE_REJECTED or \ + t == BMP_STAT_TYPE_DUPLICATE_PREFIX or \ + t == BMP_STAT_TYPE_DUPLICATE_WITHDRAW or \ + t == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_CLUSTER_LIST_LOOP or \ + t == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_PATH_LOOP or \ + t == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_ORIGINATOR_ID or \ + t == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_CONFED_LOOP: + valuepackstr = 'I' + elif t == BMP_STAT_TYPE_ADJ_RIB_IN or \ + t == BMP_STAT_TYPE_LOC_RIB: + valuepackstr = 'Q' + else: + continue + + v['len'] = struct.calcsize(valuepackstr) + msg += bytearray(struct.pack(self._TLV_PACK_STR + valuepackstr, + t, v['len'], v['value'])) + + return msg + + +@BMPMessage.register_type(BMP_MSG_PEER_DOWN_NOTIFICATION) +class BMPPeerDownNotification(BMPMessage): + """BMP Peer Down Notification Message + + ========================== =============================================== + Attribute Description + ========================== =============================================== + version Version. this packet lib defines BMP ver. 3 + len Length field. Ignored when encoding. + type Type field. one of BMP\_MSG\_ constants. + reason Reason indicates why the session was closed. + data vary by the reason. + ========================== =============================================== + """ + + def __init__(self, reason, data, type_=BMP_MSG_PEER_DOWN_NOTIFICATION, + len_=None, version=VERSION): + super(BMPPeerDownNotification, self).__init__(type_, len_, version) + self.reason = reason + self.data = data + + @classmethod + def parser(cls, buf): + reason, = struct.unpack_from('!B', buffer(buf)) + buf = buf[struct.calcsize('!B'):] + + if reason == BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION: + data, rest = BGPMessage.parser(buf) + elif reason == BMP_PEER_DOWN_REASON_LOCAL_NO_NOTIFICATION: + data = struct.unpack_from('!H', buffer(buf)) + elif reason == BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION: + data, rest = BGPMessage.parser(buf) + elif reason == BMP_PEER_DOWN_REASON_REMOTE_NO_NOTIFICATION: + data = None + else: + reason = BMP_PEER_DOWN_REASON_UNKNOWN + data = buf + + kwargs = {} + kwargs['reason'] = reason + kwargs['data'] = data + + return kwargs + + def serialize_tail(self): + msg = struct.pack('!B', self.reason) + + if self.reason == BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION: + msg += self.data.serialize() + elif self.reason == BMP_PEER_DOWN_REASON_LOCAL_NO_NOTIFICATION: + msg += struct.pack('!H', self.data) + elif self.reason == BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION: + msg += self.data.serialize() + elif self.reason == BMP_PEER_DOWN_REASON_UNKNOWN: + msg += str(self.data) + + return msg + + +@BMPMessage.register_type(BMP_MSG_PEER_UP_NOTIFICATION) +class BMPPeerUpNotification(BMPPeerMessage): + """BMP Peer Up Notification Message + + ========================== =============================================== + Attribute Description + ========================== =============================================== + version Version. this packet lib defines BMP ver. 3 + len Length field. Ignored when encoding. + type Type field. one of BMP\_MSG\_ constants. + peer_type The type of the peer. + peer_flags Provide more information about the peer. + peer_distinguisher Use for L3VPN router which can have multiple + instance. + peer_address The remote IP address associated with the TCP + session. + peer_as The Autonomous System number of the peer. + peer_bgp_id The BGP Identifier of the peer + timestamp The time when the encapsulated routes were + received. + local_address The local IP address associated with the + peering TCP session. + local_port The local port number associated with the + peering TCP session. + remote_port The remote port number associated with the + peering TCP session. + sent_open_message The full OPEN message transmitted by the + monitored router to its peer. + received_open_message The full OPEN message received by the monitored + router from its peer. + ========================== =============================================== + """ + + _PACK_STR = '!16sHH' + _MIN_LEN = struct.calcsize(_PACK_STR) + + def __init__(self, local_address, local_port, remote_port, + sent_open_message, received_open_message, + peer_type, is_post_policy, peer_distinguisher, + peer_address, peer_as, peer_bgp_id, timestamp, + version=VERSION, type_=BMP_MSG_PEER_UP_NOTIFICATION, + len_=None): + super(BMPPeerUpNotification, + self).__init__(peer_type=peer_type, + is_post_policy=is_post_policy, + peer_distinguisher=peer_distinguisher, + peer_address=peer_address, + peer_as=peer_as, + peer_bgp_id=peer_bgp_id, + timestamp=timestamp, + len_=len_, + type_=type_, + version=version) + self.local_address = local_address + self.local_port = local_port + self.remote_port = remote_port + self.sent_open_message = sent_open_message + self.received_open_message = received_open_message + + @classmethod + def parser(cls, buf): + kwargs, rest = super(BMPPeerUpNotification, cls).parser(buf) + + (local_address, local_port, + remote_port) = struct.unpack_from(cls._PACK_STR, buffer(rest)) + + local_address = buffer(local_address) + + if '.' in kwargs['peer_address']: + local_address = addrconv.ipv4.bin_to_text(local_address[:4]) + elif ':' in kwargs['peer_address']: + local_address = addrconv.ipv6.bin_to_text(local_address) + else: + raise ValueError("invalid local_address: %s" % local_address) + + kwargs['local_address'] = local_address + kwargs['local_port'] = local_port + kwargs['remote_port'] = remote_port + + rest = rest[cls._MIN_LEN:] + + sent_open_msg, rest = BGPMessage.parser(rest) + received_open_msg, rest = BGPMessage.parser(rest) + + kwargs['sent_open_message'] = sent_open_msg + kwargs['received_open_message'] = received_open_msg + + return kwargs + + def serialize_tail(self): + msg = super(BMPPeerUpNotification, self).serialize_tail() + + if '.' in self.local_address: + local_address = addrconv.ipv4.text_to_bin(self.local_address) + elif ':' in self.local_address: + local_address = addrconv.ipv6.text_to_bin(self.local_address) + else: + raise ValueError("invalid local_address: %s" % self.local_address) + + msg += struct.pack(self._PACK_STR, local_address, + self.local_port, self.remote_port) + + msg += self.sent_open_message.serialize() + msg += self.received_open_message.serialize() + + return msg + + +@BMPMessage.register_type(BMP_MSG_INITIATION) +class BMPInitiation(BMPMessage): + """BMP Initiation Message + + ========================== =============================================== + Attribute Description + ========================== =============================================== + version Version. this packet lib defines BMP ver. 3 + len Length field. Ignored when encoding. + type Type field. one of BMP\_MSG\_ constants. + info One or more piece of information encoded as a + TLV + ========================== =============================================== + """ + + _TLV_PACK_STR = '!HH' + _MIN_LEN = struct.calcsize(_TLV_PACK_STR) + + def __init__(self, info, type_=BMP_MSG_INITIATION, len_=None, + version=VERSION): + super(BMPInitiation, self).__init__(type_, len_, version) + self.info = info + + @classmethod + def parser(cls, buf): + info = [] + while len(buf): + if len(buf) < cls._MIN_LEN: + raise stream_parser.StreamParser.TooSmallException( + '%d < %d' % (len(buf), cls._MIN_LEN)) + (type_, len_) = struct.unpack_from(cls._TLV_PACK_STR, buffer(buf)) + + if len(buf) < (cls._MIN_LEN + len_): + raise stream_parser.StreamParser.TooSmallException( + '%d < %d' % (len(buf), cls._MIN_LEN + len_)) + + value = buf[cls._MIN_LEN:cls._MIN_LEN + len_] + + if type_ == BMP_INIT_TYPE_STRING: + value = value.decode('utf-8') + + buf = buf[cls._MIN_LEN + len_:] + + info.append({'type': type_, 'len': len_, 'value': value}) + + return {'info': info} + + def serialize_tail(self): + msg = bytearray() + + for v in self.info: + if v['type'] == BMP_INIT_TYPE_STRING: + value = v['value'].encode('utf-8') + else: + value = v['value'] + + v['len'] = len(value) + msg += struct.pack(self._TLV_PACK_STR, v['type'], v['len']) + msg += value + + return msg + + +@BMPMessage.register_type(BMP_MSG_TERMINATION) +class BMPTermination(BMPMessage): + """BMP Termination Message + + ========================== =============================================== + Attribute Description + ========================== =============================================== + version Version. this packet lib defines BMP ver. 3 + len Length field. Ignored when encoding. + type Type field. one of BMP\_MSG\_ constants. + info One or more piece of information encoded as a + TLV + ========================== =============================================== + """ + + _TLV_PACK_STR = '!HH' + _MIN_LEN = struct.calcsize(_TLV_PACK_STR) + + def __init__(self, info, type_=BMP_MSG_TERMINATION, len_=None, + version=VERSION): + super(BMPTermination, self).__init__(type_, len_, version) + self.info = info + + @classmethod + def parser(cls, buf): + info = [] + while len(buf): + if len(buf) < cls._MIN_LEN: + raise stream_parser.StreamParser.TooSmallException( + '%d < %d' % (len(buf), cls._MIN_LEN)) + (type_, len_) = struct.unpack_from(cls._TLV_PACK_STR, buffer(buf)) + + if len(buf) < (cls._MIN_LEN + len_): + raise stream_parser.StreamParser.TooSmallException( + '%d < %d' % (len(buf), cls._MIN_LEN + len_)) + + value = buf[cls._MIN_LEN:cls._MIN_LEN + len_] + if type_ == BMP_TERM_TYPE_STRING: + value = value.decode('utf-8') + elif type_ == BMP_TERM_TYPE_REASON: + value, = struct.unpack_from('!H', buffer(value)) + + buf = buf[cls._MIN_LEN + len_:] + + info.append({'type': type_, 'len': len_, 'value': value}) + + return {'info': info} + + def serialize_tail(self): + msg = bytearray() + + for v in self.info: + if v['type'] == BMP_TERM_TYPE_STRING: + value = v['value'].encode('utf-8') + elif v['type'] == BMP_TERM_TYPE_REASON: + value = struct.pack('!H', v['value']) + v['len'] = len(value) + msg += struct.pack(self._TLV_PACK_STR, v['type'], v['len']) + msg += value + + return msg |