433 lines
15 KiB
Python
433 lines
15 KiB
Python
|
# SPDX-License-Identifier: ISC
|
||
|
|
||
|
# Copyright 2023 6WIND S.A.
|
||
|
# Authored by Farid Mihoub <farid.mihoub@6wind.com>
|
||
|
#
|
||
|
"""
|
||
|
BMP main module:
|
||
|
- dissect monitoring messages in the way to get updated/withdrawed prefixes
|
||
|
- XXX: missing RFCs references
|
||
|
- XXX: more bmp messages types to dissect
|
||
|
- XXX: complete bgp message dissection
|
||
|
"""
|
||
|
import datetime
|
||
|
import ipaddress
|
||
|
import json
|
||
|
import os
|
||
|
import struct
|
||
|
|
||
|
from bgp.update import BGPUpdate
|
||
|
from bgp.update.rd import RouteDistinguisher
|
||
|
|
||
|
|
||
|
SEQ = 0
|
||
|
LOG_DIR = "/var/log/"
|
||
|
LOG_FILE = "/var/log/bmp.log"
|
||
|
|
||
|
IS_ADJ_RIB_OUT = 1 << 4
|
||
|
IS_AS_PATH = 1 << 5
|
||
|
IS_POST_POLICY = 1 << 6
|
||
|
IS_IPV6 = 1 << 7
|
||
|
IS_FILTERED = 1 << 7
|
||
|
|
||
|
if not os.path.exists(LOG_DIR):
|
||
|
os.makedirs(LOG_DIR)
|
||
|
|
||
|
|
||
|
def bin2str_ipaddress(ip_bytes, is_ipv6=False):
|
||
|
if is_ipv6:
|
||
|
return str(ipaddress.IPv6Address(ip_bytes))
|
||
|
return str(ipaddress.IPv4Address(ip_bytes[-4:]))
|
||
|
|
||
|
|
||
|
def log2file(logs, log_file):
|
||
|
"""
|
||
|
XXX: extract the useful information and save it in a flat dictionnary
|
||
|
"""
|
||
|
with open(log_file, "a") as f:
|
||
|
f.write(json.dumps(logs) + "\n")
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
class BMPCodes:
|
||
|
"""
|
||
|
XXX: complete the list, provide RFCs.
|
||
|
"""
|
||
|
|
||
|
VERSION = 0x3
|
||
|
|
||
|
BMP_MSG_TYPE_ROUTE_MONITORING = 0x00
|
||
|
BMP_MSG_TYPE_STATISTICS_REPORT = 0x01
|
||
|
BMP_MSG_TYPE_PEER_DOWN_NOTIFICATION = 0x02
|
||
|
BMP_MSG_TYPE_PEER_UP_NOTIFICATION = 0x03
|
||
|
BMP_MSG_TYPE_INITIATION = 0x04
|
||
|
BMP_MSG_TYPE_TERMINATION = 0x05
|
||
|
BMP_MSG_TYPE_ROUTE_MIRRORING = 0x06
|
||
|
BMP_MSG_TYPE_ROUTE_POLICY = 0x64
|
||
|
|
||
|
# initiation message types
|
||
|
BMP_INIT_INFO_STRING = 0x00
|
||
|
BMP_INIT_SYSTEM_DESCRIPTION = 0x01
|
||
|
BMP_INIT_SYSTEM_NAME = 0x02
|
||
|
BMP_INIT_VRF_TABLE_NAME = 0x03
|
||
|
BMP_INIT_ADMIN_LABEL = 0x04
|
||
|
|
||
|
# peer types
|
||
|
BMP_PEER_GLOBAL_INSTANCE = 0x00
|
||
|
BMP_PEER_RD_INSTANCE = 0x01
|
||
|
BMP_PEER_LOCAL_INSTANCE = 0x02
|
||
|
BMP_PEER_LOC_RIB_INSTANCE = 0x03
|
||
|
|
||
|
# peer header flags
|
||
|
BMP_PEER_FLAG_IPV6 = 0x80
|
||
|
BMP_PEER_FLAG_POST_POLICY = 0x40
|
||
|
BMP_PEER_FLAG_AS_PATH = 0x20
|
||
|
BMP_PEER_FLAG_ADJ_RIB_OUT = 0x10
|
||
|
|
||
|
# peer loc-rib flag
|
||
|
BMP_PEER_FLAG_LOC_RIB = 0x80
|
||
|
BMP_PEER_FLAG_LOC_RIB_RES = 0x7F
|
||
|
|
||
|
# statistics type
|
||
|
BMP_STAT_PREFIX_REJ = 0x00
|
||
|
BMP_STAT_PREFIX_DUP = 0x01
|
||
|
BMP_STAT_WITHDRAW_DUP = 0x02
|
||
|
BMP_STAT_CLUSTER_LOOP = 0x03
|
||
|
BMP_STAT_AS_LOOP = 0x04
|
||
|
BMP_STAT_INV_ORIGINATOR = 0x05
|
||
|
BMP_STAT_AS_CONFED_LOOP = 0x06
|
||
|
BMP_STAT_ROUTES_ADJ_RIB_IN = 0x07
|
||
|
BMP_STAT_ROUTES_LOC_RIB = 0x08
|
||
|
BMP_STAT_ROUTES_PER_ADJ_RIB_IN = 0x09
|
||
|
BMP_STAT_ROUTES_PER_LOC_RIB = 0x0A
|
||
|
BMP_STAT_UPDATE_TREAT = 0x0B
|
||
|
BMP_STAT_PREFIXES_TREAT = 0x0C
|
||
|
BMP_STAT_DUPLICATE_UPDATE = 0x0D
|
||
|
BMP_STAT_ROUTES_PRE_ADJ_RIB_OUT = 0x0E
|
||
|
BMP_STAT_ROUTES_POST_ADJ_RIB_OUT = 0x0F
|
||
|
BMP_STAT_ROUTES_PRE_PER_ADJ_RIB_OUT = 0x10
|
||
|
BMP_STAT_ROUTES_POST_PER_ADJ_RIB_OUT = 0x11
|
||
|
|
||
|
# peer down reason code
|
||
|
BMP_PEER_DOWN_LOCAL_NOTIFY = 0x01
|
||
|
BMP_PEER_DOWN_LOCAL_NO_NOTIFY = 0x02
|
||
|
BMP_PEER_DOWN_REMOTE_NOTIFY = 0x03
|
||
|
BMP_PEER_DOWN_REMOTE_NO_NOTIFY = 0x04
|
||
|
BMP_PEER_DOWN_INFO_NO_LONGER = 0x05
|
||
|
BMP_PEER_DOWN_SYSTEM_CLOSED = 0x06
|
||
|
|
||
|
# termincation message types
|
||
|
BMP_TERM_TYPE_STRING = 0x00
|
||
|
BMP_TERM_TYPE_REASON = 0x01
|
||
|
|
||
|
# termination reason code
|
||
|
BMP_TERM_REASON_ADMIN_CLOSE = 0x00
|
||
|
BMP_TERM_REASON_UNSPECIFIED = 0x01
|
||
|
BMP_TERM_REASON_RESOURCES = 0x02
|
||
|
BMP_TERM_REASON_REDUNDANT = 0x03
|
||
|
BMP_TERM_REASON_PERM_CLOSE = 0x04
|
||
|
|
||
|
# policy route tlv
|
||
|
BMP_ROUTE_POLICY_TLV_VRF = 0x00
|
||
|
BMP_ROUTE_POLICY_TLV_POLICY = 0x01
|
||
|
BMP_ROUTE_POLICY_TLV_PRE_POLICY = 0x02
|
||
|
BMP_ROUTE_POLICY_TLV_POST_POLICY = 0x03
|
||
|
BMP_ROUTE_POLICY_TLV_STRING = 0x04
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
class BMPMsg:
|
||
|
"""
|
||
|
XXX: should we move register_msg_type and look_msg_type
|
||
|
to generic Type class.
|
||
|
"""
|
||
|
|
||
|
TYPES = {}
|
||
|
UNKNOWN_TYPE = None
|
||
|
HDR_STR = "!BIB"
|
||
|
MIN_LEN = struct.calcsize(HDR_STR)
|
||
|
TYPES_STR = {
|
||
|
BMPCodes.BMP_MSG_TYPE_INITIATION: "initiation",
|
||
|
BMPCodes.BMP_MSG_TYPE_PEER_DOWN_NOTIFICATION: "peer down notification",
|
||
|
BMPCodes.BMP_MSG_TYPE_PEER_UP_NOTIFICATION: "peer up notification",
|
||
|
BMPCodes.BMP_MSG_TYPE_ROUTE_MONITORING: "route monitoring",
|
||
|
BMPCodes.BMP_MSG_TYPE_STATISTICS_REPORT: "statistics report",
|
||
|
BMPCodes.BMP_MSG_TYPE_TERMINATION: "termination",
|
||
|
BMPCodes.BMP_MSG_TYPE_ROUTE_MIRRORING: "route mirroring",
|
||
|
BMPCodes.BMP_MSG_TYPE_ROUTE_POLICY: "route policy",
|
||
|
}
|
||
|
|
||
|
@classmethod
|
||
|
def register_msg_type(cls, msgtype):
|
||
|
def _register_type(subcls):
|
||
|
cls.TYPES[msgtype] = subcls
|
||
|
return subcls
|
||
|
|
||
|
return _register_type
|
||
|
|
||
|
@classmethod
|
||
|
def lookup_msg_type(cls, msgtype):
|
||
|
return cls.TYPES.get(msgtype, cls.UNKNOWN_TYPE)
|
||
|
|
||
|
@classmethod
|
||
|
def dissect_header(cls, data):
|
||
|
"""
|
||
|
0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Version |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Message Length |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Message Type |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
"""
|
||
|
if len(data) < cls.MIN_LEN:
|
||
|
pass
|
||
|
else:
|
||
|
_version, _len, _type = struct.unpack(cls.HDR_STR, data[0 : cls.MIN_LEN])
|
||
|
return _version, _len, _type
|
||
|
|
||
|
@classmethod
|
||
|
def dissect(cls, data, log_file=None):
|
||
|
global SEQ
|
||
|
version, msglen, msgtype = cls.dissect_header(data)
|
||
|
|
||
|
msg_data = data[cls.MIN_LEN : msglen]
|
||
|
data = data[msglen:]
|
||
|
|
||
|
if version != BMPCodes.VERSION:
|
||
|
# XXX: log something
|
||
|
return data
|
||
|
|
||
|
msg_cls = cls.lookup_msg_type(msgtype)
|
||
|
if msg_cls == cls.UNKNOWN_TYPE:
|
||
|
# XXX: log something
|
||
|
return data
|
||
|
|
||
|
msg_cls.MSG_LEN = msglen - cls.MIN_LEN
|
||
|
logs = msg_cls.dissect(msg_data)
|
||
|
logs["seq"] = SEQ
|
||
|
log2file(logs, log_file if log_file else LOG_FILE)
|
||
|
SEQ += 1
|
||
|
|
||
|
return data
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
class BMPPerPeerMessage:
|
||
|
"""
|
||
|
0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Peer Type | Peer Flags |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Peer Address (16 bytes) |
|
||
|
~ ~
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Peer AS |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Peer BGP ID |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Timestamp (seconds) |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Timestamp (microseconds) |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
"""
|
||
|
|
||
|
PEER_UNPACK_STR = "!BB8s16sI4sII"
|
||
|
PEER_TYPE_STR = {
|
||
|
BMPCodes.BMP_PEER_GLOBAL_INSTANCE: "global instance",
|
||
|
BMPCodes.BMP_PEER_RD_INSTANCE: "route distinguisher instance",
|
||
|
BMPCodes.BMP_PEER_LOCAL_INSTANCE: "local instance",
|
||
|
BMPCodes.BMP_PEER_LOC_RIB_INSTANCE: "loc-rib instance",
|
||
|
}
|
||
|
|
||
|
@classmethod
|
||
|
def dissect(cls, data):
|
||
|
(
|
||
|
peer_type,
|
||
|
peer_flags,
|
||
|
peer_distinguisher,
|
||
|
peer_address,
|
||
|
peer_asn,
|
||
|
peer_bgp_id,
|
||
|
timestamp_secs,
|
||
|
timestamp_microsecs,
|
||
|
) = struct.unpack_from(cls.PEER_UNPACK_STR, data)
|
||
|
|
||
|
msg = {"peer_type": cls.PEER_TYPE_STR[peer_type]}
|
||
|
|
||
|
if peer_type == 0x03:
|
||
|
msg["is_filtered"] = bool(peer_flags & IS_FILTERED)
|
||
|
msg["policy"] = "loc-rib"
|
||
|
else:
|
||
|
# peer_flags = 0x0000 0000
|
||
|
# ipv6, post-policy, as-path, adj-rib-out, reserverdx4
|
||
|
is_adj_rib_out = bool(peer_flags & IS_ADJ_RIB_OUT)
|
||
|
is_as_path = bool(peer_flags & IS_AS_PATH)
|
||
|
is_post_policy = bool(peer_flags & IS_POST_POLICY)
|
||
|
is_ipv6 = bool(peer_flags & IS_IPV6)
|
||
|
msg["policy"] = "post-policy" if is_post_policy else "pre-policy"
|
||
|
msg["ipv6"] = is_ipv6
|
||
|
msg["peer_ip"] = bin2str_ipaddress(peer_address, is_ipv6)
|
||
|
|
||
|
peer_bgp_id = bin2str_ipaddress(peer_bgp_id)
|
||
|
timestamp = float(timestamp_secs) + timestamp_microsecs * (10**-6)
|
||
|
|
||
|
data = data[struct.calcsize(cls.PEER_UNPACK_STR) :]
|
||
|
msg.update(
|
||
|
{
|
||
|
"peer_distinguisher": str(RouteDistinguisher(peer_distinguisher)),
|
||
|
"peer_asn": peer_asn,
|
||
|
"peer_bgp_id": peer_bgp_id,
|
||
|
"timestamp": str(datetime.datetime.fromtimestamp(timestamp)),
|
||
|
}
|
||
|
)
|
||
|
|
||
|
return data, msg
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
@BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_ROUTE_MONITORING)
|
||
|
class BMPRouteMonitoring(BMPPerPeerMessage):
|
||
|
@classmethod
|
||
|
def dissect(cls, data):
|
||
|
data, peer_msg = super().dissect(data)
|
||
|
data, update_msg = BGPUpdate.dissect(data)
|
||
|
return {**peer_msg, **update_msg}
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
class BMPStatisticsReport:
|
||
|
"""
|
||
|
0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Stats Count |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Stat Type | Stat Len |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Stat Data |
|
||
|
~ ~
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
"""
|
||
|
|
||
|
pass
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
class BMPPeerDownNotification:
|
||
|
"""
|
||
|
0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Reason |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Data (present if Reason = 1, 2 or 3) |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
"""
|
||
|
|
||
|
pass
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
@BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_PEER_UP_NOTIFICATION)
|
||
|
class BMPPeerUpNotification(BMPPerPeerMessage):
|
||
|
"""
|
||
|
0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Local Address (16 bytes) |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Local Port | Remote Port |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Sent OPEN Message #|
|
||
|
~ ~
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Received OPEN Message |
|
||
|
~ ~
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
"""
|
||
|
|
||
|
UNPACK_STR = "!16sHH"
|
||
|
MIN_LEN = struct.calcsize(UNPACK_STR)
|
||
|
MSG_LEN = None
|
||
|
|
||
|
@classmethod
|
||
|
def dissect(cls, data):
|
||
|
data, peer_msg = super().dissect(data)
|
||
|
|
||
|
(local_addr, local_port, remote_port) = struct.unpack_from(cls.UNPACK_STR, data)
|
||
|
|
||
|
msg = {
|
||
|
**peer_msg,
|
||
|
**{
|
||
|
"local_ip": bin2str_ipaddress(local_addr, peer_msg.get("ipv6")),
|
||
|
"local_port": int(local_port),
|
||
|
"remote_port": int(remote_port),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
# XXX: dissect the bgp open message
|
||
|
|
||
|
return msg
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
@BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_INITIATION)
|
||
|
class BMPInitiation:
|
||
|
"""
|
||
|
0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Information Type | Information Length |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Information (variable) |
|
||
|
~ ~
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
"""
|
||
|
|
||
|
TLV_STR = "!HH"
|
||
|
MIN_LEN = struct.calcsize(TLV_STR)
|
||
|
FIELD_TO_STR = {
|
||
|
BMPCodes.BMP_INIT_INFO_STRING: "information",
|
||
|
BMPCodes.BMP_INIT_ADMIN_LABEL: "admin_label",
|
||
|
BMPCodes.BMP_INIT_SYSTEM_DESCRIPTION: "system_description",
|
||
|
BMPCodes.BMP_INIT_SYSTEM_NAME: "system_name",
|
||
|
BMPCodes.BMP_INIT_VRF_TABLE_NAME: "vrf_table_name",
|
||
|
}
|
||
|
|
||
|
@classmethod
|
||
|
def dissect(cls, data):
|
||
|
msg = {}
|
||
|
while len(data) > cls.MIN_LEN:
|
||
|
_type, _len = struct.unpack_from(cls.TLV_STR, data[0 : cls.MIN_LEN])
|
||
|
_value = data[cls.MIN_LEN : cls.MIN_LEN + _len].decode()
|
||
|
|
||
|
msg[cls.FIELD_TO_STR[_type]] = _value
|
||
|
data = data[cls.MIN_LEN + _len :]
|
||
|
|
||
|
return msg
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
class BMPTermination:
|
||
|
"""
|
||
|
0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Information Type | Information Length |
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
| Information (variable) |
|
||
|
~ ~
|
||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
|
"""
|
||
|
|
||
|
pass
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
class BMPRouteMirroring:
|
||
|
pass
|
||
|
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
class BMPRoutePolicy:
|
||
|
pass
|