CGNAT Bulk Port Allocation com DPDK

De Wiki BPF
Revisão de 09h06min de 11 de novembro de 2020 por Gondim (discussão | contribs)
Ir para navegação Ir para pesquisar

Objetivo

Mostrar a configuração do projeto DANOS para funcionar como um CGNAT Bulk Port Allocation utilizando um hardware de baixo custo e proporcionando uma enorme economia de IPv4 público, pois trabalha com alocação de portas TCP/UDP dinamicamente e não da forma Determinística onde pré-fixamos uma quantidade de portas por assinante. Temos que ter sempre em mente que a implantação e o uso do IPv6 é importantíssimo, porque o CGNAT não irá resolver todos os problemas da Internet. O CGNAT ajudará à manter a rede funcionando enquanto o IPv6 não for 100% utilizado no Mundo. O fato do projeto DANOS utilizar o DPDK, permite que consigamos usar um equipamento mais simples para gerenciar tráfegos bem maiores em Gbps/PPS.

Diagrama

Diagrama.png

O BNG é o responsável por gerenciar as conexões PPPoE/IPoE e no nosso artigo fará o encaminhamento de todos os pacotes do pool CGNAT 100.64.0.0/10 via PBR (Policy Based Routing) para a caixa DANOS CGNAT. O DANOS CGNAT estará conectado via iBGP com o Router Borda e fará a tradução e alocação dinâmica das portas TCP/UDP usando como exemplo de saída o prefixo 198.51.100.0/24.Clientes conectados no BNG com IPv4 público e IPv6 não devem ser encaminhados para a caixa CGNAT.

Teremos uma rede lógica 192.168.254.0/24 entre os BNGs e o DANOS CGNAT e outra rede lógica 10.69.0.8/30 entre o DANOS CGNAT e o Router Borda. Usaremos os IPs das loopbacks para fecharmos o iBGP. O iBGP está sendo fechado somente de um lado para facilitar o artigo, mas cabe à você decidir a melhor forma de implementar em seu cenário.

Também usaremos nessa configuração um bonding (LACP) de duas portas de 10GbE para entrada de dados provenientes do Router Borda e outro bonding também com duas portas de 10GbE para a saída do tráfego em direção aos BNGs.

Embora esteja no diagrama, não falaremos sobre BNG e nem o Router Borda nesse artigo e por isso deve-se estudar como fazer o PBR em seu equipamento concentrador e também a configuração do iBGP no seu router pois será muito importante para o funcionamento da nossa caixa CGNAT.

Você pode adicionar outras caixas CGNAT basta seguir a mesma lógica apresentada aqui nesse artigo.

Descritivo sobre o DANOS

É um projeto desenvolvido pelo pessoal da AT&T, que está atualmente na versão 2009, que utiliza o Vyatta como interface de configuração. A syntax me lembra bastante a configuração de um JunOS, possui diversos recursos semelhantes como commit, commit-confirm, rollback, validate para validar uma alteração dentre outras características. Por baixo do capô temos um sistema GNU/Linux Debian 10, Linux Kernel 5.4, FRR 7.3.1 e DPDK 19.11. O projeto possui utilidade inclusive para roteamento mas nesse artigo falaremos sobre CGNAT. Embora o sistema seja um Debian, não é disponibilizado uma atualização via apt, pois poderia quebrá-lo já que este possui partes não Debian incluídas no sistema como o DPDK. A atualização pode ser feita sempre que sai uma nova ISO do DANOS, através de comandos simples e salvando sua configuração atual.

Equipamento utilizado no artigo e em produção atualmente

- Dell PowerEdge R230 - Quad Core Intel(R) Xeon(R) CPU E3-1240 v6 @ 3.70GHz.
- 16G ram.
- 2x Intel X520-SR2.
- SSD 120Gb.

Dados estatísticos desse equipamento em produção

Atualmente passando 24Gbps agregado com mais de 1.5Mpps. Esse sistema tem 382 IPv4 públicos, 9980 clientes utilizando e mais de 50% dos IPs públicos ainda livres no CGNAT. Como o sistema utiliza DPDK, não temos como acompanhar visualmente a utilização dos processadores pois o dataplane ocupa todos eles ou quase todos durante seu uso mas essa é uma característica dele. Por isso temos que observar o comportamento do tráfego e erros que possam aparecer nas portas da switch e perdas de pacotes. Dessa forma estamos documentando o hardware utilizado em produção e quanto está suportando para usarmos de referência futura:

Zabbix 12G.png
Htop 12G.png

Instalação do DANOS

Não vou falar sobre a instalação do sistema porque é extremamente simples. Não tem muitas opções durante a instalação, é feita através de um simples boot com um pendrive já com a ISO nele.

O passo à passo da instalação pode ser vista aqui. Basicamente é iniciar o boot e ao final dele executar: install image e responder as perguntas. Depois disso vamos para o nosso "mão na massa".

Começando a configuração

Bem a primeira coisa é colocarmos os IPs nas interfaces para inclusive podermos acessar remotamente via ssh e também habilitar o serviço ssh. Vou utilizar uma configuração com IPv6 para fazer meu acesso remoto à esse sistema. Se você tiver outra interface reconhecida pelo sistema, você pode configurar um IP de gerência nela também e acessar por ali. No DANOS nem todas as interfaces de rede são reconhecidas e por isso você precisa consultar essa página e checar as suportadas. Também vamos configurar o bonding antes de configurarmos os IPs:

Após se logar com o usuário e senha que escolheu na instalação, basta executar o comando abaixo e passar os sets. Lembrando que os IPs que estou utilizando você deve alterar para o seu ambiente.:

$ configure 
# set interfaces bonding dp0bond0 address '2804:XXXX:XXXX:faca:192:168:255:2/64'
# set interfaces bonding dp0bond0 address 10.69.0.10/30
# set interfaces bonding dp0bond0 mode lacp 
# set interfaces bonding dp0bond1 address 192.168.254.1/24
# set interfaces bonding dp0bond1 mode lacp
# set interfaces dataplane dp0p1s0f0 bond-group dp0bond0
# set interfaces dataplane dp0p1s0f1 bond-group dp0bond0
# set interfaces dataplane dp0p2s0f0 bond-group dp0bond1
# set interfaces dataplane dp0p2s0f1 bond-group dp0bond1
# set protocols static route6 '::/0' next-hop '2804:XXXX:XXXX:faca::3'
# service ssh authentication-retries 5

Estamos definindo 2 bondings, sendo 1 pra cada interface de rede. Não faça bonding entre portas de interfaces de rede diferentes, isso prejudica o cpu affinity. Os nomes utilizados também possuem uma certa regra de criação e que pode ser vista digitando um "?" por exemplo:

# set interfaces bonding ?
Possible Completions:
> <dpFbondN> Bonding interface name

As 5 primeiras linhas criamos os bondings, definimos o mode lacp e colocamos os IPs. As 4 próximas linhas dizemos quais interfaces de rede fazem parte desse bonding, a penúltima linha definindo o gateway default para o meu acesso IPv6 ao sistema e a última linha define quantas vezes você pode errar a senha via ssh, antes de ser bloqueado por um tempo.

Configuração do iBGP

Vamos definir nosso endereço de loopback para usar no nosso iBGP com o Router Borda e setar uma rota apontando pro IP da loopback do Router Borda:

# set interfaces loopback lo address 186.xxx.xxx.163/32
# set protocols static route 186.xxx.xxx.160/32 next-hop 10.69.0.9

Agora vamos definir uma policy prefix-list para o prefixo que vamos anunciar no iBGP e outra prefix-list de rota default que só aceitaremos vindo do Router Borda:

# set policy route prefix-list bloco-publico rule 5 action permit
# set policy route prefix-list bloco-publico rule 5 prefix 198.51.100.0/24
# set policy route prefix-list rota-default rule 5 action permit
# set policy route prefix-list rota-default rule 5 prefix 0.0.0.0/0

Definição do policy route-map que usaremos:

# set policy route route-map router-IN rule 5 action permit
# set policy route route-map router-IN rule 5 match ip address prefix-list rota-default
# set policy route route-map router-IN rule 10 action deny
# set policy route route-map router-OUT rule 5 action permit
# set policy route route-map router-OUT rule 5 match ip address prefix-list bloco-publico
# set policy route route-map router-OUT rule 10 action deny

O que queremos é anunciar o prefixo público 198.51.100.0/24 para o Router e só aceitar receber dele o prefixo 0.0.0.0/0 assim:

# set protocols bgp 53XXX address-family ipv4-unicast network 198.51.100.0/24
# set protocols bgp 53XXX neighbor 186.xxx.xxx.160 address-family ipv4-unicast route-map export router-OUT
# set protocols bgp 53XXX neighbor 186.xxx.xxx.160 address-family ipv4-unicast route-map import router-IN
# set protocols bgp 53XXX neighbor 186.xxx.xxx.160 remote-as 53XXX
# set protocols bgp 53XXX neighbor 186.xxx.xxx.160 update-source lo
# set protocols bgp 53XXX parameters router-id 186.xxx.xxx.163

Roteamento estático para o BNG

Aqui definimos as rotas estáticas para o BNG no IP 192.168.254.98, conforme nosso diagrama do artigo. Também pode ser feito por exemplo com iBGP mas para simplificar o artigo, coloquei como sendo rotas estáticas.

# set protocols static route 100.64.0.0/22 next-hop 192.168.254.98

# set protocols static route 100.64.8.0/22 next-hop 192.168.254.98

# set protocols static route 100.64.12.0/22 next-hop 192.168.254.98

# set protocols static route 100.64.16.0/22 next-hop 192.168.254.98

Definindo blackholes para evitar ataques de static loop

Fazemos isso para evitar os ataques à IPs que não estão conectados mas que existem rotas para eles. Nesse caso definimos um blackhole para cada prefixo de IPv4 público que usaremos no nosso CGNAT.

# set protocols static route 198.51.100.0/24 blackhole

Entrando na configuração do CGNAT

Agora começaremos a configuração do nosso CGNAT. Basicamente vamos definir quem são os pools de IPs privados usados no CGNAT (100.64.0.0/10), os pools de IPv4 públicos que serão usados no NAT e a configuração propriamente dita do cgnat dentro do DANOS. Vamos começar então:

Os pools CGNAT:

# set resources group address-group AG_MATCH address 100.64.0.0/22

# set resources group address-group AG_MATCH address 100.64.8.0/22

# set resources group address-group AG_MATCH address 100.64.12.0/22

# set resources group address-group AG_MATCH address 100.64.16.0/22

O pool IPv4 público que será feito NAT de saída. Nessa parte da configuração abaixo além da definição do prefixo público que usaremos, utilizei a configuração de até 8 blocos de 512 portas tcp/udp por assinante. Fiz isso porque em meus testes muitos assinantes estouravam a configuração de até 8 blocos de 256 portas tcp/udp e quando isso ocorre o cliente tem problemas de acesso. Mesmo utilizando esse perfil de até 4096 portas tcp/udp por assinante, a economia de IPv4 público ainda assim é muito grande porque a maioria dos clientes utilizam muito menos que 1024 portas tcp/udp durante o uso da Internet. Também por boa prática setamos o início das portas em 1024 e finalizando em 65535. Essa parte precisa ser bem definida porque mudanças no dynamic-block-allocation block-size e dynamic-block-allocation max-blocks-per-subscriber requerem uma mudança grande de estratégia e por isso um reboot no sistema será necessário para efetivar essas alterações devido as sessões anteriores ainda estarem em uso.

# set service nat pool NAT_POOL1 address-allocation round-robin

# set service nat pool NAT_POOL1 address-pooling paired

# set service nat pool NAT_POOL1 entry RANGE1 ip-address prefix 198.51.100.0/24

# set service nat pool NAT_POOL1 port allocation sequential

# set service nat pool NAT_POOL1 port dynamic-block-allocation block-size 512

# set service nat pool NAT_POOL1 port dynamic-block-allocation max-blocks-per-subscriber 8

# set service nat pool NAT_POOL1 port range end 65535

# set service nat pool NAT_POOL1 port range start 1024

# set service nat pool NAT_POOL1 select event port-block-allocation

# set service nat pool NAT_POOL1 type CGNAT

Veremos agora a definição restante do nosso CGNAT. Abaixo habilitamos o log que precisaremos gerar e armazenar para futuras requisições jurídicas, criaremos uma policy para dizermos qual grupo de IPs privados usaremos no source do NAT (nesse caso AG_MATCH) e qual o pool de IPs públicos usaremos na saída (nesse caso NAT_POOL1). Também são definidos alguns timeouts:

# set service nat cgnat log event port-block-allocation

# set service nat cgnat policy POLICY match source address-group AG_MATCH

# set service nat cgnat policy POLICY priority 10

# set service nat cgnat policy POLICY translation pool NAT_POOL1

# set service nat cgnat session-timeout other established 30

# set service nat cgnat session-timeout other partially-open 20

# set service nat cgnat session-timeout tcp established 1800

# set service nat cgnat session-timeout tcp partially-open 240

# set service nat cgnat session-timeout tcp port 53 established 10

# set service nat cgnat session-timeout udp established 1800

# set service nat cgnat session-timeout udp partially-open 240

Vamos habilitar também os ALGs:

# set system alg ftp

# set system alg pptp

# set system alg rpc

# set system alg sip

# set system alg tftp

Até então só criamos as configurações do CGNAT mas ainda não dizemos em qual interface devemos aplicá-las. Devemos aplicá-las na interface de saída que liga com o Router Borda:

# set service nat cgnat interface dp0bond0 policy POLICY

Para checar se a configuração não possui erros fazemos ainda na configuração:

# validate

Se nenhum erro for exibido, podemos fazer um commit ou commit-confirm. A diferença é que o commit-confirm você passa como parâmetro um valor em minutos que o sistema esperará antes de fazer um rollback automático. Para confirmar e evitar o rollback só fazer o comando "confirm". No nosso caso aqui vamos apenas fazer o commit e um exit para sair do modo configuração:

# commit

# exit

Alguns comandos para administração do DANOS

$ show configuration => lista a configuração do sistema em produção.

$ show configuration commands => lista a configuração do sistema em produção em formato de sets de comandos. Igual como fizemos acima. Muito bom para guardarmos também como backup.

$ show interfaces => lista as interfaces do servidor.

Cgnat interfaces.png

$ show cgnat subscriber => mostra IP de CGNAT, IP público pareado, número de sessões, quantidade de blocos e portas alocadas, se houve falha pra alguém por ter estourado a quantidade máxima de blocos.Você também pode fazer um: show cgnat subscriber 100.64.0.2 e nesse caso vai mostrar apenas as informações referente à esse cliente que está usando esse IP.

Cgnat subscriber.png

$ show cgnat public => mostra informações referente aos IPs públicos usados. O mesmo ocorre se fizermos: show cgnat public 170.xxx.xxx.208, nesse caso irá mostrar apenas esse IP. Na imagem abaixo podemos observar que esse mesmo IP tem limite de 126 blocos de portas tcp/udp mas só está usando 55.

Cgnat public.png

$ show cgnat summary => mostra um sumário de valores do CGNAT. Os campos importantes são "Table full" não pode dar Yes, caso contrário precisará aumentar o "Maximum table size". "Public address mapping table" indica quantos IPs públicos temos disponíveis para o nosso CGNAT. É o somatório de todos os IPs dos prefixos que disponibilizamos, nesse meu caso abaixo são 382. "Subscriber address table" temos quantos assinantes estão usando o CGNAT. Nesse caso temos 10025.

Cgnat summary.png

$ show cgnat errors => mostra algumas estatísticas e erros.

Cgnat errors.png

Para mais comandos de administração consulte aqui.

Mais algumas configurações complementares

Bem de nada adianta configurar este CGNAT se você não enviar esses logs para algum servidor de logs seu e/ou para alguma base de dados, onde será mais fácil fazer consultas. Nesse artigo não cobriremos o servidor de logs mas vou indicar o que usamos aqui, o Graylog. Existe uma versão free e que tem nos atendido muito bem, mas deixamos em aberto para sua escolha. Então vamos adicionar algumas coisas na nossa configuração. Entre em modo configuração e vamos aos próximos comandos:

# set system host-name danos-cgnat

# set system name-server 'fc00::10:20:20:20'

# set system ntp server a.ntp.br

# set system syslog host '[2804:XXXX:XXXX:XXXX:191:XXXX:XXXX:215]:1514' facility all level info

# set system time-zone America/Sao_Paulo

Acima estamos dando nome ao host, setando um servidor de DNS, sim no meu caso aqui é esse mesmo fc00::10:20:20:20, um ntp server, estamos apontando para um servidor syslog que no nosso caso é um graylog e dele jogamos para uma base de dados MySQL. Também setamos o time-zone.

Os logs que serão enviados para o servidor de logs tem esse formato:

Nov 10 00:38:18 danos-cgnat dataplane[2689]: CGNAT: PB_RELEASED subs-addr=100.69.82.117 policy=POLICY pub-addr=186.xxx.xxx.238 pool=NAT_POOL1 port=11264-11775 start-time=1604953031015000 end-time=1604953119545000

O PB_RELEASED informa quando é finalizado o uso de um bloco de portas tcp/udp por um assinante, informa a sequência das portas quando iniciou no formato timestamp em micro segundos e quando terminou, ou seja, quando foram liberadas as portas tcp/udp para outro assinante. O timestamp pode ser convertido para a nossa data/hora através de programas e funções mas ele sempre virá no timezone GMT. Nesse site você pode testar e converter esses timestamps acima.

Timestamp.png

Quando instalamos o sistema escolhemos um usuário e senha de administrador no sistema e ele é mais que suficiente para fazer as configurações no Vyatta. Mas você pode querer ter acesso root ao sistema e pra isso precisamos transformar seu usuário admin em superuser. No meu exemplo o usuário é admin e faríamos dessa forma:

# set system login user admin level superuser

Sempre no final, antes de fazer um commit, cheque se está tudo OK com o comando validate. Após sair do modo configuração com o exit, termine a sessão ssh ou da console e entre novamente. Nesse momento você conseguirá fazer um "sudo su -" e virar root.

Para mostrar as senhas encriptadas ao invés de escondidas, isso é bom no caso de querer guardar a configuração como backup e poder restaurar inclusive os usuários:

# set system login group secrets

# set system login user admin group secrets

Criamos o grupo secrets e colocamos nosso user admin no grupo secrets. Pronto, agora quando fizermos um: show configuration commands, os users serão listados com suas senhas encriptadas.

Criando um PROTECT-RE para o seu sistema

O sistema quando instalamos já vem com um recurso de Access Control Management (ACM) já pré configurado para proteção do sistema mas além disso podemos criar algumas regras básicas de Firewall para proteger a caixa. Abaixo um exemplo simples que uso para esse artigo:

# set security firewall name PROTECT-RE default-action accept

# set security firewall name PROTECT-RE rule 10 action accept

# set security firewall name PROTECT-RE rule 10 destination port 22

# set security firewall name PROTECT-RE rule 10 protocol tcp

# set security firewall name PROTECT-RE rule 10 source address '2804:XXXX:XXXX:XXXX::/64'

# set security firewall name PROTECT-RE rule 15 action drop

# set security firewall name PROTECT-RE rule 15 destination port 22

# set security firewall name PROTECT-RE rule 15 protocol tcp

# set security firewall name PROTECT-RE rule 20 action drop

# set security firewall name PROTECT-RE rule 20 destination port 123

# set security firewall name PROTECT-RE rule 20 protocol udp

# set security firewall name PROTECT-RE rule 25 action drop

# set security firewall name PROTECT-RE rule 25 destination port 179

# set security firewall name PROTECT-RE rule 25 protocol tcp

Basicamente as regras acima liberam o ssh vindo de uma rede IPv6 específica minha e bloqueia o ssh para o restante. Bloqueia consultas externas ao ntpd do servidor e bloqueia conexão externa ao BGP. No nosso caso o DANOS que se conecta ao Router Borda.

Essas regras só criadas não fazem efeito algum. Para ativar as regras acima, precisamos aplicar elas assim:

# set interfaces loopback lo firewall local PROTECT-RE

Finalizando

Bem, essa foi mais uma contribuição para a comunidade. Meus agradecimentos ao meu amigo Douglas Fisher por insistir que déssemos uma olhada nesse projeto e se não fosse sua pesquisa, eu não teria olhado com carinho o DANOS e provavelmente esse artigo não existiria. Também gostaria de agradecer à outro amigo, Fernando Frediani por sempre me apoiar e me ajudar à melhorar visualmente os artigos. Valeu pessoal!

Para aqueles que tiverem dúvidas só ajudarei com a condição de já estarem com IPv6 implantado e funcionando para seus assinantes. Afinal a solução definitiva para a falta de IPv4 não é o CGNAT e sim a adoção do IPv6 no mundo todo.

A referência principal para o conteúdo aqui apresentado foi tirado do site oficial do Projeto DANOS. Acessem, explorem, interajam porque vale a pena contribuir com esse projeto.

Autor: Marcelo Gondim