
Um dos problemas das redes modernas é sua fragilidade. Muitas regras de filtragem, políticas de troca de informações de roteamento, protocolos de roteamento dinâmico tornam as redes confusas e sujeitas a fatores humanos. Uma falha na rede pode ocorrer involuntariamente ao fazer alterações em um mapa de rota ou ACL (
um ,
dois ). Definitivamente, não temos uma ferramenta para avaliar o comportamento de uma rede com uma nova configuração antes de fazer alterações na produção. Quero saber exatamente se a Rede A estará disponível para mim se filtrar alguns dos anúncios BGP recebidos do provedor B? Qual rota os pacotes irão da rede C para o servidor D se em um dos links de trânsito eu dobrar a métrica IGP? O Batfish nos ajudará a responder a essas e muitas outras perguntas!
Revisão do Batfish
Batfish é uma ferramenta de modelagem de rede. Seu principal objetivo é testar as alterações na configuração antes de fazê-las na rede de produção. O Batfish também pode ser usado para analisar e verificar o status atual da rede. Os processos de CI / CD existentes no mundo da rede claramente carecem de uma ferramenta para testar novas configurações. Batfish resolve esse problema.
O Batfish não requer acesso direto direto ao equipamento de rede existente. O Batfish modela o comportamento da rede com base nos dados contidos nos arquivos de configuração do dispositivo.
Batfish pode:
- determinar o status do vizinho de protocolos de roteamento dinâmico na rede (BGP, IS-IS, OSPF)
- calcular o RIB de cada elemento de rede
- verifique as configurações de NTP, AAA, MTU
- permita determinar se o ACL obstrui a passagem do tráfego de rede (análogo do packet-tracer no Cisco ASA)
- verifique se há conectividade de ponta a ponta entre hosts na rede
- mostra o caminho do tráfego através da rede (rastreamento virtual)
Plataformas suportadas:
- Arista
- Aruba
- AWS (VPCs, ACLs de rede, VPN GW, NAT GW, Internet GW, Grupos de segurança)
- Cisco (NX-OS, IOS, IOS-XE, IOS-XR e ASA)
- Dell force10
- Fundição
- iptables
- Juniper (MX, EX, QFX, SRX, série T, PTX)
- Mrv
- Redes de Palo alto
- Quagga / FRR
- Quanta
- Vyos

Batfish é um aplicativo Java. Para um trabalho conveniente, foi escrito Pybatfish - python SDK.
Vamos seguir praticando. Vou mostrar-lhe as possibilidades do Batfish com um exemplo.
Exemplo
Gerenciamos dois sistemas autônomos: AS 41214 e AS 10631. Como IGP, o AS-41214 usa IS-IS e o AS-10631 - OSPF. Dentro de cada AS, é utilizado o IBGP-fullmesh. O LDN-CORE-01 anuncia o prefixo 135.65.0.0/19 dos vizinhos BGP, MSK-CORE-01 - 140.0.0.0/24. A troca de informações de roteamento entre sistemas autônomos ocorre na junção de HKI-CORE-01 - SPB-CORE-01.
HKI-CORE-01, STH-CORE-01 - Roteadores Junos
LDN-CORE-01, AMS-CORE-01, SPB-CORE-01, MSK-CORE-01 - roteadores Cisco IOS

Instale o contêiner com o Batfish e o python SDK:
docker pull batfish/allinone docker run batfish/allinone docker container exec -it <container> bash
Conheça a biblioteca através do modo interativo python:
root@ea9a1559d88e:/
bf_init_snapshot ('tmp / habr') - a função carrega os arquivos de configuração no Batfish e os prepara para análise.
/ tmp / habr - um diretório com arquivos de configuração do roteador.
root@ea9a1559d88e:/tmp/habr
Agora vamos determinar o status das sessões BGP no roteador 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
Bem como? Parece a verdade?
LDN-CORE-01
Agora vamos ver quais rotas IS-IS estão no RIB no roteador HKI-CORE-01, de acordo com 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
Na linha de comando:
showroute@HKI-CORE-01
Ótimo! Suponho que ficou mais claro para você que existe Batfish.
No começo do artigo, escrevi que o Batfish pode ser usado para verificar alterações na configuração antes de levá-las à rede de "batalhas". Agora, proponho considerar o processo de testar uma rede baseada no
RobotFramework . Para fazer isso, escrevi um pequeno módulo baseado no PyBatfish que permite executar as seguintes verificações:
- Determinar o status das sessões BGP na rede
- Determinar o status de vizinho IS-IS
- Verifique a conectividade de ponta a ponta entre nós em uma rede com demonstração de rastreamento
- Determine o tamanho do RIB no roteador para um protocolo de roteamento 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
Cenário N1

Sob meu controle ainda é a mesma rede. Suponha que eu precise colocar os filtros na borda do
AS 41214 e do
AS 10631 e bloquear os pacotes de junção que contêm os endereços IP de origem ou destino do intervalo BOGONS.
Execute o teste antes de fazer alterações.

Testes aprovados.
Faremos alterações na configuração de teste do roteador
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
Execute o teste.

Eu estava muito perto, mas como a saída de teste mostra, após as alterações feitas no BGP, o bairro 192.168.30.0 - 192.168.30.1 não está no estado Estabelecido -> como resultado, a conectividade IP entre os pontos 135.65.0.1 <-> 140.0.0.1 é perdida. O que está errado? Observamos atentamente a configuração do
HKI-CORE-01 e vemos que o peering do eBGP está instalado em endereços privados:
showroute@HKI-CORE-01
Conclusão: é necessário alterar os endereços na junção ou adicionar a sub-rede 192.168.30.0/31 à exceção.
Adicionarei uma rede no cruzamento à exceção, atualizarei o /tmp/habr/configs/HKI-CORE-01.cfg novamente:
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
Execute o teste.

Agora, o tráfego indesejado não passará pela interface ebgp AS 41214 - AS 10631. É possível fazer alterações com segurança, sem medo de consequências.
Cenário N2

Aqui eu preciso finalizar a rede 150.0.0.0/24 no roteador
MSK-CORE-01 e garantir a conectividade entre os pontos 135.65.0.1 e 150.0.0.1
Eu adiciono as seguintes linhas à configuração de teste do roteador 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 !
Eu mudo o script de teste e execute o teste:
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
Agora, espero ver duas rotas eBGP no roteador HKI-CORE-01, uma verificação de conectividade adicional é adicionada
Não há conexão entre 135.65.0.1 e 150.0.0.1, além disso, no roteador
HKI-CORE-01, existe apenas uma rota eBGP, em vez de duas.
Verifique o conteúdo do RIB no
HKI-CORE-01 ao adicionar uma nova configuração ao roteador
MSK-CORE-01 :
showroute@HKI-CORE-01
Observe a política de importação para os prefixos recebidos do
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
A regra que permite 150.0.0.0/24 está ausente. Adicione-o à configuração de teste e execute o teste:
showroute@HKI-CORE-01

Ótimo, existe conectividade entre as redes, todos os testes foram aprovados! Então você pode fazer essas alterações no trabalho da rede "combate".
Conclusão
Na minha opinião, o Batfish é uma ferramenta poderosa com enorme potencial. Experimente e veja por si mesmo.
Se este tópico for interessante para você - participe do chat folgado, os desenvolvedores do Batfish terão prazer em responder a quaisquer perguntas e rapidamente corrigir bugs.
batfish-org.slack.comObrigado pela atenção.
Referências
www.batfish.orgwww.youtube.com/channel/UCA-OUW_3IOt9U_s60KvmJYAgithub.com/batfish/batfishmedia.readthedocs.org/pdf/pybatfish/latest/pybatfish.pdfgithub.com/showroute/batfish-habr