Merge pull request #15448 from louis-6wind/bmp-labels

bgpd: export labels into BMP
This commit is contained in:
Donatas Abraitis 2024-03-03 20:21:17 +02:00 committed by GitHub
commit 9feb1aab76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 247 additions and 103 deletions

View File

@ -910,7 +910,8 @@ static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags,
static struct stream *bmp_update(const struct prefix *p, struct prefix_rd *prd, static struct stream *bmp_update(const struct prefix *p, struct prefix_rd *prd,
struct peer *peer, struct attr *attr, struct peer *peer, struct attr *attr,
afi_t afi, safi_t safi) afi_t afi, safi_t safi, mpls_label_t *label,
uint32_t num_labels)
{ {
struct bpacket_attr_vec_arr vecarr; struct bpacket_attr_vec_arr vecarr;
struct stream *s; struct stream *s;
@ -946,8 +947,8 @@ static struct stream *bmp_update(const struct prefix *p, struct prefix_rd *prd,
mpattrlen_pos = bgp_packet_mpattr_start(s, peer, afi, safi, mpattrlen_pos = bgp_packet_mpattr_start(s, peer, afi, safi,
&vecarr, attr); &vecarr, attr);
bgp_packet_mpattr_prefix(s, afi, safi, p, prd, NULL, 0, 0, 0, bgp_packet_mpattr_prefix(s, afi, safi, p, prd, label,
attr); num_labels, 0, 0, attr);
bgp_packet_mpattr_end(s, mpattrlen_pos); bgp_packet_mpattr_end(s, mpattrlen_pos);
total_attr_len += stream_get_endp(s) - p1; total_attr_len += stream_get_endp(s) - p1;
} }
@ -1002,7 +1003,8 @@ static struct stream *bmp_withdraw(const struct prefix *p,
static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags, static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags,
uint8_t peer_type_flag, const struct prefix *p, uint8_t peer_type_flag, const struct prefix *p,
struct prefix_rd *prd, struct attr *attr, afi_t afi, struct prefix_rd *prd, struct attr *attr, afi_t afi,
safi_t safi, time_t uptime) safi_t safi, time_t uptime, mpls_label_t *label,
uint32_t num_labels)
{ {
struct stream *hdr, *msg; struct stream *hdr, *msg;
struct timeval tv = { .tv_sec = uptime, .tv_usec = 0 }; struct timeval tv = { .tv_sec = uptime, .tv_usec = 0 };
@ -1019,7 +1021,8 @@ static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags,
monotime_to_realtime(&tv, &uptime_real); monotime_to_realtime(&tv, &uptime_real);
if (attr) if (attr)
msg = bmp_update(p, prd, peer, attr, afi, safi); msg = bmp_update(p, prd, peer, attr, afi, safi, label,
num_labels);
else else
msg = bmp_withdraw(p, prd, afi, safi); msg = bmp_withdraw(p, prd, afi, safi);
@ -1219,18 +1222,24 @@ afibreak:
bmp_monitor(bmp, bpi->peer, 0, BMP_PEER_TYPE_LOC_RIB_INSTANCE, bmp_monitor(bmp, bpi->peer, 0, BMP_PEER_TYPE_LOC_RIB_INSTANCE,
bn_p, prd, bpi->attr, afi, safi, bn_p, prd, bpi->attr, afi, safi,
bpi && bpi->extra ? bpi->extra->bgp_rib_uptime bpi && bpi->extra ? bpi->extra->bgp_rib_uptime
: (time_t)(-1L)); : (time_t)(-1L),
bpi->extra ? bpi->extra->label : NULL,
bpi->extra ? bpi->extra->num_labels : 0);
} }
if (bpi && CHECK_FLAG(bpi->flags, BGP_PATH_VALID) && if (bpi && CHECK_FLAG(bpi->flags, BGP_PATH_VALID) &&
CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_POSTPOLICY)) CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_POSTPOLICY))
bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L, bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L,
BMP_PEER_TYPE_GLOBAL_INSTANCE, bn_p, prd, bpi->attr, BMP_PEER_TYPE_GLOBAL_INSTANCE, bn_p, prd, bpi->attr,
afi, safi, bpi->uptime); afi, safi, bpi->uptime,
bpi->extra ? bpi->extra->label : NULL,
bpi->extra ? bpi->extra->num_labels : 0);
if (adjin) if (adjin)
/* TODO: set label here when adjin supports labels */
bmp_monitor(bmp, adjin->peer, 0, BMP_PEER_TYPE_GLOBAL_INSTANCE, bmp_monitor(bmp, adjin->peer, 0, BMP_PEER_TYPE_GLOBAL_INSTANCE,
bn_p, prd, adjin->attr, afi, safi, adjin->uptime); bn_p, prd, adjin->attr, afi, safi, adjin->uptime,
NULL, 0);
if (bn) if (bn)
bgp_dest_unlock_node(bn); bgp_dest_unlock_node(bn);
@ -1343,7 +1352,9 @@ static bool bmp_wrqueue_locrib(struct bmp *bmp, struct pullwr *pullwr)
bmp_monitor(bmp, peer, 0, BMP_PEER_TYPE_LOC_RIB_INSTANCE, &bqe->p, prd, bmp_monitor(bmp, peer, 0, BMP_PEER_TYPE_LOC_RIB_INSTANCE, &bqe->p, prd,
bpi ? bpi->attr : NULL, afi, safi, bpi ? bpi->attr : NULL, afi, safi,
bpi && bpi->extra ? bpi->extra->bgp_rib_uptime bpi && bpi->extra ? bpi->extra->bgp_rib_uptime
: (time_t)(-1L)); : (time_t)(-1L),
(bpi && bpi->extra) ? bpi->extra->label : NULL,
(bpi && bpi->extra) ? bpi->extra->num_labels : 0);
written = true; written = true;
out: out:
@ -1416,7 +1427,9 @@ static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr)
bmp_monitor(bmp, peer, BMP_PEER_FLAG_L, bmp_monitor(bmp, peer, BMP_PEER_FLAG_L,
BMP_PEER_TYPE_GLOBAL_INSTANCE, &bqe->p, prd, BMP_PEER_TYPE_GLOBAL_INSTANCE, &bqe->p, prd,
bpi ? bpi->attr : NULL, afi, safi, bpi ? bpi->attr : NULL, afi, safi,
bpi ? bpi->uptime : monotime(NULL)); bpi ? bpi->uptime : monotime(NULL),
(bpi && bpi->extra) ? bpi->extra->label : NULL,
(bpi && bpi->extra) ? bpi->extra->num_labels : 0);
written = true; written = true;
} }
@ -1428,9 +1441,10 @@ static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr)
if (adjin->peer == peer) if (adjin->peer == peer)
break; break;
} }
/* TODO: set label here when adjin supports labels */
bmp_monitor(bmp, peer, 0, BMP_PEER_TYPE_GLOBAL_INSTANCE, bmp_monitor(bmp, peer, 0, BMP_PEER_TYPE_GLOBAL_INSTANCE,
&bqe->p, prd, adjin ? adjin->attr : NULL, afi, safi, &bqe->p, prd, adjin ? adjin->attr : NULL, afi, safi,
adjin ? adjin->uptime : monotime(NULL)); adjin ? adjin->uptime : monotime(NULL), NULL, 0);
written = true; written = true;
} }

View File

@ -6,9 +6,17 @@ router bgp 65501
neighbor 192:168::2 remote-as 65502 neighbor 192:168::2 remote-as 65502
! !
bmp targets bmp1 bmp targets bmp1
bmp connect 192.0.178.10 port 1789 min-retry 100 max-retry 10000 bmp connect 192.0.2.10 port 1789 min-retry 100 max-retry 10000
exit exit
! !
address-family ipv4 vpn
neighbor 192.168.0.2 activate
neighbor 192.168.0.2 soft-reconfiguration inbound
exit-address-family
address-family ipv6 vpn
neighbor 192:168::2 activate
neighbor 192:168::2 soft-reconfiguration inbound
exit-address-family
address-family ipv4 unicast address-family ipv4 unicast
neighbor 192.168.0.2 activate neighbor 192.168.0.2 activate
neighbor 192.168.0.2 soft-reconfiguration inbound neighbor 192.168.0.2 soft-reconfiguration inbound
@ -20,3 +28,21 @@ router bgp 65501
neighbor 192:168::2 soft-reconfiguration inbound neighbor 192:168::2 soft-reconfiguration inbound
exit-address-family exit-address-family
! !
router bgp 65502 vrf vrf1
bgp router_id 192.168.0.1
bgp log-neighbor-changes
address-family ipv4 unicast
label vpn export 101
rd vpn export 444:1
rt vpn both 52:100
export vpn
import vpn
exit-address-family
address-family ipv6 unicast
label vpn export 103
rd vpn export 555:1
rt vpn both 54:200
export vpn
import vpn
exit-address-family
exit

View File

@ -1,5 +1,5 @@
interface r1-eth0 interface r1-eth0
ip address 192.0.178.1/24 ip address 192.0.2.1/24
! !
interface r1-eth1 interface r1-eth1
ip address 192.168.0.1/24 ip address 192.168.0.1/24

View File

@ -11,9 +11,36 @@ router bgp 65502
no neighbor 192:168::1 activate no neighbor 192:168::1 activate
redistribute connected redistribute connected
exit-address-family exit-address-family
!
address-family ipv4 vpn
neighbor 192.168.0.1 activate
exit-address-family
!
address-family ipv6 vpn
neighbor 192:168::1 activate
exit-address-family
! !
address-family ipv6 unicast address-family ipv6 unicast
neighbor 192:168::1 activate neighbor 192:168::1 activate
redistribute connected redistribute connected
exit-address-family exit-address-family
! !
router bgp 65502 vrf vrf1
bgp router-id 192.168.0.2
bgp log-neighbor-changes
no bgp network import-check
address-family ipv4 unicast
label vpn export 102
rd vpn export 444:2
rt vpn both 52:100
export vpn
import vpn
exit-address-family
address-family ipv6 unicast
label vpn export 105
rd vpn export 555:2
rt vpn both 54:200
export vpn
import vpn
exit-address-family
exit

View File

@ -55,7 +55,7 @@ LOC_RIB = "loc-rib"
def build_topo(tgen): def build_topo(tgen):
tgen.add_router("r1") tgen.add_router("r1")
tgen.add_router("r2") tgen.add_router("r2")
tgen.add_bmp_server("bmp1", ip="192.0.178.10", defaultRoute="via 192.0.178.1") tgen.add_bmp_server("bmp1", ip="192.0.2.10", defaultRoute="via 192.0.2.1")
switch = tgen.add_switch("s1") switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["r1"]) switch.add_link(tgen.gears["r1"])
@ -81,8 +81,8 @@ def setup_module(mod):
tgen.start_router() tgen.start_router()
logger.info("starting BMP servers") logger.info("starting BMP servers")
for _, server in tgen.get_bmp_servers().items(): for bmp_name, server in tgen.get_bmp_servers().items():
server.start() server.start(log_file=os.path.join(tgen.logdir, bmp_name, "bmp.log"))
def teardown_module(_mod): def teardown_module(_mod):
@ -105,7 +105,9 @@ def get_bmp_messages():
""" """
messages = [] messages = []
tgen = get_topogen() tgen = get_topogen()
text_output = tgen.gears["bmp1"].run("cat /var/log/bmp.log") text_output = tgen.gears["bmp1"].run(
"cat {}".format(os.path.join(tgen.logdir, "bmp1", "bmp.log"))
)
for m in text_output.splitlines(): for m in text_output.splitlines():
# some output in the bash can break the message decoding # some output in the bash can break the message decoding
@ -121,7 +123,7 @@ def get_bmp_messages():
return messages return messages
def check_for_prefixes(expected_prefixes, bmp_log_type, policy): def check_for_prefixes(expected_prefixes, bmp_log_type, policy, labels=None):
""" """
Check for the presence of the given prefixes in the BMP server logs with Check for the presence of the given prefixes in the BMP server logs with
the given message type and the set policy. the given message type and the set policy.
@ -140,6 +142,12 @@ def check_for_prefixes(expected_prefixes, bmp_log_type, policy):
and "bmp_log_type" in m.keys() and "bmp_log_type" in m.keys()
and m["bmp_log_type"] == bmp_log_type and m["bmp_log_type"] == bmp_log_type
and m["policy"] == policy and m["policy"] == policy
and (
labels is None
or (
m["ip_prefix"] in labels.keys() and m["label"] == labels[m["ip_prefix"]]
)
)
] ]
# check for prefixes # check for prefixes
@ -174,7 +182,7 @@ def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True):
Configure the bgp prefixes. Configure the bgp prefixes.
""" """
withdraw = "no " if not update else "" withdraw = "no " if not update else ""
vrf = " vrf {}" if vrf else "" vrf = " vrf {}".format(vrf) if vrf else ""
for p in prefixes: for p in prefixes:
ip = ip_network(p) ip = ip_network(p)
cmd = [ cmd = [
@ -216,6 +224,45 @@ def unicast_prefixes(policy):
assert success, "Checking the withdrawed prefixes has been failed !." assert success, "Checking the withdrawed prefixes has been failed !."
def vpn_prefixes(policy):
"""
Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes.
Check if the previous actions are logged in the BMP server with the right
message type and the right policy.
"""
tgen = get_topogen()
set_bmp_policy(tgen, "r1", 65501, "bmp1", "vpn", policy)
prefixes = ["172.31.10.1/32", "2001::2222/128"]
if policy == PRE_POLICY:
# labels are not yet supported in adj-RIB-in. Do not test for the moment
labels = None
else:
# "label vpn export" value in r2/bgpd.conf
labels = {
"172.31.10.1/32": 102,
"2001::2222/128": 105,
}
# add prefixes
configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, vrf="vrf1")
logger.info("checking for updated prefixes")
# check
test_func = partial(check_for_prefixes, prefixes, "update", policy, labels=labels)
success, _ = topotest.run_and_expect(test_func, True, wait=0.5)
assert success, "Checking the updated prefixes has been failed !."
# withdraw prefixes
configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, vrf="vrf1", update=False)
logger.info("checking for withdrawed prefixes")
# check
test_func = partial(check_for_prefixes, prefixes, "withdraw", policy)
success, _ = topotest.run_and_expect(test_func, True, wait=0.5)
assert success, "Checking the withdrawed prefixes has been failed !."
def test_bmp_server_logging(): def test_bmp_server_logging():
""" """
Assert the logging of the bmp server. Assert the logging of the bmp server.
@ -223,7 +270,9 @@ def test_bmp_server_logging():
def check_for_log_file(): def check_for_log_file():
tgen = get_topogen() tgen = get_topogen()
output = tgen.gears["bmp1"].run("ls /var/log/") output = tgen.gears["bmp1"].run(
"ls {}".format(os.path.join(tgen.logdir, "bmp1"))
)
if "bmp.log" not in output: if "bmp.log" not in output:
return False return False
return True return True
@ -244,6 +293,16 @@ def test_bmp_bgp_unicast():
unicast_prefixes(LOC_RIB) unicast_prefixes(LOC_RIB)
def test_bmp_bgp_vpn():
# check for the prefixes in the BMP server logging file
logger.info("***** VPN prefixes pre-policy logging *****")
vpn_prefixes(PRE_POLICY)
logger.info("***** VPN prefixes post-policy logging *****")
vpn_prefixes(POST_POLICY)
logger.info("***** VPN prefixes loc-rib logging *****")
vpn_prefixes(LOC_RIB)
if __name__ == "__main__": if __name__ == "__main__":
args = ["-s"] + sys.argv[1:] args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args)) sys.exit(pytest.main(args))

View File

@ -33,16 +33,18 @@ IS_FILTERED = 1 << 7
if not os.path.exists(LOG_DIR): if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR) os.makedirs(LOG_DIR)
def bin2str_ipaddress(ip_bytes, is_ipv6=False): def bin2str_ipaddress(ip_bytes, is_ipv6=False):
if is_ipv6: if is_ipv6:
return str(ipaddress.IPv6Address(ip_bytes)) return str(ipaddress.IPv6Address(ip_bytes))
return str(ipaddress.IPv4Address(ip_bytes[-4:])) return str(ipaddress.IPv4Address(ip_bytes[-4:]))
def log2file(logs):
def log2file(logs, log_file):
""" """
XXX: extract the useful information and save it in a flat dictionnary XXX: extract the useful information and save it in a flat dictionnary
""" """
with open(LOG_FILE, 'a') as f: with open(log_file, "a") as f:
f.write(json.dumps(logs) + "\n") f.write(json.dumps(logs) + "\n")
@ -51,6 +53,7 @@ class BMPCodes:
""" """
XXX: complete the list, provide RFCs. XXX: complete the list, provide RFCs.
""" """
VERSION = 0x3 VERSION = 0x3
BMP_MSG_TYPE_ROUTE_MONITORING = 0x00 BMP_MSG_TYPE_ROUTE_MONITORING = 0x00
@ -107,15 +110,15 @@ class BMPCodes:
# peer down reason code # peer down reason code
BMP_PEER_DOWN_LOCAL_NOTIFY = 0x01 BMP_PEER_DOWN_LOCAL_NOTIFY = 0x01
BMP_PEER_DOWN_LOCAL_NO_NOTIFY = 0X02 BMP_PEER_DOWN_LOCAL_NO_NOTIFY = 0x02
BMP_PEER_DOWN_REMOTE_NOTIFY = 0X03 BMP_PEER_DOWN_REMOTE_NOTIFY = 0x03
BMP_PEER_DOWN_REMOTE_NO_NOTIFY = 0X04 BMP_PEER_DOWN_REMOTE_NO_NOTIFY = 0x04
BMP_PEER_DOWN_INFO_NO_LONGER = 0x05 BMP_PEER_DOWN_INFO_NO_LONGER = 0x05
BMP_PEER_DOWN_SYSTEM_CLOSED = 0X06 BMP_PEER_DOWN_SYSTEM_CLOSED = 0x06
# termincation message types # termincation message types
BMP_TERM_TYPE_STRING = 0x00 BMP_TERM_TYPE_STRING = 0x00
BMP_TERM_TYPE_REASON = 0X01 BMP_TERM_TYPE_REASON = 0x01
# termination reason code # termination reason code
BMP_TERM_REASON_ADMIN_CLOSE = 0x00 BMP_TERM_REASON_ADMIN_CLOSE = 0x00
@ -138,19 +141,20 @@ class BMPMsg:
XXX: should we move register_msg_type and look_msg_type XXX: should we move register_msg_type and look_msg_type
to generic Type class. to generic Type class.
""" """
TYPES = {} TYPES = {}
UNKNOWN_TYPE = None UNKNOWN_TYPE = None
HDR_STR = '!BIB' HDR_STR = "!BIB"
MIN_LEN = struct.calcsize(HDR_STR) MIN_LEN = struct.calcsize(HDR_STR)
TYPES_STR = { TYPES_STR = {
BMPCodes.BMP_MSG_TYPE_INITIATION: 'initiation', BMPCodes.BMP_MSG_TYPE_INITIATION: "initiation",
BMPCodes.BMP_MSG_TYPE_PEER_DOWN_NOTIFICATION: 'peer down notification', BMPCodes.BMP_MSG_TYPE_PEER_DOWN_NOTIFICATION: "peer down notification",
BMPCodes.BMP_MSG_TYPE_PEER_UP_NOTIFICATION: 'peer up notification', BMPCodes.BMP_MSG_TYPE_PEER_UP_NOTIFICATION: "peer up notification",
BMPCodes.BMP_MSG_TYPE_ROUTE_MONITORING: 'route monitoring', BMPCodes.BMP_MSG_TYPE_ROUTE_MONITORING: "route monitoring",
BMPCodes.BMP_MSG_TYPE_STATISTICS_REPORT: 'statistics report', BMPCodes.BMP_MSG_TYPE_STATISTICS_REPORT: "statistics report",
BMPCodes.BMP_MSG_TYPE_TERMINATION: 'termination', BMPCodes.BMP_MSG_TYPE_TERMINATION: "termination",
BMPCodes.BMP_MSG_TYPE_ROUTE_MIRRORING: 'route mirroring', BMPCodes.BMP_MSG_TYPE_ROUTE_MIRRORING: "route mirroring",
BMPCodes.BMP_MSG_TYPE_ROUTE_POLICY: 'route policy', BMPCodes.BMP_MSG_TYPE_ROUTE_POLICY: "route policy",
} }
@classmethod @classmethod
@ -158,6 +162,7 @@ class BMPMsg:
def _register_type(subcls): def _register_type(subcls):
cls.TYPES[msgtype] = subcls cls.TYPES[msgtype] = subcls
return subcls return subcls
return _register_type return _register_type
@classmethod @classmethod
@ -183,7 +188,7 @@ class BMPMsg:
return _version, _len, _type return _version, _len, _type
@classmethod @classmethod
def dissect(cls, data): def dissect(cls, data, log_file=None):
global SEQ global SEQ
version, msglen, msgtype = cls.dissect_header(data) version, msglen, msgtype = cls.dissect_header(data)
@ -202,7 +207,7 @@ class BMPMsg:
msg_cls.MSG_LEN = msglen - cls.MIN_LEN msg_cls.MSG_LEN = msglen - cls.MIN_LEN
logs = msg_cls.dissect(msg_data) logs = msg_cls.dissect(msg_data)
logs["seq"] = SEQ logs["seq"] = SEQ
log2file(logs) log2file(logs, log_file if log_file else LOG_FILE)
SEQ += 1 SEQ += 1
return data return data
@ -229,30 +234,33 @@ class BMPPerPeerMessage:
| Timestamp (microseconds) | | Timestamp (microseconds) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
""" """
PEER_UNPACK_STR = '!BB8s16sI4sII'
PEER_UNPACK_STR = "!BB8s16sI4sII"
PEER_TYPE_STR = { PEER_TYPE_STR = {
BMPCodes.BMP_PEER_GLOBAL_INSTANCE: 'global instance', BMPCodes.BMP_PEER_GLOBAL_INSTANCE: "global instance",
BMPCodes.BMP_PEER_RD_INSTANCE: 'route distinguisher instance', BMPCodes.BMP_PEER_RD_INSTANCE: "route distinguisher instance",
BMPCodes.BMP_PEER_LOCAL_INSTANCE: 'local instance', BMPCodes.BMP_PEER_LOCAL_INSTANCE: "local instance",
BMPCodes.BMP_PEER_LOC_RIB_INSTANCE: 'loc-rib instance', BMPCodes.BMP_PEER_LOC_RIB_INSTANCE: "loc-rib instance",
} }
@classmethod @classmethod
def dissect(cls, data): def dissect(cls, data):
(peer_type, (
peer_type,
peer_flags, peer_flags,
peer_distinguisher, peer_distinguisher,
peer_address, peer_address,
peer_asn, peer_asn,
peer_bgp_id, peer_bgp_id,
timestamp_secs, timestamp_secs,
timestamp_microsecs) = struct.unpack_from(cls.PEER_UNPACK_STR, data) timestamp_microsecs,
) = struct.unpack_from(cls.PEER_UNPACK_STR, data)
msg = {'peer_type': cls.PEER_TYPE_STR[peer_type]} msg = {"peer_type": cls.PEER_TYPE_STR[peer_type]}
if peer_type == 0x03: if peer_type == 0x03:
msg['is_filtered'] = bool(peer_flags & IS_FILTERED) msg["is_filtered"] = bool(peer_flags & IS_FILTERED)
msg['policy'] = 'loc-rib' msg["policy"] = "loc-rib"
else: else:
# peer_flags = 0x0000 0000 # peer_flags = 0x0000 0000
# ipv6, post-policy, as-path, adj-rib-out, reserverdx4 # ipv6, post-policy, as-path, adj-rib-out, reserverdx4
@ -260,21 +268,22 @@ class BMPPerPeerMessage:
is_as_path = bool(peer_flags & IS_AS_PATH) is_as_path = bool(peer_flags & IS_AS_PATH)
is_post_policy = bool(peer_flags & IS_POST_POLICY) is_post_policy = bool(peer_flags & IS_POST_POLICY)
is_ipv6 = bool(peer_flags & IS_IPV6) is_ipv6 = bool(peer_flags & IS_IPV6)
msg['policy'] = 'post-policy' if is_post_policy else 'pre-policy' msg["policy"] = "post-policy" if is_post_policy else "pre-policy"
msg['ipv6'] = is_ipv6 msg["ipv6"] = is_ipv6
msg['peer_ip'] = bin2str_ipaddress(peer_address, is_ipv6) msg["peer_ip"] = bin2str_ipaddress(peer_address, is_ipv6)
peer_bgp_id = bin2str_ipaddress(peer_bgp_id) peer_bgp_id = bin2str_ipaddress(peer_bgp_id)
timestamp = float(timestamp_secs) + timestamp_microsecs * (10**-6) timestamp = float(timestamp_secs) + timestamp_microsecs * (10**-6)
data = data[struct.calcsize(cls.PEER_UNPACK_STR) :] data = data[struct.calcsize(cls.PEER_UNPACK_STR) :]
msg.update({ msg.update(
'peer_distinguisher': str(RouteDistinguisher(peer_distinguisher)), {
'peer_asn': peer_asn, "peer_distinguisher": str(RouteDistinguisher(peer_distinguisher)),
'peer_bgp_id': peer_bgp_id, "peer_asn": peer_asn,
'timestamp': str(datetime.datetime.fromtimestamp(timestamp)), "peer_bgp_id": peer_bgp_id,
}) "timestamp": str(datetime.datetime.fromtimestamp(timestamp)),
}
)
return data, msg return data, msg
@ -282,7 +291,6 @@ class BMPPerPeerMessage:
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_ROUTE_MONITORING) @BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_ROUTE_MONITORING)
class BMPRouteMonitoring(BMPPerPeerMessage): class BMPRouteMonitoring(BMPPerPeerMessage):
@classmethod @classmethod
def dissect(cls, data): def dissect(cls, data):
data, peer_msg = super().dissect(data) data, peer_msg = super().dissect(data)
@ -303,6 +311,7 @@ class BMPStatisticsReport:
~ ~ ~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
""" """
pass pass
@ -316,6 +325,7 @@ class BMPPeerDownNotification:
| Data (present if Reason = 1, 2 or 3) | | Data (present if Reason = 1, 2 or 3) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
""" """
pass pass
@ -336,7 +346,8 @@ class BMPPeerUpNotification(BMPPerPeerMessage):
~ ~ ~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
""" """
UNPACK_STR = '!16sHH'
UNPACK_STR = "!16sHH"
MIN_LEN = struct.calcsize(UNPACK_STR) MIN_LEN = struct.calcsize(UNPACK_STR)
MSG_LEN = None MSG_LEN = None
@ -344,16 +355,14 @@ class BMPPeerUpNotification(BMPPerPeerMessage):
def dissect(cls, data): def dissect(cls, data):
data, peer_msg = super().dissect(data) data, peer_msg = super().dissect(data)
(local_addr, (local_addr, local_port, remote_port) = struct.unpack_from(cls.UNPACK_STR, data)
local_port,
remote_port) = struct.unpack_from(cls.UNPACK_STR, data)
msg = { msg = {
**peer_msg, **peer_msg,
**{ **{
'local_ip': bin2str_ipaddress(local_addr, peer_msg.get('ipv6')), "local_ip": bin2str_ipaddress(local_addr, peer_msg.get("ipv6")),
'local_port': int(local_port), "local_port": int(local_port),
'remote_port': int(remote_port), "remote_port": int(remote_port),
}, },
} }
@ -374,14 +383,15 @@ class BMPInitiation:
~ ~ ~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
""" """
TLV_STR = '!HH'
TLV_STR = "!HH"
MIN_LEN = struct.calcsize(TLV_STR) MIN_LEN = struct.calcsize(TLV_STR)
FIELD_TO_STR = { FIELD_TO_STR = {
BMPCodes.BMP_INIT_INFO_STRING: 'information', BMPCodes.BMP_INIT_INFO_STRING: "information",
BMPCodes.BMP_INIT_ADMIN_LABEL: 'admin_label', BMPCodes.BMP_INIT_ADMIN_LABEL: "admin_label",
BMPCodes.BMP_INIT_SYSTEM_DESCRIPTION: 'system_description', BMPCodes.BMP_INIT_SYSTEM_DESCRIPTION: "system_description",
BMPCodes.BMP_INIT_SYSTEM_NAME: 'system_name', BMPCodes.BMP_INIT_SYSTEM_NAME: "system_name",
BMPCodes.BMP_INIT_VRF_TABLE_NAME: 'vrf_table_name', BMPCodes.BMP_INIT_VRF_TABLE_NAME: "vrf_table_name",
} }
@classmethod @classmethod
@ -408,6 +418,7 @@ class BMPTermination:
~ ~ ~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
""" """
pass pass

View File

@ -16,10 +16,12 @@ BGP_MAX_SIZE = 4096
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("-a", "--address", type=str, default="0.0.0.0") parser.add_argument("-a", "--address", type=str, default="0.0.0.0")
parser.add_argument("-p", "--port", type=int, default=1789) parser.add_argument("-p", "--port", type=int, default=1789)
parser.add_argument("-l", "--logfile", type=str, default="/var/log/bmp.log")
def main(): def main():
args = parser.parse_args() args = parser.parse_args()
ADDRESS, PORT = args.address, args.port ADDRESS, PORT = args.address, args.port
LOG_FILE = args.logfile
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@ -31,7 +33,7 @@ def main():
while True: while True:
data = connection.recv(BGP_MAX_SIZE) data = connection.recv(BGP_MAX_SIZE)
while len(data) > BMPMsg.MIN_LEN: while len(data) > BMPMsg.MIN_LEN:
data = BMPMsg.dissect(data) data = BMPMsg.dissect(data, log_file=LOG_FILE)
except Exception as e: except Exception as e:
# XXX: do something # XXX: do something
pass pass

View File

@ -94,7 +94,9 @@ def get_exabgp_cmd(commander=None):
return False return False
version = m.group(1) version = m.group(1)
if topotest.version_cmp(version, "4.2.11") < 0: if topotest.version_cmp(version, "4.2.11") < 0:
logging.debug("found exabgp version < 4.2.11 in %s will keep looking", exacmd) logging.debug(
"found exabgp version < 4.2.11 in %s will keep looking", exacmd
)
return False return False
logger.info("Using ExaBGP version %s in %s", version, exacmd) logger.info("Using ExaBGP version %s in %s", version, exacmd)
return True return True
@ -1253,9 +1255,12 @@ class TopoBMPCollector(TopoHost):
gear += " TopoBMPCollector<>".format() gear += " TopoBMPCollector<>".format()
return gear return gear
def start(self): def start(self, log_file=None):
log_arg = "-l {}".format(log_file) if log_file else ""
self.run( self.run(
"{}/bmp_collector/bmpserver -a {} -p {}&".format(CWD, self.ip, self.port), "{}/bmp_collector/bmpserver -a {} -p {} {}&".format(
CWD, self.ip, self.port, log_arg
),
stdout=None, stdout=None,
) )