1 # Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 BGP Monitoring Protocol draft-ietf-grow-bmp-07
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
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
39 BMP_PEER_TYPE_GLOBAL = 0
40 BMP_PEER_TYPE_L3VPN = 1
42 BMP_INIT_TYPE_STRING = 0
43 BMP_INIT_TYPE_SYSDESCR = 1
44 BMP_INIT_TYPE_SYSNAME = 2
46 BMP_TERM_TYPE_STRING = 0
47 BMP_TERM_TYPE_REASON = 1
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
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
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
71 class BMPMessage(packet_base.PacketBase, TypeDisp):
72 r"""Base class for BGP Monitoring Protocol messages.
74 An instance has the following attributes at least.
75 Most of them are same to the on-wire counterparts but in host byte
77 __init__ takes the corresponding args in this order.
79 ========================== ===============================================
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 ========================== ===============================================
88 _HDR_PACK_STR = '!BIB' # version, padding, len, type, padding
89 _HDR_LEN = struct.calcsize(_HDR_PACK_STR)
91 def __init__(self, type_, len_=None, version=VERSION):
92 self.version = version
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))
104 return version, len_, type_
107 def parser(cls, buf):
108 version, msglen, type_ = cls.parse_header(buf)
110 if version != VERSION:
111 raise ValueError("not supportted bmp version: %d" % version)
113 if len(buf) < msglen:
114 raise stream_parser.StreamParser.TooSmallException(
115 '%d < %d' % (len(buf), msglen))
117 binmsg = buf[cls._HDR_LEN:msglen]
119 subcls = cls._lookup_type(type_)
121 if subcls == cls._UNKNOWN_TYPE:
122 raise ValueError("unknown bmp type: %d" % type_)
124 kwargs = subcls.parser(binmsg)
125 return subcls(len_=msglen,
126 type_=type_, version=version, **kwargs), rest
130 tail = self.serialize_tail()
131 self.len = self._HDR_LEN + len(tail)
133 hdr = bytearray(struct.pack(self._HDR_PACK_STR, self.version,
134 self.len, self.type))
139 buf = self.serialize()
143 class BMPPeerMessage(BMPMessage):
144 r"""BMP Message with Per Peer Header
146 Following BMP Messages contain Per Peer Header after Common BMP Header.
148 - BMP_MSG_TYPE_ROUTE_MONITRING
149 - BMP_MSG_TYPE_STATISTICS_REPORT
150 - BMP_MSG_PEER_UP_NOTIFICATION
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
161 peer_distinguisher Use for L3VPN router which can have multiple
163 peer_address The remote IP address associated with the TCP
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
169 ========================== ===============================================
172 _PEER_HDR_PACK_STR = '!BBQ16sI4sII'
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,
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
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))
201 rest = buf[struct.calcsize(cls._PEER_HDR_PACK_STR):]
203 if peer_flags & (1 << 6):
204 is_post_policy = True
206 is_post_policy = False
208 if peer_flags & (1 << 7):
209 peer_address = addrconv.ipv6.bin_to_text(peer_address)
211 peer_address = addrconv.ipv4.bin_to_text(peer_address[-4:])
213 peer_bgp_id = addrconv.ipv4.bin_to_text(peer_bgp_id)
215 timestamp = float(timestamp1) + timestamp2 * (10 ** -6)
218 "peer_type": peer_type,
219 "is_post_policy": is_post_policy,
220 "peer_distinguisher": peer_distinguisher,
221 "peer_address": peer_address,
223 "peer_bgp_id": peer_bgp_id,
224 "timestamp": timestamp
227 def serialize_tail(self):
230 if self.is_post_policy:
233 if ':' in self.peer_address:
235 peer_address = addrconv.ipv6.text_to_bin(self.peer_address)
237 peer_address = struct.pack(
238 '!12x4s', addrconv.ipv4.text_to_bin(self.peer_address))
240 peer_bgp_id = addrconv.ipv4.text_to_bin(self.peer_bgp_id)
242 t1, t2 = [int(t) for t in ("%.6f" % self.timestamp).split('.')]
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))
251 @BMPMessage.register_type(BMP_MSG_ROUTE_MONITORING)
252 class BMPRouteMonitoring(BMPPeerMessage):
253 r"""BMP Route Monitoring Message
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
265 peer_address The remote IP address associated with the TCP
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
271 bgp_update BGP Update PDU
272 ========================== ===============================================
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,
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,
285 peer_bgp_id=peer_bgp_id,
290 self.bgp_update = bgp_update
293 def parser(cls, buf):
294 kwargs, buf = super(BMPRouteMonitoring, cls).parser(buf)
296 bgp_update, _, buf = BGPMessage.parser(buf)
298 kwargs['bgp_update'] = bgp_update
302 def serialize_tail(self):
303 msg = super(BMPRouteMonitoring, self).serialize_tail()
304 msg += self.bgp_update.serialize()
309 @BMPMessage.register_type(BMP_MSG_STATISTICS_REPORT)
310 class BMPStatisticsReport(BMPPeerMessage):
311 r"""BMP Statistics Report Message
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
323 peer_address The remote IP address associated with the TCP
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
329 stats Statistics (one or more stats encoded as a TLV)
330 ========================== ===============================================
333 _TLV_PACK_STR = '!HH'
334 _MIN_LEN = struct.calcsize(_TLV_PACK_STR)
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,
345 peer_bgp_id=peer_bgp_id,
353 def parser(cls, buf):
354 kwargs, rest = super(BMPStatisticsReport, cls).parser(buf)
356 stats_count, = struct.unpack_from('!I', six.binary_type(rest))
358 buf = rest[struct.calcsize('!I'):]
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))
369 if len(buf) < (cls._MIN_LEN + len_):
370 raise stream_parser.StreamParser.TooSmallException(
371 '%d < %d' % (len(buf), cls._MIN_LEN + len_))
373 value = buf[cls._MIN_LEN:cls._MIN_LEN + len_]
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))
387 buf = buf[cls._MIN_LEN + len_:]
389 stats.append({'type': type_, 'len': len_, 'value': value})
391 kwargs['stats'] = stats
395 def serialize_tail(self):
396 msg = super(BMPStatisticsReport, self).serialize_tail()
398 stats_count = len(self.stats)
400 msg += bytearray(struct.pack('!I', stats_count))
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:
412 elif t == BMP_STAT_TYPE_ADJ_RIB_IN or \
413 t == BMP_STAT_TYPE_LOC_RIB:
418 v['len'] = struct.calcsize(valuepackstr)
419 msg += bytearray(struct.pack(self._TLV_PACK_STR + valuepackstr,
420 t, v['len'], v['value']))
425 @BMPMessage.register_type(BMP_MSG_PEER_DOWN_NOTIFICATION)
426 class BMPPeerDownNotification(BMPPeerMessage):
427 r"""BMP Peer Down Notification Message
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 ========================== ===============================================
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):
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,
451 peer_bgp_id=peer_bgp_id,
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'):]
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:
475 reason = BMP_PEER_DOWN_REASON_UNKNOWN
478 kwargs['reason'] = reason
479 kwargs['data'] = data
483 def serialize_tail(self):
484 msg = super(BMPPeerDownNotification, self).serialize_tail()
485 msg += struct.pack('!B', self.reason)
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)
499 @BMPMessage.register_type(BMP_MSG_PEER_UP_NOTIFICATION)
500 class BMPPeerUpNotification(BMPPeerMessage):
501 r"""BMP Peer Up Notification Message
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
513 peer_address The remote IP address associated with the TCP
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
519 local_address The local IP address associated with the
521 local_port The local port number associated with the
523 remote_port The remote port number associated with the
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 ========================== ===============================================
533 _MIN_LEN = struct.calcsize(_PACK_STR)
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,
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,
547 peer_bgp_id=peer_bgp_id,
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
559 def parser(cls, buf):
560 kwargs, rest = super(BMPPeerUpNotification, cls).parser(buf)
562 (local_address, local_port,
563 remote_port) = struct.unpack_from(cls._PACK_STR, six.binary_type(rest))
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)
570 raise ValueError("invalid local_address: %s" % local_address)
572 kwargs['local_address'] = local_address
573 kwargs['local_port'] = local_port
574 kwargs['remote_port'] = remote_port
576 rest = rest[cls._MIN_LEN:]
578 sent_open_msg, _, rest = BGPMessage.parser(rest)
579 received_open_msg, _, rest = BGPMessage.parser(rest)
581 kwargs['sent_open_message'] = sent_open_msg
582 kwargs['received_open_message'] = received_open_msg
586 def serialize_tail(self):
587 msg = super(BMPPeerUpNotification, self).serialize_tail()
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)
595 raise ValueError("invalid local_address: %s" % self.local_address)
597 msg += struct.pack(self._PACK_STR, local_address,
598 self.local_port, self.remote_port)
600 msg += self.sent_open_message.serialize()
601 msg += self.received_open_message.serialize()
606 @BMPMessage.register_type(BMP_MSG_INITIATION)
607 class BMPInitiation(BMPMessage):
608 r"""BMP Initiation Message
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
618 ========================== ===============================================
621 _TLV_PACK_STR = '!HH'
622 _MIN_LEN = struct.calcsize(_TLV_PACK_STR)
624 def __init__(self, info, type_=BMP_MSG_INITIATION, len_=None,
626 super(BMPInitiation, self).__init__(type_, len_, version)
630 def parser(cls, 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))
639 if len(buf) < (cls._MIN_LEN + len_):
640 raise stream_parser.StreamParser.TooSmallException(
641 '%d < %d' % (len(buf), cls._MIN_LEN + len_))
643 value = buf[cls._MIN_LEN:cls._MIN_LEN + len_]
645 if type_ == BMP_INIT_TYPE_STRING:
646 value = value.decode('utf-8')
648 buf = buf[cls._MIN_LEN + len_:]
650 info.append({'type': type_, 'len': len_, 'value': value})
652 return {'info': info}
654 def serialize_tail(self):
658 if v['type'] == BMP_INIT_TYPE_STRING:
659 value = v['value'].encode('utf-8')
663 v['len'] = len(value)
664 msg += struct.pack(self._TLV_PACK_STR, v['type'], v['len'])
670 @BMPMessage.register_type(BMP_MSG_TERMINATION)
671 class BMPTermination(BMPMessage):
672 r"""BMP Termination Message
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
682 ========================== ===============================================
685 _TLV_PACK_STR = '!HH'
686 _MIN_LEN = struct.calcsize(_TLV_PACK_STR)
688 def __init__(self, info, type_=BMP_MSG_TERMINATION, len_=None,
690 super(BMPTermination, self).__init__(type_, len_, version)
694 def parser(cls, 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))
703 if len(buf) < (cls._MIN_LEN + len_):
704 raise stream_parser.StreamParser.TooSmallException(
705 '%d < %d' % (len(buf), cls._MIN_LEN + len_))
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))
713 buf = buf[cls._MIN_LEN + len_:]
715 info.append({'type': type_, 'len': len_, 'value': value})
717 return {'info': info}
719 def serialize_tail(self):
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'])