
Eines der Probleme moderner Netzwerke ist ihre Fragilität. Viele Filterregeln, Richtlinien für den Routing-Informationsaustausch und dynamische Routing-Protokolle machen Netzwerke verwirrend und unterliegen menschlichen Faktoren. Ein Netzwerkabsturz kann unbeabsichtigt auftreten, wenn Änderungen an einer Routenkarte oder ACL (
eins ,
zwei ) vorgenommen werden. Es fehlt definitiv ein Tool, um das Verhalten eines Netzwerks mit einer neuen Konfiguration zu bewerten, bevor Änderungen an der Produktion vorgenommen werden. Ich möchte sicher wissen, ob Netzwerk A für mich verfügbar ist, wenn ich einige der von Anbieter B erhaltenen BGP-Ankündigungen herausfiltere. Auf welcher Route werden die Pakete von Netzwerk C zu Server D geleitet, wenn ich auf einer der Transitverbindungen die IGP-Metrik verdopple? Batfish hilft uns bei der Beantwortung dieser und vieler anderer Fragen!
Bewertung von Batfish
Batfish ist ein Netzwerkmodellierungswerkzeug. Der Hauptzweck besteht darin, Konfigurationsänderungen zu testen, bevor sie im Produktionsnetzwerk vorgenommen werden. Batfish kann auch verwendet werden, um den aktuellen Status des Netzwerks zu analysieren und zu überprüfen. Bestehende CI / CD-Prozesse in der Netzwerkwelt verfügen eindeutig nicht über ein Tool zum Testen neuer Konfigurationen. Batfish löst dieses Problem.
Batfish erfordert keinen direkten direkten Zugriff auf vorhandene Netzwerkgeräte. Batfish modelliert das Netzwerkverhalten basierend auf den in den Gerätekonfigurationsdateien enthaltenen Daten.
Batfish kann:
- Bestimmen des Nachbarstatus von dynamischen Routing-Protokollen im Netzwerk (BGP, IS-IS, OSPF)
- Berechnen Sie die RIB jedes Netzwerkelements
- Überprüfen Sie die NTP-, AAA- und MTU-Einstellungen
- Ermöglichen Sie die Bestimmung, ob die ACL den Durchgang des Netzwerkverkehrs blockiert (analog zum Packet-Tracer auf dem Cisco ASA).
- Überprüfen Sie die End-to-End-Konnektivität zwischen Hosts im Netzwerk
- Zeigen Sie den Verkehrspfad durch das Netzwerk an (virtuelle Ablaufverfolgung).
Unterstützte Plattformen:
- Arista
- Aruba
- AWS (VPCs, Netzwerk-ACLs, VPN GW, NAT GW, Internet GW, Sicherheitsgruppen)
- Cisco (NX-OS, IOS, IOS-XE, IOS-XR und ASA)
- Dell force10
- Gießerei
- iptables
- Wacholder (MX, EX, QFX, SRX, T-Serie, PTX)
- Mrv
- Palo-Alt-Netzwerke
- Quagga / FRR
- Quanta
- Vyos

Batfish ist eine Java-Anwendung. Für die bequeme Arbeit wurde Pybatfish - Python SDK geschrieben.
Lass uns weiter üben. Ich werde Ihnen die Möglichkeiten von Batfish anhand eines Beispiels zeigen.
Beispiel
Wir verwalten zwei autonome Systeme: AS 41214 und AS 10631. Als IGP verwendet AS-41214 IS-IS und AS-10631 - OSPF. In jedem AS wird IBGP-Fullmesh verwendet. LDN-CORE-01 gibt das Präfix 135.65.0.0/19 der BGP-Nachbarn, MSK-CORE-01 - 140.0.0.0/24 bekannt. Der Austausch von Routing-Informationen zwischen autonomen Systemen erfolgt an der Kreuzung von HKI-CORE-01 - SPB-CORE-01.
HKI-CORE-01, STH-CORE-01 - Junos-Router
LDN-CORE-01, AMS-CORE-01, SPB-CORE-01, MSK-CORE-01 - Cisco IOS-Router

Installieren Sie den Container mit Batfish und dem Python SDK:
docker pull batfish/allinone docker run batfish/allinone docker container exec -it <container> bash
Lernen Sie die Bibliothek im interaktiven Python-Modus kennen:
root@ea9a1559d88e:/
bf_init_snapshot ('tmp / habr') - Die Funktion lädt Konfigurationsdateien in Batfish und bereitet sie für die Analyse vor.
/ tmp / habr - ein Verzeichnis mit
Routerkonfigurationsdateien .
root@ea9a1559d88e:/tmp/habr
Lassen Sie uns nun den Status von BGP-Sitzungen auf dem LDN-CORE-01-Router ermitteln:
>>> bgp_peers = bfq.bgpSessionStatus(nodes='LDN-CORE-01').answer().frame() >>> bgp_peers Node VRF Local_AS Local_IP Remote_AS Remote_Node Remote_IP Session_Type Est_Status 0 ldn-core-01 default 41214 172.20.20.1 41214 sth-core-01 172.20.20.2 IBGP EST 1 ldn-core-01 default 41214 172.20.20.1 41214 ams-core-01 172.20.20.3 IBGP EST 2 ldn-core-01 default 41214 172.20.20.1 41214 hki-core-01 172.20.20.4 IBGP EST
Und wie? Klingt nach der Wahrheit?
LDN-CORE-01
Nun wollen wir sehen, welche IS-IS-Routen in der RIB des HKI-CORE-01-Routers laut Batfish enthalten sind:
>>> isis_routes = bfq.routes(nodes='HKI-CORE-01', protocols='isis').answer().frame() >>> isis_routes Node VRF Network Next_Hop Next_Hop_IP Protocol Admin_Distance Metric Tag 0 hki-core-01 default 172.20.20.3/32 ams-core-01 10.0.0.6 isisL2 18 20 None 1 hki-core-01 default 172.20.20.1/32 ams-core-01 10.0.0.6 isisL2 18 30 None 2 hki-core-01 default 172.20.20.2/32 sth-core-01 10.0.0.4 isisL2 18 10 None 3 hki-core-01 default 172.20.20.1/32 sth-core-01 10.0.0.4 isisL2 18 30 None 4 hki-core-01 default 10.0.0.0/31 sth-core-01 10.0.0.4 isisL2 18 20 None 5 hki-core-01 default 10.0.0.2/31 ams-core-01 10.0.0.6 isisL2 18 20 None
In der Kommandozeile:
showroute@HKI-CORE-01
Großartig! Ich nehme an, es ist Ihnen klarer geworden, dass es Batfish gibt.
Am Anfang des Artikels schrieb ich, dass Batfish verwendet werden kann, um Konfigurationsänderungen zu überprüfen, bevor sie in das "Battle" -Netzwerk übernommen werden. Jetzt schlage ich vor, den Prozess des Testens eines auf
RobotFramework basierenden Netzwerks in
Betracht zu ziehen . Zu diesem Zweck habe ich ein kleines Modul auf Basis von PyBatfish geschrieben, mit dem Sie die folgenden Überprüfungen durchführen können:
- Bestimmen Sie den Status von BGP-Sitzungen im Netzwerk
- Bestimmen Sie den IS-IS-Nachbarstatus
- Überprüfen Sie die End-to-End-Konnektivität zwischen Knoten in einem Netzwerk mit Ablaufverfolgungsdemonstration
- Bestimmen Sie die Größe der RIB auf dem Router für ein bestimmtes dynamisches Routing-Protokoll
LibraryBatfish.py import logging from pybatfish.client.commands import bf_logger, bf_init_snapshot from pybatfish.question.question import load_questions, list_questions from pybatfish.question import bfq from pybatfish.datamodel.flow import HeaderConstraints, PathConstraints from robot.api import logger class LibraryBatfish(object): def __init__(self, snapshot): bf_logger.setLevel(logging.ERROR) load_questions() bf_init_snapshot(snapshot) def check_bgp_peers(self): not_established_peers = list() bgp_peers = bfq.bgpSessionStatus().answer() for peer in bgp_peers.rows: if peer.get('Established_Status') != 'ESTABLISHED': not_established_peers.append(dict.fromkeys(peer.get('Local_IP').split(), peer.get('Remote_IP').get('value'))) if len(not_established_peers) == 0: return 1 else: logger.warn('BGP neighbors are not in an established state:') for neighborship in not_established_peers: for peer in neighborship: logger.warn('{} - {}'.format(peer, neighborship.get(peer))) return 0 def check_routes(self, node, protocol): routes = bfq.routes(nodes=node, protocols=protocol).answer() return len(routes.rows) def check_isis_neighbors(self, description): not_isis_enabled_links = list() for link in self._get_isis_enabled_links(description): if link not in self._get_isis_neighbors(): not_isis_enabled_links.append(link) if len(not_isis_enabled_links) == 0: return 1 else: for link in not_isis_enabled_links: logger.warn('{} {} has no IS-IS neighbor'.format(link.get('hostname'), link.get('interface'))) return 0 def ping(self, source_ip, destination_ip): ip_owners = bfq.ipOwners().answer() traceroute = self._get_traceroute_status(source_ip, destination_ip, ip_owners) reverse_traceroute = self._get_traceroute_status(destination_ip, source_ip, ip_owners) if traceroute == True and reverse_traceroute == True: self._show_trace(source_ip, destination_ip, ip_owners) return 1 else: logger.warn('Ping {} -> {} failed'.format(source_ip, destination_ip)) return 0 def _get_traceroute_status(self, source_ip, destination_ip, addresses): tracert = self._unidirectional_virtual_traceroute(source_ip, destination_ip, addresses) isAccepted = True if tracert != None: for trace in tracert.rows[0].get('Traces'): if trace.get('disposition') != 'ACCEPTED': isAccepted = False if isAccepted == True: return True else: return False def _get_paths(self, source_ip, destination_ip, addresses): tracert = self._unidirectional_virtual_traceroute(source_ip, destination_ip, addresses) traces = tracert.rows[0].get('Traces') paths = dict() path_number = 1 for trace in traces: if trace.get('disposition') == 'ACCEPTED': path = list() for hop in trace.get('hops'): path.append(hop.get('node').get('name')) paths[path_number] = path path_number += 1 return paths def _unidirectional_virtual_traceroute(self, source_ip, destination_ip, addresses): for address in addresses.rows: if address.get('IP') == source_ip: node = address.get('Node').get('name') int = address.get('Interface') headers = HeaderConstraints(srcIps=source_ip, dstIps=destination_ip, ipProtocols=['ICMP']) try: tracert = bfq.traceroute(startLocation="{}[{}]".format(node,int), headers=headers).answer() return tracert except: logger.warn('{} address has not been found'.format(source_ip)) def _get_isis_enabled_links(self, description='core-link'): isis_enabled_links = list() interfaces = bfq.interfaceProperties().answer() for int in interfaces.rows: if int.get('Description') != None and description in int.get('Description'): isis_enabled_links.append({'hostname' : int.get('Interface').get('hostname'), 'interface' : int.get('Interface').get('interface')}) return isis_enabled_links def _get_isis_neighbors(self): isis_neighbors = list() isis_adjacencies = bfq.edges(edgeType='isis').answer() for neighbor in isis_adjacencies.rows: isis_neighbors.append(neighbor.get('Interface')) return isis_neighbors def _show_trace(self, source_ip, destination_ip, addresses): logger.console('\nTraceroute to {} from {}'.format(destination_ip, source_ip)) paths = self._get_paths(source_ip, destination_ip, addresses) path_num = 1 for path in paths: n = 1 logger.console('\n Path N{}'.format(path_num)) for hop in paths.get(path): logger.console(' {} {}'.format(n, hop)) n += 1 path_num += 1
Szenario N1

Unter meiner Kontrolle befindet sich immer noch das gleiche Netzwerk. Angenommen, ich muss die Filter an der Grenze zwischen
AS 41214 und
AS 10631 ordnen und an den Verbindungspaketen blockieren, die Quell- oder Ziel-IP-Adressen aus dem BOGONS-Bereich enthalten.
Führen Sie den Test aus, bevor Sie Änderungen vornehmen.

Tests bestanden.
Wir werden Änderungen an der Testkonfiguration des
HKI-CORE-01- Routers vornehmen - /tmp/habr/configs/HKI-CORE-01.cfg:
set firewall family inet filter BOGONS term TERM010 from address 0.0.0.0/8 set firewall family inet filter BOGONS term TERM010 from address 10.0.0.0/8 set firewall family inet filter BOGONS term TERM010 from address 100.64.0.0/10 set firewall family inet filter BOGONS term TERM010 from address 127.0.0.0/8 set firewall family inet filter BOGONS term TERM010 from address 169.254.0.0/16 set firewall family inet filter BOGONS term TERM010 from address 172.16.0.0/12 set firewall family inet filter BOGONS term TERM010 from address 192.0.2.0/24 set firewall family inet filter BOGONS term TERM010 from address 192.88.99.0/24 set firewall family inet filter BOGONS term TERM010 from address 192.168.0.0/16 set firewall family inet filter BOGONS term TERM010 from address 198.18.0.0/15 set firewall family inet filter BOGONS term TERM010 from address 198.51.100.0/24 set firewall family inet filter BOGONS term TERM010 from address 203.0.113.0/24 set firewall family inet filter BOGONS term TERM010 from address 224.0.0.0/4 set firewall family inet filter BOGONS term TERM010 from address 240.0.0.0/4 set firewall family inet filter BOGONS term TERM010 then discard set firewall family inet filter BOGONS term PERMIT-IP-ANY-ANY then accept set interfaces ge-0/0/2.0 family inet filter input BOGONS set interfaces ge-0/0/2.0 family inet filter output BOGONS
Führen Sie den Test aus.

Ich war sehr nah dran, aber wie die Testausgabe zeigt, befindet sich die Nachbarschaft 192.168.30.0 - 192.168.30.1 nach den vorgenommenen BGP-Änderungen nicht im Status "Etabliert" -> Infolgedessen geht die IP-Konnektivität zwischen den Punkten 135.65.0.1 <-> 140.0.0.1 verloren. Was ist los Wir sehen uns die
HKI-CORE-01- Konfiguration genau an und stellen fest, dass eBGP-Peering auf privaten Adressen installiert ist:
showroute@HKI-CORE-01
Schlussfolgerung: Es ist erforderlich, die Adressen an der Kreuzung zu ändern oder das Subnetz 192.168.30.0/31 zur Ausnahme hinzuzufügen.
Ich werde der Ausnahme ein Netzwerk an der Kreuzung hinzufügen und /tmp/habr/configs/HKI-CORE-01.cfg erneut aktualisieren:
set firewall family inet filter BOGONS term TERM005 from address 192.168.0.0/31 set firewall family inet filter BOGONS term TERM005 then accept
Führen Sie den Test aus.

Jetzt wird unerwünschter Datenverkehr nicht mehr über die ebgp-Schnittstelle AS 41214 - AS 10631 geleitet. Sie können ohne Änderungen sicher Konsequenzen ändern.
Szenario N2

Hier muss ich das Netzwerk 150.0.0.0/24 auf dem
MSK-CORE-01- Router beenden und die Konnektivität zwischen den Punkten 135.65.0.1 und 150.0.0.1 sicherstellen
Ich füge der Testkonfiguration des MSK-CORE-01-Routers die folgenden Zeilen hinzu: tmp / habr / configs / MSK-CORE-01.cfg:
interface Loopback2 ip address 150.0.0.1 255.255.255.255 ! ip route 150.0.0.0 255.255.255.0 Null0 ! router bgp 10631 ! address-family ipv4 network 150.0.0.0 mask 255.255.255.0 !
Ich ändere das Testskript und führe den Test aus:
git diff HEAD~ diff --git a/batfish-robot.robot b/batfish-robot.robot index 8d963c5..ce8cb6a 100644 --- a/batfish-robot.robot +++ b/batfish-robot.robot @@ -5,7 +5,7 @@ Library LibraryBatfish.py tmp/habr ${ISIS-ENABLED-LINK-DESCRIPTION} ISIS-LINK ${NODE} HKI-CORE-01 ${PROTOCOL} ebgp -${RIB-SIZE} 1 +${RIB-SIZE} 2 *** Test Cases *** ISIS @@ -27,3 +27,8 @@ Ping [Documentation] Test end-to-end ICMP connectivity & show traceroute ${result}= Ping 135.65.0.1 140.0.0.1 Should Be Equal As Integers ${result} 1 + +Ping2 + [Documentation] Test end-to-end ICMP connectivity & show traceroute + ${result}= Ping 135.65.0.1 150.0.0.1 + Should Be Equal As Integers ${result} 1
Jetzt erwarte ich zwei eBGP-Routen auf dem HKI-CORE-01-Router. Eine zusätzliche Konnektivitätsprüfung wird hinzugefügt
Es besteht keine Verbindung zwischen 135.65.0.1 und 150.0.0.1. Außerdem gibt es auf dem
HKI-CORE-01- Router nur eine eBGP-Route anstelle von zwei.
Überprüfen Sie den Inhalt der RIB auf dem
HKI-CORE-01, wenn Sie dem
MSK-CORE-01- Router eine neue Konfiguration hinzufügen:
showroute@HKI-CORE-01
Beachten Sie die Importrichtlinie für Präfixe, die von
SPB-CORE-01 empfangen wurden:
set protocols bgp group AS10631 import FROM-AS10631 set protocols bgp group AS10631 neighbor 192.168.30.1 description SPB-CORE-01 set protocols bgp group AS10631 neighbor 192.168.30.1 peer-as 10631 set policy-options policy-statement FROM-AS10631 term TERM010 from route-filter 140.0.0.0/24 exact set policy-options policy-statement FROM-AS10631 term TERM010 then accept set policy-options policy-statement FROM-AS10631 term DENY then reject
Die Regel, die 150.0.0.0/24 zulässt, fehlt. Fügen Sie es der Testkonfiguration hinzu und führen Sie den Test aus:
showroute@HKI-CORE-01

Großartig, es gibt Konnektivität zwischen den Netzwerken, alle Tests sind bestanden! Sie können diese Änderungen also an der Arbeit des "Kampf" -Netzwerks vornehmen.
Fazit
Meiner Meinung nach ist Batfish ein mächtiges Werkzeug mit großem Potenzial. Probieren Sie es aus und überzeugen Sie sich selbst.
Wenn dieses Thema für Sie interessant ist - nehmen Sie am Slack-Chat teil. Die Batfish-Entwickler beantworten gerne alle Fragen und beheben Fehler schnell.
batfish-org.slack.comDanke für die Aufmerksamkeit.
Referenzen
www.batfish.orgwww.youtube.com/channel/UCA-OUW_3IOt9U_s60KvmJYAgithub.com/batfish/batfishmedia.readthedocs.org/pdf/pybatfish/latest/pybatfish.pdfgithub.com/showroute/batfish-habr