Batfish 1. Introdução

imagem

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

imagem

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

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

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

Ó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 


batfish-test.robot
imagem

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.

imagem

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

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

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



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

Obrigado pela atenção.

Referências


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


All Articles