Pez murciélago Introduccion

imagen

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

imagen

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:/# 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 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# 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 

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#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 

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# 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 

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 


batfish-test.robot
imagen

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.

imagen

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# 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 

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# 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 

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# 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] 



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

Gracias por su atencion

Referencias


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/441532/


All Articles