Batfish Présentation

image

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

image

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:/# python3 -------------------- >>> from pybatfish.client.commands import bf_logger, bf_init_snapshot >>> from pybatfish.question.question import load_questions >>> from pybatfish.question import bfq >>> import logging >>> bf_logger.setLevel(logging.ERROR) >>> load_questions() >>> bf_init_snapshot('tmp/habr') 'ss_e8065858-a911-4f8a-b020-49c9b96d0381' 

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# tree . `-- configs |-- AMS-CORE-01.cfg |-- HKI-CORE-01.cfg |-- LDN-CORE-01.cfg |-- MSK-CORE-01.cfg |-- SPB-CORE-01.cfg `-- STH-CORE-01.cfg 1 directory, 6 files 

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#show ip bgp summary … Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd 172.20.20.2 4 41214 629 669 9 0 0 00:56:51 0 172.20.20.3 4 41214 826 827 9 0 0 01:10:18 0 172.20.20.4 4 41214 547 583 9 0 0 00:49:24 1 

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# run show route table inet.0 protocol isis inet.0: 18 destinations, 18 routes (18 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both 10.0.0.0/31 *[IS-IS/18] 00:51:25, metric 20 > to 10.0.0.4 via ge-0/0/0.0 10.0.0.2/31 *[IS-IS/18] 00:51:45, metric 20 > to 10.0.0.6 via ge-0/0/1.0 172.20.20.1/32 *[IS-IS/18] 00:51:25, metric 30 to 10.0.0.4 via ge-0/0/0.0 > to 10.0.0.6 via ge-0/0/1.0 172.20.20.2/32 *[IS-IS/18] 00:51:25, metric 10 > to 10.0.0.4 via ge-0/0/0.0 172.20.20.3/32 *[IS-IS/18] 00:51:45, metric 20 > to 10.0.0.6 via ge-0/0/1.0 

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 


batfish-test.robot
image

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.

image

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# show interfaces ge-0/0/2 | display set set interfaces ge-0/0/2 description SPB-CORE-01 set interfaces ge-0/0/2 unit 0 family inet filter input BOGONS set interfaces ge-0/0/2 unit 0 family inet filter output BOGONS set interfaces ge-0/0/2 unit 0 family inet address 192.168.30.0/31 

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# run show route table inet.0 protocol bgp inet.0: 20 destinations, 20 routes (19 active, 0 holddown, 1 hidden) + = Active Route, - = Last Active, * = Both 135.65.0.0/19 *[BGP/170] 02:25:38, MED 0, localpref 100, from 172.20.20.1 AS path: I, validation-state: unverified > to 10.0.0.4 via ge-0/0/0.0 to 10.0.0.6 via ge-0/0/1.0 140.0.0.0/24 *[BGP/170] 01:38:02, localpref 100 AS path: 10631 I, validation-state: unverified > to 192.168.30.1 via ge-0/0/2.0 showroute@HKI-CORE-01# run show route table inet.0 protocol bgp hidden detail inet.0: 20 destinations, 20 routes (19 active, 0 holddown, 1 hidden) 150.0.0.0/24 (1 entry, 0 announced) BGP /-101 Next hop type: Router, Next hop index: 563 Address: 0x940f43c Next-hop reference count: 4 Source: 192.168.30.1 Next hop: 192.168.30.1 via ge-0/0/2.0, selected Session Id: 0x9 State: <Hidden Ext> Local AS: 41214 Peer AS: 10631 Age: 1:42:03 Validation State: unverified Task: BGP_10631.192.168.30.1+179 AS path: 10631 I Localpref: 100 Router ID: 10.68.1.1 Hidden reason: rejected by import policy 

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# show | compare [edit policy-options policy-statement FROM-AS10631 term TERM010 from] route-filter 140.0.0.0/24 exact { ... } + route-filter 150.0.0.0/24 exact; [edit] 



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.com

Merci de votre attention.

Les références


www.batfish.org

www.youtube.com/channel/UCA-OUW_3IOt9U_s60KvmJYA

github.com/batfish/batfish

media.readthedocs.org/pdf/pybatfish/latest/pybatfish.pdf

github.com/showroute/batfish-habr

Source: https://habr.com/ru/post/fr441532/


All Articles