mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-25 22:01:08 +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