Da Ideia para a Rede: Implementando a Arquitetura Dual-GAM
No post anterior, mostrei o experimento inicial da minha IC: dois modelos de IA em competição, onde uma GAN Atacante aprende a gerar tráfego DDoS capaz de evadir detecção, e uma GAN Defensora aprende continuamente a identificar esses ataques. O experimento rodou em notebook, com dados estáticos, e validou o conceito central.
Esse post é sobre o próximo passo: tirar isso do notebook e construir a arquitetura real — com rede Docker, tráfego de pacotes de verdade, e os dois modelos se comunicando em tempo real.
Recapitulando rapidamente
A questão central da IC não é "detectar DDoS melhor" — isso já existe e funciona bem. A questão é: como criar uma defesa que se adapte a ataques que ela nunca viu antes?
No experimento inicial, o resultado mais importante foi mostrar que um defensor com 99.91% de acurácia em dados estáticos é vulnerável quando o atacante evolui: na primeira rodada do ciclo adversarial, 75% dos ataques perturbados passaram sem ser detectados. Após 20 rodadas de co-evolução, o defensor chegou a ~98.3% — pagando um trade-off honesto entre acurácia e robustez.
O próximo problema era óbvio: no experimento inicial, o "ataque" era um vetor de números em memória. Na vida real, um ataque DDoS são pacotes de rede chegando num servidor. Precisávamos fechar esse gap.
A arquitetura
O diagrama que guiou a implementação tem três camadas:

Camada de rede (Docker): hosts legítimos (h1-h4) e uma fonte de ataque (h-attack) conectados a um switch (s1) que encaminha tráfego para um servidor alvo (h-target).
Plano de controle SDN: um controlador que monitora o tráfego, extrai features e aplica regras de bloqueio. Essa camada ainda está em desenvolvimento — por enquanto o switch é simulado pela bridge do Docker.
Camada ML — Dual GAN: os dois modelos do experimento inicial, agora integrados ao pipeline de rede.
Essa separação em camadas não é só organização — ela reflete como sistemas de defesa reais funcionam. Em redes SDN, o plano de dados (onde o tráfego passa) é separado do plano de controle (onde as decisões são tomadas). Colocar a inteligência adversarial no plano de controle significa que ela pode observar o tráfego de toda a rede e responder em tempo real, sem depender de um único ponto de inspeção. É uma arquitetura que escala e que tem paralelo direto com sistemas de detecção de intrusão em produção.
O fluxo completo funciona assim:
GAN Atacante → perturba amostras reais de DDoS
↓
GAN Defensora → avalia probabilidade de detecção
↓
Translator → converte vetor de features → parâmetros de pacote
↓
Scapy → envia pacotes TCP/UDP reais na rede Docker
↓
Controller → registra taxa de evasão → próximo ciclo
Durante a implementação percebemos que existia um componente que o experimento inicial nunca precisou resolver: a tradução entre o espaço matemático aprendido pela GAN e o mundo real dos pacotes de rede. O Translator nasceu para resolver exatamente esse problema.
O Translator: o problema mais interessante
O CIC-IDS2017, dataset que usamos, descreve fluxos de rede — estatísticas agregadas como taxa de pacotes por segundo, tamanho médio de pacote, contagem de flags TCP. Não descreve pacotes individuais.
A GAN Atacante aprende a perturbar vetores nesse espaço de 77 features. Mas o Scapy precisa de parâmetros concretos: quantos pacotes por segundo enviar, qual o tamanho do payload, quais flags TCP usar.
O Translator resolve isso desnormalizando o vetor (revertendo o StandardScaler do pré-processamento) e mapeando as features relevantes para parâmetros de ataque:
flow_packets_per_sec→ taxa de envio (pps)fwd_packet_length_mean+avg_pkt_size→ tamanho do pacotesyn_flag_count,ack_flag_count, etc. → flags TCP e tipo de ataqueflow_duration→ duração do ataqueinit_fwd_win_bytes→ TCP window size
O mapeamento não é 1-para-1 perfeito — as estatísticas de fluxo não determinam unicamente os parâmetros de cada pacote. Mas é suficientemente fiel para que o tráfego gerado tenha as características gerais do que a GAN aprendeu.
Decisões de desenvolvimento
Por que Scapy com perturbação, não geração pura?
No experimento inicial testamos três abordagens para o atacante. As duas primeiras falharam:
Geração pura (ruído → vetor): o gerador aprendeu a produzir vetores fora de qualquer distribuição real — 100% de evasão técnica, mas 0% de realismo. O defensor classificava como benigno justamente porque não se parecia com nada que ele conhecia.
Geração com loss de realismo: melhorou a distribuição estatística, mas o problema de fundo persistiu.
Perturbação adversarial: pega amostras reais de DDoS e aprende perturbações pequenas que enganam o defensor. Essa foi a única que funcionou — e é o que implementamos aqui. O Scapy executa exatamente o que a perturbação indica: tráfego que ainda se parece com DDoS, mas sutilmente modificado para escapar da detecção.
O problema do checkpoint final
Depois de treinar os modelos e rodar o primeiro ataque real, encontramos um problema inesperado: 0% de evasão em todos os ciclos.
O motivo foi sutil. O treino salva o estado de ambos os modelos ao final de cada rodada. Quando o Controller carregava atacante_final.pth e defensor_adaptativo_final.pth, estava usando o par da rodada 20 — onde o defensor já tinha respondido ao atacante daquela rodada. Naturalmente, a evasão era zero.
A solução foi fazer o Controller escolher automaticamente o melhor par de checkpoints: o atacante da rodada com maior taxa de evasão na segunda metade do treino, combinado com o defensor da rodada anterior (que ainda não tinha visto aquele atacante específico).
melhor_rodada = rodada com maior evasão nas rodadas 10-20
atacante ← checkpoint da melhor_rodada
defensor ← checkpoint da melhor_rodada - 1
Essa lógica captura exatamente o momento em que o atacante está à frente — antes do defensor reagir.
Resultados
Aviso: infelizmente vou dever screenshots do terminal no momento das execuções por dois motivos:
- O logging atual não favorece documentação - O terminal exibe cada ataque conforme ele acontece, mas não apresenta um resumo consolidado ao final da execução. Isso é ótimo para depuração, mas ruim para documentação. Na prática, era mais simples salvar toda a saída do terminal do que produzir diversos screenshots. Segue um exemplo:
(print referente a segunda execução, o que leva diretamente ao segundo problema)
- Divergência - Durante cada ciclo, o Controller seleciona aleatoriamente amostras reais de DDoS e o Atacante gera novas perturbações sobre elas. Como consequência, cada execução produz conjuntos de ataques ligeiramente diferentes. Por isso, não faria sentido simplesmente executar novamente o experimento apenas para produzir screenshots. A interpretação permanece a mesma, mas os valores variam entre execuções.
Por causa desses motivos, usei no texto um resumo das tabelas que utilizei para análise.
Prometo que na próxima implementação isso não acontece!!
Treino
| Rodada | Taxa de Evasão | Acc Defensor |
|---|---|---|
| 1 | ~74% | 98.2% |
| 5 | ~21% | 98.3% |
| 10 | ~0% | 98.3% |
| 19 | ~24% | 99.3% |
| 20 | ~1% | 99.2% |
O comportamento replicou o experimento inicial, e algumas coisas merecem atenção.
A queda de 74% para ~0% nas primeiras rodadas não significa que o defensor "resolveu o problema" — significa que ele aprendeu a reconhecer aquele atacante específico daquele momento. Nas rodadas seguintes o atacante recomeça do zero, encontra novas perturbações que funcionam, e o ciclo se repete. É por isso que rodadas como a 19 mostram picos de 24% de evasão mesmo depois de o defensor ter suprimido o atacante várias vezes antes.
Esse padrão de oscilação é esperado em sistemas adversariais e tem um nome na literatura: o problema do equilíbrio trivial entre atacante e defensor. Nenhum dos dois converge para uma solução estável de longo prazo — eles continuam se adaptando um ao outro. Para fins de pesquisa, isso é o resultado desejado: mostra que o ciclo de co-evolução está funcionando de verdade.
O defensor adaptativo ficou em ~98.3% de acurácia ao longo do ciclo, contra os 99.91% do baseline estático. Esse trade-off já havia aparecido no experimento inicial e vale repetir: robustez adversarial tem custo. Um defensor que nunca viu ataques perturbados vai ter acurácia maior em dados limpos — mas vai ser mais vulnerável quando o atacante evoluir.
Ataque real (10 ciclos, Scapy na bridge Docker)
| Ciclo | Vetores gerados | Evadiram | Taxa |
|---|---|---|---|
| 1 | 100 | 14 | 14% |
| 2 | 100 | 28 | 28% |
| 5 | 100 | 23 | 23% |
| 10 | 100 | 33 | 33% |
A taxa de evasão no pipeline real (14-33%) é menor do que nos ciclos de treino (até 74% na rodada 1), o que faz sentido: o Controller usa o par atacante/defensor da rodada 19, quando o defensor já tinha sido exposto a várias rodadas de perturbações. Não é o defensor mais fraco — é um defensor com experiência, e mesmo assim ~25% dos vetores gerados conseguem evadir.
Os parâmetros de ataque gerados pelo Translator foram coerentes com o perfil DDoS do CIC-IDS2017: ~24k pps, pacotes de 80-278 bytes, flags TCP variando entre SA, SAP, AP e A. A variação nas flags é particularmente interessante — o atacante não converge para um único padrão, continua explorando combinações diferentes a cada ciclo.
Uma nota sobre o volume de pacotes: a taxa real de envio do Scapy (~10-15 pacotes por 0.5s) é bem menor do que o pps pedido pelos AttackParams (~24k). Isso é uma limitação de raw sockets em container Docker — o kernel limita a taxa de envio. Para os fins desse experimento, o que importa é que os pacotes chegam ao h-target com as características certas.
Métricas do servidor alvo
Durante o ataque, é possível monitorar o impacto em tempo real via endpoint /metrics do h-target:
watch -n1 'curl -s http://localhost:8080/metrics | python3 -m json.tool'
Algumas limitações da implementação atual
Vale ser explícito sobre o que essa implementação ainda não faz, para não vender mais do que entrega.
O feedback ainda é offline. O retreino do Defensor usa amostras do dataset CIC-IDS2017, não tráfego capturado da rede em tempo real. Isso significa que o ciclo co-evolutivo acontece no espaço de features do dataset, não no espaço de pacotes reais. A ponte entre os dois existe (via Translator + Scapy), mas ainda não fecha o loop: o que o Defensor aprende não é alimentado de volta pelo que o Sender envia.
O SDN ainda não fecha o ciclo. A camada de controle SDN (Traffic Monitor → Extractor → Rule Enforcer) está no diagrama mas ainda não foi implementada. Por enquanto o switch s1 é a bridge padrão do Docker, sem inspeção de tráfego nem aplicação de regras. Fechar esse ciclo é o próximo passo concreto da IC.
Volume de tráfego limitado em container. Como mencionado nos resultados, raw sockets em Docker têm limitações de taxa de envio. O ambiente atual é suficiente para validar o pipeline, mas não para simular ataques volumétricos de escala real.
Hosts legítimos simplificados. Os h1-h4 são containers que fazem curl em loop — tráfego benigno mas pouco realista. Num ambiente mais completo, o tráfego legítimo deveria ter mais variedade para testar se o Defensor consegue distinguir DDoS de tráfego legítimo em condições mais difíceis.
Melhorias possíveis
Algumas direções que valem ser pensadas (organizadas por área):
Engenharia
- Arquivo de configuração centralizado (
config.yaml) para hiperparâmetros, endereços de rede, número de rodadas, epsilon de perturbação. Hoje esses valores estão espalhados pelo código. - Componente AttackEngine. O Controller atualmente concentra responsabilidades demais (carregamento de modelos, geração, avaliação, tradução e execução dos ataques). Um
AttackEnginepermitiria separar a lógica de execução da lógica de orquestração. - Interfaces abstratas para os componentes principais, desacoplando as implementações concretas do Controller.
- Testes automatizados — especialmente para o Translator, que tem lógica de mapeamento que pode silenciosamente produzir parâmetros inválidos sem testes.
- Persistência do histórico de ataques. O Controller já registra métricas de cada ciclo em memória, mas o arquivo
historico_ataques.jsonsó é salvo quandoexecutar_loop()é utilizado. Em execuções que chamam apenasexecutar_ciclo(), o histórico é perdido ao final do processo. - Resumo consolidado ao final de cada ciclo. Atualmente o logging registra cada ataque individualmente, mas não apresenta um resumo geral da execução. Gerar automaticamente uma tabela consolidada ao final de cada ciclo facilitaria tanto a análise quanto a documentação dos experimentos.
Pesquisa
- Feedback online: retreinar o Defensor com features extraídas do tráfego real capturado na rede, não com dados do dataset.
- Métricas de realismo mais sofisticadas para avaliar se as amostras perturbadas são plausíveis como tráfego de rede real.
- Outros tipos de ataque além de DDoS — o framework está estruturado para suportar isso, mas ainda não foi testado.
Infraestrutura
- Implementar o plano de controle SDN completo (Ryu + Open vSwitch).
- Tráfego legítimo mais realista nos hosts h1-h4.
Revisão do projeto
Olhando para o plano de trabalho original da IC, ele descreve a pesquisa como dois modelos de IA em competição — GAN A vs GAN B. Essa descrição ainda é válida, mas ficou pequena para o que o projeto se tornou.
As GANs continuam sendo o núcleo da inteligência adaptativa, mas são um componente dentro de algo maior. O que construímos até agora é mais precisamente descrito como:
Um ambiente experimental para pesquisa em defesa adaptativa baseada em aprendizado adversarial.
A diferença importa. Um ambiente experimental tem topologia de rede, geração de tráfego real, pipeline de pré-processamento, sistema de checkpoints, métricas de avaliação, e uma separação clara entre camada de dados e camada de controle. As GANs deixaram de ser o projeto — agora são o que torna o ambiente inteligente.
Essa mudança de perspectiva abre alguns caminhos que não eram óbvios no plano original. O ambiente pode ser usado para testar outros mecanismos de defesa além de GANs — qualquer componente que implemente a interface do Defensor pode ser plugado. O ciclo adversarial pode ser estudado em condições variadas de rede, não só com o dataset CIC-IDS2017. E a arquitetura em camadas (rede → SDN → ML) tem correspondência direta com sistemas reais, o que torna os resultados mais transferíveis.
O foco da IC continua sendo a questão original: como criar uma defesa que se adapte a ataques que ela nunca viu antes. A diferença é que agora temos uma plataforma para investigar isso de formas que um notebook nunca permitiria.
Código
O repositório com o código completo, instruções de uso e resultados está em: LINK_REPOSITÓRIO
obs: todo esse post foi feito em cima da primeira versão da arquitetura (primeiro commit)
A estrutura do projeto, decisões de arquitetura e como rodar estão documentadas no README.