
L'un des problèmes des réseaux modernes est leur fragilité. De nombreuses règles de filtrage, des politiques d'échange d'informations de routage, des protocoles de routage dynamique rendent les réseaux déroutants et soumis à des facteurs humains. Un plantage du réseau peut se produire involontairement lors de la modification d'une carte d'itinéraire ou d'une liste de contrôle d'accès (
un ,
deux ). Il nous manque définitivement un outil pour évaluer le comportement d'un réseau avec une nouvelle configuration avant d'apporter des modifications à la production. Je veux savoir exactement si le réseau A sera à ma disposition si je filtre certaines des annonces BGP reçues du fournisseur B? Quelle route les paquets iront-ils du réseau C au serveur D si sur l'une des liaisons de transit je double la métrique IGP? Batfish nous aidera à répondre à ces questions et à bien d'autres!
Avis sur Batfish
Batfish est un outil de modélisation de réseau. Son objectif principal est de tester les modifications de configuration avant de les apporter au réseau de production. Batfish peut également être utilisé pour analyser et vérifier l'état actuel du réseau. Les processus CI / CD existants dans le monde des réseaux manquent clairement d'un outil pour tester de nouvelles configurations. Batfish résout ce problème.
Batfish ne nécessite pas d'accès direct direct à l'équipement réseau existant, Batfish modélise le comportement du réseau en fonction des données contenues dans les fichiers de configuration de l'appareil.
Batfish peut:
- déterminer l'état de voisinage des protocoles de routage dynamique dans le réseau (BGP, IS-IS, OSPF)
- calculer le RIB de chaque élément de réseau
- vérifier les paramètres NTP, AAA, MTU
- permettent de déterminer si l'ACL bloque le passage du trafic réseau (analogue de packet-tracer sur Cisco ASA)
- vérifier la connectivité de bout en bout entre les hôtes du réseau
- montrer le chemin du trafic à travers le réseau (traçage virtuel)
Plateformes prises en charge:
- Arista
- Aruba
- AWS (VPC, ACL réseau, VPN GW, NAT GW, Internet GW, groupes de sécurité)
- Cisco (NX-OS, IOS, IOS-XE, IOS-XR et ASA)
- Dell force10
- Fonderie
- iptables
- Juniper (MX, EX, QFX, SRX, série T, PTX)
- Mrv
- Réseaux Palo Alto
- Quagga / FRR
- Quanta
- Vyos

Batfish est une application Java. Pour un travail pratique, il a été écrit Pybatfish - SDK python.
Passons à la pratique. Je vais vous montrer les possibilités de Batfish avec un exemple.
Exemple
Nous gérons deux systèmes autonomes: AS 41214 et AS 10631. Comme IGP, AS-41214 utilise IS-IS et AS-10631 - OSPF. À l'intérieur de chaque AS, IBGP-fullmesh est utilisé. LDN-CORE-01 annonce son préfixe de voisins BGP 135.65.0.0/19, MSK-CORE-01 - 140.0.0.0/24. L'échange d'informations de routage entre les systèmes autonomes a lieu à la jonction de HKI-CORE-01 - SPB-CORE-01.
HKI-CORE-01, STH-CORE-01 - Routeurs Junos
LDN-CORE-01, AMS-CORE-01, SPB-CORE-01, MSK-CORE-01 - Routeurs Cisco IOS

Installez le conteneur avec Batfish et le SDK python:
docker pull batfish/allinone docker run batfish/allinone docker container exec -it <container> bash
Apprenez à connaître la bibliothèque via le mode interactif python:
root@ea9a1559d88e:/
bf_init_snapshot ('tmp / habr') - la fonction charge les fichiers de configuration dans Batfish et les prépare pour l'analyse.
/ tmp / habr - un répertoire avec les fichiers de configuration du routeur.
root@ea9a1559d88e:/tmp/habr
Déterminons maintenant l'état des sessions BGP sur le routeur LDN-CORE-01:
>>> 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
Et bien comment? Sonne comme la vérité?
LDN-CORE-01
Voyons maintenant quelles sont les routes IS-IS dans le RIB sur le routeur HKI-CORE-01 selon Batfish:
>>> 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
Sur la ligne de commande:
showroute@HKI-CORE-01
Super! Je suppose qu'il est devenu plus clair pour vous qu'il y a Batfish.
Au début de l'article, j'ai écrit que Batfish peut être utilisé pour vérifier les changements de configuration avant de les apporter au réseau de «bataille». Je propose maintenant de considérer le processus de test d'un réseau basé sur
RobotFramework . Pour ce faire, j'ai écrit un petit module basé sur PyBatfish qui vous permet d'effectuer les vérifications suivantes:
- Déterminer l'état des sessions BGP sur le réseau
- Déterminer le statut de voisin IS-IS
- Vérifier la connectivité de bout en bout entre les nœuds d'un réseau avec démonstration de trace
- Déterminer la taille du RIB sur le routeur pour un protocole de routage dynamique spécifique
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
Scénario N1

Sous mon contrôle est toujours le même réseau. Supposons que je
doive mettre en ordre les filtres à la frontière de l'
AS 41214 et de l'
AS 10631 et les bloquer au niveau des paquets de jonction contenant les adresses IP source ou de destination de la plage BOGONS.
Exécutez le test avant d'apporter des modifications.

Les tests ont réussi.
Nous apporterons des modifications à la configuration de test du routeur
HKI-CORE-01 - /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
Exécutez le test.

J'étais très proche, mais comme le montre le résultat du test, après les modifications BGP, le voisinage 192.168.30.0 - 192.168.30.1 n'est pas à l'état établi -> en conséquence, la connectivité IP entre les points 135.65.0.1 <-> 140.0.0.1 est perdue. Qu'est-ce qui ne va pas? Nous examinons attentivement la configuration
HKI-CORE-01 et voyons que le peering eBGP est installé sur des adresses privées:
showroute@HKI-CORE-01
Conclusion: il est nécessaire de modifier les adresses à la jonction ou d'ajouter le sous-réseau 192.168.30.0/31 à l'exception.
J'ajouterai un réseau à la jonction à l'exception, je mettrai à jour /tmp/habr/configs/HKI-CORE-01.cfg:
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
Exécutez le test.

Désormais, le trafic indésirable ne passera pas par l'interface ebgp AS 41214 - AS 10631. Vous pouvez effectuer des modifications en toute sécurité sans craindre les conséquences.
Scénario N2

Ici, je dois mettre fin au réseau 150.0.0.0/24 sur le routeur
MSK-CORE-01 et assurer la connectivité entre les points 135.65.0.1 et 150.0.0.1
J'ajoute les lignes suivantes à la configuration de test du routeur MSK-CORE-01 - 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 !
Je change le script de test et lance le test:
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
Maintenant, je m'attends à voir deux routes eBGP sur le routeur HKI-CORE-01, une vérification de connectivité supplémentaire est ajoutée
Il n'y a pas de connexion entre 135.65.0.1 et 150.0.0.1, de plus, le routeur
HKI-CORE-01 n'a qu'une seule route eBGP, au lieu de deux.
Vérifiez le contenu du RIB sur le
HKI-CORE-01 lors de l'ajout d'une nouvelle configuration au routeur
MSK-CORE-01 :
showroute@HKI-CORE-01
Notez la politique d'importation des préfixes reçus de
SPB-CORE-01 :
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
La règle autorisant 150.0.0.0/24 est manquante. Ajoutez-le à la configuration de test et exécutez le test:
showroute@HKI-CORE-01

Génial, il y a une connectivité entre les réseaux, tous les tests sont passés! Vous pouvez donc apporter ces modifications au travail du réseau "combat".
Conclusion
À mon avis, Batfish est un outil puissant avec un énorme potentiel. Essayez-le et voyez par vous-même.
Si ce sujet vous intéresse - rejoignez le chat mou, les développeurs de Batfish seront heureux de répondre à toutes les questions et de corriger rapidement les bugs.
batfish-org.slack.comMerci de votre attention.
Les références
www.batfish.orgwww.youtube.com/channel/UCA-OUW_3IOt9U_s60KvmJYAgithub.com/batfish/batfishmedia.readthedocs.org/pdf/pybatfish/latest/pybatfish.pdfgithub.com/showroute/batfish-habr