
Uno de los problemas de las redes modernas es su fragilidad. Muchas reglas de filtrado, políticas de intercambio de información de enrutamiento, protocolos de enrutamiento dinámico hacen que las redes sean confusas y estén sujetas a factores humanos. Un bloqueo de red puede ocurrir involuntariamente al hacer cambios en un mapa de ruta o ACL (
uno ,
dos ). Definitivamente carecemos de una herramienta para evaluar el comportamiento de una red con una nueva configuración antes de realizar cambios en la producción. ¿Quiero saber con certeza si la Red A estará disponible para mí si filtro algunos de los anuncios de BGP recibidos del proveedor B? ¿Qué ruta irán los paquetes desde la red C al servidor D si en uno de los enlaces de tránsito doblo la métrica IGP? ¡Batfish nos ayudará a responder estas y muchas otras preguntas!
Opinión sobre batfish
Batfish es una herramienta de modelado de red. Su objetivo principal es probar los cambios de configuración antes de realizarlos en la red de producción. Batfish también se puede utilizar para analizar y verificar el estado actual de la red. Los procesos existentes de CI / CD en el mundo de la red claramente carecen de una herramienta para probar nuevas configuraciones. Batfish resuelve este problema.
Batfish no requiere acceso directo directo a los equipos de red existentes, Batfish modela el comportamiento de la red en función de los datos contenidos en los archivos de configuración del dispositivo.
Batfish puede:
- Determinar el estado vecino de los protocolos de enrutamiento dinámico en la red (BGP, IS-IS, OSPF)
- calcular el RIB de cada elemento de red
- compruebe la configuración de NTP, AAA, MTU
- permitir determinar si la ACL bloquea el paso del tráfico de la red (análogo del trazador de paquetes en Cisco ASA)
- verificar la conectividad de extremo a extremo entre hosts dentro de la red
- mostrar la ruta del tráfico a través de la red (rastreo virtual)
Plataformas compatibles:
- Arista
- Aruba
- AWS (VPC, ACL de red, VPN GW, NAT GW, Internet GW, Grupos de seguridad)
- Cisco (NX-OS, IOS, IOS-XE, IOS-XR y ASA)
- Dell force10
- Fundición
- iptables
- Enebro (MX, EX, QFX, SRX, serie T, PTX)
- Mrv
- Redes de Palo Alto
- Quagga / FRR
- Quanta
- Vyos

Batfish es una aplicación Java. Para un trabajo conveniente con él se escribió Pybatfish - python SDK.
Pasemos a practicar. Te mostraré las posibilidades de Batfish con un ejemplo.
Ejemplo
Gestionamos dos sistemas autónomos: AS 41214 y AS 10631. Como IGP, AS-41214 usa IS-IS y AS-10631 - OSPF. Dentro de cada AS, se utiliza IBGP-fullmesh. LDN-CORE-01 anuncia su prefijo de vecinos BGP 135.65.0.0/19, MSK-CORE-01 - 140.0.0.0/24. El intercambio de información de enrutamiento entre sistemas autónomos se produce en la unión de HKI-CORE-01 - SPB-CORE-01.
HKI-CORE-01, STH-CORE-01 - Enrutadores Junos
LDN-CORE-01, AMS-CORE-01, SPB-CORE-01, MSK-CORE-01 - Enrutadores Cisco IOS

Instale el contenedor con Batfish y el SDK de Python:
docker pull batfish/allinone docker run batfish/allinone docker container exec -it <container> bash
Conozca la biblioteca a través del modo interactivo de Python:
root@ea9a1559d88e:/
bf_init_snapshot ('tmp / habr') : la función carga los archivos de configuración en Batfish y los prepara para su análisis.
/ tmp / habr : un directorio con archivos de configuración del enrutador.
root@ea9a1559d88e:/tmp/habr
Ahora determinemos el estado de las sesiones BGP en el enrutador 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
Bien como? Suena como la verdad?
LDN-CORE-01
Ahora veamos qué rutas IS-IS están en la RIB en el enrutador HKI-CORE-01 según 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
En la línea de comando:
showroute@HKI-CORE-01
Genial Supongo que te ha quedado más claro qué es Batfish.
Al comienzo del artículo, escribí que Batfish se puede usar para verificar los cambios de configuración antes de ingresar a la red de "batalla". Ahora propongo considerar el proceso de prueba de red basado en
RobotFramework . Para hacer esto, escribí un pequeño módulo basado en PyBatfish que le permite realizar las siguientes comprobaciones:
- Determinar el estado de las sesiones BGP en la red
- Determinar el estado del vecino IS-IS
- Verifique la conectividad de extremo a extremo entre nodos en una red con una demostración de rastreo
- Determine el tamaño de la RIB en el enrutador para un protocolo de enrutamiento dinámico específico
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
Escenario N1

Bajo mi control sigue siendo la misma red. Supongamos que necesito ordenar los filtros en el borde de
AS 41214 y
AS 10631 y bloquear en los paquetes de unión que contienen las direcciones IP de origen o destino del rango BOGONS.
Ejecute la prueba antes de hacer cambios.

Pruebas aprobadas.
Haremos cambios en la configuración de prueba del enrutador
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
Ejecute la prueba

Estaba muy cerca, pero como muestra el resultado de la prueba, después de que se hicieron los cambios de BGP, el vecindario 192.168.30.0 - 192.168.30.1 no está en el estado establecido -> como resultado, la conectividad IP entre los puntos 135.65.0.1 <-> 140.0.0.1 se pierde. Que esta mal Observamos detenidamente la configuración
HKI-CORE-01 y vemos que el emparejamiento eBGP está instalado en direcciones privadas:
showroute@HKI-CORE-01
Conclusión: es necesario cambiar las direcciones en la unión o agregar la subred 192.168.30.0/31 a la excepción.
Agregaré una red en el cruce a la excepción, actualizaré /tmp/habr/configs/HKI-CORE-01.cfg nuevamente:
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
Ejecute la prueba

Ahora el tráfico no deseado no pasará por la interfaz ebgp AS 41214 - AS 10631. Puede realizar cambios de forma segura sin temor a las consecuencias.
Escenario N2

Aquí necesito terminar la red 150.0.0.0/24 en el enrutador
MSK-CORE-01 y asegurar la conectividad entre los puntos 135.65.0.1 y 150.0.0.1
Agrego las siguientes líneas a la configuración de prueba del enrutador 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 !
Cambio el script de prueba y ejecuto la prueba:
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
Ahora espero ver dos rutas eBGP en el enrutador HKI-CORE-01, se agrega una verificación de conectividad adicional
No hay conexión entre 135.65.0.1 y 150.0.0.1, además, en el enrutador
HKI-CORE-01 solo hay una ruta eBGP, en lugar de dos.
Verifique el contenido de la RIB en el
HKI-CORE-01 al agregar una nueva configuración al enrutador
MSK-CORE-01 :
showroute@HKI-CORE-01
Tenga en cuenta la política de importación para los prefijos recibidos 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
Falta la regla que permite 150.0.0.0/24. Agréguelo a la configuración de prueba y ejecute la prueba:
showroute@HKI-CORE-01

¡Genial, hay conectividad entre las redes, se pasan todas las pruebas! Para que pueda hacer estos cambios en el trabajo de la red de "combate".
Conclusión
En mi opinión, Batfish es una herramienta poderosa con un gran potencial. Pruébalo y compruébalo por ti mismo.
Si este tema es interesante para usted, únase al chat flojo, los desarrolladores de Batfish estarán encantados de responder cualquier pregunta y corregir rápidamente los errores.
batfish-org.slack.comGracias por su atencion
Referencias
www.batfish.orgwww.youtube.com/channel/UCA-OUW_3IOt9U_s60KvmJYAgithub.com/batfish/batfishmedia.readthedocs.org/pdf/pybatfish/latest/pybatfish.pdfgithub.com/showroute/batfish-habr