mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-24 17:52:19 +00:00
Revert "tests: add bgp_linkstate_topo1"
This reverts commita0e6cd4c8f
. (cherry picked from commitc4edb3a453
)
This commit is contained in:
parent
492d542ba0
commit
880183419a
@ -1,202 +0,0 @@
|
||||
// Check content with
|
||||
// cat bgp_injector.cfg | sed -e 's|//.*||g' | jq .
|
||||
{
|
||||
"my_as": 65001,
|
||||
"hold_time": 30,
|
||||
"bgp_identifier": "192.0.2.1",
|
||||
"local_address": "192.0.2.1",
|
||||
"peer_address": "192.0.2.2",
|
||||
"mss": 4000,
|
||||
"port": 179,
|
||||
"path_attributes":
|
||||
{
|
||||
"as-path": "65001",
|
||||
"next-hop": "192.0.2.1",
|
||||
"origin": 0
|
||||
},
|
||||
"link_states":
|
||||
[
|
||||
{
|
||||
"nlri":
|
||||
{
|
||||
"proto": "01", // IS-IS L1
|
||||
"id": "0000000000000020",
|
||||
"type": "0002", // Link-NLRI
|
||||
"256": { // Local Link-Node Descriptor TLV
|
||||
"512": "0000fde9", // AS 65001
|
||||
"513": "00000000", // BGP-LS ID
|
||||
"515": "000000001001" // router-id: 0000.0000.1001
|
||||
},
|
||||
"257": { // Remote Link-Node Descriptor TLV
|
||||
"512": "0000fde9", // AS 65001
|
||||
"513": "00000000", // BGP-LS ID
|
||||
"515": "000000001000" // router-id: 0000.0000.1000
|
||||
},
|
||||
"259": "0a010001", // IPv4 interface address TLV
|
||||
"260": "0a010002", // IPv4 Neighbor address TLV
|
||||
"261": "20010000000000000000000000000001", // IPv6 interface address TLV
|
||||
"262": "20010000000000000000000000000002", // IPv6 Neighbor address TLV
|
||||
"263": "00000002" // MT-ID
|
||||
},
|
||||
"attr":
|
||||
{
|
||||
"1028": "01010101", //IPv4 Router-ID of Local Node TLV
|
||||
"1030": "0a0a0a0a", //IPv4 Router-ID of Remote Node TLV
|
||||
"1089": "4d2817c8", // Maximum link bandwidth TLV 1410.07 Mbps
|
||||
"1090": "4d2817c8", // Maximum reservable link bandwidth TLV 1410.07 Mbps
|
||||
"1091": "4d2817c84d2817c84d2817c84d2817c84d2817c84d2817c84d2817c84d2817c8", // Unreserved bandwidth TLV
|
||||
"1092": "00000064", // TE Default Metric TLV
|
||||
"1095": "00000a", // Metric TLV
|
||||
// Adjacency SID TLV
|
||||
// Flags: 0x30, Value Flag (V), Local Flag (L)
|
||||
// Weight: 0
|
||||
// .... 0000 0011 1010 1001 1000 = SID/Label: 15000
|
||||
"1099": "30000000003a98",
|
||||
//Unidirectional Link Delay TLV
|
||||
// TE Metric Flags: 0x00
|
||||
// Delay: 8500
|
||||
"1114": "00002134",
|
||||
//Min/Max Unidirectional Link Delay TLV
|
||||
// TE Metric Flags: 0x00
|
||||
// Min Delay: 8000
|
||||
// Reserved: 0x00
|
||||
// Max Delay: 9000
|
||||
"1115": "00001f4000002328",
|
||||
"1122": { //Application-Specific Link Attributes TLV
|
||||
// Type: 1122
|
||||
// Length: 48
|
||||
// SABM Length: 4
|
||||
// UDABM Length: 4
|
||||
// Reserved: 0x0000
|
||||
// Standard Application Identifier Bit Mask: 0x10000000, Flexible Algorithm (X)
|
||||
// User-Defined Application Identifier Bit Mask: 00 00 00 00
|
||||
"0": "040400001000000000000000", // 0 means encode data directly
|
||||
"1088": "00000001", // Administrative group (color) TLV
|
||||
"1092": "00000064", // TE Default Metric TLV
|
||||
"1115": "00001f4000002328", // Min/Max Unidirectional Link Delay TLV
|
||||
"1173": "00000001"// Extended Administrative Group TLV
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"nlri":
|
||||
{
|
||||
"proto": "01", // IS-IS L1
|
||||
"id": "0000000000000020",
|
||||
"type": "0001", // Node-NLRI
|
||||
"256": { // Local Link-Node Descriptor TLV
|
||||
"512": "0000fde9", // AS 65001
|
||||
"513": "00000000", // BGP-LS ID
|
||||
"515": "00000000100300" // router-id: 0000.0000.1003.00
|
||||
}
|
||||
},
|
||||
"attr":
|
||||
{
|
||||
"0": "0107000400000002010a00020108040200027233040300034910000404000403030303040a000cc000000fa004890003004e20040b0003008082040c000c00000003e804890003003a98"
|
||||
}
|
||||
},
|
||||
{
|
||||
"nlri":
|
||||
{
|
||||
"proto": "03", // OSPFv2
|
||||
"id": "0000000000000020",
|
||||
"type": "0001", // Node-NLRI
|
||||
"256": { // Local Link-Node Descriptor TLV
|
||||
"512": "0000fde9", // AS 65001
|
||||
"513": "00000000", // BGP-LS ID
|
||||
"514": "00000000", // Area 0
|
||||
"515": "0a0a0a0a" // router-id: 10.10.10.10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"nlri":
|
||||
{
|
||||
"proto": "03", // OSPFv2
|
||||
"id": "0000000000000020",
|
||||
"type": "0001", // Node-NLRI
|
||||
"256": { // Local Link-Node Descriptor TLV
|
||||
"512": "0000fde9", // AS 65001
|
||||
"513": "00000000", // BGP-LS ID
|
||||
"514": "00000000", // Area 0
|
||||
"515": "0a0a0a0a01010101" // router-id: 10.10.10.10:1.1.1.1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"nlri":
|
||||
{
|
||||
"proto": "03", // OSPFv2
|
||||
"id": "0000000000000020",
|
||||
"type": "0003", // IPv4-topo-prefix-NLRI
|
||||
"256": { // Local Link-Node Descriptor TLV
|
||||
"512": "0000fde9", // AS 65001
|
||||
"513": "00000000", // BGP-LS ID
|
||||
"514": "00000000", // Area 0
|
||||
"515": "0a0a0a0a01010101" // router-id: 10.10.10.10:1.1.1.1
|
||||
},
|
||||
"265": "18590a0b" // IP Reachability Information TLV (89.10.11.0/24)
|
||||
}
|
||||
},
|
||||
{
|
||||
"nlri":
|
||||
{
|
||||
"proto": "02", // IS-IS L2
|
||||
"id": "0000000000000020",
|
||||
"type": "0004", // IPv6-topo-prefix-NLRI
|
||||
"256": { // Local Link-Node Descriptor TLV
|
||||
"512": "0000fde9", // AS 65001
|
||||
"513": "00000000", // BGP-LS ID
|
||||
"515": "00000000100300" // router-id: 0000.0000.1003.00
|
||||
},
|
||||
"263": "0002", // MT-ID
|
||||
// IP Reachability Information TLV (12:12::12:12/128)
|
||||
"265": "8000120012000000000000000000120012"
|
||||
}
|
||||
},
|
||||
{
|
||||
"nlri":
|
||||
{
|
||||
"proto": "06", // OSPFv3
|
||||
"id": "0000000000000020",
|
||||
"type": "0004", // IPv6-topo-prefix-NLRI
|
||||
"256": { // Local Link-Node Descriptor TLV
|
||||
"512": "0000fde9", // AS 65001
|
||||
"513": "00000000", // BGP-LS ID
|
||||
"514": "00000000", // Area 0
|
||||
"515": "0a0a0a0a" // router-id: 10.10.10.10
|
||||
},
|
||||
"263": "0002", // MT-ID
|
||||
"264": "01", // OSPF: route-type Intra-Area (0x1)
|
||||
// IP Reachability Information TLV (12:12::12:12/128)
|
||||
"265": "8000120012000000000000000000120012"
|
||||
}
|
||||
},
|
||||
{
|
||||
"nlri":
|
||||
{
|
||||
"proto": "06", // OSPFv3
|
||||
"id": "ffffffffffffffff",
|
||||
"type": "0002", // Link-NLRI
|
||||
"256": { // Local Link-Node Descriptor TLV
|
||||
"512": "ffffffff", // AS
|
||||
"513": "ffffffff", // BGP-LS ID
|
||||
"514": "ffffffff", // OSPF area ID
|
||||
"515": "0a0a0a0b02020202" // router-id: 10.10.10.11:2.2.2.2
|
||||
},
|
||||
"257": { // Remote Link-Node Descriptor TLV
|
||||
"512": "ffffffff", // AS
|
||||
"513": "ffffffff", // BGP-LS ID
|
||||
"514": "ffffffff", // OSPF area ID
|
||||
"515": "0a0a0a0a01010101" // router-id: 10.10.10.10:1.1.1.1
|
||||
},
|
||||
"259": "0a010001", // IPv4 interface address TLV
|
||||
"260": "0a010002", // IPv4 Neighbor address TLV
|
||||
"261": "20010000000000000000000000000001", // IPv6 interface address TLV
|
||||
"262": "20010000000000000000000000000002", // IPv6 Neighbor address TLV
|
||||
"263": "00000002", // MT-ID
|
||||
"424": "200100000000000001" // unknown TLV
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,596 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
#
|
||||
# Copyright 2018 Jorge Borreicho
|
||||
# Copyright 2023 6WIND S.A.
|
||||
|
||||
"""
|
||||
BGP prefix injection tool
|
||||
"""
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
import struct
|
||||
import threading
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import errno
|
||||
|
||||
|
||||
AFI_IPV4 = 1
|
||||
SAFI_UNICAST = 1
|
||||
|
||||
AFI_LINKSTATE = 16388
|
||||
SAFI_LINKSTATE = 71
|
||||
|
||||
saved_pid = False
|
||||
global pid_file
|
||||
|
||||
class Unbuffered(object):
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
def write(self, data):
|
||||
self.stream.write(data)
|
||||
self.stream.flush()
|
||||
def writelines(self, datas):
|
||||
self.stream.writelines(datas)
|
||||
self.stream.flush()
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
def keepalive_thread(conn, interval):
|
||||
|
||||
# infinite loop so that function do not terminate and thread do not end.
|
||||
while True:
|
||||
time.sleep(interval)
|
||||
keepalive_bgp(conn)
|
||||
|
||||
|
||||
def receive_thread(conn):
|
||||
|
||||
# infinite loop so that function do not terminate and thread do not end.
|
||||
while True:
|
||||
|
||||
# Receiving from client
|
||||
r = conn.recv(1500)
|
||||
while True:
|
||||
start_ptr = (
|
||||
r.find(
|
||||
b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
||||
)
|
||||
+ 16
|
||||
)
|
||||
end_ptr = (
|
||||
r[16:].find(
|
||||
b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
||||
)
|
||||
+ 16
|
||||
)
|
||||
if (
|
||||
start_ptr >= end_ptr
|
||||
): # a single message was sent in the BGP packet OR it is the last message of the BGP packet
|
||||
decode_bgp(r[start_ptr:])
|
||||
break
|
||||
else: # more messages left to decode
|
||||
decode_bgp(r[start_ptr:end_ptr])
|
||||
r = r[end_ptr:]
|
||||
|
||||
|
||||
def decode_bgp(msg):
|
||||
if len(msg) < 3:
|
||||
return
|
||||
msg_length, msg_type = struct.unpack("!HB", msg[0:3])
|
||||
if msg_type == 4:
|
||||
# print(timestamp + " - " + "Received KEEPALIVE") #uncomment to debug
|
||||
pass
|
||||
elif msg_type == 2:
|
||||
timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print(timestamp + " - " + "Received UPDATE")
|
||||
elif msg_type == 1:
|
||||
version, remote_as, holdtime, i1, i2, i3, i4, opt_length = struct.unpack(
|
||||
"!BHHBBBBB", msg[3:13]
|
||||
)
|
||||
timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print(timestamp + " - " + "Received OPEN")
|
||||
print()
|
||||
print(
|
||||
"--> Version:"
|
||||
+ str(version)
|
||||
+ ", Remote AS: "
|
||||
+ str(remote_as)
|
||||
+ ", Hold Time:"
|
||||
+ str(holdtime)
|
||||
+ ", Remote ID: "
|
||||
+ str(i1)
|
||||
+ "."
|
||||
+ str(i2)
|
||||
+ "."
|
||||
+ str(i3)
|
||||
+ "."
|
||||
+ str(i4)
|
||||
)
|
||||
print()
|
||||
elif msg_type == 3:
|
||||
timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print(timestamp + " - " + "Received NOTIFICATION")
|
||||
|
||||
|
||||
def multiprotocol_capability(afi, safi):
|
||||
hexstream = bytes.fromhex("02060104")
|
||||
hexstream += struct.pack("!H", afi)
|
||||
hexstream += struct.pack("!B", 0)
|
||||
hexstream += struct.pack("!B", safi)
|
||||
|
||||
return hexstream
|
||||
|
||||
|
||||
def open_bgp(conn, config):
|
||||
|
||||
# Build the BGP Message
|
||||
bgp_version = b"\x04"
|
||||
bgp_as = struct.pack("!H", config["my_as"])
|
||||
bgp_hold_time = struct.pack("!H", config["hold_time"])
|
||||
|
||||
octet = config["bgp_identifier"].split(".")
|
||||
bgp_identifier = struct.pack(
|
||||
"!BBBB", int(octet[0]), int(octet[1]), int(octet[2]), int(octet[3])
|
||||
)
|
||||
|
||||
bgp_opt = b""
|
||||
bgp_opt += multiprotocol_capability(AFI_IPV4, SAFI_UNICAST)
|
||||
bgp_opt += multiprotocol_capability(AFI_LINKSTATE, SAFI_LINKSTATE)
|
||||
|
||||
bgp_opt_lenght = struct.pack("!B", len(bgp_opt))
|
||||
|
||||
bgp_message = (
|
||||
bgp_version + bgp_as + bgp_hold_time + bgp_identifier + bgp_opt_lenght + bgp_opt
|
||||
)
|
||||
|
||||
# Build the BGP Header
|
||||
total_length = len(bgp_message) + 16 + 2 + 1
|
||||
bgp_marker = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
||||
bgp_length = struct.pack("!H", total_length)
|
||||
bgp_type = b"\x01"
|
||||
bgp_header = bgp_marker + bgp_length + bgp_type
|
||||
|
||||
bgp_packet = bgp_header + bgp_message
|
||||
|
||||
conn.send(bgp_packet)
|
||||
return 0
|
||||
|
||||
|
||||
def keepalive_bgp(conn):
|
||||
|
||||
# Build the BGP Header
|
||||
total_length = 16 + 2 + 1
|
||||
bgp_marker = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
||||
bgp_length = struct.pack("!H", total_length)
|
||||
bgp_type = b"\x04"
|
||||
bgp_header = bgp_marker + bgp_length + bgp_type
|
||||
|
||||
bgp_packet = bgp_header
|
||||
|
||||
conn.send(bgp_packet)
|
||||
return 0
|
||||
|
||||
|
||||
def encode_ipv4_prefix(address, netmask):
|
||||
|
||||
octet = address.split(".")
|
||||
length = struct.pack("!B", int(netmask))
|
||||
|
||||
if int(netmask) <= 8:
|
||||
prefix = struct.pack("!B", int(octet[0]))
|
||||
elif int(netmask) <= 16:
|
||||
prefix = struct.pack("!BB", int(octet[0]), int(octet[1]))
|
||||
elif int(netmask) <= 24:
|
||||
prefix = struct.pack("!BBB", int(octet[0]), int(octet[1]), int(octet[2]))
|
||||
else:
|
||||
prefix = struct.pack(
|
||||
"!BBBB", int(octet[0]), int(octet[1]), int(octet[2]), int(octet[3])
|
||||
)
|
||||
|
||||
return length + prefix
|
||||
|
||||
|
||||
def encode_path_attribute_mp_reach_nrli(afi, safi, data, config):
|
||||
hexstream = b""
|
||||
hexstream += b"\x90" # flags optional, extended
|
||||
hexstream += struct.pack("!B", 14) # type code MP_REACH_NLRI
|
||||
|
||||
hexstream2 = b""
|
||||
hexstream2 += struct.pack("!H", afi)
|
||||
hexstream2 += struct.pack("!B", safi)
|
||||
hexstream2 += struct.pack("!B", 4) # nexthop length
|
||||
hexstream2 += socket.inet_aton(config["local_address"]) # nexthop IPv4
|
||||
hexstream2 += b"\x00" # SNPA
|
||||
hexstream2 += data
|
||||
|
||||
hexstream += struct.pack("!H", len(hexstream2)) # length
|
||||
hexstream += hexstream2
|
||||
|
||||
return hexstream
|
||||
|
||||
|
||||
def encode_path_attribute_linkstate(data):
|
||||
hexstream = b""
|
||||
hexstream += b"\x80" # flags optional
|
||||
hexstream += struct.pack("!B", 29) # type code BGP-LS
|
||||
hexstream += struct.pack("!B", len(data)) # length
|
||||
hexstream += data
|
||||
|
||||
return hexstream
|
||||
|
||||
|
||||
def encode_path_attribute(type, value):
|
||||
|
||||
path_attributes = {
|
||||
"origin": [b"\x40", 1],
|
||||
"as-path": [b"\x40", 2],
|
||||
"next-hop": [b"\x40", 3],
|
||||
"med": [b"\x80", 4],
|
||||
"local_pref": [b"\x40", 5],
|
||||
"communities": [b"\xc0", 8],
|
||||
}
|
||||
|
||||
attribute_flag = path_attributes[type][0]
|
||||
attribute_type_code = struct.pack("!B", int(path_attributes[type][1]))
|
||||
|
||||
if type == "origin":
|
||||
attribute_value = struct.pack("!B", value)
|
||||
elif type == "as-path":
|
||||
as_number_list = value.split(" ")
|
||||
attribute_value = struct.pack("!BB", 2, len(as_number_list))
|
||||
for as_number in as_number_list:
|
||||
attribute_value += struct.pack("!H", int(as_number))
|
||||
elif type == "next-hop":
|
||||
octet = value.split(".")
|
||||
attribute_value = struct.pack(
|
||||
"!BBBB", int(octet[0]), int(octet[1]), int(octet[2]), int(octet[3])
|
||||
)
|
||||
elif type == "med":
|
||||
attribute_value = struct.pack("!I", value)
|
||||
elif type == "local_pref":
|
||||
attribute_value = struct.pack("!I", value)
|
||||
elif type == "communities":
|
||||
communities_list = value.split(" ")
|
||||
attribute_value = b""
|
||||
for community in communities_list:
|
||||
aux = community.split(":")
|
||||
attribute_value += struct.pack("!HH", int(aux[0]), int(aux[1]))
|
||||
|
||||
attribute_length = struct.pack("!B", len(attribute_value))
|
||||
|
||||
return attribute_flag + attribute_type_code + attribute_length + attribute_value
|
||||
|
||||
|
||||
def encode_tlvs(tlvs):
|
||||
stream = b""
|
||||
for key, tlv_data in tlvs.items():
|
||||
if isinstance(key, str) and key.isdigit():
|
||||
tlv_type = int(key)
|
||||
else:
|
||||
# key is not a TLV
|
||||
continue
|
||||
if isinstance(tlv_data, str):
|
||||
if tlv_type != 0:
|
||||
# TLV type 0 is fake TLV
|
||||
stream += struct.pack("!H", tlv_type)
|
||||
stream += struct.pack("!H", len(bytes.fromhex(tlv_data)))
|
||||
stream += bytes.fromhex(tlv_data)
|
||||
elif isinstance(tlv_data, dict):
|
||||
# TLV contains sub-TLV
|
||||
stream += struct.pack("!H", tlv_type)
|
||||
|
||||
stream_subtlv = encode_tlvs(tlv_data)
|
||||
stream += struct.pack("!H", len(stream_subtlv))
|
||||
stream += stream_subtlv
|
||||
else:
|
||||
# invalid input
|
||||
assert 0
|
||||
|
||||
return stream
|
||||
|
||||
|
||||
def encode_linkstate_nrli_tlv(nlri):
|
||||
stream = b""
|
||||
stream += bytes.fromhex(nlri["type"])
|
||||
|
||||
stream2 = b""
|
||||
stream2 += bytes.fromhex(nlri["proto"])
|
||||
stream2 += bytes.fromhex(nlri["id"])
|
||||
stream2 += encode_tlvs(nlri)
|
||||
|
||||
stream += struct.pack("!H", len(stream2))
|
||||
stream += stream2
|
||||
|
||||
return stream
|
||||
|
||||
|
||||
def update_bgp(conn, link_state, config):
|
||||
|
||||
# Build the BGP Message
|
||||
|
||||
# Expired Routes
|
||||
# 1 - Withdrawn Routes
|
||||
|
||||
bgp_withdrawn_routes = b""
|
||||
max_length_reached = False
|
||||
|
||||
bgp_withdrawn_routes_length = struct.pack("!H", len(bgp_withdrawn_routes))
|
||||
bgp_withdrawn_routes = bgp_withdrawn_routes_length + bgp_withdrawn_routes
|
||||
|
||||
# New Routes
|
||||
# 2 - Path Attributes
|
||||
|
||||
path_attributes = config["path_attributes"]
|
||||
bgp_mss = config["mss"]
|
||||
|
||||
bgp_total_path_attributes = b""
|
||||
|
||||
# encode link-state MP_REACH NLRI
|
||||
data = encode_linkstate_nrli_tlv(link_state["nlri"])
|
||||
bgp_total_path_attributes += encode_path_attribute_mp_reach_nrli(
|
||||
AFI_LINKSTATE, SAFI_LINKSTATE, data, config
|
||||
)
|
||||
|
||||
# encode classic attributes
|
||||
for key in path_attributes.keys():
|
||||
bgp_total_path_attributes += encode_path_attribute(key, path_attributes[key])
|
||||
|
||||
# encode link-state attributes
|
||||
if "attr" in link_state:
|
||||
data = encode_tlvs(link_state["attr"])
|
||||
else:
|
||||
data = b""
|
||||
bgp_total_path_attributes += encode_path_attribute_linkstate(data)
|
||||
|
||||
bgp_total_path_attributes_length = struct.pack("!H", len(bgp_total_path_attributes))
|
||||
bgp_total_path_attributes = (
|
||||
bgp_total_path_attributes_length + bgp_total_path_attributes
|
||||
)
|
||||
|
||||
# 3- Network Layer Reachability Information (NLRI)
|
||||
|
||||
bgp_new_routes = b""
|
||||
|
||||
bgp_message = bgp_withdrawn_routes + bgp_total_path_attributes + bgp_new_routes
|
||||
|
||||
# Build the BGP Header
|
||||
total_length = len(bgp_message) + 16 + 2 + 1
|
||||
bgp_marker = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
||||
bgp_length = struct.pack("!H", total_length)
|
||||
bgp_type = b"\x02"
|
||||
bgp_header = bgp_marker + bgp_length + bgp_type
|
||||
|
||||
bgp_packet = bgp_header + bgp_message
|
||||
|
||||
conn.send(bgp_packet)
|
||||
|
||||
timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print(timestamp + " - " + "Sent UPDATE")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def str2ip(ip_str):
|
||||
s_octet = ip_str.split(".")
|
||||
ip_addr = struct.pack(
|
||||
"!BBBB", int(s_octet[0]), int(s_octet[1]), int(s_octet[2]), int(s_octet[3])
|
||||
)
|
||||
return ip_addr
|
||||
|
||||
|
||||
def check_pid(pid):
|
||||
if pid < 0: # user input error
|
||||
return False
|
||||
if pid == 0: # all processes
|
||||
return False
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
return True
|
||||
except OSError as err:
|
||||
if err.errno == errno.EPERM: # a process we were denied access to
|
||||
return True
|
||||
if err.errno == errno.ESRCH: # No such process
|
||||
return False
|
||||
# should never happen
|
||||
return False
|
||||
|
||||
|
||||
def savepid():
|
||||
ownid = os.getpid()
|
||||
|
||||
flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
|
||||
mode = ((os.R_OK | os.W_OK) << 6) | (os.R_OK << 3) | os.R_OK
|
||||
|
||||
try:
|
||||
fd = os.open(pid_file, flags, mode)
|
||||
except OSError:
|
||||
try:
|
||||
pid = open(pid_file, "r").readline().strip()
|
||||
if check_pid(int(pid)):
|
||||
sys.stderr.write(
|
||||
"PIDfile already exists and program still running %s\n" % pid_file
|
||||
)
|
||||
return False
|
||||
else:
|
||||
# If pid is not running, reopen file without O_EXCL
|
||||
fd = os.open(pid_file, flags ^ os.O_EXCL, mode)
|
||||
except (OSError, IOError, ValueError):
|
||||
sys.stderr.write(
|
||||
"issue accessing PID file %s (most likely permission or ownership)\n"
|
||||
% pid_file
|
||||
)
|
||||
return False
|
||||
|
||||
try:
|
||||
f = os.fdopen(fd, "w")
|
||||
line = "%d\n" % ownid
|
||||
f.write(line)
|
||||
f.close()
|
||||
saved_pid = True
|
||||
except IOError:
|
||||
sys.stderr.write("Can not create PIDfile %s\n" % pid_file)
|
||||
return False
|
||||
print("Created PIDfile %s with value %d\n" % (pid_file, ownid))
|
||||
return True
|
||||
|
||||
|
||||
def removepid():
|
||||
if not saved_pid:
|
||||
return
|
||||
try:
|
||||
os.remove(pid_file)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.ENOENT:
|
||||
pass
|
||||
else:
|
||||
sys.stderr.write("Can not remove PIDfile %s\n" % pid_file)
|
||||
return
|
||||
sys.stderr.write("Removed PIDfile %s\n" % pid_file)
|
||||
|
||||
|
||||
def daemonize():
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# Exit first parent
|
||||
sys.exit(0)
|
||||
except OSError as e:
|
||||
print("Fork #1 failed: %d (%s)" % (e.errno, e.strerror))
|
||||
sys.exit(1)
|
||||
|
||||
# Decouple from parent environment
|
||||
os.chdir("/")
|
||||
os.setsid()
|
||||
os.umask(0)
|
||||
|
||||
# Do second fork
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# Exit from second parent
|
||||
sys.exit(0)
|
||||
except OSError as e:
|
||||
print("Fork #2 failed: %d (%s)" % (e.errno, e.strerror))
|
||||
sys.exit(1)
|
||||
|
||||
# Redirect standard file descriptors
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
si = open(os.devnull, "r")
|
||||
so = open(os.devnull, "a+")
|
||||
se = open(os.devnull, "a+")
|
||||
|
||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
|
||||
|
||||
def term(signal, frame):
|
||||
timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print(timestamp + " - " + "^C received, shutting down.\n")
|
||||
bgp_socket.close()
|
||||
removepid()
|
||||
exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
# daemonize and log to file
|
||||
daemonize()
|
||||
pid_file = os.path.join(sys.argv[1], "bgp_injector.pid")
|
||||
savepid()
|
||||
# deal with daemon termination
|
||||
signal.signal(signal.SIGTERM, term)
|
||||
signal.signal(signal.SIGINT, term) # CTRL + C
|
||||
|
||||
log_dir = os.path.join(sys.argv[1], "bgp_injector.log")
|
||||
f = open(log_dir, 'w')
|
||||
sys.stdout = Unbuffered(f)
|
||||
sys.stderr = Unbuffered(f)
|
||||
|
||||
timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print(timestamp + " - " + "Starting BGP injector ")
|
||||
|
||||
CONFIG_FILENAME = os.path.join(sys.path[0], "bgp_injector.cfg")
|
||||
|
||||
timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print(timestamp + " - " + "Reading config file " + CONFIG_FILENAME)
|
||||
|
||||
input_file = open(CONFIG_FILENAME, "r")
|
||||
|
||||
input = input_file.read()
|
||||
# cleanup comments that are not supported by JSON
|
||||
json_input = re.sub(r"//.*\n", "", input, flags=re.MULTILINE)
|
||||
|
||||
config = json.loads(json_input)
|
||||
|
||||
bgp_peer = config["peer_address"]
|
||||
bgp_local = config["local_address"]
|
||||
bgp_mss = config["mss"]
|
||||
bgp_port = config["port"]
|
||||
rib = dict()
|
||||
timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print(timestamp + " - " + "Starting BGP... (peer: " + str(bgp_peer) + ")")
|
||||
|
||||
retry = 30
|
||||
while retry:
|
||||
retry -= 1
|
||||
try:
|
||||
bgp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
bgp_socket.bind((bgp_local, 0))
|
||||
bgp_socket.connect((bgp_peer, bgp_port))
|
||||
open_bgp(bgp_socket, config)
|
||||
break
|
||||
except TimeoutError:
|
||||
if retry == 0:
|
||||
timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print(timestamp + " - " + "Error: timeout connecting to the peer.")
|
||||
exit()
|
||||
time.sleep(1)
|
||||
except OSError as e:
|
||||
if retry == 0:
|
||||
timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print(timestamp + " - " + "Error: cannot connect to the peer: " + str(e))
|
||||
exit()
|
||||
time.sleep(1)
|
||||
|
||||
receive_worker = threading.Thread(
|
||||
target=receive_thread, args=(bgp_socket,)
|
||||
) # wait from BGP msg from peer and process them
|
||||
receive_worker.setDaemon(True)
|
||||
receive_worker.start()
|
||||
|
||||
keepalive_worker = threading.Thread(
|
||||
target=keepalive_thread,
|
||||
args=(
|
||||
bgp_socket,
|
||||
(config["hold_time"]) / 3,
|
||||
),
|
||||
) # send keep alives every 10s by default
|
||||
keepalive_worker.setDaemon(True)
|
||||
keepalive_worker.start()
|
||||
|
||||
# send a first keepalive packet before sending the initial UPDATE packet
|
||||
keepalive_bgp(bgp_socket)
|
||||
|
||||
timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print(timestamp + " - " + "BGP is up.")
|
||||
|
||||
time.sleep(3)
|
||||
for link_state in config["link_states"]:
|
||||
update_bgp(
|
||||
bgp_socket,
|
||||
link_state,
|
||||
config,
|
||||
)
|
||||
|
||||
while True:
|
||||
time.sleep(60)
|
@ -1 +0,0 @@
|
||||
ip route 192.0.2.2/32 192.168.1.2
|
@ -1,7 +0,0 @@
|
||||
!
|
||||
interface lo
|
||||
ip address 192.0.2.1/32
|
||||
!
|
||||
interface r1-eth0
|
||||
ip address 192.168.1.1/24
|
||||
!
|
@ -1,20 +0,0 @@
|
||||
router bgp 65002
|
||||
no bgp ebgp-requires-policy
|
||||
neighbor 192.0.2.1 remote-as 65001
|
||||
neighbor 192.0.2.1 timers connect 1
|
||||
neighbor 192.0.2.1 ebgp-multihop 3
|
||||
neighbor 192.0.2.1 update-source 192.0.2.2
|
||||
neighbor 192.0.2.3 remote-as 65003
|
||||
neighbor 192.0.2.3 timers 1 3
|
||||
neighbor 192.0.2.3 timers connect 1
|
||||
neighbor 192.0.2.3 ebgp-multihop 3
|
||||
neighbor 192.0.2.3 update-source 192.0.2.2
|
||||
address-family ipv4 unicast
|
||||
no neighbor 192.0.2.1 activate
|
||||
no neighbor 192.0.2.3 activate
|
||||
exit-address-family
|
||||
address-family link-state link-state
|
||||
neighbor 192.0.2.1 activate
|
||||
neighbor 192.0.2.3 activate
|
||||
exit-address-family
|
||||
!
|
@ -1,189 +0,0 @@
|
||||
{
|
||||
"routes": {
|
||||
"Link OSPFv3 ID:0xffffffffffffffff {Local {AS:4294967295 ID:4294967295 Area:4294967295 Rtr:10.10.10.11:2.2.2.2} Remote {AS:4294967295 ID:4294967295 Area:4294967295 Rtr:10.10.10.10:1.1.1.1} IPv4:10.1.0.1 Neigh-IPv4:10.1.0.2 IPv6:2001::1 Neigh-IPv6:2001::2 MT:0,2 424:0x200100000000000001}/XX": [
|
||||
{
|
||||
"valid": true,
|
||||
"bestpath": true,
|
||||
"pathFrom": "external",
|
||||
"linkStateNLRI": {
|
||||
"nlriType": "Link",
|
||||
"protocol": "OSPFv3",
|
||||
"identifier": "0xffffffffffffffff",
|
||||
"localNode": {
|
||||
"as": 4294967295,
|
||||
"identifier": 4294967295,
|
||||
"area": 4294967295,
|
||||
"routerID": "10.10.10.11:2.2.2.2"
|
||||
},
|
||||
"remoteNode": {
|
||||
"as": 4294967295,
|
||||
"identifier": 4294967295,
|
||||
"area": 4294967295,
|
||||
"routerID": "10.10.10.10:1.1.1.1"
|
||||
},
|
||||
"interfaceIPv4": "10.1.0.1",
|
||||
"neighborIPv4": "10.1.0.2",
|
||||
"interfaceIPv6": "2001::1",
|
||||
"neighborIPv6": "2001::2",
|
||||
"mtID": [0, 2],
|
||||
"424": ["0x2001000000000000", "0x01"]
|
||||
},
|
||||
"weight": 0,
|
||||
"origin": "IGP"
|
||||
}
|
||||
],
|
||||
"IPv6-Prefix OSPFv3 ID:0x20 {Local {AS:65001 ID:0 Area:0 Rtr:10.10.10.10} MT:2 OSPF-Route-Type:1 IPv6:12:12::12:12/128}/XX": [
|
||||
{
|
||||
"valid": true,
|
||||
"bestpath": true,
|
||||
"pathFrom": "external",
|
||||
"linkStateNLRI": {
|
||||
"nlriType": "IPv6-Prefix",
|
||||
"protocol": "OSPFv3",
|
||||
"identifier": "0x20",
|
||||
"localNode": {
|
||||
"as": 65001,
|
||||
"identifier": 0,
|
||||
"area": 0,
|
||||
"routerID": "10.10.10.10"
|
||||
},
|
||||
"ospfRouteType": 1,
|
||||
"ipReachability": "12:12::12:12/128",
|
||||
"mtID": [2]
|
||||
},
|
||||
"weight": 0,
|
||||
"origin": "IGP"
|
||||
}
|
||||
],
|
||||
"IPv6-Prefix ISIS-L2 ID:0x20 {Local {AS:65001 ID:0 Rtr:0000.0000.1003.00} MT:2 IPv6:12:12::12:12/128}/XX": [
|
||||
{
|
||||
"valid": true,
|
||||
"bestpath": true,
|
||||
"pathFrom": "external",
|
||||
"linkStateNLRI": {
|
||||
"nlriType": "IPv6-Prefix",
|
||||
"protocol": "ISIS-L2",
|
||||
"identifier": "0x20",
|
||||
"localNode": {
|
||||
"as": 65001,
|
||||
"identifier": 0,
|
||||
"routerID": "0000.0000.1003.00"
|
||||
},
|
||||
"ipReachability": "12:12::12:12/128",
|
||||
"mtID": [2]
|
||||
},
|
||||
"weight": 0,
|
||||
"origin": "IGP"
|
||||
}
|
||||
],
|
||||
"IPv4-Prefix OSPFv2 ID:0x20 {Local {AS:65001 ID:0 Area:0 Rtr:10.10.10.10:1.1.1.1} IPv4:89.10.11.0/24}/XX": [
|
||||
{
|
||||
"valid": true,
|
||||
"bestpath": true,
|
||||
"pathFrom": "external",
|
||||
"linkStateNLRI": {
|
||||
"nlriType": "IPv4-Prefix",
|
||||
"protocol": "OSPFv2",
|
||||
"identifier": "0x20",
|
||||
"localNode": {
|
||||
"as": 65001,
|
||||
"identifier": 0,
|
||||
"area": 0,
|
||||
"routerID": "10.10.10.10:1.1.1.1"
|
||||
},
|
||||
"ipReachability": "89.10.11.0/24"
|
||||
},
|
||||
"weight": 0,
|
||||
"origin": "IGP"
|
||||
}
|
||||
],
|
||||
"Node OSPFv2 ID:0x20 {Local {AS:65001 ID:0 Area:0 Rtr:10.10.10.10:1.1.1.1}}/XX": [
|
||||
{
|
||||
"valid": true,
|
||||
"bestpath": true,
|
||||
"pathFrom": "external",
|
||||
"linkStateNLRI": {
|
||||
"nlriType": "Node",
|
||||
"protocol": "OSPFv2",
|
||||
"identifier": "0x20",
|
||||
"localNode": {
|
||||
"as": 65001,
|
||||
"identifier": 0,
|
||||
"area": 0,
|
||||
"routerID": "10.10.10.10:1.1.1.1"
|
||||
}
|
||||
},
|
||||
"weight": 0,
|
||||
"origin": "IGP"
|
||||
}
|
||||
],
|
||||
"Node OSPFv2 ID:0x20 {Local {AS:65001 ID:0 Area:0 Rtr:10.10.10.10}}/XX": [
|
||||
{
|
||||
"valid": true,
|
||||
"bestpath": true,
|
||||
"pathFrom": "external",
|
||||
"linkStateNLRI": {
|
||||
"nlriType": "Node",
|
||||
"protocol": "OSPFv2",
|
||||
"identifier": "0x20",
|
||||
"localNode": {
|
||||
"as": 65001,
|
||||
"identifier": 0,
|
||||
"area": 0,
|
||||
"routerID": "10.10.10.10"
|
||||
}
|
||||
},
|
||||
"weight": 0,
|
||||
"origin": "IGP"
|
||||
}
|
||||
],
|
||||
"Node ISIS-L1 ID:0x20 {Local {AS:65001 ID:0 Rtr:0000.0000.1003.00}}/XX": [
|
||||
{
|
||||
"valid": true,
|
||||
"bestpath": true,
|
||||
"pathFrom": "external",
|
||||
"linkStateNLRI": {
|
||||
"nlriType": "Node",
|
||||
"protocol": "ISIS-L1",
|
||||
"identifier": "0x20",
|
||||
"localNode": {
|
||||
"as": 65001,
|
||||
"identifier": 0,
|
||||
"routerID": "0000.0000.1003.00"
|
||||
}
|
||||
},
|
||||
"weight": 0,
|
||||
"origin": "IGP"
|
||||
}
|
||||
],
|
||||
"Link ISIS-L1 ID:0x20 {Local {AS:65001 ID:0 Rtr:0000.0000.1001} Remote {AS:65001 ID:0 Rtr:0000.0000.1000} IPv4:10.1.0.1 Neigh-IPv4:10.1.0.2 IPv6:2001::1 Neigh-IPv6:2001::2 MT:0,2}/XX": [
|
||||
{
|
||||
"valid": true,
|
||||
"bestpath": true,
|
||||
"pathFrom": "external",
|
||||
"linkStateNLRI": {
|
||||
"nlriType": "Link",
|
||||
"protocol": "ISIS-L1",
|
||||
"identifier": "0x20",
|
||||
"localNode": {
|
||||
"as": 65001,
|
||||
"identifier": 0,
|
||||
"routerID": "0000.0000.1001"
|
||||
},
|
||||
"remoteNode": {
|
||||
"as": 65001,
|
||||
"identifier": 0,
|
||||
"routerID": "0000.0000.1000"
|
||||
},
|
||||
"interfaceIPv4": "10.1.0.1",
|
||||
"neighborIPv4": "10.1.0.2",
|
||||
"interfaceIPv6": "2001::1",
|
||||
"neighborIPv6": "2001::2",
|
||||
"mtID": [0, 2]
|
||||
},
|
||||
"weight": 0,
|
||||
"origin": "IGP"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
ip route 192.0.2.1/32 192.168.1.1
|
||||
ip route 192.0.2.3/32 192.168.2.3
|
@ -1,11 +0,0 @@
|
||||
!
|
||||
int lo
|
||||
ip address 192.0.2.2/32
|
||||
!
|
||||
interface r2-eth0
|
||||
ip address 192.168.1.2/24
|
||||
!
|
||||
interface r2-eth1
|
||||
ip address 192.168.2.2/24
|
||||
!
|
||||
|
@ -1,14 +0,0 @@
|
||||
router bgp 65003
|
||||
no bgp ebgp-requires-policy
|
||||
neighbor 192.0.2.2 remote-as 65002
|
||||
neighbor 192.0.2.2 timers 1 3
|
||||
neighbor 192.0.2.2 timers connect 1
|
||||
neighbor 192.0.2.2 ebgp-multihop 3
|
||||
neighbor 192.0.2.2 update-source 192.0.2.3
|
||||
address-family ipv4 unicast
|
||||
no neighbor 192.0.2.2 activate
|
||||
exit-address-family
|
||||
address-family link-state link-state
|
||||
neighbor 192.0.2.2 activate
|
||||
exit-address-family
|
||||
!
|
@ -1 +0,0 @@
|
||||
../r2/linkstate.json
|
@ -1 +0,0 @@
|
||||
ip route 192.0.2.2/32 192.168.2.2
|
@ -1,7 +0,0 @@
|
||||
!
|
||||
int lo
|
||||
ip address 192.0.2.3/32
|
||||
!
|
||||
interface r3-eth0
|
||||
ip address 192.168.2.3/24
|
||||
!
|
@ -1,113 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# SPDX-License-Identifier: ISC
|
||||
|
||||
# Copyright 2023 6WIND S.A.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import pytest
|
||||
import functools
|
||||
|
||||
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.append(os.path.join(CWD, "../"))
|
||||
|
||||
# pylint: disable=C0413
|
||||
from lib import topotest
|
||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||
from lib.common_config import step
|
||||
from lib.topolog import logger
|
||||
|
||||
pytestmark = [pytest.mark.bgpd]
|
||||
|
||||
|
||||
def build_topo(tgen):
|
||||
for routern in range(1, 4):
|
||||
tgen.add_router("r{}".format(routern))
|
||||
|
||||
switch = tgen.add_switch("s1")
|
||||
switch.add_link(tgen.gears["r1"])
|
||||
switch.add_link(tgen.gears["r2"])
|
||||
|
||||
switch = tgen.add_switch("s2")
|
||||
switch.add_link(tgen.gears["r2"])
|
||||
switch.add_link(tgen.gears["r3"])
|
||||
|
||||
|
||||
def setup_module(mod):
|
||||
tgen = Topogen(build_topo, mod.__name__)
|
||||
tgen.start_topology()
|
||||
|
||||
router_list = tgen.routers()
|
||||
|
||||
for i, (rname, router) in enumerate(router_list.items(), 1):
|
||||
router.load_config(
|
||||
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
|
||||
)
|
||||
router.load_config(
|
||||
TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
|
||||
)
|
||||
if rname == "r1":
|
||||
# use bgp_injector.py to inject BGP prefixes
|
||||
continue
|
||||
router.load_config(
|
||||
TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
|
||||
)
|
||||
|
||||
tgen.start_router()
|
||||
|
||||
r1_path = os.path.join(CWD, "r1")
|
||||
log_dir = os.path.join(tgen.logdir, "r1")
|
||||
tgen.gears["r1"].cmd("chmod u+x {}/bgp_injector.py".format(r1_path))
|
||||
tgen.gears["r1"].run("{}/bgp_injector.py {}".format(r1_path, log_dir))
|
||||
|
||||
|
||||
def teardown_module(mod):
|
||||
tgen = get_topogen()
|
||||
|
||||
log_dir = os.path.join(tgen.logdir, "r1")
|
||||
pid_file = os.path.join(log_dir, "bgp_injector.pid")
|
||||
|
||||
logger.info("r1: sending SIGTERM to bgp_injector")
|
||||
tgen.gears["r1"].cmd("kill $(cat {})".format(pid_file))
|
||||
tgen.stop_topology()
|
||||
|
||||
|
||||
def test_show_bgp_link_state():
|
||||
tgen = get_topogen()
|
||||
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
def _remove_prefixlen(tmp_json):
|
||||
new_json = {
|
||||
prefix.split("}/")[0] + "}/XX": data
|
||||
for prefix, data in tmp_json["routes"].items()
|
||||
}
|
||||
|
||||
return new_json
|
||||
|
||||
def _show_bgp_link_state_json(rname, tmp_expected):
|
||||
tmp_output = json.loads(
|
||||
tgen.gears[rname].vtysh_cmd("show bgp link-state link-state json")
|
||||
)
|
||||
# prefix length is the size of prefix in memory
|
||||
# which differs on 32 and 64 bits.
|
||||
# do not compare the prefix length
|
||||
output = _remove_prefixlen(tmp_output)
|
||||
expected = _remove_prefixlen(tmp_expected)
|
||||
|
||||
return topotest.json_cmp(output, expected)
|
||||
|
||||
step("Check BGP Link-State tables")
|
||||
for rname in ["r2", "r3"]:
|
||||
expected = open(os.path.join(CWD, "{}/linkstate.json".format(rname))).read()
|
||||
expected_json = json.loads(expected)
|
||||
test_func = functools.partial(_show_bgp_link_state_json, rname, expected_json)
|
||||
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
|
||||
assert result is None, "Failed to see BGP prefixes on {}".format(rname)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = ["-s"] + sys.argv[1:]
|
||||
sys.exit(pytest.main(args))
|
Loading…
Reference in New Issue
Block a user