Roteamento rápido e NAT no Linux

Como os endereços IPv4 estão esgotados, muitos operadores de telecomunicações enfrentam a necessidade de organizar o acesso de seus clientes à rede usando a conversão de endereços. Neste artigo, mostrarei como obter o desempenho no nível NAT de grau de operadora em servidores básicos.

Um pouco de história


O tópico de ficar sem espaço de endereço IPv4 não é mais novo. Em algum momento, as listas de espera apareceram no RIPE, depois houve trocas nas quais eles trocaram blocos de endereços e concluíram transações pelo aluguel. Gradualmente, as operadoras de telecomunicações começaram a fornecer serviços de acesso à Internet através da tradução de endereços e portas. Alguém não conseguiu obter endereços suficientes para fornecer um endereço "branco" a cada assinante, enquanto alguém começou a economizar dinheiro, recusando-se a comprar endereços no mercado secundário. Os fabricantes de equipamentos de rede apoiaram essa ideia, pois essa funcionalidade geralmente requer módulos ou licenças de expansão adicionais. Por exemplo, com o Juniper na linha de roteadores MX (exceto os mais recentes MX104 e MX204), o NAPT pode ser executado em um cartão de serviço MS-MIC separado, o Cisco ASR1k requer uma licença GN,no Cisco ASR9k, um módulo A9K-ISM-100 separado e uma licença A9K-CGN-LIC para ele. Em geral, o prazer custa muito dinheiro.

Iptables


A tarefa de executar o NAT não requer recursos de computação especializados; os processadores de uso geral instalados, por exemplo, em qualquer roteador doméstico, podem resolvê-lo. Em uma escala de operadora, esse problema pode ser resolvido usando servidores comuns executando o FreeBSD (ipfw / pf) ou GNU / Linux (iptables). Não consideraremos o FreeBSD, porque Recusei-me a usar este sistema operacional por um longo tempo, então vamos nos concentrar no GNU / Linux.

Ativar a tradução de endereços não é nada difícil. Primeiro você precisa escrever a regra em iptables na tabela nat:

iptables -t nat -A POSTROUTING -s 100.64.0.0/10 -j SNAT --to <pool_start_addr>-<pool_end_addr> --persistent

O sistema operacional carregará o módulo nf_conntrack, que monitorará todas as conexões ativas e executará as conversões necessárias. Existem várias sutilezas. Em primeiro lugar, como estamos falando de NAT na escala da transportadora, é necessário aumentar o tempo limite, porque, com os valores padrão, o tamanho da tabela de conversão aumentará rapidamente para valores catastróficos. Abaixo está um exemplo das configurações que eu usei nos meus servidores:

net.ipv4.ip_forward = 1
net.ipv4.ip_local_port_range = 8192 65535

net.netfilter.nf_conntrack_generic_timeout = 300
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 60
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60
net.netfilter.nf_conntrack_tcp_timeout_established = 600
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 45
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 300
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 300
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 60
net.netfilter.nf_conntrack_icmpv6_timeout = 30
net.netfilter.nf_conntrack_icmp_timeout = 30
net.netfilter.nf_conntrack_events_retry_timeout = 15
net.netfilter.nf_conntrack_checksum=0

E segundo, como o tamanho padrão da tabela de conversão não foi projetado para funcionar nas condições de um operador de telecomunicações, ele deve ser aumentado:

net.netfilter.nf_conntrack_max = 3145728

Também é necessário aumentar o número de buckets para uma tabela de hash que armazena todas as traduções (esta é uma opção do módulo nf_conntrack):

options nf_conntrack hashsize=1572864

Após essas manipulações simples, é obtida uma construção completamente funcional, que pode converter um grande número de endereços de clientes em um pool externo. No entanto, o desempenho desta solução é ruim. Nas minhas primeiras tentativas de usar o GNU / Linux para NAT (por volta de 2013), consegui obter um desempenho de cerca de 7Gbit / s a ​​0.8Mpps por servidor (Xeon E5-1650v2). Desde então, muitas otimizações diferentes foram feitas na pilha de rede do kernel GNU / Linux, o desempenho de um servidor no mesmo hardware aumentou quase para 18-19 Gbit / s a ​​1,8-1,9 Mpps (esses eram os valores limite), mas a necessidade de volume de tráfego, processado por um único servidor, cresceu muito mais rápido. Como resultado, foram desenvolvidos esquemas de balanceamento de carga para diferentes servidores, mas tudo isso aumentou a complexidade da instalação,manutenção e manutenção da qualidade dos serviços prestados.

Nftables


Atualmente, o uso de DPDK e XDP é uma direção moderna na “transferência de pacotes” de software. Muitos artigos foram escritos sobre esse assunto, muitas apresentações diferentes foram feitas, produtos comerciais aparecem (por exemplo, SKAT da VasExperts). Mas nas condições de recursos limitados de programadores de operadoras de telecomunicações, é bastante problemático cortar algum tipo de "compartilhamento" com base nessas estruturas. Operar essa solução no futuro será muito mais difícil, em particular, será necessário desenvolver ferramentas de diagnóstico. Por exemplo, um tcpdump normal com DPDK simplesmente não funciona e não "vê" pacotes enviados de volta aos fios usando XDP. Em meio a toda a conversa sobre novas tecnologias para enviar o encaminhamento de pacotes ao espaço do usuário, relatórios e artigos passaram despercebidosPablo Neira Ayuso, mantenedor do iptables, sobre o desenvolvimento de descarga de fluxo em nftables. Vejamos esse mecanismo com mais detalhes.

A idéia principal é que, se o roteador passou pacotes de uma sessão nos dois lados do fluxo (a sessão TCP mudou para o estado ESTABLISHED), não é necessário passar pacotes subseqüentes desta sessão por todas as regras de firewall, porque todas essas verificações terão o mesmo fim transferindo o pacote para o roteamento. Sim, e na verdade a escolha da rota não precisa ser executada - já sabemos qual interface e qual host deve encaminhar os pacotes dentro desta sessão. Resta apenas salvar essas informações e usá-las no roteamento em um estágio inicial do processamento de pacotes. Ao executar o NAT, é necessário salvar adicionalmente informações sobre alterações nos endereços e portas convertidos pelo módulo nf_conntrack. Sim, é claro, nesse caso, vários polisadores e outras regras estatísticas da informação no iptables param de funcionar,mas como parte da tarefa de um NAT permanente separado ou, por exemplo, de uma borda, isso não é tão importante, porque os serviços são distribuídos pelos dispositivos.

Configuração


Para usar esta função, precisamos:

  • Use um kernel novo. Apesar do fato de que a funcionalidade em si apareceu no kernel 4.16, por algum tempo ficou muito "crua" e regularmente chamada de pânico do kernel. Tudo se estabilizou em dezembro de 2019, quando os kernels LTS 4.19.90 e 5.4.5 foram lançados.
  • Reescreva as regras do iptables no formato nftables usando uma versão bastante recente do nftables. Funciona bem na versão 0.9.0

Se tudo estiver claro em princípio com o primeiro parágrafo, o principal é não esquecer de incluir o módulo na configuração durante a montagem (CONFIG_NFT_FLOW_OFFLOAD = m), então o segundo parágrafo exige explicação. As regras do nftables são descritas de maneira bem diferente da do iptables. A documentação revela quase todos os pontos; também existem conversores de regras especiais do iptables para o nftables. Portanto, darei apenas um exemplo de configuração de NAT e descarregamento de fluxo. Uma pequena legenda para um exemplo: <i_if>, <o_if> são as interfaces de rede pelas quais o tráfego passa; na realidade, pode haver mais de duas. <pool_addr_start>, <pool_addr_end> - o endereço inicial e final do intervalo de endereços "brancos".

A configuração do NAT é muito simples:

#! /usr/sbin/nft -f

table nat {
        chain postrouting {
                type nat hook postrouting priority 100;
                oif <o_if> snat to <pool_addr_start>-<pool_addr_end> persistent
        }
}

A descarga de fluxo é um pouco mais complicada, mas compreensível:
#! /usr/sbin/nft -f

table inet filter {
        flowtable fastnat {
                hook ingress priority 0
                devices = { <i_if>, <o_if> }
        }

        chain forward {
                type filter hook forward priority 0; policy accept;
                ip protocol { tcp , udp } flow offload @fastnat;
        }
}

Essa, de fato, é toda a configuração. Agora todo o tráfego TCP / UDP irá para a tabela fastnat e será processado muito mais rapidamente.

resultados


Para deixar claro o quão "mais rápido" isso é, anexarei uma captura de tela da carga em dois servidores reais, com o mesmo hardware (Xeon E5-1650v2), igualmente configurado, usando o mesmo kernel do Linux, mas executando o NAT no iptables (NAT4) e no nftables (NAT5).



Não há gráfico de pacotes por segundo na captura de tela, mas no perfil de carregamento desses servidores, o tamanho médio dos pacotes é de cerca de 800 bytes; portanto, os valores vão para 1,5Mpps. Como você pode ver, a margem de desempenho do servidor com nftables é enorme. Atualmente, esse servidor processa até 30Gbit / s a ​​3Mpps e é claramente capaz de executar as limitações físicas da rede de 40Gbps, enquanto possui recursos livres de CPU.

Espero que este material seja útil para os engenheiros de rede que tentam melhorar o desempenho de seus servidores.

All Articles