Geração de tráfego no espaço do usuário


Geração de tráfego usando MoonGen + DPDK + Lua na visão do artista

A neutralização dos ataques DDoS em condições reais requer testes preliminares e testes de várias técnicas. O equipamento e o software de rede devem ser testados em condições artificiais próximas às reais - com fluxos intensivos de tráfego simulando ataques. Sem essas experiências, é extremamente difícil obter informações confiáveis ​​sobre os recursos e limitações específicos de qualquer ferramenta complexa.

Neste artigo, revelaremos alguns dos métodos de geração de tráfego usados ​​no Qrator Labs.

ATENÇÃO

É altamente recomendável que o leitor não tente usar as ferramentas mencionadas para atacar objetos de infraestrutura real. A organização dos ataques DoS é punível por lei e pode levar a penalidades severas. O Qrator Labs realiza todos os testes em um ambiente de laboratório isolado.

Nível técnico moderno


Uma tarefa significativa em nossa área é saturar a interface Ethernet 10G com pacotes pequenos, o que significa processar 14,88 Mpps (milhões de pacotes por segundo). A seguir, consideramos os menores pacotes de rede Ethernet - 64 bytes -, pois nosso principal interesse é maximizar o número de pacotes transmitidos por unidade de tempo. Um cálculo simples mostra que temos apenas 67 nanossegundos para processar um desses pacotes.

Apenas para comparação, esse tempo está próximo do que um processador moderno precisa para obter um pedaço de dados da memória, se perder o cache. Tudo se torna ainda mais complicado quando começamos a trabalhar com interfaces Ethernet 40G e 100G e tentamos saturá-las completamente até a taxa de linha (o máximo desempenho possível declarado do dispositivo de rede).

Como, no caso usual, o fluxo de dados passa pelo aplicativo no espaço do usuário (espaço do usuário), depois pelo kernel, finalmente entrando no controlador de rede (NIC), a primeira e mais direta idéia é tentar configurar a geração de pacotes diretamente no kernel. Um exemplo dessa solução é o módulo nuclear pktgen [2]. Esse método permite melhorar significativamente o desempenho, mas não é flexível o suficiente, pois a menor alteração no código-fonte no kernel leva a um longo ciclo de compilação, reinicialização dos módulos do kernel ou mesmo de todo o sistema e, de fato, testes, o que reduz a produtividade geral (ou seja, requer mais tempo do programador) e esforço).

Outra abordagem possível é obter acesso direto do espaço do usuário aos buffers de memória do controlador de rede. Esse caminho é mais complicado, mas vale o esforço para obter maior produtividade. As desvantagens incluem alta complexidade e baixa flexibilidade. Exemplos dessa abordagem são netmap , PF_RING e DPDK [4].

Outra maneira eficaz, embora muito cara, de obter alto desempenho é usar equipamentos não universais, mas especializados. Exemplo: Ixia .

Também existem soluções baseadas no DPDK usando scripts, o que aumenta a flexibilidade no controle dos parâmetros do gerador e também permite variar o tipo de pacotes gerados durante a inicialização. Abaixo, descrevemos nossa própria experiência com uma dessas ferramentas - MoonGen.

Arquitetura MoonGen


Os recursos distintos do MoonGen são:

  1. O processamento de dados DPDK no espaço do usuário é a principal razão para o ganho de desempenho;
  2. Lua [ 5 ] empilha com scripts simples no nível superior e ligações à biblioteca DPDK escrita em C, na parte inferior;
  3. Graças à tecnologia JIT (just in time), os scripts Lua funcionam rápido o suficiente, o que contradiz as idéias geralmente aceitas sobre a eficácia das linguagens de script.

MoonGen pode ser pensado como um wrapper Lua em torno da biblioteca DPDK. Pelo menos as seguintes operações do DPDK são visíveis no nível da interface do usuário Lua:

  • Configurando controladores de rede;
  • Alocação e acesso direto a pools e buffers de memória que, para fins de otimização, devem ser alocados em áreas alinhadas contínuas;
  • Acesso direto às filas RSS dos controladores de rede;
  • API para gerenciamento de fluxos computacionais, levando em consideração a heterogeneidade do acesso à memória (afinidade NUMA e CPU) [ 12 ].



Arquitetura MoonGen, esquema de material [ 1 ].

Moongen


MoonGen é um gerador de pacotes de alta velocidade com script baseado na biblioteca DPDK. Os scripts Lua controlam todo o processo: o script criado pelo usuário é responsável por criar, modificar e enviar pacotes. Graças à rápida biblioteca de processamento de pacotes LuaJIT e DPDK, essa arquitetura permite saturar uma interface Ethernet de 10 gigabits com pacotes de 64 bytes usando apenas um núcleo da CPU. MoonGen permite que você atinja essa velocidade mesmo quando o script Lua modifica cada pacote. Ele não usa truques como reutilizar o mesmo buffer do controlador de rede.

O MoonGen também pode receber pacotes, ou seja, verificar quais pacotes foram descartados pelo sistema em teste. Como a recepção de pacotes é controlada exclusivamente por um script Lua personalizado, ela também pode ser usada para criar scripts de teste mais complexos. Por exemplo, você pode usar duas instâncias do MoonGen para estabelecer uma conexão entre si. Essa configuração pode ser usada, em particular, para testar as chamadas caixas do meio (equipamentos entre o ponto de envio e recebimento de tráfego), por exemplo, firewalls. O MoonGen foca em quatro áreas principais:

  • Alto desempenho e dimensionamento de vários núcleos: mais de 20 milhões de pacotes por segundo em um único núcleo de CPU;
  • Flexibilidade: cada pacote é gerado em tempo real com base em um script Lua criado pelo usuário;
  • Carimbos de data e hora exatos: no hardware comum (de mercadorias), a marcação de tempo é executada com precisão de milissegundos;
  • Controle exato dos intervalos entre pacotes enviados: geração confiável dos padrões e tipos de tráfego necessários no hardware comum.

DPDK


DPDK significa Data Plane Development Kit e consiste em bibliotecas cujas principais funções são aumentar o desempenho da geração de pacotes de rede em uma ampla variedade de arquiteturas de processador central.

Em um mundo em que as redes de computadores estão se tornando a base da comunicação humana, desempenho, largura de banda e latência, estão se tornando parâmetros cada vez mais críticos para sistemas como redes sem fio e infraestrutura de cabos, incluindo todos os seus componentes individuais: roteadores, balanceadores de carga, firewalls; bem como áreas de aplicação: transferência de mídia (streaming), VoIP, etc.

O DPDK é uma maneira leve e conveniente de criar testes e scripts. A transferência de dados no espaço do usuário é algo que não observamos com tanta frequência, principalmente porque a maioria dos aplicativos se comunica com o equipamento de rede por meio do sistema operacional e da pilha do kernel, que é o oposto do modelo DPDK.

Lua


O principal objetivo da existência de Lua é fornecer ferramentas expressivas simples e flexíveis, expansíveis para tarefas atuais específicas, em vez de um conjunto de primitivas aplicáveis ​​em apenas um paradigma de programação. Como resultado, a linguagem base é muito leve - o intérprete inteiro ocupa apenas 180 kB na forma compilada e se adapta facilmente a uma ampla variedade de implementações possíveis.

Lua é uma linguagem dinâmica. É tão compacto que pode ser colocado em praticamente qualquer dispositivo. Lua suporta um pequeno conjunto de tipos: valores booleanos, números (ponto flutuante de precisão dupla) e seqüências de caracteres. Estruturas de dados convencionais, como matrizes, conjuntos e listas, podem ser representadas pela única estrutura de dados interna em Lua - uma tabela, que é uma matriz associativa heterogênea.

Lua usa a compilação JIT (just in time), portanto, sendo uma linguagem de script, mostra desempenho comparável a linguagens compiladas como C [ 10 ].

Por que moongen


Como uma empresa especializada em neutralizar ataques DDoS, o Qrator Labs precisa de uma maneira confiável de criar, atualizar e testar suas próprias soluções de segurança. É para o último teste, que são necessários vários métodos de geração de tráfego que simulam ataques reais. No entanto, não é tão fácil simular um ataque de inundação perigoso, mas direto, nos níveis 2-3 do modelo OSI, principalmente devido às dificuldades em obter alto desempenho na geração de pacotes.

Em outras palavras, para uma empresa envolvida na disponibilidade e neutralização contínuas de DDoS, simular vários ataques de DoS em um ambiente isolado de laboratório é uma maneira de entender como os vários equipamentos que fazem parte dos sistemas de hardware da empresa se comportarão na realidade.

MoonGen é uma boa maneira de gerar valores de tráfego próximos ao limite para o controlador de rede em um mínimo de núcleos de CPU. A transferência de dados no espaço do usuário melhora significativamente o desempenho da pilha em questão (MoonGen + DPDK), em comparação com muitas outras opções para gerar altos valores de tráfego. O uso do DPDK puro exige muito mais esforço; portanto, você não deve se surpreender com o nosso desejo de otimizar o desempenho. Também suportamos um clone [ 7 ] do repositório MoonGen original para expandir a funcionalidade e implementação de nossos próprios testes.

Para obter a máxima flexibilidade, a lógica para gerar pacotes é definida pelo usuário usando o script Lua, que é um dos principais recursos do MoonGen. No caso de processamento de pacotes relativamente simples, esta solução funciona rápido o suficiente para saturar a interface 10G em um único núcleo da CPU. Uma maneira típica de modificar pacotes recebidos e criar novos é trabalhar com pacotes do mesmo tipo, nos quais apenas alguns dos campos são alterados.

Um exemplo é o teste l3-tcp-syn-ack-flood, descrito abaixo. Observe que qualquer modificação do pacote pode ser feita no mesmo buffer, onde o pacote gerado ou recebido na etapa anterior acabou sendo. De fato, essas conversões de pacotes são executadas muito rapidamente, uma vez que não envolvem operações caras, como chamadas do sistema, acesso a seções de memória potencialmente não armazenadas em cache e similares.

Testes no hardware do Qrator Labs


O Qrator Labs realiza todos os testes em laboratório em vários equipamentos. Nesse caso, usamos os seguintes controladores de interface de rede:

  • Intel 82599ES 10G
  • Mellanox ConnectX-4 40G
  • Mellanox ConnectX-5 100G

Observamos separadamente que, ao trabalhar com controladores de rede que operam em padrões acima de 10G, o problema de desempenho está se tornando mais agudo. Hoje não é possível saturar a interface 40G com um núcleo, embora com um pequeno número de núcleos isso já seja realista.

No caso de controladores de rede fabricados pela Mellanox, é possível alterar alguns parâmetros e configurações do dispositivo usando o guia de ajuste [ 3 ] fornecido pelo fabricante. Isso permite aumentar o desempenho e, em alguns casos especiais, aprofundar o comportamento da NIC. Outros fabricantes podem ter documentos semelhantes para seus próprios dispositivos de alto desempenho destinados ao uso profissional. Mesmo que você não consiga encontrar esse documento em domínio público, sempre faz sentido entrar em contato diretamente com o fabricante. No nosso caso, os representantes da Mellanox foram muito gentis e, além de fornecer documentação, responderam rapidamente às nossas perguntas, pelas quais conseguimos obter 100% de utilização da tira, o que foi muito importante para nós.

Teste de inundação TCP SYN


O L3-tcp-syn-ack-flood é um exemplo de simulação de um ataque como o SYN flood [ 6 ]. Esta é uma versão estendida do Qrator Labs do teste l3-tcp-syn-flood do repositório principal MoonGen, que é armazenado em nosso clone do repositório.

Nosso teste pode executar três tipos de processos:

  1. Gere o fluxo de pacotes TCP SYN do zero, variando os campos obrigatórios, como endereço IP de origem, número da porta de origem, etc.
  2. Crie uma resposta ACK válida para cada pacote SYN recebido de acordo com o TCP;
  3. Crie uma resposta SYN-ACK válida para cada pacote ACK recebido, de acordo com o protocolo TCP.

Por exemplo, o loop de código interno (respectivamente, o "mais quente") para criar respostas ACK é o seguinte:

local tx = 0 local rx = rxQ:recv(rxBufs) for i = 1, rx do local buf = rxBufs[i] local pkt = buf:getTcpPacket(ipv4) if pkt.ip4:getProtocol() == ip4.PROTO_TCP and pkt.tcp:getSyn() and (pkt.tcp:getAck() or synack) then local seq = pkt.tcp:getSeqNumber() local ack = pkt.tcp:getAckNumber() pkt.tcp:unsetSyn() pkt.tcp:setAckNumber(seq+1) pkt.tcp:setSeqNumber(ack) local tmp = pkt.ip4.src:get() pkt.ip4.src:set(pkt.ip4.dst:get()) pkt.ip4.dst:set(tmp) … -- some more manipulations with packet fields tx = tx + 1 txBufs[tx] = buf end end if tx > 0 then txBufs:resize(tx) txBufs:offloadTcpChecksums(ipv4) -- offload checksums to NIC txQ:send(txBufs) end 

A idéia geral de criar um pacote de resposta é a seguinte. Primeiro, você precisa remover o pacote da fila RX e verificar se o tipo de pacote corresponde ao esperado. Em caso de coincidência, prepare uma resposta modificando alguns campos da embalagem original. Por fim, coloque o pacote criado na fila TX usando o mesmo buffer. Para melhorar o desempenho, em vez de pegar pacotes um por um e modificá-los um por um, agregamos-os, extraindo todos os pacotes disponíveis da fila RX, criamos as respostas correspondentes e colocamos todos na fila TX. Apesar de um número bastante grande de manipulações em um pacote, o desempenho permanece alto, principalmente devido ao fato de o Lua JIT compilar todas essas operações em um pequeno número de instruções do processador. Muitos outros testes, não apenas o TCP SYN / ACK, funcionam com o mesmo princípio.

A tabela abaixo mostra os resultados do teste de inundação SYN (geração SYN sem tentativas de resposta) usando o Mellanox ConnectX-4. Essa placa de rede possui duas portas 40G com um limite teórico de desempenho de 59,52 Mpps em uma porta e 2 * 50 Mpps para duas portas. A implementação específica de conectar a NIC ao PCIe limita um pouco a largura de banda (fornecendo 2 * 50 em vez dos 2 * 59.52 esperados).
núcleos por porta1 porta, Mpps2 portas, Mpps por cada porta
12019
238.36.
356,547
459,550.

Teste de inundação SYN; NIC: Família Mellanox Technologies MT27700 (ConnectX-4), porta dupla de 40G; CPU: CPU Intel® Xeon® Silver 4114 a 2,20GHz

A tabela a seguir mostra os resultados do mesmo teste de inundação SYN realizado em um Mellanox ConnectX-5 com uma porta 100G.
núcleosMpps
135
269
3104
4127
5120
6131
7132
8144

Teste de inundação SYN; NIC: Família Mellanox Technologies MT27800 (ConnectX-5), porta única de 100G; CPU: CPU Intel® Xeon® Silver 4114 a 2,20GHz

Observe que em todos os casos atingimos mais de 96% do limite teórico de desempenho em um pequeno número de núcleos de processador.

Capture o tráfego recebido e salve em arquivos PCAP


Outro exemplo do teste é o rx-to -capcap, que tenta capturar todo o tráfego recebido e salvar em um determinado número de arquivos PCAP [ 8 ]. Embora esse teste não se refira especificamente à geração de pacotes, ele serve como demonstração do fato de que o elo mais fraco da organização da transferência de dados pelo espaço do usuário é o sistema de arquivos. Até o sistema de arquivos virtual tmpfs diminui significativamente o fluxo. Nesse caso, são necessários 8 núcleos do processador central para a utilização de 14,88 Mpps, enquanto apenas um núcleo é suficiente para receber (e redefinir ou redirecionar) a mesma quantidade de tráfego.

A tabela a seguir mostra a quantidade de tráfego (em Mpps) recebido e salvo nos arquivos PCAP localizados no sistema de arquivos ext2 no SSD (segunda coluna) ou no sistema de arquivos tmpfs (terceira coluna).
núcleosem SSD, Mppsem tmpfs, Mpps
11,481,62
244.6
36,948.1
49,7511,65
512,113,8
613,3814,47
714,414,86
814,8814,88

Teste de Rx para pcap; NIC: Intel 82599ES 10 Gigabit; CPU: CPU Intel® Xeon® E5-2683 v4 a 2.10GHz

Modificação MoonGen: Gerenciador de tarefas tman


Também gostaríamos de apresentar ao leitor nossa própria extensão da funcionalidade MoonGen, que fornece outra maneira de iniciar um grupo de tarefas para teste. A idéia principal aqui é separar a configuração geral e as configurações específicas de cada tarefa, permitindo executar um número arbitrário de tarefas diferentes (por exemplo, scripts Lua) ao mesmo tempo. No nosso clone do repositório MoonGen, é apresentada a implementação do MoonGen com o gerenciador de tarefas [ 9 ], aqui apenas listaremos brevemente suas principais funções.

A nova interface da linha de comandos permite executar várias tarefas de diferentes tipos simultaneamente. O cenário básico é o seguinte:

 ./build/tman [tman options...] [-- <task1-file> [task1 options...]] [-- <task2-file> [task2 options...]] [-- ...] 

Além disso, ./build/tman -h fornece ajuda detalhada.

No entanto, há uma limitação - os arquivos regulares de tarefas Lua não são compatíveis com a interface tman . O arquivo de trabalho tman deve definir claramente os seguintes objetos:

  • A função configure (analisador) que descreve os parâmetros do trabalho;
  • A função de tarefa (taskNum, txInfo, rxInfo, args), que descreve o processo real da tarefa. Aqui txInfo e rxInfo são matrizes de filas RX e TX, respectivamente; args contém os parâmetros do gerenciador de tarefas e da própria tarefa.
  • Exemplos podem ser encontrados em examples / tman.

O uso do gerenciador de tarefas oferece mais flexibilidade na execução de testes heterogêneos.

Conclusões


O método oferecido pela MoonGen mostrou-se bem adequado aos nossos objetivos e satisfez os funcionários com os resultados obtidos. Temos uma ferramenta com alto desempenho, mantendo o ambiente de teste e o idioma bastante simples. O alto desempenho dessa configuração é alcançado graças a dois recursos principais: acesso direto aos buffers do controlador de interface de rede e técnica de compilação Just-In-Time em Lua.

Como regra, atingir um teto teórico para o desempenho de um controlador de interface de rede é uma tarefa viável. Como mostramos, um único núcleo pode ser suficiente para saturar uma porta 10G, enquanto uma carga completa de uma porta 100G não é um problema com um número maior de núcleos.

Somos especialmente gratos à equipe da Mellanox por sua ajuda com seus equipamentos e à equipe MoonGen por sua reação à correção dos erros.

Materiais


  1. MoonGen: um gerador de pacotes de alta velocidade com script - Paul Emmerich et al., Internet Measurement Conference 2015 (IMC'15), 2015
  2. Pktgen
  3. Guia de ajuste da Mellanox
  4. Kit de desenvolvimento de plano de dados
  5. Lua
  6. Syn flood
  7. Clone do repositório MoonGen do Qrator Labs
  8. Formato de arquivo PCAP
  9. Gerenciador de tarefas
  10. Desempenho Lua
  11. White paper sobre virtualização de funções de rede
  12. NUMA, acesso não uniforme à memória

Source: https://habr.com/ru/post/pt423957/


All Articles