+++ /dev/null
-// 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
- }
- }
-]
-}
+++ /dev/null
-#!/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)
+++ /dev/null
-ip route 192.0.2.2/32 192.168.1.2
+++ /dev/null
-!
-interface lo
- ip address 192.0.2.1/32
-!
-interface r1-eth0
- ip address 192.168.1.1/24
-!
\ No newline at end of file
+++ /dev/null
-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
-!
\ No newline at end of file
+++ /dev/null
-{
- "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"
- }
- ]
- }
-}
+++ /dev/null
-ip route 192.0.2.1/32 192.168.1.1
-ip route 192.0.2.3/32 192.168.2.3
\ No newline at end of file
+++ /dev/null
-!
-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
-!
-
+++ /dev/null
-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
-!
\ No newline at end of file
+++ /dev/null
-../r2/linkstate.json
\ No newline at end of file
+++ /dev/null
-ip route 192.0.2.2/32 192.168.2.2
\ No newline at end of file
+++ /dev/null
-!
-int lo
- ip address 192.0.2.3/32
-!
-interface r3-eth0
- ip address 192.168.2.3/24
-!
+++ /dev/null
-#!/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))