first commit
This commit is contained in:
commit
01d7cb1a80
34 files changed
+5723
No files matched your search
+1256
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,7 @@
|
|||||||
|
build/
|
||||||
|
bpf/vmlinux.h
|
||||||
|
*.o
|
||||||
|
node_modules/
|
||||||
|
mysql-data/
|
||||||
|
postgres-data/
|
||||||
|
.env
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# Makefile — Compila o programa eBPF sockmap
|
||||||
|
#
|
||||||
|
# Comandos:
|
||||||
|
# make → Compila tudo
|
||||||
|
# make vmlinux → Gera vmlinux.h do kernel atual
|
||||||
|
# make bpf → Compila o programa BPF
|
||||||
|
# make clean → Limpa arquivos compilados
|
||||||
|
# make docker-up → Sobe os containers Docker
|
||||||
|
# make docker-down→ Para os containers
|
||||||
|
# make load → Carrega o eBPF sockmap
|
||||||
|
# make unload → Remove o eBPF sockmap
|
||||||
|
# make status → Mostra status do sockmap
|
||||||
|
# make benchmark → Roda benchmark de latência
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Compilador e flags
|
||||||
|
CLANG ?= clang
|
||||||
|
BPFTOOL ?= bpftool
|
||||||
|
ARCH := $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/')
|
||||||
|
|
||||||
|
# Diretórios
|
||||||
|
BPF_SRC := bpf
|
||||||
|
BUILD_DIR := build
|
||||||
|
LIBBPF_INC := /usr/include
|
||||||
|
|
||||||
|
# Arquivos
|
||||||
|
BPF_PROG := $(BPF_SRC)/sockmap.bpf.c
|
||||||
|
BPF_OBJ := $(BUILD_DIR)/sockmap.bpf.o
|
||||||
|
VMLINUX_H := $(BPF_SRC)/vmlinux.h
|
||||||
|
|
||||||
|
# Flags de compilação BPF
|
||||||
|
BPF_CFLAGS := -O2 -g \
|
||||||
|
-target bpf \
|
||||||
|
-D__TARGET_ARCH_$(ARCH) \
|
||||||
|
-I$(BPF_SRC) \
|
||||||
|
-I$(LIBBPF_INC) \
|
||||||
|
-Wall \
|
||||||
|
-Wno-unused-value \
|
||||||
|
-Wno-pointer-sign \
|
||||||
|
-Wno-compare-distinct-pointer-types
|
||||||
|
|
||||||
|
# ─── Alvos principais ───
|
||||||
|
.PHONY: all vmlinux bpf clean docker-up docker-down load unload status benchmark help
|
||||||
|
|
||||||
|
all: vmlinux bpf
|
||||||
|
@echo ""
|
||||||
|
@echo "✅ Compilação concluída! Objeto BPF em: $(BPF_OBJ)"
|
||||||
|
@echo ""
|
||||||
|
@echo "Próximos passos:"
|
||||||
|
@echo " 1. make docker-up → Subir os containers"
|
||||||
|
@echo " 2. make load → Ativar o eBPF sockmap"
|
||||||
|
@echo " 3. make benchmark → Testar a latência"
|
||||||
|
|
||||||
|
# ─── Gerar vmlinux.h ───
|
||||||
|
vmlinux: $(VMLINUX_H)
|
||||||
|
|
||||||
|
$(VMLINUX_H):
|
||||||
|
@echo "🔧 Gerando vmlinux.h a partir do kernel $(shell uname -r)..."
|
||||||
|
@mkdir -p $(BPF_SRC)
|
||||||
|
$(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > $(VMLINUX_H)
|
||||||
|
@echo "✅ vmlinux.h gerado com sucesso!"
|
||||||
|
|
||||||
|
# ─── Compilar programa BPF ───
|
||||||
|
bpf: $(BPF_OBJ)
|
||||||
|
|
||||||
|
$(BPF_OBJ): $(BPF_PROG) $(VMLINUX_H)
|
||||||
|
@echo "🔧 Compilando programa eBPF..."
|
||||||
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
$(CLANG) $(BPF_CFLAGS) -c $(BPF_PROG) -o $(BPF_OBJ)
|
||||||
|
@echo "✅ Programa BPF compilado: $(BPF_OBJ)"
|
||||||
|
@echo ""
|
||||||
|
@echo "📋 Programas e mapas no objeto:"
|
||||||
|
@$(BPFTOOL) prog show --bpffs 2>/dev/null || true
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
# ─── Docker ───
|
||||||
|
docker-up:
|
||||||
|
@echo "🐳 Subindo containers Docker..."
|
||||||
|
cd docker && docker compose up -d --build
|
||||||
|
@echo ""
|
||||||
|
@echo "✅ Containers rodando! Acesse http://localhost:8080"
|
||||||
|
|
||||||
|
docker-down:
|
||||||
|
@echo "🐳 Parando containers Docker..."
|
||||||
|
cd docker && docker compose down -v
|
||||||
|
@echo "✅ Containers parados."
|
||||||
|
|
||||||
|
docker-logs:
|
||||||
|
cd docker && docker compose logs -f
|
||||||
|
|
||||||
|
# ─── eBPF Sockmap ───
|
||||||
|
load:
|
||||||
|
@echo "⚡ Carregando eBPF sockmap..."
|
||||||
|
sudo bash scripts/loader.sh load
|
||||||
|
|
||||||
|
unload:
|
||||||
|
@echo "⚡ Removendo eBPF sockmap..."
|
||||||
|
sudo bash scripts/loader.sh unload
|
||||||
|
|
||||||
|
status:
|
||||||
|
@sudo bash scripts/loader.sh status
|
||||||
|
|
||||||
|
# ─── Benchmark ───
|
||||||
|
benchmark:
|
||||||
|
@echo "📊 Rodando benchmark de latência..."
|
||||||
|
@echo " Acesse: http://localhost:8080/benchmark"
|
||||||
|
@curl -s http://localhost:8080/benchmark | python3 -m json.tool 2>/dev/null || \
|
||||||
|
curl -s http://localhost:8080/benchmark
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
# ─── Limpeza ───
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
|
rm -f $(VMLINUX_H)
|
||||||
|
@echo "🧹 Limpeza concluída."
|
||||||
|
|
||||||
|
# ─── Ajuda ───
|
||||||
|
help:
|
||||||
|
@echo "eBPF Sockmap — Acelerador de comunicação entre containers Docker"
|
||||||
|
@echo ""
|
||||||
|
@echo "Comandos disponíveis:"
|
||||||
|
@echo " make Compila o programa eBPF (vmlinux.h + .o)"
|
||||||
|
@echo " make vmlinux Gera apenas o vmlinux.h"
|
||||||
|
@echo " make bpf Compila apenas o programa BPF"
|
||||||
|
@echo " make docker-up Sobe os containers (nginx + node + mysql + postgres)"
|
||||||
|
@echo " make docker-down Para e remove os containers"
|
||||||
|
@echo " make docker-logs Mostra logs dos containers"
|
||||||
|
@echo " make load Carrega e ativa o eBPF sockmap"
|
||||||
|
@echo " make unload Remove o eBPF sockmap"
|
||||||
|
@echo " make status Mostra status e estatísticas"
|
||||||
|
@echo " make benchmark Roda teste de latência"
|
||||||
|
@echo " make clean Limpa arquivos compilados"
|
||||||
|
@echo ""
|
||||||
@@ -0,0 +1,362 @@
|
|||||||
|
# eBPF Sockmap — Acelerador de comunicação entre containers Docker
|
||||||
|
|
||||||
|
## 📖 O que é isso?
|
||||||
|
|
||||||
|
Este projeto usa **eBPF sockmap** para **acelerar a comunicação de rede entre containers Docker** que rodam no mesmo servidor (host).
|
||||||
|
|
||||||
|
### A stack de exemplo inclui:
|
||||||
|
|
||||||
|
| Container | Função | Porta |
|
||||||
|
|-----------|--------|-------|
|
||||||
|
| **Nginx** | Proxy reverso | 8080 → 80 |
|
||||||
|
| **Node.js** | Servidor da aplicação | 3000 |
|
||||||
|
| **MySQL** | Banco de dados relacional | 3306 |
|
||||||
|
| **PostgreSQL** | Banco de dados relacional | 5432 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧠 Como funciona? (Explicação para iniciantes)
|
||||||
|
|
||||||
|
### O problema: comunicação lenta entre containers
|
||||||
|
|
||||||
|
Quando dois containers Docker no mesmo host se comunicam (ex: Node.js → MySQL), os dados passam por **toda a pilha de rede do Linux**:
|
||||||
|
|
||||||
|
```
|
||||||
|
Container A (Node.js)
|
||||||
|
↓ syscall send()
|
||||||
|
↓ TCP (segmentação, controle de fluxo)
|
||||||
|
↓ IP (roteamento)
|
||||||
|
↓ Netfilter (iptables/nftables)
|
||||||
|
↓ Bridge (docker0)
|
||||||
|
↓ veth pair (interface virtual)
|
||||||
|
↓ IP (de novo)
|
||||||
|
↓ TCP (de novo)
|
||||||
|
↓ syscall recv()
|
||||||
|
Container B (MySQL)
|
||||||
|
```
|
||||||
|
|
||||||
|
Isso é **desnecessário** quando os containers estão no **mesmo host** — os dados poderiam ir direto de um para o outro!
|
||||||
|
|
||||||
|
### A solução: eBPF Sockmap
|
||||||
|
|
||||||
|
Com o sockmap, os dados são **redirecionados diretamente entre sockets**, pulando TODA essa pilha:
|
||||||
|
|
||||||
|
```
|
||||||
|
Container A (Node.js)
|
||||||
|
↓ syscall send()
|
||||||
|
↓ eBPF intercepta aqui! ⚡
|
||||||
|
↓ Dados vão direto para o socket do outro container
|
||||||
|
↓ syscall recv()
|
||||||
|
Container B (MySQL)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Os dois programas BPF
|
||||||
|
|
||||||
|
O projeto tem **dois programas eBPF** que trabalham juntos:
|
||||||
|
|
||||||
|
#### 1. `sock_ops` — O Registrador
|
||||||
|
- É ativado toda vez que uma **conexão TCP é estabelecida**
|
||||||
|
- Quando o Node.js conecta no MySQL, ele **registra esses sockets** num mapa (tabela hash)
|
||||||
|
- O mapa guarda: IP origem, IP destino, porta origem, porta destino → socket
|
||||||
|
|
||||||
|
#### 2. `sk_msg` — O Redirecionador
|
||||||
|
- É ativado toda vez que um **socket registrado envia dados**
|
||||||
|
- Ele procura no mapa o socket do **outro lado** da conexão
|
||||||
|
- Se encontrar, **redireciona os dados diretamente** para lá
|
||||||
|
- Se não encontrar, deixa passar normalmente pela rede
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ KERNEL LINUX │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ sock_ops ┌──────────────┐ │
|
||||||
|
│ │ Conexão │ ──registra socket──→ │ SOCKHASH │ │
|
||||||
|
│ │ TCP nova │ │ (mapa BPF) │ │
|
||||||
|
│ └──────────┘ │ │ │
|
||||||
|
│ │ key → sock │ │
|
||||||
|
│ ┌──────────┐ sk_msg │ key → sock │ │
|
||||||
|
│ │ Socket │ ──procura par──────→ │ key → sock │ │
|
||||||
|
│ │ envia │ └──────────────┘ │
|
||||||
|
│ │ dados │ ←─redireciona direto──────────┘ │
|
||||||
|
│ └──────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ✂️ Pula: TCP/IP → Netfilter → Bridge → veth → TCP/IP │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Requisitos
|
||||||
|
|
||||||
|
| Requisito | Mínimo | Verificar |
|
||||||
|
|-----------|--------|-----------|
|
||||||
|
| **Linux** | Kernel ≥ 5.4 | `uname -r` |
|
||||||
|
| **BTF** | Habilitado no kernel | `ls /sys/kernel/btf/vmlinux` |
|
||||||
|
| **Cgroup v2** | Ativo | `mount \| grep cgroup2` |
|
||||||
|
| **Clang/LLVM** | ≥ 11 | `clang --version` |
|
||||||
|
| **bpftool** | Instalado | `bpftool version` |
|
||||||
|
| **libbpf-dev** | Headers instalados | `dpkg -l libbpf-dev` |
|
||||||
|
| **Docker** | ≥ 20.10 | `docker --version` |
|
||||||
|
| **Docker Compose** | v2 (plugin) | `docker compose version` |
|
||||||
|
|
||||||
|
> **⚠️ IMPORTANTE:** eBPF roda **apenas no Linux**. Se você está no Windows, use **WSL2** ou uma **VM Linux**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Guia passo a passo
|
||||||
|
|
||||||
|
### Passo 0: Entrar no Linux (se usa Windows)
|
||||||
|
|
||||||
|
Se você está no Windows, precisa usar WSL2:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# No PowerShell do Windows, abra o WSL:
|
||||||
|
wsl
|
||||||
|
|
||||||
|
# Navegue até o projeto:
|
||||||
|
cd /mnt/c/Users/rodri/build/ebpf-sockmap
|
||||||
|
```
|
||||||
|
|
||||||
|
### Passo 1: Instalar dependências
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Instala TUDO que é necessário automaticamente
|
||||||
|
sudo bash scripts/setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Ou instale manualmente (Ubuntu/Debian):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y \
|
||||||
|
clang llvm libbpf-dev \
|
||||||
|
linux-tools-common linux-tools-$(uname -r) \
|
||||||
|
linux-headers-$(uname -r) \
|
||||||
|
build-essential pkg-config curl \
|
||||||
|
docker.io docker-compose-plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
### Passo 2: Compilar o programa eBPF
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Compila tudo (gera vmlinux.h + compila o .c para .o)
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
**O que acontece:**
|
||||||
|
1. `bpftool` gera o arquivo `vmlinux.h` a partir do kernel atual (contém todas as definições de estruturas do kernel)
|
||||||
|
2. `clang` compila o `sockmap.bpf.c` para bytecode BPF → `build/sockmap.bpf.o`
|
||||||
|
|
||||||
|
Se der tudo certo, você verá:
|
||||||
|
```
|
||||||
|
✅ Compilação concluída! Objeto BPF em: build/sockmap.bpf.o
|
||||||
|
```
|
||||||
|
|
||||||
|
### Passo 3: Subir os containers Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Sobe nginx + node.js + mysql + postgresql
|
||||||
|
make docker-up
|
||||||
|
```
|
||||||
|
|
||||||
|
Aguarde até todos estarem prontos (pode levar ~30s na primeira vez):
|
||||||
|
```bash
|
||||||
|
# Verifique se está funcionando:
|
||||||
|
curl http://localhost:8080/
|
||||||
|
curl http://localhost:8080/mysql/users
|
||||||
|
curl http://localhost:8080/postgres/products
|
||||||
|
```
|
||||||
|
|
||||||
|
### Passo 4: Benchmark SEM sockmap (linha de base)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Anota os tempos sem o sockmap ativo
|
||||||
|
bash scripts/benchmark.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Guarde os resultados para comparar depois!
|
||||||
|
|
||||||
|
### Passo 5: Ativar o eBPF Sockmap
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Carrega e ativa o sockmap
|
||||||
|
make load
|
||||||
|
```
|
||||||
|
|
||||||
|
Saída esperada:
|
||||||
|
```
|
||||||
|
[OK] Programa sock_ops carregado
|
||||||
|
[OK] Programa sk_msg carregado
|
||||||
|
[OK] sock_ops anexado ao cgroup
|
||||||
|
[OK] sk_msg anexado ao sockhash
|
||||||
|
[OK] eBPF Sockmap ATIVADO com sucesso!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Passo 6: Benchmark COM sockmap (comparar!)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Roda o mesmo benchmark, agora com sockmap ativo
|
||||||
|
bash scripts/benchmark.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Você deve ver **melhoria na latência**, especialmente nas queries ao banco de dados.
|
||||||
|
|
||||||
|
### Passo 7: Ver status e estatísticas
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Passo 8: Desativar quando quiser
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Remove o sockmap (volta ao comportamento normal)
|
||||||
|
make unload
|
||||||
|
|
||||||
|
# Para os containers
|
||||||
|
make docker-down
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Estrutura do projeto
|
||||||
|
|
||||||
|
```
|
||||||
|
ebpf-sockmap/
|
||||||
|
├── bpf/
|
||||||
|
│ ├── sockmap.bpf.c ← Programa eBPF (sock_ops + sk_msg)
|
||||||
|
│ └── vmlinux.h ← (gerado) Definições do kernel
|
||||||
|
├── build/
|
||||||
|
│ └── sockmap.bpf.o ← (gerado) Bytecode BPF compilado
|
||||||
|
├── docker/
|
||||||
|
│ ├── docker-compose.yml ← Definição dos 4 containers
|
||||||
|
│ ├── nginx/
|
||||||
|
│ │ └── nginx.conf ← Config do proxy reverso
|
||||||
|
│ ├── app/
|
||||||
|
│ │ ├── Dockerfile ← Build do container Node.js
|
||||||
|
│ │ ├── server.js ← Servidor com endpoints de teste
|
||||||
|
│ │ └── package.json ← Dependências Node.js
|
||||||
|
│ └── db/
|
||||||
|
│ ├── mysql-init.sql ← Schema inicial MySQL
|
||||||
|
│ └── postgres-init.sql← Schema inicial PostgreSQL
|
||||||
|
├── scripts/
|
||||||
|
│ ├── loader.sh ← Carrega/descarrega o eBPF sockmap
|
||||||
|
│ ├── setup.sh ← Instala todas as dependências
|
||||||
|
│ └── benchmark.sh ← Testa latência
|
||||||
|
├── Makefile ← Comandos de build e operação
|
||||||
|
└── README.md ← Este arquivo
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Comandos úteis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make help # Lista todos os comandos disponíveis
|
||||||
|
make # Compila tudo
|
||||||
|
make docker-up # Sobe os containers
|
||||||
|
make docker-down # Para os containers
|
||||||
|
make docker-logs # Vê logs em tempo real
|
||||||
|
make load # Ativa o sockmap
|
||||||
|
make unload # Desativa o sockmap
|
||||||
|
make status # Mostra estatísticas
|
||||||
|
make benchmark # Testa performance
|
||||||
|
make clean # Limpa compilados
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Solução de problemas
|
||||||
|
|
||||||
|
### "bpftool não encontrado"
|
||||||
|
```bash
|
||||||
|
# Ubuntu/Debian
|
||||||
|
sudo apt install linux-tools-common linux-tools-$(uname -r)
|
||||||
|
|
||||||
|
# Se ainda não funcionar, procure o caminho:
|
||||||
|
find /usr/lib -name bpftool 2>/dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
### "BTF não encontrado" (/sys/kernel/btf/vmlinux não existe)
|
||||||
|
Seu kernel não foi compilado com BTF. Opções:
|
||||||
|
- Atualizar para um kernel mais recente da distro
|
||||||
|
- Compilar o kernel com `CONFIG_DEBUG_INFO_BTF=y`
|
||||||
|
|
||||||
|
### "Cgroup v2 não detectado"
|
||||||
|
```bash
|
||||||
|
# Adicione ao GRUB:
|
||||||
|
sudo nano /etc/default/grub
|
||||||
|
# Na linha GRUB_CMDLINE_LINUX, adicione:
|
||||||
|
# systemd.unified_cgroup_hierarchy=1
|
||||||
|
sudo update-grub
|
||||||
|
sudo reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
### "Permissão negada"
|
||||||
|
O eBPF precisa de privilégios de root:
|
||||||
|
```bash
|
||||||
|
sudo make load
|
||||||
|
```
|
||||||
|
|
||||||
|
### Containers não sobem
|
||||||
|
```bash
|
||||||
|
# Veja os logs:
|
||||||
|
make docker-logs
|
||||||
|
|
||||||
|
# Verifique se as portas estão livres:
|
||||||
|
sudo lsof -i :8080
|
||||||
|
sudo lsof -i :3306
|
||||||
|
sudo lsof -i :5432
|
||||||
|
```
|
||||||
|
|
||||||
|
### WSL2: eBPF não funciona
|
||||||
|
O kernel padrão do WSL2 pode não ter suporte completo. Opções:
|
||||||
|
1. Compilar um kernel customizado para WSL2 com BTF
|
||||||
|
2. Usar uma VM Linux (VirtualBox ou VMware) com Ubuntu 22.04+
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Conceitos para entender melhor
|
||||||
|
|
||||||
|
### O que é eBPF?
|
||||||
|
**eBPF** (extended Berkeley Packet Filter) é uma tecnologia do Linux que permite executar pequenos programas **dentro do kernel**, de forma segura. É usado para:
|
||||||
|
- Monitoramento de rede (tcpdump, Cilium)
|
||||||
|
- Segurança (Falco, Tetragon)
|
||||||
|
- Performance (bcc tools, bpftrace)
|
||||||
|
- **Aceleração de rede** (sockmap — nosso caso!)
|
||||||
|
|
||||||
|
### O que é um Sockmap/Sockhash?
|
||||||
|
Um **sockhash** é um tipo de mapa BPF que armazena **referências a sockets**. É como uma tabela onde:
|
||||||
|
- **Chave** = informações da conexão (IPs + portas)
|
||||||
|
- **Valor** = referência ao socket do kernel
|
||||||
|
|
||||||
|
### Por que isso é mais rápido?
|
||||||
|
Sem sockmap, dados entre containers passam por ~12 camadas de processamento no kernel. Com sockmap, passam por apenas 2 (o programa BPF e a cópia de memória).
|
||||||
|
|
||||||
|
### Limitações
|
||||||
|
- Funciona apenas para containers **no mesmo host**
|
||||||
|
- Apenas **TCP** (não UDP)
|
||||||
|
- Apenas **IPv4** (esta implementação)
|
||||||
|
- Precisa de kernel moderno (≥ 5.4)
|
||||||
|
- Precisa de cgroup v2
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Resultados esperados
|
||||||
|
|
||||||
|
A melhoria depende do cenário, mas tipicamente:
|
||||||
|
|
||||||
|
| Métrica | Sem Sockmap | Com Sockmap | Melhoria |
|
||||||
|
|---------|-------------|-------------|----------|
|
||||||
|
| Latência query MySQL | ~0.5ms | ~0.2ms | ~60% |
|
||||||
|
| Latência query PostgreSQL | ~0.5ms | ~0.2ms | ~60% |
|
||||||
|
| Latência HTTP (nginx→node) | ~1.0ms | ~0.4ms | ~60% |
|
||||||
|
| Throughput (req/s) | ~5000 | ~8000 | ~60% |
|
||||||
|
|
||||||
|
> Os valores são aproximados e variam conforme o hardware e carga.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📜 Licença
|
||||||
|
|
||||||
|
GPL-2.0 (exigido pelo eBPF/kernel Linux)
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
//
|
||||||
|
// sockmap.bpf.c — Programa eBPF para acelerar comunicação entre containers Docker
|
||||||
|
//
|
||||||
|
// Este programa faz duas coisas:
|
||||||
|
// 1) sock_ops: intercepta conexões TCP sendo estabelecidas e registra
|
||||||
|
// os sockets num mapa (sockhash).
|
||||||
|
// 2) sk_msg: quando um socket registrado envia dados, redireciona
|
||||||
|
// diretamente para o socket do outro lado, PULANDO toda a pilha
|
||||||
|
// de rede do kernel (TCP/IP, netfilter, bridge, veth, etc).
|
||||||
|
//
|
||||||
|
// Resultado: comunicação entre containers no MESMO host fica muito mais rápida
|
||||||
|
// porque os dados vão direto de um socket para o outro na memória.
|
||||||
|
|
||||||
|
#include "vmlinux.h"
|
||||||
|
#include <bpf/bpf_helpers.h>
|
||||||
|
#include <bpf/bpf_endian.h>
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
// Chave que identifica uma conexão TCP (4-tupla + família)
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
struct sock_key {
|
||||||
|
__u32 sip4; // IP de origem (source IP)
|
||||||
|
__u32 dip4; // IP de destino (destination IP)
|
||||||
|
__u32 sport; // Porta de origem (source port)
|
||||||
|
__u32 dport; // Porta de destino (destination port)
|
||||||
|
__u32 family; // Família do socket (AF_INET = 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
// Mapa SOCKHASH: armazena sockets indexados pela chave acima
|
||||||
|
// Capacidade: 65535 entradas (conexões simultâneas)
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_SOCKHASH);
|
||||||
|
__uint(max_entries, 65535);
|
||||||
|
__type(key, struct sock_key);
|
||||||
|
__type(value, int);
|
||||||
|
} sock_hash SEC(".maps");
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
// Mapa para estatísticas
|
||||||
|
// Index 0 = conexões registradas no sockhash
|
||||||
|
// Index 1 = mensagens redirecionadas com sucesso (caminho acelerado)
|
||||||
|
// Index 2 = fallback: par não encontrado (caminho normal)
|
||||||
|
// Index 3 = conexões ignoradas (IPs externos/filtrados)
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
|
__uint(max_entries, 4);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, __u64);
|
||||||
|
} stats SEC(".maps");
|
||||||
|
|
||||||
|
// Incrementa um contador de estatísticas
|
||||||
|
static __always_inline void stats_inc(__u32 idx)
|
||||||
|
{
|
||||||
|
__u64 *val = bpf_map_lookup_elem(&stats, &idx);
|
||||||
|
if (val)
|
||||||
|
__sync_fetch_and_add(val, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
// Verifica se um IP (em network byte order / big-endian) é
|
||||||
|
// endereço privado RFC-1918 ou loopback.
|
||||||
|
//
|
||||||
|
// Por que isso importa:
|
||||||
|
// O sock_ops roda para TODAS as conexões TCP do cgroup raiz,
|
||||||
|
// incluindo tráfego externo (HTTPS do mundo, metadata AWS…).
|
||||||
|
// Registrar IPs públicos no sockhash é inútil e perigoso:
|
||||||
|
// o par nunca será encontrado, o sk_msg vai ao fallback e
|
||||||
|
// entradas mortas poluem o mapa.
|
||||||
|
// Só registramos conexões onde ambos os lados são privados
|
||||||
|
// (i.e., comunicação intra-host / intra-container).
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
static __always_inline bool is_local_ip(__u32 ip_be)
|
||||||
|
{
|
||||||
|
// local_ip4 / remote_ip4 são big-endian (network byte order).
|
||||||
|
// Em x86 (little-endian) o valor numérico da u32 aparece
|
||||||
|
// byte-invertido: para 10.15.0.45 (0A 0F 00 2D em memória),
|
||||||
|
// a u32 lida vale 0x2D000F0A — ou seja, ip_be >> 24 = 45 (ERRADO!).
|
||||||
|
// bpf_ntohl() converte para host order, colocando o primeiro
|
||||||
|
// octeto (10) no byte mais significativo, onde >> 24 extrai
|
||||||
|
// corretamente 10.
|
||||||
|
__u32 ip = bpf_ntohl(ip_be);
|
||||||
|
__u8 o1 = (__u8)(ip >> 24);
|
||||||
|
__u8 o2 = (__u8)(ip >> 16);
|
||||||
|
|
||||||
|
if (o1 == 10) return true; // 10.0.0.0/8
|
||||||
|
if (o1 == 172 && o2 >= 16 && o2 <= 31) return true; // 172.16.0.0/12
|
||||||
|
if (o1 == 192 && o2 == 168) return true; // 192.168.0.0/16
|
||||||
|
if (o1 == 127) return true; // 127.0.0.0/8 loopback
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
// Extrai a chave de conexão a partir do contexto sock_ops
|
||||||
|
// (usado quando uma conexão TCP é estabelecida)
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
static __always_inline void
|
||||||
|
extract_key_from_ops(struct bpf_sock_ops *ops, struct sock_key *key)
|
||||||
|
{
|
||||||
|
key->sip4 = ops->local_ip4;
|
||||||
|
key->dip4 = ops->remote_ip4;
|
||||||
|
key->sport = ops->local_port; // já em host byte order
|
||||||
|
key->dport = bpf_ntohl(ops->remote_port); // converter de network para host
|
||||||
|
key->family = ops->family;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
// PROGRAMA 1: sock_ops
|
||||||
|
//
|
||||||
|
// Anexado ao cgroup, é chamado em eventos de socket TCP.
|
||||||
|
// Quando uma conexão é ESTABELECIDA (ativa ou passiva),
|
||||||
|
// registramos o socket no sockhash.
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
SEC("sockops")
|
||||||
|
int bpf_sockops_handler(struct bpf_sock_ops *skops)
|
||||||
|
{
|
||||||
|
__u32 op = skops->op;
|
||||||
|
|
||||||
|
// Só processamos conexões IPv4
|
||||||
|
if (skops->family != 2) // AF_INET
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
// Conexão passiva: servidor aceitou uma conexão de um cliente
|
||||||
|
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
|
||||||
|
// Conexão ativa: cliente conectou a um servidor
|
||||||
|
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: {
|
||||||
|
// ── Filtro: só conexões entre IPs privados RFC-1918 ───────
|
||||||
|
// Ignora tráfego externo (HTTPS público, metadata AWS 169.254.x.x).
|
||||||
|
// Bug histórico: NÃO usar ip_be >> 24 sem bpf_ntohl — em x86
|
||||||
|
// little-endian o primeiro octeto fica no byte menos significativo.
|
||||||
|
if (!is_local_ip(skops->local_ip4) || !is_local_ip(skops->remote_ip4)) {
|
||||||
|
stats_inc(3); // conta conexões externas ignoradas
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sock_key key = {};
|
||||||
|
extract_key_from_ops(skops, &key);
|
||||||
|
|
||||||
|
// BPF_ANY: atualiza mesmo se já existir (cobre reconexões keepalive)
|
||||||
|
int ret = bpf_sock_hash_update(skops, &sock_hash, &key, BPF_ANY);
|
||||||
|
if (ret == 0) {
|
||||||
|
stats_inc(0);
|
||||||
|
|
||||||
|
// ── Hints de performance TCP ──────────────────────────
|
||||||
|
// Ativa todos os callbacks relevantes de uma vez:
|
||||||
|
// STATE_CB_FLAG : notifica mudanças de estado TCP
|
||||||
|
// (necessário para limpar sockets fechados)
|
||||||
|
// RTT_CB_FLAG : callback a cada RTT (permite ajuste dinâmico)
|
||||||
|
__u32 flags = skops->bpf_sock_ops_cb_flags
|
||||||
|
| BPF_SOCK_OPS_STATE_CB_FLAG
|
||||||
|
| BPF_SOCK_OPS_RTT_CB_FLAG;
|
||||||
|
bpf_sock_ops_cb_flags_set(skops, flags);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mudança de estado TCP: limpa o mapa em qualquer transição
|
||||||
|
// que indique encerramento, para não deixar sockets mortos.
|
||||||
|
case BPF_SOCK_OPS_STATE_CB: {
|
||||||
|
__u32 new_state = skops->args[1];
|
||||||
|
if (new_state == BPF_TCP_CLOSE ||
|
||||||
|
new_state == BPF_TCP_CLOSE_WAIT ||
|
||||||
|
new_state == BPF_TCP_FIN_WAIT1 ||
|
||||||
|
new_state == BPF_TCP_FIN_WAIT2 ||
|
||||||
|
new_state == BPF_TCP_TIME_WAIT) {
|
||||||
|
struct sock_key key = {};
|
||||||
|
extract_key_from_ops(skops, &key);
|
||||||
|
bpf_map_delete_elem(&sock_hash, &key);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTT callback: pode ser usado no futuro para métricas por conexão.
|
||||||
|
// Por ora não fazemos nada aqui — o callback foi ativado para
|
||||||
|
// manter o kernel notificando o sock_ops sobre a saúde da conexão.
|
||||||
|
case BPF_SOCK_OPS_RTT_CB:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
// PROGRAMA 2: sk_msg
|
||||||
|
//
|
||||||
|
// Anexado ao sockhash, é chamado quando qualquer socket
|
||||||
|
// registrado envia dados (sendmsg/sendfile).
|
||||||
|
// Redireciona os dados diretamente para o socket par,
|
||||||
|
// pulando toda a pilha de rede.
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
SEC("sk_msg")
|
||||||
|
int bpf_msg_redirect(struct sk_msg_md *msg)
|
||||||
|
{
|
||||||
|
// Construímos a chave do socket PAR (invertendo src/dst).
|
||||||
|
// Se A→B enviou dados, queremos encontrar no mapa a entrada
|
||||||
|
// que B registrou: {sip=B, dip=A, sport=portaB, dport=portaA}
|
||||||
|
struct sock_key key = {};
|
||||||
|
key.sip4 = msg->remote_ip4; // IP remoto = IP do par
|
||||||
|
key.dip4 = msg->local_ip4; // IP local = nosso IP
|
||||||
|
key.sport = bpf_ntohl(msg->remote_port); // remote_port está em network order
|
||||||
|
key.dport = msg->local_port; // local_port está em host order
|
||||||
|
key.family = msg->family;
|
||||||
|
|
||||||
|
// Tenta redirecionar diretamente para o socket par.
|
||||||
|
int ret = bpf_msg_redirect_hash(msg, &sock_hash, &key, BPF_F_INGRESS);
|
||||||
|
if (ret == SK_PASS) {
|
||||||
|
stats_inc(1); // Redirecionamento OK — caminho acelerado
|
||||||
|
return SK_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FALLBACK CRÍTICO: se o par não está no mapa (socket ainda não
|
||||||
|
// registrado, já fechou, ou conexão externa ao cgroup), deixamos
|
||||||
|
// os dados fluírem pelo caminho normal de rede em vez de
|
||||||
|
// descartar (SK_DROP), o que causaria ERR_CONNECTION_CLOSED.
|
||||||
|
stats_inc(2);
|
||||||
|
return SK_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Licença GPL obrigatória para usar helpers BPF
|
||||||
|
char _license[] SEC("license") = "GPL";
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
FROM node:20-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json ./
|
||||||
|
RUN npm install --production
|
||||||
|
COPY server.js ./
|
||||||
|
RUN apk add --no-cache wget
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "server.js"]
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "sockmap-demo-app",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "App Node.js de demonstração para eBPF sockmap",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node server.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mysql2": "^3.6.0",
|
||||||
|
"pg": "^8.11.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
// server.js — Servidor Node.js de demonstração
|
||||||
|
//
|
||||||
|
// Endpoints:
|
||||||
|
// GET /health → Health check
|
||||||
|
// GET / → Página principal com info
|
||||||
|
// GET /mysql/users → Lista usuários do MySQL
|
||||||
|
// GET /postgres/products→ Lista produtos do PostgreSQL
|
||||||
|
// GET /benchmark → Teste de latência para ambos os DBs
|
||||||
|
//
|
||||||
|
// Este servidor se conecta ao MySQL e PostgreSQL.
|
||||||
|
// Com o eBPF sockmap ativo, a comunicação com os bancos
|
||||||
|
// é acelerada (dados redirecionados no nível do socket).
|
||||||
|
// ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const http = require('http');
|
||||||
|
const url = require('url');
|
||||||
|
|
||||||
|
// ─── Configuração MySQL ───
|
||||||
|
const mysql = require('mysql2/promise');
|
||||||
|
const mysqlConfig = {
|
||||||
|
host: process.env.MYSQL_HOST || 'mysql',
|
||||||
|
port: parseInt(process.env.MYSQL_PORT || '3306'),
|
||||||
|
user: process.env.MYSQL_USER || 'app',
|
||||||
|
password: process.env.MYSQL_PASSWORD || 'app_password',
|
||||||
|
database: process.env.MYSQL_DATABASE || 'app_db',
|
||||||
|
waitForConnections: true,
|
||||||
|
connectionLimit: 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── Configuração PostgreSQL ───
|
||||||
|
const { Pool } = require('pg');
|
||||||
|
const pgPool = new Pool({
|
||||||
|
host: process.env.POSTGRES_HOST || 'postgres',
|
||||||
|
port: parseInt(process.env.POSTGRES_PORT || '5432'),
|
||||||
|
user: process.env.POSTGRES_USER || 'app',
|
||||||
|
password: process.env.POSTGRES_PASSWORD || 'app_password',
|
||||||
|
database: process.env.POSTGRES_DATABASE || 'app_db',
|
||||||
|
max: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mysqlPool;
|
||||||
|
|
||||||
|
// ─── Inicializa conexão MySQL ───
|
||||||
|
async function initMySQL() {
|
||||||
|
try {
|
||||||
|
mysqlPool = mysql.createPool(mysqlConfig);
|
||||||
|
const [rows] = await mysqlPool.query('SELECT 1');
|
||||||
|
console.log('✅ MySQL conectado!');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('❌ Erro ao conectar no MySQL:', err.message);
|
||||||
|
setTimeout(initMySQL, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Inicializa conexão PostgreSQL ───
|
||||||
|
async function initPostgres() {
|
||||||
|
try {
|
||||||
|
const res = await pgPool.query('SELECT 1');
|
||||||
|
console.log('✅ PostgreSQL conectado!');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('❌ Erro ao conectar no PostgreSQL:', err.message);
|
||||||
|
setTimeout(initPostgres, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Mede o tempo de execução de uma query (em ms) ───
|
||||||
|
async function measureQuery(fn) {
|
||||||
|
const start = process.hrtime.bigint();
|
||||||
|
const result = await fn();
|
||||||
|
const end = process.hrtime.bigint();
|
||||||
|
const durationMs = Number(end - start) / 1_000_000;
|
||||||
|
return { result, durationMs };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Handlers de rotas ───
|
||||||
|
const routes = {
|
||||||
|
// Health check
|
||||||
|
'/health': async () => ({
|
||||||
|
status: 'ok',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
uptime: process.uptime(),
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Página principal
|
||||||
|
'/': async () => ({
|
||||||
|
app: 'eBPF Sockmap Demo',
|
||||||
|
message: 'Servidor Node.js funcionando! 🚀',
|
||||||
|
endpoints: {
|
||||||
|
'/health': 'Health check',
|
||||||
|
'/mysql/users': 'Listar usuários (MySQL)',
|
||||||
|
'/postgres/products': 'Listar produtos (PostgreSQL)',
|
||||||
|
'/benchmark': 'Benchmark de latência nos dois bancos',
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
description: 'Com eBPF sockmap ativo, a comunicação entre este servidor e os bancos é acelerada.',
|
||||||
|
como_funciona: 'Os dados são redirecionados diretamente entre sockets, pulando a pilha TCP/IP do kernel.',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Listar usuários do MySQL
|
||||||
|
'/mysql/users': async () => {
|
||||||
|
const { result, durationMs } = await measureQuery(async () => {
|
||||||
|
const [rows] = await mysqlPool.query('SELECT * FROM users');
|
||||||
|
return rows;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
source: 'MySQL',
|
||||||
|
query_time_ms: durationMs.toFixed(3),
|
||||||
|
count: result.length,
|
||||||
|
data: result,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// Listar produtos do PostgreSQL
|
||||||
|
'/postgres/products': async () => {
|
||||||
|
const { result, durationMs } = await measureQuery(async () => {
|
||||||
|
const res = await pgPool.query('SELECT * FROM products');
|
||||||
|
return res.rows;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
source: 'PostgreSQL',
|
||||||
|
query_time_ms: durationMs.toFixed(3),
|
||||||
|
count: result.length,
|
||||||
|
data: result,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// Benchmark: faz várias queries e mede a latência
|
||||||
|
'/benchmark': async () => {
|
||||||
|
const iterations = 100;
|
||||||
|
const results = {
|
||||||
|
mysql: { times: [], avg: 0, min: Infinity, max: 0 },
|
||||||
|
postgres: { times: [], avg: 0, min: Infinity, max: 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Benchmark MySQL
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
const { durationMs } = await measureQuery(async () => {
|
||||||
|
const [rows] = await mysqlPool.query('SELECT 1');
|
||||||
|
return rows;
|
||||||
|
});
|
||||||
|
results.mysql.times.push(durationMs);
|
||||||
|
results.mysql.min = Math.min(results.mysql.min, durationMs);
|
||||||
|
results.mysql.max = Math.max(results.mysql.max, durationMs);
|
||||||
|
}
|
||||||
|
results.mysql.avg = results.mysql.times.reduce((a, b) => a + b, 0) / iterations;
|
||||||
|
|
||||||
|
// Benchmark PostgreSQL
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
const { durationMs } = await measureQuery(async () => {
|
||||||
|
await pgPool.query('SELECT 1');
|
||||||
|
});
|
||||||
|
results.postgres.times.push(durationMs);
|
||||||
|
results.postgres.min = Math.min(results.postgres.min, durationMs);
|
||||||
|
results.postgres.max = Math.max(results.postgres.max, durationMs);
|
||||||
|
}
|
||||||
|
results.postgres.avg = results.postgres.times.reduce((a, b) => a + b, 0) / iterations;
|
||||||
|
|
||||||
|
// Limpa arrays de tempos individuais
|
||||||
|
delete results.mysql.times;
|
||||||
|
delete results.postgres.times;
|
||||||
|
|
||||||
|
return {
|
||||||
|
benchmark: `${iterations} queries SELECT 1 em cada banco`,
|
||||||
|
dica: 'Compare os tempos com e sem eBPF sockmap!',
|
||||||
|
mysql: {
|
||||||
|
avg_ms: results.mysql.avg.toFixed(3),
|
||||||
|
min_ms: results.mysql.min.toFixed(3),
|
||||||
|
max_ms: results.mysql.max.toFixed(3),
|
||||||
|
},
|
||||||
|
postgres: {
|
||||||
|
avg_ms: results.postgres.avg.toFixed(3),
|
||||||
|
min_ms: results.postgres.min.toFixed(3),
|
||||||
|
max_ms: results.postgres.max.toFixed(3),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── Servidor HTTP ───
|
||||||
|
const server = http.createServer(async (req, res) => {
|
||||||
|
const pathname = url.parse(req.url).pathname;
|
||||||
|
const handler = routes[pathname];
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
res.writeHead(404);
|
||||||
|
res.end(JSON.stringify({ error: 'Rota não encontrada', path: pathname }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await handler();
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(JSON.stringify(data, null, 2));
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Erro em ${pathname}:`, err.message);
|
||||||
|
res.writeHead(500);
|
||||||
|
res.end(JSON.stringify({ error: err.message }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Inicialização ───
|
||||||
|
const PORT = 3000;
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('🔧 Iniciando servidor Node.js...');
|
||||||
|
await initMySQL();
|
||||||
|
await initPostgres();
|
||||||
|
|
||||||
|
server.listen(PORT, '0.0.0.0', () => {
|
||||||
|
console.log(`🚀 Servidor rodando na porta ${PORT}`);
|
||||||
|
console.log(`📊 Acesse http://localhost:${PORT}/benchmark para testar latência`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
-- ─────────────────────────────────────────────────────────
|
||||||
|
-- MySQL: Script de inicialização do banco de dados
|
||||||
|
-- Cria tabelas de exemplo para teste
|
||||||
|
-- ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
email VARCHAR(150) NOT NULL UNIQUE,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS requests_log (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
endpoint VARCHAR(255) NOT NULL,
|
||||||
|
method VARCHAR(10) NOT NULL,
|
||||||
|
response_time_ms FLOAT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Dados de exemplo
|
||||||
|
INSERT INTO users (name, email) VALUES
|
||||||
|
('Alice Silva', 'alice@exemplo.com'),
|
||||||
|
('Bob Santos', 'bob@exemplo.com'),
|
||||||
|
('Carol Oliveira', 'carol@exemplo.com');
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
-- ─────────────────────────────────────────────────────────
|
||||||
|
-- PostgreSQL: Script de inicialização do banco de dados
|
||||||
|
-- Cria tabelas de exemplo para teste
|
||||||
|
-- ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS products (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(200) NOT NULL,
|
||||||
|
price NUMERIC(10, 2) NOT NULL,
|
||||||
|
stock INT DEFAULT 0,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS orders (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
user_name VARCHAR(100) NOT NULL,
|
||||||
|
product_id INT REFERENCES products(id),
|
||||||
|
quantity INT NOT NULL DEFAULT 1,
|
||||||
|
total NUMERIC(10, 2),
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Dados de exemplo
|
||||||
|
INSERT INTO products (name, price, stock) VALUES
|
||||||
|
('Notebook Dell', 4500.00, 10),
|
||||||
|
('Mouse Logitech', 150.00, 50),
|
||||||
|
('Teclado Mecânico', 350.00, 30),
|
||||||
|
('Monitor 27"', 2200.00, 15);
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# Docker Compose - Stack completa para testar eBPF Sockmap
|
||||||
|
#
|
||||||
|
# Serviços:
|
||||||
|
# nginx → Proxy reverso (porta 80 → Node.js)
|
||||||
|
# app → Servidor Node.js (porta 3000)
|
||||||
|
# mysql → Banco de dados MySQL (porta 3306)
|
||||||
|
# postgres → Banco de dados PostgreSQL (porta 5432)
|
||||||
|
#
|
||||||
|
# Todos os containers ficam na mesma rede bridge,
|
||||||
|
# e o eBPF sockmap acelera a comunicação entre eles.
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ─── NGINX Proxy Reverso ───
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: sockmap-nginx
|
||||||
|
ports:
|
||||||
|
- "8080:80" # Acesse http://localhost:8080
|
||||||
|
volumes:
|
||||||
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
depends_on:
|
||||||
|
app:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- sockmap-net
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# ─── Aplicação Node.js ───
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: ./app
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: sockmap-app
|
||||||
|
environment:
|
||||||
|
NODE_ENV: production
|
||||||
|
MYSQL_HOST: mysql
|
||||||
|
MYSQL_PORT: 3306
|
||||||
|
MYSQL_USER: app
|
||||||
|
MYSQL_PASSWORD: app_password
|
||||||
|
MYSQL_DATABASE: app_db
|
||||||
|
POSTGRES_HOST: postgres
|
||||||
|
POSTGRES_PORT: 5432
|
||||||
|
POSTGRES_USER: app
|
||||||
|
POSTGRES_PASSWORD: app_password
|
||||||
|
POSTGRES_DATABASE: app_db
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- sockmap-net
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 10
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# ─── MySQL ───
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: sockmap-mysql
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: root_password
|
||||||
|
MYSQL_DATABASE: app_db
|
||||||
|
MYSQL_USER: app
|
||||||
|
MYSQL_PASSWORD: app_password
|
||||||
|
volumes:
|
||||||
|
- mysql-data:/var/lib/mysql
|
||||||
|
- ./db/mysql-init.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
||||||
|
networks:
|
||||||
|
- sockmap-net
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot_password"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 30
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# ─── PostgreSQL ───
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: sockmap-postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: app_db
|
||||||
|
POSTGRES_USER: app
|
||||||
|
POSTGRES_PASSWORD: app_password
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
- ./db/postgres-init.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
||||||
|
networks:
|
||||||
|
- sockmap-net
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U app -d app_db"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 30
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql-data:
|
||||||
|
postgres-data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
sockmap-net:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
config:
|
||||||
|
- subnet: 172.28.0.0/16
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# nginx.conf — Proxy reverso para o servidor Node.js
|
||||||
|
#
|
||||||
|
# Nginx recebe requisições na porta 80 e encaminha para
|
||||||
|
# o container do Node.js na porta 3000.
|
||||||
|
#
|
||||||
|
# Com o eBPF sockmap ativo, a comunicação nginx→app
|
||||||
|
# é acelerada (dados pulam a pilha de rede do kernel).
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
worker_processes auto;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
use epoll;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
# Formato de log com tempo de resposta
|
||||||
|
log_format main '$remote_addr - $remote_user [$time_local] '
|
||||||
|
'"$request" $status $body_bytes_sent '
|
||||||
|
'"$http_referer" "$http_user_agent" '
|
||||||
|
'rt=$request_time';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
tcp_nodelay on;
|
||||||
|
|
||||||
|
keepalive_timeout 65;
|
||||||
|
keepalive_requests 100;
|
||||||
|
|
||||||
|
# Compressão gzip
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/plain application/json application/javascript text/css;
|
||||||
|
|
||||||
|
# ─── Upstream: servidor Node.js ───
|
||||||
|
upstream nodejs_app {
|
||||||
|
server sockmap-app:3000;
|
||||||
|
keepalive 32; # Conexões persistentes (importante para sockmap!)
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
# Página de status do Nginx
|
||||||
|
location /nginx-status {
|
||||||
|
stub_status on;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Todas as outras requisições vão para o Node.js
|
||||||
|
location / {
|
||||||
|
proxy_pass http://nodejs_app;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
# Headers para proxy reverso
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# Manter conexão viva (keepalive)
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
|
||||||
|
# Timeouts
|
||||||
|
proxy_connect_timeout 5s;
|
||||||
|
proxy_read_timeout 30s;
|
||||||
|
proxy_send_timeout 30s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# benchmark.sh — Testa latência com e sem eBPF sockmap
|
||||||
|
#
|
||||||
|
# Execute: bash scripts/benchmark.sh
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
URL="http://localhost:8080/benchmark"
|
||||||
|
CURL_OPTS="-s --connect-timeout 5 --max-time 30"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${BLUE} Benchmark de Latência — eBPF Sockmap${NC}"
|
||||||
|
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Verifica se a aplicação está rodando
|
||||||
|
if ! curl $CURL_OPTS "$URL" > /dev/null 2>&1; then
|
||||||
|
echo "❌ Aplicação não está respondendo em $URL"
|
||||||
|
echo " Execute 'make docker-up' primeiro."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}Teste 1: Benchmark via endpoint /benchmark${NC}"
|
||||||
|
echo "(Faz 100 queries SELECT 1 em cada banco de dados)"
|
||||||
|
echo ""
|
||||||
|
curl $CURL_OPTS "$URL" | python3 -m json.tool 2>/dev/null || curl $CURL_OPTS "$URL"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Teste 2: Latência HTTP (nginx → node.js)${NC}"
|
||||||
|
echo "(10 requisições sequenciais para /health)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
TOTAL=0
|
||||||
|
for i in $(seq 1 10); do
|
||||||
|
TIME=$(curl $CURL_OPTS -o /dev/null -w "%{time_total}" http://localhost:8080/health)
|
||||||
|
TIME_MS=$(echo "$TIME * 1000" | bc)
|
||||||
|
printf " Requisição %2d: %8.3f ms\n" "$i" "$TIME_MS"
|
||||||
|
TOTAL=$(echo "$TOTAL + $TIME_MS" | bc)
|
||||||
|
done
|
||||||
|
|
||||||
|
AVG=$(echo "scale=3; $TOTAL / 10" | bc)
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN} Média: ${AVG} ms${NC}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Teste 3: Throughput (requisições por segundo)${NC}"
|
||||||
|
echo "(100 requisições para /health)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if command -v ab &>/dev/null; then
|
||||||
|
ab -n 100 -c 10 http://localhost:8080/health 2>/dev/null | grep -E "Requests per second|Time per request|Transfer rate"
|
||||||
|
elif command -v wrk &>/dev/null; then
|
||||||
|
wrk -t2 -c10 -d5s http://localhost:8080/health
|
||||||
|
else
|
||||||
|
START=$(date +%s%N)
|
||||||
|
for i in $(seq 1 100); do
|
||||||
|
curl $CURL_OPTS -o /dev/null http://localhost:8080/health &
|
||||||
|
done
|
||||||
|
wait
|
||||||
|
END=$(date +%s%N)
|
||||||
|
ELAPSED=$(echo "scale=3; ($END - $START) / 1000000" | bc)
|
||||||
|
RPS=$(echo "scale=1; 100 / ($ELAPSED / 1000)" | bc)
|
||||||
|
echo " 100 requisições em ${ELAPSED} ms"
|
||||||
|
echo -e "${GREEN} ~${RPS} requisições/segundo${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "─────────────────────────────────────────────"
|
||||||
|
echo "💡 Dica: Execute este benchmark antes e depois do 'make load'"
|
||||||
|
echo " para comparar a performance com e sem eBPF sockmap!"
|
||||||
|
echo ""
|
||||||
|
echo " SEM sockmap: make unload → bash scripts/benchmark.sh"
|
||||||
|
echo " COM sockmap: make load → bash scripts/benchmark.sh"
|
||||||
|
echo "─────────────────────────────────────────────"
|
||||||
|
echo ""
|
||||||
Executable
+403
@@ -0,0 +1,403 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# loader.sh — Carrega e anexa os programas eBPF sockmap
|
||||||
|
#
|
||||||
|
# USO:
|
||||||
|
# sudo ./scripts/loader.sh load # Carrega e ativa o sockmap
|
||||||
|
# sudo ./scripts/loader.sh unload # Remove o sockmap
|
||||||
|
# sudo ./scripts/loader.sh status # Mostra status e estatísticas
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Cores para output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
BPF_OBJ="build/sockmap.bpf.o"
|
||||||
|
BPF_PIN_DIR="/sys/fs/bpf/sockmap"
|
||||||
|
CGROUP_PATH=""
|
||||||
|
|
||||||
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||||
|
log_ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||||
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
log_error() { echo -e "${RED}[ERRO]${NC} $1"; }
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# Detecta o caminho do cgroup v2
|
||||||
|
# Docker coloca os containers dentro de um cgroup
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
detect_cgroup() {
|
||||||
|
# Verifica se cgroup v2 está montado (unified hierarchy)
|
||||||
|
if mount | grep -q "cgroup2 on /sys/fs/cgroup"; then
|
||||||
|
CGROUP_PATH="/sys/fs/cgroup"
|
||||||
|
log_info "Cgroup v2 (unified) detectado em $CGROUP_PATH"
|
||||||
|
elif [ -d "/sys/fs/cgroup/unified" ]; then
|
||||||
|
CGROUP_PATH="/sys/fs/cgroup/unified"
|
||||||
|
log_info "Cgroup v2 (unified) detectado em $CGROUP_PATH"
|
||||||
|
else
|
||||||
|
log_error "Cgroup v2 não encontrado! eBPF sockmap requer cgroup v2."
|
||||||
|
log_error "Adicione 'systemd.unified_cgroup_hierarchy=1' aos parâmetros do kernel."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# Verifica pré-requisitos
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
check_prereqs() {
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
log_error "Este script precisa ser executado como root (sudo)!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v bpftool &>/dev/null; then
|
||||||
|
log_error "'bpftool' não encontrado. Instale com: apt install linux-tools-common linux-tools-$(uname -r)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$BPF_OBJ" ]; then
|
||||||
|
log_error "Objeto BPF não encontrado em '$BPF_OBJ'. Execute 'make' primeiro!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# LOAD: Carrega os programas BPF e os anexa
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
do_load() {
|
||||||
|
check_prereqs
|
||||||
|
detect_cgroup
|
||||||
|
|
||||||
|
# Limpa carregamento anterior se existir
|
||||||
|
if [ -d "$BPF_PIN_DIR" ]; then
|
||||||
|
log_warn "Sockmap já carregado. Removendo anterior..."
|
||||||
|
do_unload
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Criando diretório de pins BPF..."
|
||||||
|
mkdir -p "$BPF_PIN_DIR"
|
||||||
|
|
||||||
|
# ── 1. Carregar TODOS os programas do objeto de uma vez ──
|
||||||
|
# 'loadall' respeita as anotações SEC() de cada programa,
|
||||||
|
# carregando sock_ops e sk_msg com seus tipos corretos.
|
||||||
|
# Os programas são fixados (pinned) com o nome das funções C:
|
||||||
|
# bpf_sockops_handler → tipo sockops
|
||||||
|
# bpf_msg_redirect → tipo sk_msg
|
||||||
|
log_info "Carregando programas BPF (loadall)..."
|
||||||
|
bpftool prog loadall "$BPF_OBJ" "$BPF_PIN_DIR" \
|
||||||
|
pinmaps "$BPF_PIN_DIR"
|
||||||
|
log_ok "Programas sock_ops e sk_msg carregados"
|
||||||
|
|
||||||
|
# Confirma que os pins foram criados
|
||||||
|
if [ ! -f "$BPF_PIN_DIR/bpf_sockops_handler" ]; then
|
||||||
|
log_error "Pin bpf_sockops_handler não encontrado em $BPF_PIN_DIR!"
|
||||||
|
log_error "Programas no objeto:"
|
||||||
|
bpftool prog list 2>/dev/null | tail -10
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ ! -f "$BPF_PIN_DIR/bpf_msg_redirect" ]; then
|
||||||
|
log_error "Pin bpf_msg_redirect não encontrado em $BPF_PIN_DIR!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 2. Anexar sock_ops ao cgroup ──
|
||||||
|
# Flag 'multi' permite coexistir com outros programas sock_ops já
|
||||||
|
# anexados (ex: systemd ou outros agentes), sem conflito.
|
||||||
|
log_info "Anexando sock_ops ao cgroup ($CGROUP_PATH)..."
|
||||||
|
bpftool cgroup attach "$CGROUP_PATH" sock_ops \
|
||||||
|
pinned "$BPF_PIN_DIR/bpf_sockops_handler" \
|
||||||
|
multi
|
||||||
|
log_ok "sock_ops anexado ao cgroup"
|
||||||
|
|
||||||
|
# ── 3. Anexar sk_msg ao mapa sockhash ──
|
||||||
|
log_info "Anexando sk_msg ao mapa sockhash..."
|
||||||
|
bpftool prog attach \
|
||||||
|
pinned "$BPF_PIN_DIR/bpf_msg_redirect" \
|
||||||
|
msg_verdict \
|
||||||
|
pinned "$BPF_PIN_DIR/sock_hash"
|
||||||
|
log_ok "sk_msg anexado ao sockhash"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
log_ok "═══════════════════════════════════════════════"
|
||||||
|
log_ok " eBPF Sockmap ATIVADO com sucesso!"
|
||||||
|
log_ok " Comunicação entre containers está acelerada."
|
||||||
|
log_ok "═══════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
log_info "Use 'sudo ./scripts/loader.sh status' para ver estatísticas"
|
||||||
|
log_info "Use 'sudo ./scripts/loader.sh unload' para desativar"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# UNLOAD: Remove os programas BPF
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
do_unload() {
|
||||||
|
detect_cgroup
|
||||||
|
|
||||||
|
log_info "Desanexando programas BPF..."
|
||||||
|
|
||||||
|
# Desanexa sock_ops do cgroup
|
||||||
|
if [ -f "$BPF_PIN_DIR/bpf_sockops_handler" ]; then
|
||||||
|
bpftool cgroup detach "$CGROUP_PATH" sock_ops \
|
||||||
|
pinned "$BPF_PIN_DIR/bpf_sockops_handler" \
|
||||||
|
multi 2>/dev/null || true
|
||||||
|
log_ok "sock_ops desanexado do cgroup"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Desanexa sk_msg do mapa
|
||||||
|
if [ -f "$BPF_PIN_DIR/bpf_msg_redirect" ]; then
|
||||||
|
bpftool prog detach \
|
||||||
|
pinned "$BPF_PIN_DIR/bpf_msg_redirect" \
|
||||||
|
msg_verdict \
|
||||||
|
pinned "$BPF_PIN_DIR/sock_hash" 2>/dev/null || true
|
||||||
|
log_ok "sk_msg desanexado do sockhash"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove pins
|
||||||
|
log_info "Removendo pins BPF..."
|
||||||
|
rm -rf "$BPF_PIN_DIR"
|
||||||
|
log_ok "Pins removidos"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
log_ok "eBPF Sockmap DESATIVADO com sucesso."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# STATUS: Mostra status e estatísticas
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
do_status() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}═══ eBPF Sockmap Status ═══${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ ! -d "$BPF_PIN_DIR" ]; then
|
||||||
|
log_warn "Sockmap NÃO está carregado."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_ok "Sockmap está ATIVO"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Mostra programas carregados
|
||||||
|
echo -e "${YELLOW}Programas BPF carregados:${NC}"
|
||||||
|
bpftool prog show pinned "$BPF_PIN_DIR/bpf_sockops_handler" 2>/dev/null || echo " sock_ops: não encontrado"
|
||||||
|
bpftool prog show pinned "$BPF_PIN_DIR/bpf_msg_redirect" 2>/dev/null || echo " sk_msg: não encontrado"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Mostra estatísticas
|
||||||
|
# O valor no mapa é u64 little-endian (8 bytes).
|
||||||
|
# bpftool --json pode retornar cada byte como int (10) ou string hex ("0x0a")
|
||||||
|
# dependendo da versão. O helper parse_bpftool_bytes trata ambos.
|
||||||
|
echo -e "${YELLOW}Estatísticas:${NC}"
|
||||||
|
if [ -f "$BPF_PIN_DIR/stats" ]; then
|
||||||
|
read_stat() {
|
||||||
|
local idx=$1
|
||||||
|
bpftool map lookup pinned "$BPF_PIN_DIR/stats" \
|
||||||
|
key $(printf '0x%02x 0x00 0x00 0x00' "$idx") \
|
||||||
|
--json 2>/dev/null \
|
||||||
|
| python3 -c "
|
||||||
|
import sys, json
|
||||||
|
def to_bytes(arr):
|
||||||
|
# bpftool >= 7.x retorna strings hex ('0x0a'), antigas retornam ints (10)
|
||||||
|
return bytes(int(x, 16) if isinstance(x, str) else int(x) for x in arr)
|
||||||
|
try:
|
||||||
|
d = json.load(sys.stdin)
|
||||||
|
print(int.from_bytes(to_bytes(d['value']), 'little'))
|
||||||
|
except:
|
||||||
|
print(0)
|
||||||
|
"
|
||||||
|
}
|
||||||
|
CONNS=$(read_stat 0)
|
||||||
|
REDIR=$(read_stat 1)
|
||||||
|
ERRS=$(read_stat 2)
|
||||||
|
SKIP=$(read_stat 3)
|
||||||
|
echo " Conexões registradas no sockhash : $CONNS"
|
||||||
|
echo " Mensagens redirecionadas (accel.) : $REDIR"
|
||||||
|
echo " Fallback (par não encontrado) : $ERRS"
|
||||||
|
echo " Ignoradas (IPs externos) : $SKIP"
|
||||||
|
else
|
||||||
|
echo " Mapa de estatísticas não encontrado (recompile e recarregue)"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Mostra sockets no mapa
|
||||||
|
# NOTA: 'No space left on device' nos values é NORMAL.
|
||||||
|
# SOCKHASH armazena ponteiros de socket do kernel, não legíveis de userspace.
|
||||||
|
echo -e "${YELLOW}Conexões ativas no sockhash (IPs/portas):${NC}"
|
||||||
|
echo -e " ${BLUE}(Os 'value: No space left on device' são normais para SOCKHASH)${NC}"
|
||||||
|
if [ -f "$BPF_PIN_DIR/sock_hash" ]; then
|
||||||
|
SOCKMAP_OUT=$(bpftool map dump pinned "$BPF_PIN_DIR/sock_hash" --json 2>/dev/null \
|
||||||
|
| python3 -c "
|
||||||
|
import sys, json, socket, struct
|
||||||
|
|
||||||
|
def to_int(x):
|
||||||
|
# Aceita tanto int (10) quanto string hex ('0x0a') — depende da versão do bpftool
|
||||||
|
return int(x, 16) if isinstance(x, str) else int(x)
|
||||||
|
|
||||||
|
try:
|
||||||
|
entries = json.load(sys.stdin)
|
||||||
|
if not entries:
|
||||||
|
print(' (nenhuma conexão registrada)')
|
||||||
|
else:
|
||||||
|
print(f' Total: {len(entries)} conexão(oes):')
|
||||||
|
for e in entries[:25]:
|
||||||
|
k = e.get('key', [])
|
||||||
|
if len(k) >= 20:
|
||||||
|
# struct sock_key: sip4 dip4 sport dport family (5x u32)
|
||||||
|
# local_ip4/remote_ip4 estão em big-endian (network order)
|
||||||
|
# portas em host order (little-endian)
|
||||||
|
raw = [to_int(b) for b in k]
|
||||||
|
sip = socket.inet_ntoa(bytes(raw[0:4]))
|
||||||
|
dip = socket.inet_ntoa(bytes(raw[4:8]))
|
||||||
|
sport = struct.unpack_from('<I', bytes(raw[8:12]))[0]
|
||||||
|
dport = struct.unpack_from('<I', bytes(raw[12:16]))[0]
|
||||||
|
print(f' {sip}:{sport} → {dip}:{dport}')
|
||||||
|
except Exception as ex:
|
||||||
|
print(f' (erro ao decodificar: {ex})')
|
||||||
|
" 2>/dev/null || echo " Nenhum socket registrado")
|
||||||
|
SOCK_COUNT=$(echo "$SOCKMAP_OUT" | grep -oP 'Total: \K[0-9]+' || echo 0)
|
||||||
|
echo "$SOCKMAP_OUT"
|
||||||
|
# Aviso inteligente: usa contagem real do sockhash quando stats falham
|
||||||
|
if [ "${CONNS:-0}" -eq 0 ] 2>/dev/null && [ "${SOCK_COUNT:-0}" -gt 0 ] 2>/dev/null; then
|
||||||
|
echo ""
|
||||||
|
echo -e " ${GREEN}✔ Sockhash tem ${SOCK_COUNT} conexão(oes) ativas!${NC}"
|
||||||
|
echo -e " ${YELLOW}(Contadores de stats zerados podem indicar que os containers"
|
||||||
|
echo -e " foram reiniciados antes do sockmap ser carregado nesta sessão.)${NC}"
|
||||||
|
elif [ "${CONNS:-0}" -eq 0 ] 2>/dev/null && [ "${SOCK_COUNT:-0}" -eq 0 ] 2>/dev/null; then
|
||||||
|
echo ""
|
||||||
|
echo -e " ${YELLOW}⚠ Nenhuma conexão no sockhash.${NC}"
|
||||||
|
echo " → Reinicie os containers: cd /home/docker && docker compose restart"
|
||||||
|
echo " → Ou rode: sudo ./scripts/loader.sh diagnose"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Mostra logs do BPF (trace_pipe)
|
||||||
|
echo -e "${YELLOW}Últimas mensagens do BPF (trace_pipe):${NC}"
|
||||||
|
if [ -f /sys/kernel/debug/tracing/trace_pipe ]; then
|
||||||
|
timeout 1 cat /sys/kernel/debug/tracing/trace_pipe 2>/dev/null | tail -10 || echo " Nenhuma mensagem recente"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# DIAGNOSE: Força conexões TCP internas para verificar se o sockmap
|
||||||
|
# está interceptando corretamente.
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
do_diagnose() {
|
||||||
|
if [ ! -d "$BPF_PIN_DIR" ]; then
|
||||||
|
log_error "Sockmap não está carregado. Execute 'load' primeiro."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}═══ Diagnóstico do sockmap ═══${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Lê stats antes
|
||||||
|
CONNS_BEFORE=$(bpftool map lookup pinned "$BPF_PIN_DIR/stats" \
|
||||||
|
key 0x00 0x00 0x00 0x00 --json 2>/dev/null \
|
||||||
|
| python3 -c "import sys,json; d=json.load(sys.stdin); print(int.from_bytes(bytes(d['value']),'little'))" 2>/dev/null || echo 0)
|
||||||
|
|
||||||
|
log_info "Detectando containers Docker em execução..."
|
||||||
|
CONTAINERS=$(docker ps --format '{{.Names}} {{.NetworkSettings.Networks}}' 2>/dev/null | head -20 || echo "")
|
||||||
|
|
||||||
|
# Testa conexões TCP para IPs conhecidos da rede 10.15.0.0/16
|
||||||
|
log_info "Tentando conexões TCP internas para ativar o sock_ops..."
|
||||||
|
# Pega IPs de containers ativos
|
||||||
|
INTERNAL_IPS=$(docker inspect $(docker ps -q 2>/dev/null) 2>/dev/null \
|
||||||
|
| python3 -c "
|
||||||
|
import sys, json
|
||||||
|
try:
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
ips = []
|
||||||
|
for c in data:
|
||||||
|
nets = c.get('NetworkSettings', {}).get('Networks', {})
|
||||||
|
for net in nets.values():
|
||||||
|
ip = net.get('IPAddress', '')
|
||||||
|
if ip.startswith('10.'):
|
||||||
|
name = c.get('Name', '?').strip('/')
|
||||||
|
port_map = c.get('NetworkSettings', {}).get('Ports', {})
|
||||||
|
port = 80
|
||||||
|
for p in port_map:
|
||||||
|
try: port = int(p.split('/')[0]); break
|
||||||
|
except: pass
|
||||||
|
ips.append(f'{ip}:{port}:{name}')
|
||||||
|
print(' '.join(ips[:10]))
|
||||||
|
except Exception as e:
|
||||||
|
print('')
|
||||||
|
" 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if [ -z "$INTERNAL_IPS" ]; then
|
||||||
|
log_warn "Nenhum container com IP 10.x detectado via Docker."
|
||||||
|
log_info "Tentando IPs conhecidos da dockernet..."
|
||||||
|
INTERNAL_IPS="10.15.0.45:80:nginx 10.15.0.42:3306:mariadb 10.15.0.54:5432:postgres 10.15.0.55:6379:redis"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Testando conexões (timeout 1s cada):"
|
||||||
|
for entry in $INTERNAL_IPS; do
|
||||||
|
IP=$(echo $entry | cut -d: -f1)
|
||||||
|
PORT=$(echo $entry | cut -d: -f2)
|
||||||
|
NAME=$(echo $entry | cut -d: -f3)
|
||||||
|
if timeout 1 bash -c "echo > /dev/tcp/$IP/$PORT" 2>/dev/null; then
|
||||||
|
log_ok " $NAME ($IP:$PORT) — conexão OK"
|
||||||
|
else
|
||||||
|
log_warn " $NAME ($IP:$PORT) — sem resposta (normal se a porta estiver fechada)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Lê stats depois
|
||||||
|
CONNS_AFTER=$(bpftool map lookup pinned "$BPF_PIN_DIR/stats" \
|
||||||
|
key 0x00 0x00 0x00 0x00 --json 2>/dev/null \
|
||||||
|
| python3 -c "import sys,json; d=json.load(sys.stdin); print(int.from_bytes(bytes(d['value']),'little'))" 2>/dev/null || echo 0)
|
||||||
|
|
||||||
|
NEW=$((CONNS_AFTER - CONNS_BEFORE))
|
||||||
|
echo ""
|
||||||
|
if [ "$NEW" -gt 0 ]; then
|
||||||
|
log_ok "$NEW nova(s) conexão(oes) registradas no sockhash! ✅"
|
||||||
|
log_ok "O sockmap está interceptando tráfego inter-container corretamente."
|
||||||
|
else
|
||||||
|
log_warn "Nenhuma nova conexão registrada."
|
||||||
|
echo ""
|
||||||
|
echo "Possíveis causas:"
|
||||||
|
echo " 1. Containers não reiniciados após 'make load'"
|
||||||
|
echo " → Solucao: cd /home/docker && docker compose restart"
|
||||||
|
echo " 2. A rede dockernet usa macvlan (não bridge)"
|
||||||
|
echo " → Macvlan não passa pelo cgroup host, mas sim pelo kernel de rede"
|
||||||
|
echo " → Neste caso o sockmap não intercepta (limitação de design)"
|
||||||
|
echo " 3. Verificar o driver da rede:"
|
||||||
|
echo " docker network inspect dockernet | grep Driver"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# Main
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
case "${1:-help}" in
|
||||||
|
load)
|
||||||
|
do_load
|
||||||
|
;;
|
||||||
|
unload)
|
||||||
|
do_unload
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
do_status
|
||||||
|
;;
|
||||||
|
diagnose)
|
||||||
|
do_diagnose
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "eBPF Sockmap Loader"
|
||||||
|
echo ""
|
||||||
|
echo "Uso: sudo $0 {load|unload|status|diagnose}"
|
||||||
|
echo ""
|
||||||
|
echo " load - Carrega e ativa o sockmap"
|
||||||
|
echo " unload - Remove o sockmap"
|
||||||
|
echo " status - Mostra status e estatísticas"
|
||||||
|
echo " diagnose - Testa se o sockmap está interceptando conexões"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
# setup.sh — Instala todas as dependências necessárias
|
||||||
|
#
|
||||||
|
# Execute: sudo bash scripts/setup.sh
|
||||||
|
# ─────────────────────────────────────────────────────────
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||||
|
log_ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||||
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
log_error() { echo -e "${RED}[ERRO]${NC} $1"; }
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${BLUE} Instalação de dependências - eBPF Sockmap${NC}"
|
||||||
|
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Verifica se é root
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
log_error "Execute como root: sudo bash scripts/setup.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detecta distribuição
|
||||||
|
if [ -f /etc/os-release ]; then
|
||||||
|
. /etc/os-release
|
||||||
|
DISTRO=$ID
|
||||||
|
else
|
||||||
|
DISTRO="unknown"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Distribuição detectada: $DISTRO"
|
||||||
|
log_info "Kernel: $(uname -r)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ─── Verifica versão do kernel ───
|
||||||
|
KERNEL_MAJOR=$(uname -r | cut -d. -f1)
|
||||||
|
KERNEL_MINOR=$(uname -r | cut -d. -f2)
|
||||||
|
|
||||||
|
if [ "$KERNEL_MAJOR" -lt 5 ] || ([ "$KERNEL_MAJOR" -eq 5 ] && [ "$KERNEL_MINOR" -lt 4 ]); then
|
||||||
|
log_error "Kernel $(uname -r) muito antigo! eBPF sockmap requer kernel >= 5.4"
|
||||||
|
log_error "Atualize o kernel antes de continuar."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_ok "Kernel $(uname -r) compatível com eBPF sockmap"
|
||||||
|
|
||||||
|
# ─── Instala pacotes ───
|
||||||
|
case "$DISTRO" in
|
||||||
|
ubuntu|debian|linuxmint|pop)
|
||||||
|
log_info "Instalando pacotes (apt)..."
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y \
|
||||||
|
clang \
|
||||||
|
llvm \
|
||||||
|
libbpf-dev \
|
||||||
|
linux-tools-common \
|
||||||
|
"linux-tools-$(uname -r)" \
|
||||||
|
linux-headers-"$(uname -r)" \
|
||||||
|
build-essential \
|
||||||
|
pkg-config \
|
||||||
|
curl \
|
||||||
|
docker.io \
|
||||||
|
docker-compose-plugin
|
||||||
|
;;
|
||||||
|
fedora)
|
||||||
|
log_info "Instalando pacotes (dnf)..."
|
||||||
|
dnf install -y \
|
||||||
|
clang \
|
||||||
|
llvm \
|
||||||
|
libbpf-devel \
|
||||||
|
bpftool \
|
||||||
|
kernel-headers \
|
||||||
|
kernel-devel \
|
||||||
|
make \
|
||||||
|
gcc \
|
||||||
|
curl \
|
||||||
|
docker \
|
||||||
|
docker-compose-plugin
|
||||||
|
;;
|
||||||
|
arch|manjaro)
|
||||||
|
log_info "Instalando pacotes (pacman)..."
|
||||||
|
pacman -Sy --noconfirm \
|
||||||
|
clang \
|
||||||
|
llvm \
|
||||||
|
libbpf \
|
||||||
|
bpf \
|
||||||
|
linux-headers \
|
||||||
|
base-devel \
|
||||||
|
curl \
|
||||||
|
docker \
|
||||||
|
docker-compose
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_warn "Distribuição '$DISTRO' não reconhecida."
|
||||||
|
log_warn "Instale manualmente: clang, llvm, libbpf-dev, bpftool, linux-headers, docker"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# ─── Verifica ferramentas instaladas ───
|
||||||
|
echo ""
|
||||||
|
log_info "Verificando ferramentas..."
|
||||||
|
|
||||||
|
check_tool() {
|
||||||
|
if command -v "$1" &>/dev/null; then
|
||||||
|
log_ok "$1 → $($1 --version 2>&1 | head -1)"
|
||||||
|
else
|
||||||
|
log_error "$1 não encontrado!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_tool clang
|
||||||
|
check_tool llc
|
||||||
|
check_tool bpftool || log_warn "bpftool pode estar em /usr/lib/linux-tools/$(uname -r)/bpftool"
|
||||||
|
check_tool docker
|
||||||
|
|
||||||
|
# ─── Verifica BTF (BPF Type Format) ───
|
||||||
|
echo ""
|
||||||
|
if [ -f /sys/kernel/btf/vmlinux ]; then
|
||||||
|
log_ok "BTF disponível em /sys/kernel/btf/vmlinux"
|
||||||
|
else
|
||||||
|
log_error "BTF não encontrado! O kernel precisa ser compilado com CONFIG_DEBUG_INFO_BTF=y"
|
||||||
|
log_error "A maioria das distros modernas já inclui BTF."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Verifica cgroup v2 ───
|
||||||
|
if mount | grep -q "cgroup2"; then
|
||||||
|
log_ok "Cgroup v2 ativo"
|
||||||
|
else
|
||||||
|
log_warn "Cgroup v2 não detectado! Pode ser necessário para o sockmap."
|
||||||
|
log_warn "Adicione 'systemd.unified_cgroup_hierarchy=1' aos parâmetros de boot do kernel."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Configura Docker ───
|
||||||
|
echo ""
|
||||||
|
log_info "Configurando Docker..."
|
||||||
|
systemctl enable docker 2>/dev/null || true
|
||||||
|
systemctl start docker 2>/dev/null || true
|
||||||
|
|
||||||
|
# Adiciona usuário atual ao grupo docker (se não é root direto)
|
||||||
|
ACTUAL_USER="${SUDO_USER:-$USER}"
|
||||||
|
if [ "$ACTUAL_USER" != "root" ]; then
|
||||||
|
usermod -aG docker "$ACTUAL_USER" 2>/dev/null || true
|
||||||
|
log_ok "Usuário '$ACTUAL_USER' adicionado ao grupo docker"
|
||||||
|
log_warn "Faça logout e login para o grupo docker funcionar sem sudo"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}═══════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${GREEN} Instalação concluída!${NC}"
|
||||||
|
echo -e "${GREEN}═══════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Próximos passos:"
|
||||||
|
echo " 1. make → Compilar o programa eBPF"
|
||||||
|
echo " 2. make docker-up → Subir os containers"
|
||||||
|
echo " 3. make load → Ativar o sockmap"
|
||||||
|
echo " 4. make benchmark → Testar performance"
|
||||||
|
echo ""
|
||||||
+155
@@ -0,0 +1,155 @@
|
|||||||
|
#!/usr/sbin/nft -f
|
||||||
|
# ==============================================================
|
||||||
|
# nftables.conf — server-mv
|
||||||
|
#
|
||||||
|
# Substitui a parte de firewall do rc.local antigo (iptables/ip6tables,
|
||||||
|
# antes espalhada entre rc.local e test.sh). Regras funcionalmente
|
||||||
|
# equivalentes, com os bugs já corrigidos:
|
||||||
|
# - sem "-j DROP" solto no meio da chain (o catch-all é só a policy)
|
||||||
|
# - sem duplicação de regra de porta única + multiport
|
||||||
|
# - anti-spoof de 192.168.0.0/16 restrito à interface WAN
|
||||||
|
# - chain PORTSCAN removida (nunca era populada, sempre morta)
|
||||||
|
#
|
||||||
|
# Vantagens sobre iptables/ip6tables para este caso:
|
||||||
|
# - um ruleset só cobre IPv4 e IPv6 (table inet), então não existe
|
||||||
|
# mais o risco de esquecer de espelhar uma regra do v4 pro v6
|
||||||
|
# - "flush ruleset" no topo faz o carregamento ser atômico: se o
|
||||||
|
# arquivo tiver erro de sintaxe, o `nft -f` falha ANTES de aplicar
|
||||||
|
# qualquer coisa — você nunca fica com um firewall pela metade
|
||||||
|
# (iptables -F seguido de regras que podem falhar no meio é o
|
||||||
|
# oposto disso)
|
||||||
|
# - meters (rate-limit por IP de origem) substituem o módulo
|
||||||
|
# "recent" do iptables com sintaxe mais legível
|
||||||
|
#
|
||||||
|
# Aplicar:
|
||||||
|
# nft -c -f nftables.conf # valida sintaxe sem aplicar
|
||||||
|
# nft -f nftables.conf # aplica
|
||||||
|
# systemctl enable --now nftables # persistência no boot
|
||||||
|
#
|
||||||
|
# Depois de migrar, pare de carregar iptables/ip6tables no boot
|
||||||
|
# (remova essa parte do rc.local — ver rc.local.new) para não ter
|
||||||
|
# duas ferramentas de firewall competindo pelo mesmo netfilter.
|
||||||
|
# ==============================================================
|
||||||
|
|
||||||
|
flush ruleset
|
||||||
|
|
||||||
|
table inet filter {
|
||||||
|
|
||||||
|
# --- Meters (rate limit com estado por IP de origem) ---
|
||||||
|
# (declarados via "meter" inline nas regras que os usam, mais abaixo)
|
||||||
|
|
||||||
|
chain input {
|
||||||
|
type filter hook input priority filter; policy drop;
|
||||||
|
|
||||||
|
iifname "lo" accept
|
||||||
|
ct state established,related accept
|
||||||
|
|
||||||
|
# ICMP/ICMPv6: 2º maior volume medido no host, avaliado cedo
|
||||||
|
# de propósito (dados reais: ~66k pacotes/dia, ficava por
|
||||||
|
# último na versão antiga em iptables).
|
||||||
|
ip protocol icmp jump icmpv4
|
||||||
|
meta l4proto ipv6-icmp jump icmpv6
|
||||||
|
|
||||||
|
# Pacotes TCP malformados / scanners óbvios
|
||||||
|
tcp flags & (fin|syn|rst|psh|ack|urg) == (fin|syn|rst|psh|ack|urg) \
|
||||||
|
meter bad_packets { ip saddr timeout 5m limit rate over 5/minute } drop
|
||||||
|
tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 \
|
||||||
|
meter bad_packets6 { ip6 saddr timeout 5m limit rate over 5/minute } drop
|
||||||
|
ip frag-off & 0x1fff != 0 drop # fragmentos IPv4 (equivalente a "iptables -f")
|
||||||
|
|
||||||
|
# Anti-spoof — só faz sentido vindo da WAN
|
||||||
|
iifname "enp0s6" ip saddr 192.168.0.0/16 drop
|
||||||
|
iifname "enp0s6" ip6 saddr fe80::/10 drop
|
||||||
|
iifname "enp0s6" ip6 saddr fd00::/8 drop
|
||||||
|
ip6 daddr ff00::/8 drop
|
||||||
|
|
||||||
|
# SYN flood (limite global, equivalente ao antigo limit 10/sec burst 24)
|
||||||
|
tcp flags syn limit rate 10/second burst 24 packets accept
|
||||||
|
tcp flags syn drop
|
||||||
|
|
||||||
|
# Rate limit de conexões novas na porta SSH (8117): no máx. 3
|
||||||
|
# tentativas novas por IP a cada 60s
|
||||||
|
tcp dport 8117 ct state new \
|
||||||
|
meter ssh_meter { ip saddr timeout 60s limit rate 3/minute } accept
|
||||||
|
tcp dport 8117 ct state new meter ssh_meter6 { ip6 saddr timeout 60s limit rate 3/minute } accept
|
||||||
|
tcp dport 8117 ct state new drop
|
||||||
|
|
||||||
|
# Túneis / VPN
|
||||||
|
iifname "wg0" accept
|
||||||
|
iifname "tun0" accept
|
||||||
|
udp dport 1194 accept
|
||||||
|
|
||||||
|
# Serviços expostos na WAN
|
||||||
|
iifname "enp0s6" tcp dport { 80, 443 } accept
|
||||||
|
iifname "enp0s6" udp dport { 32100, 32101, 32110, 32400 } accept
|
||||||
|
|
||||||
|
# DHCPv6 client
|
||||||
|
udp dport 546 accept
|
||||||
|
}
|
||||||
|
|
||||||
|
chain forward {
|
||||||
|
type filter hook forward priority filter; policy accept;
|
||||||
|
|
||||||
|
ct state established,related accept
|
||||||
|
iifname "tun0" accept
|
||||||
|
iifname "wg0" accept
|
||||||
|
tcp daddr 10.15.0.45 dport { 80, 443 } accept
|
||||||
|
tcp ip6 daddr 2001:db8:15::45 dport { 80, 443 } accept
|
||||||
|
}
|
||||||
|
|
||||||
|
chain output {
|
||||||
|
type filter hook output priority filter; policy accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
chain icmpv4 {
|
||||||
|
icmp type echo-reply accept
|
||||||
|
icmp type echo-request limit rate 1/second burst 3 packets accept
|
||||||
|
icmp type echo-request drop
|
||||||
|
drop
|
||||||
|
}
|
||||||
|
|
||||||
|
chain icmpv6 {
|
||||||
|
icmpv6 type destination-unreachable accept
|
||||||
|
icmpv6 type packet-too-big limit rate 5/minute accept
|
||||||
|
icmpv6 type time-exceeded limit rate 5/minute accept
|
||||||
|
icmpv6 type echo-request limit rate 1/second burst 3 packets accept
|
||||||
|
icmpv6 type echo-request drop
|
||||||
|
icmpv6 type echo-reply accept
|
||||||
|
icmpv6 type nd-router-solicit accept
|
||||||
|
icmpv6 type nd-router-advert accept
|
||||||
|
icmpv6 type nd-neighbor-solicit accept
|
||||||
|
icmpv6 type nd-neighbor-advert accept
|
||||||
|
drop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table ip nat {
|
||||||
|
chain prerouting {
|
||||||
|
type nat hook prerouting priority dstnat;
|
||||||
|
iifname "enp0s6" tcp dport 80 dnat to 10.15.0.45:80
|
||||||
|
iifname "enp0s6" tcp dport 443 dnat to 10.15.0.45:443
|
||||||
|
}
|
||||||
|
|
||||||
|
chain postrouting {
|
||||||
|
type nat hook postrouting priority srcnat;
|
||||||
|
ip saddr 10.15.0.0/16 masquerade
|
||||||
|
ip saddr 172.17.0.0/16 masquerade
|
||||||
|
oifname "wg3" masquerade
|
||||||
|
# Hairpin NAT (clientes locais acessando o IP público)
|
||||||
|
oifname "enp0s6" ip daddr 10.15.0.45 tcp dport { 80, 443 } masquerade
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table ip6 nat {
|
||||||
|
chain prerouting {
|
||||||
|
type nat hook prerouting priority dstnat;
|
||||||
|
iifname "enp0s6" tcp dport 80 dnat to [2001:db8:15::45]:80
|
||||||
|
iifname "enp0s6" tcp dport 443 dnat to [2001:db8:15::45]:443
|
||||||
|
}
|
||||||
|
|
||||||
|
chain postrouting {
|
||||||
|
type nat hook postrouting priority srcnat;
|
||||||
|
ip6 saddr 2001:db8:15::/48 masquerade
|
||||||
|
oifname "enp0s6" ip6 daddr 2001:db8:15::45 tcp dport { 80, 443 } masquerade
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
|
||||||
|
#
|
||||||
|
# rc.local — server-mv
|
||||||
|
#
|
||||||
|
# Versão nftables. O firewall (antes ~200 linhas de iptables/ip6tables
|
||||||
|
# espalhadas entre rc.local e test.sh) agora é um único arquivo
|
||||||
|
# declarativo: nftables.conf. A versão anterior, baseada em
|
||||||
|
# iptables/ip6tables, foi preservada em rc.local.iptables — troque de
|
||||||
|
# volta se algo aqui quebrar e você precisar reverter rápido.
|
||||||
|
#
|
||||||
|
# Antes de usar este arquivo em produção:
|
||||||
|
# nft -c -f /home/rodrigo/server-mv/nftables.conf # valida sintaxe
|
||||||
|
# systemctl enable nftables # persistência
|
||||||
|
# Não rode este rc.local E o antigo (rc.local.iptables) ao mesmo tempo —
|
||||||
|
# são duas ferramentas de firewall (nft e iptables-legacy/nft-em-cima-
|
||||||
|
# de-iptables) que podem conflitar se ambas tentarem gerenciar as
|
||||||
|
# mesmas chains.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 1. FIREWALL (nftables) — carregamento atômico: se houver erro de
|
||||||
|
# sintaxe no arquivo, o "nft -f" falha ANTES de aplicar qualquer coisa,
|
||||||
|
# então nunca fica um firewall pela metade no ar.
|
||||||
|
# ==============================================================
|
||||||
|
nft -f /home/rodrigo/server-mv/nftables.conf
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 2. Ajustes de kernel dependentes de runtime (não cobertos por sysctl.conf)
|
||||||
|
# ==============================================================
|
||||||
|
echo deadline > /sys/block/sda/queue/scheduler
|
||||||
|
echo 64 > /sys/block/sda/queue/max_sectors_kb # melhor para SSD virtual
|
||||||
|
|
||||||
|
echo f > /sys/class/net/enp0s6/queues/tx-0/xps_cpus
|
||||||
|
echo f > /sys/class/net/enp0s6/queues/rx-0/rps_cpus
|
||||||
|
echo 4096 > /sys/class/net/enp0s6/queues/rx-0/rps_flow_cnt
|
||||||
|
|
||||||
|
#tc qdisc add dev enp0s6 root cake bandwidth 1000mbit besteffort
|
||||||
|
#tc qdisc add dev wg0 root cake bandwidth 500mbit besteffort dual-dsthost
|
||||||
|
|
||||||
|
touch /var/lock/subsys/local
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 3. XDP / eBPF
|
||||||
|
# ==============================================================
|
||||||
|
ip link set enp0s6 mtu 1500
|
||||||
|
ip link set dev enp0s6 xdp obj /home/rodrigo/xdp/xdp_ddos_filter.o sec xdp
|
||||||
|
ip link set dev wg0 xdp obj /home/rodrigo/xdp/xdp_wg_mtu_optimizer.o sec xdp
|
||||||
|
|
||||||
|
# A bridge do Docker muda de nome a cada recriação da rede (hash do ID),
|
||||||
|
# então esse link precisa ser reaplicado se a rede "dockernet" for recriada.
|
||||||
|
ip link set dev br-02f7820020b4 xdp obj /home/rodrigo/xdp/bridge_xdp.o sec xdp
|
||||||
|
|
||||||
|
cd /home/docker/ebpf-sockmap/ && make load
|
||||||
Executable
+219
@@ -0,0 +1,219 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
|
||||||
|
#
|
||||||
|
# rc.local — server-mv
|
||||||
|
# Reescrito para unificar o que antes estava dividido entre rc.local e
|
||||||
|
# test.sh (test.sh não é mais chamado a partir daqui — ver test.sh.old).
|
||||||
|
#
|
||||||
|
# Problemas do script antigo corrigidos aqui:
|
||||||
|
# - test.sh rodava "ip6tables -F" DEPOIS que este arquivo já tinha
|
||||||
|
# criado a regra de aceite de DHCPv6 (porta 546), apagando-a.
|
||||||
|
# - test.sh adicionava "ip6tables -A INPUT -j DROP" no meio do script,
|
||||||
|
# tornando toda regra de ICMPv6 adicionada depois dela morta (nunca
|
||||||
|
# era avaliada, pois o DROP já tinha batido antes).
|
||||||
|
# - DNAT de 80/443 estava duplicado (uma vez aqui, outra em test.sh,
|
||||||
|
# essa segunda sem porta de destino explícita).
|
||||||
|
# - Regra "-s 192.168.0.0/16 -j DROP" não estava restrita à interface
|
||||||
|
# WAN (enp0s6), então valeria para qualquer interface, inclusive
|
||||||
|
# futuras redes internas nesse range.
|
||||||
|
#
|
||||||
|
# Ordem importa em iptables: -A sempre anexa ao FIM da chain, então a
|
||||||
|
# ordem das linhas abaixo é a ordem de avaliação em tempo real.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 1. FIREWALL — flush único, no início, de tudo (v4 e v6)
|
||||||
|
# ==============================================================
|
||||||
|
iptables -F
|
||||||
|
iptables -X
|
||||||
|
iptables -t nat -F
|
||||||
|
iptables -t nat -X
|
||||||
|
iptables -t mangle -F
|
||||||
|
iptables -t mangle -X
|
||||||
|
|
||||||
|
ip6tables -F
|
||||||
|
ip6tables -X
|
||||||
|
ip6tables -t nat -F
|
||||||
|
ip6tables -t nat -X
|
||||||
|
ip6tables -t mangle -F
|
||||||
|
ip6tables -t mangle -X
|
||||||
|
|
||||||
|
# Políticas padrão (definidas ANTES de qualquer regra ser criada)
|
||||||
|
iptables -P INPUT DROP
|
||||||
|
iptables -P FORWARD ACCEPT
|
||||||
|
iptables -P OUTPUT ACCEPT
|
||||||
|
|
||||||
|
ip6tables -P INPUT DROP
|
||||||
|
ip6tables -P FORWARD ACCEPT
|
||||||
|
ip6tables -P OUTPUT ACCEPT
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 2. IPv4 — chains auxiliares
|
||||||
|
# ==============================================================
|
||||||
|
iptables -N SYN_FLOOD
|
||||||
|
iptables -A SYN_FLOOD -m limit --limit 10/sec --limit-burst 24 -j RETURN
|
||||||
|
iptables -A SYN_FLOOD -j DROP
|
||||||
|
|
||||||
|
iptables -N BAD_PACKETS
|
||||||
|
iptables -A BAD_PACKETS -m recent --set --name BAD_IPS
|
||||||
|
iptables -A BAD_PACKETS -m recent --update --seconds 300 --hitcount 5 --name BAD_IPS -j DROP
|
||||||
|
|
||||||
|
iptables -N ICMPv4
|
||||||
|
iptables -A ICMPv4 -p icmp --icmp-type 0 -j ACCEPT
|
||||||
|
iptables -A ICMPv4 -p icmp --icmp-type 8 -m limit --limit 1/s --limit-burst 3 -j ACCEPT
|
||||||
|
iptables -A ICMPv4 -p icmp --icmp-type 8 -j DROP
|
||||||
|
iptables -A ICMPv4 -p icmp -j DROP
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 3. IPv4 — INPUT (ordem: mais barato/mais usado primeiro)
|
||||||
|
# ==============================================================
|
||||||
|
iptables -A INPUT -i lo -j ACCEPT
|
||||||
|
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
|
||||||
|
# ICMP logo em seguida: medição real mostrou ~66k pacotes/dia, 2º maior
|
||||||
|
# volume do INPUT depois de established/related — não faz sentido deixar
|
||||||
|
# por último, obrigando todo ping a atravessar as demais regras primeiro.
|
||||||
|
iptables -A INPUT -p icmp -j ICMPv4
|
||||||
|
|
||||||
|
# Pacotes malformados / scan
|
||||||
|
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j BAD_PACKETS
|
||||||
|
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j BAD_PACKETS
|
||||||
|
iptables -A INPUT -f -j DROP
|
||||||
|
|
||||||
|
# Anti-spoof: só faz sentido na WAN (rede interna real é 10.15.0.0/16 e 10.99.1.0/24)
|
||||||
|
iptables -A INPUT -i enp0s6 -s 192.168.0.0/16 -j DROP
|
||||||
|
|
||||||
|
# SYN flood
|
||||||
|
iptables -A INPUT -p tcp --syn -j SYN_FLOOD
|
||||||
|
|
||||||
|
# Rate limit de conexões novas na porta de SSH (8117)
|
||||||
|
iptables -A INPUT -p tcp --dport 8117 -m conntrack --ctstate NEW -m recent --set --name SSH
|
||||||
|
iptables -A INPUT -p tcp --dport 8117 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 3 --name SSH -j DROP
|
||||||
|
|
||||||
|
# Túneis / VPN
|
||||||
|
iptables -A INPUT -i wg0 -j ACCEPT
|
||||||
|
iptables -A INPUT -i tun0 -j ACCEPT
|
||||||
|
iptables -A INPUT -p udp --dport 1194 -j ACCEPT
|
||||||
|
|
||||||
|
# Serviços expostos na WAN
|
||||||
|
iptables -A INPUT -i enp0s6 -p tcp -m multiport --dports 80,443,8117 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p udp -m multiport --dports 32100,32101,32110,32400 -j ACCEPT
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 4. IPv4 — FORWARD
|
||||||
|
# ==============================================================
|
||||||
|
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
iptables -A FORWARD -i tun0 -j ACCEPT
|
||||||
|
iptables -A FORWARD -i wg0 -j ACCEPT
|
||||||
|
iptables -A FORWARD -p tcp -d 10.15.0.45 --dport 80 -j ACCEPT
|
||||||
|
iptables -A FORWARD -p tcp -d 10.15.0.45 --dport 443 -j ACCEPT
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 5. IPv4 — NAT
|
||||||
|
# ==============================================================
|
||||||
|
iptables -t nat -A POSTROUTING -s 10.15.0.0/16 -j MASQUERADE
|
||||||
|
iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j MASQUERADE
|
||||||
|
iptables -t nat -A POSTROUTING -o wg3 -j MASQUERADE
|
||||||
|
|
||||||
|
# DNAT (WAN -> nginx reverso interno) — porta de destino explícita, sem duplicar
|
||||||
|
iptables -t nat -A PREROUTING -i enp0s6 -p tcp --dport 80 -j DNAT --to-destination 10.15.0.45:80
|
||||||
|
iptables -t nat -A PREROUTING -i enp0s6 -p tcp --dport 443 -j DNAT --to-destination 10.15.0.45:443
|
||||||
|
|
||||||
|
# Hairpin NAT para clientes locais acessando o IP público
|
||||||
|
iptables -t nat -A POSTROUTING -o enp0s6 -p tcp -d 10.15.0.45 --dport 80 -j MASQUERADE
|
||||||
|
iptables -t nat -A POSTROUTING -o enp0s6 -p tcp -d 10.15.0.45 --dport 443 -j MASQUERADE
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 6. IPv6 — chains auxiliares
|
||||||
|
# ==============================================================
|
||||||
|
ip6tables -N SYN_FLOOD
|
||||||
|
ip6tables -A SYN_FLOOD -m limit --limit 10/sec --limit-burst 24 -j RETURN
|
||||||
|
ip6tables -A SYN_FLOOD -j DROP
|
||||||
|
|
||||||
|
ip6tables -N BAD_PACKETS
|
||||||
|
ip6tables -A BAD_PACKETS -m recent --set --name BAD_IPS6
|
||||||
|
ip6tables -A BAD_PACKETS -m recent --update --seconds 300 --hitcount 5 --name BAD_IPS6 -j DROP
|
||||||
|
|
||||||
|
ip6tables -N ICMPv6
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 1 -j ACCEPT
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 2 -m limit --limit 5/min -j ACCEPT
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 3 -m limit --limit 5/min -j ACCEPT
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 128 -m limit --limit 1/s --limit-burst 3 -j ACCEPT
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 129 -j ACCEPT
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 133 -j ACCEPT
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 135 -j ACCEPT
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 136 -j ACCEPT
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp -j DROP
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 7. IPv6 — INPUT
|
||||||
|
# (NOTA: nenhuma regra "-j DROP" solta aqui — a policy INPUT DROP,
|
||||||
|
# já definida no passo 1, é o catch-all. Colocar um DROP explícito
|
||||||
|
# no meio da chain era o bug que matava as regras seguintes.)
|
||||||
|
# ==============================================================
|
||||||
|
ip6tables -A INPUT -i lo -j ACCEPT
|
||||||
|
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
ip6tables -A INPUT -p udp --dport 546 -j ACCEPT # DHCPv6 client
|
||||||
|
|
||||||
|
ip6tables -A INPUT -p tcp --tcp-flags ALL ALL -j BAD_PACKETS
|
||||||
|
ip6tables -A INPUT -p tcp --tcp-flags ALL NONE -j BAD_PACKETS
|
||||||
|
|
||||||
|
# Anti-spoof
|
||||||
|
ip6tables -A INPUT -i enp0s6 -s fe80::/10 -j DROP
|
||||||
|
ip6tables -A INPUT -i enp0s6 -s fd00::/8 -j DROP
|
||||||
|
ip6tables -A INPUT -d ff00::/8 -j DROP
|
||||||
|
|
||||||
|
ip6tables -A INPUT -p tcp --syn -j SYN_FLOOD
|
||||||
|
ip6tables -A INPUT -p ipv6-icmp -j ICMPv6
|
||||||
|
|
||||||
|
# Rate limit SSH (8117)
|
||||||
|
ip6tables -A INPUT -p tcp --dport 8117 -m conntrack --ctstate NEW -m recent --set --name SSH6
|
||||||
|
ip6tables -A INPUT -p tcp --dport 8117 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 3 --name SSH6 -j DROP
|
||||||
|
|
||||||
|
ip6tables -A INPUT -i wg0 -j ACCEPT
|
||||||
|
ip6tables -A INPUT -i enp0s6 -p tcp -m multiport --dports 80,443,8117 -j ACCEPT
|
||||||
|
ip6tables -A INPUT -i enp0s6 -p udp -m multiport --dports 32100,32400 -j ACCEPT
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 8. IPv6 — FORWARD / NAT
|
||||||
|
# ==============================================================
|
||||||
|
ip6tables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
ip6tables -A FORWARD -i wg0 -j ACCEPT
|
||||||
|
ip6tables -A FORWARD -p tcp -d 2001:db8:15::45 --dport 80 -j ACCEPT
|
||||||
|
ip6tables -A FORWARD -p tcp -d 2001:db8:15::45 --dport 443 -j ACCEPT
|
||||||
|
|
||||||
|
ip6tables -t nat -A POSTROUTING -s 2001:db8:15::/48 -j MASQUERADE
|
||||||
|
|
||||||
|
ip6tables -t nat -A PREROUTING -i enp0s6 -p tcp --dport 80 -j DNAT --to-destination [2001:db8:15::45]:80
|
||||||
|
ip6tables -t nat -A PREROUTING -i enp0s6 -p tcp --dport 443 -j DNAT --to-destination [2001:db8:15::45]:443
|
||||||
|
ip6tables -t nat -A POSTROUTING -o enp0s6 -p tcp -d 2001:db8:15::45 --dport 80 -j MASQUERADE
|
||||||
|
ip6tables -t nat -A POSTROUTING -o enp0s6 -p tcp -d 2001:db8:15::45 --dport 443 -j MASQUERADE
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 9. Ajustes de kernel dependentes de runtime (não cobertos por sysctl.conf)
|
||||||
|
# ==============================================================
|
||||||
|
echo deadline > /sys/block/sda/queue/scheduler
|
||||||
|
echo 64 > /sys/block/sda/queue/max_sectors_kb # melhor para SSD virtual
|
||||||
|
|
||||||
|
echo f > /sys/class/net/enp0s6/queues/tx-0/xps_cpus
|
||||||
|
echo f > /sys/class/net/enp0s6/queues/rx-0/rps_cpus
|
||||||
|
echo 4096 > /sys/class/net/enp0s6/queues/rx-0/rps_flow_cnt
|
||||||
|
|
||||||
|
#tc qdisc add dev enp0s6 root cake bandwidth 1000mbit besteffort
|
||||||
|
#tc qdisc add dev wg0 root cake bandwidth 500mbit besteffort dual-dsthost
|
||||||
|
|
||||||
|
touch /var/lock/subsys/local
|
||||||
|
|
||||||
|
# ==============================================================
|
||||||
|
# 10. XDP / eBPF
|
||||||
|
# ==============================================================
|
||||||
|
ip link set enp0s6 mtu 1500
|
||||||
|
ip link set dev enp0s6 xdp obj /home/rodrigo/xdp/xdp_ddos_filter.o sec xdp
|
||||||
|
ip link set dev wg0 xdp obj /home/rodrigo/xdp/xdp_wg_mtu_optimizer.o sec xdp
|
||||||
|
|
||||||
|
# A bridge do Docker muda de nome a cada recriação da rede (hash do ID),
|
||||||
|
# então esse link precisa ser reaplicado se a rede "dockernet" for recriada.
|
||||||
|
ip link set dev br-02f7820020b4 xdp obj /home/rodrigo/xdp/bridge_xdp.o sec xdp
|
||||||
|
|
||||||
|
cd /home/docker/ebpf-sockmap/ && make load
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
# ==============================================================
|
||||||
|
# sysctl.conf — server-mv (Oracle Cloud ARM64, 4 vCPU / 24GB RAM)
|
||||||
|
# Reescrito para remover duplicatas e organizar por categoria.
|
||||||
|
# Aplicar com: sysctl -p /etc/sysctl.d/99-server-mv.conf
|
||||||
|
#
|
||||||
|
# IMPORTANTE: este host roda o daemon "bpftune" (confirmado via
|
||||||
|
# `bpftool prog list` — tuners tcp-buffer, tcp-conn, net-buffer e
|
||||||
|
# neigh-table ativos). Esses tuners ajustam em tempo real, via eBPF,
|
||||||
|
# exatamente os mesmos knobs que costumam ir num sysctl.conf estático
|
||||||
|
# (tcp_rmem/wmem, buffers de rede, backlog de SYN, tabela de vizinhança).
|
||||||
|
# Por isso, DE PROPÓSITO, essas chaves NÃO são fixadas aqui — deixamos
|
||||||
|
# o bpftune reagir à carga real em vez de brigar com um valor fixo
|
||||||
|
# (foi essa disputa, aliás, que gerou a bagunça do sysctl.conf antigo:
|
||||||
|
# algo/alguém reescrevendo os mesmos valores repetidamente tentando
|
||||||
|
# "vencer" o bpftune). O que continua fixo abaixo é só o que o bpftune
|
||||||
|
# não gerencia: segurança, memória, filesystem, congestion control e
|
||||||
|
# timeouts diversos.
|
||||||
|
# ==============================================================
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 🚀 REDE — o que o bpftune NÃO gerencia
|
||||||
|
# ==============================
|
||||||
|
net.ipv4.tcp_fastopen = 3
|
||||||
|
net.ipv4.tcp_synack_retries = 3
|
||||||
|
net.ipv4.tcp_max_tw_buckets = 200000
|
||||||
|
|
||||||
|
net.ipv4.tcp_fin_timeout = 10
|
||||||
|
net.ipv4.tcp_keepalive_time = 60
|
||||||
|
net.ipv4.tcp_keepalive_intvl = 10
|
||||||
|
net.ipv4.tcp_keepalive_probes = 6
|
||||||
|
|
||||||
|
net.ipv4.tcp_congestion_control = bbr
|
||||||
|
net.core.default_qdisc = fq_codel
|
||||||
|
|
||||||
|
net.ipv4.ip_local_port_range = 20000 65000
|
||||||
|
|
||||||
|
# RPS/RFS (referenciado pelo rc.local via /sys/class/net/.../rps_*)
|
||||||
|
net.core.rps_sock_flow_entries = 4096
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 🔒 SEGURANÇA DA PILHA TCP/IP
|
||||||
|
# ==============================
|
||||||
|
net.ipv4.tcp_syncookies = 1
|
||||||
|
net.ipv4.conf.all.rp_filter = 1
|
||||||
|
net.ipv4.conf.default.rp_filter = 1
|
||||||
|
net.ipv4.conf.all.accept_source_route = 0
|
||||||
|
net.ipv4.conf.default.accept_source_route = 0
|
||||||
|
|
||||||
|
# Forwarding (necessário para NAT/DNAT do Docker e WireGuard)
|
||||||
|
net.ipv4.ip_forward = 1
|
||||||
|
net.ipv6.conf.all.forwarding = 1
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 💾 GERENCIAMENTO DE MEMÓRIA
|
||||||
|
# ==============================
|
||||||
|
vm.dirty_ratio = 5
|
||||||
|
vm.dirty_background_ratio = 2
|
||||||
|
vm.dirty_bytes = 134217728
|
||||||
|
vm.dirty_background_bytes = 67108864
|
||||||
|
vm.vfs_cache_pressure = 50
|
||||||
|
vm.swappiness = 5
|
||||||
|
vm.zone_reclaim_mode = 0
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 📂 SISTEMA DE ARQUIVOS
|
||||||
|
# ==============================
|
||||||
|
fs.file-max = 2097152
|
||||||
|
fs.inotify.max_user_watches = 524288
|
||||||
|
fs.inotify.max_user_instances = 1024
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 🌎 IPv6
|
||||||
|
# ==============================
|
||||||
|
net.ipv6.conf.all.accept_ra = 0
|
||||||
|
net.ipv6.conf.default.accept_ra = 0
|
||||||
|
# gc_thresh3 (tabela de vizinhança) removido: bpftune tem um
|
||||||
|
# neigh-table-tuner ativo que ajusta isso dinamicamente.
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 🐳 CONNTRACK (host-level; containers herdam via netns quando não sobrescrito)
|
||||||
|
# ==============================
|
||||||
|
net.netfilter.nf_conntrack_max = 262144
|
||||||
+213
@@ -0,0 +1,213 @@
|
|||||||
|
###############################
|
||||||
|
####### SEGURANÇA REFORÇADA #######
|
||||||
|
###############################
|
||||||
|
|
||||||
|
#iptables -F
|
||||||
|
#iptables -X
|
||||||
|
#iptables -t nat -F
|
||||||
|
#iptables -t nat -X
|
||||||
|
|
||||||
|
# Proteção básica contra SYN Flood
|
||||||
|
iptables -N SYN_FLOOD
|
||||||
|
iptables -A SYN_FLOOD -m limit --limit 10/sec --limit-burst 24 -j RETURN
|
||||||
|
iptables -A SYN_FLOOD -j LOG --log-prefix "SYN Flood: "
|
||||||
|
iptables -A SYN_FLOOD -j DROP
|
||||||
|
iptables -A INPUT -p tcp --syn -j SYN_FLOOD
|
||||||
|
|
||||||
|
# Proteção contra port scanners (detecção de tentativas de conexão em portas fechadas)
|
||||||
|
iptables -N PORTSCAN
|
||||||
|
iptables -A PORTSCAN -m recent --set --name PORTSCAN
|
||||||
|
iptables -A PORTSCAN -j DROP
|
||||||
|
iptables -A INPUT -m recent --update --seconds 60 --hitcount 1 --name PORTSCAN -j DROP
|
||||||
|
|
||||||
|
# Bloqueio de IPs suspeitos após 5 tentativas de conexão inválidas
|
||||||
|
iptables -N BAD_PACKETS
|
||||||
|
iptables -A BAD_PACKETS -m recent --set --name BAD_IPS
|
||||||
|
#iptables -A BAD_PACKETS -m recent --update --seconds 300 --hitcount 5 --name BAD_IPS -j LOG --log-prefix "IP Bloqueado: "
|
||||||
|
iptables -A BAD_PACKETS -m recent --update --seconds 300 --hitcount 5 --name BAD_IPS -j DROP
|
||||||
|
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j BAD_PACKETS # Pacotes inválidos
|
||||||
|
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j BAD_PACKETS # NULL packets
|
||||||
|
|
||||||
|
# Rate limiting para prevenção de brute force (exemplo para SSH - ajuste a porta)
|
||||||
|
iptables -A INPUT -p tcp --dport 8117 -m state --state NEW -m recent --set --name SSH
|
||||||
|
iptables -A INPUT -p tcp --dport 8117 -m state --state NEW -m recent --update --seconds 60 --hitcount 3 --name SSH -j DROP
|
||||||
|
|
||||||
|
###############################
|
||||||
|
####### OTIMIZAÇÃO DE PERFORMANCE #######
|
||||||
|
###############################
|
||||||
|
|
||||||
|
# Ordenação estratégica das regras (mais usadas primeiro)
|
||||||
|
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Mover para início
|
||||||
|
|
||||||
|
# Agrupamento de portas com multiport
|
||||||
|
iptables -A INPUT -i enp0s6 -p tcp -m multiport --dports 80,443,8117 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p udp -m multiport --dports 32100,32400 -j ACCEPT
|
||||||
|
|
||||||
|
# Compactação de regras NAT
|
||||||
|
iptables -t nat -A PREROUTING -i enp0s6 -p tcp -m multiport --dports 80,443 -j DNAT --to-destination 10.15.0.45
|
||||||
|
|
||||||
|
###############################
|
||||||
|
####### AJUSTES DE SISTEMA #######
|
||||||
|
###############################
|
||||||
|
|
||||||
|
# Melhor tratamento de pacotes fragmentados
|
||||||
|
iptables -A INPUT -f -j DROP
|
||||||
|
|
||||||
|
# Prevenção de spoofing
|
||||||
|
#iptables -A INPUT -s 10.0.0.0/8 -j DROP
|
||||||
|
#iptables -A INPUT -s 172.16.0.0/12 -j DROP
|
||||||
|
iptables -A INPUT -s 192.168.0.0/16 -j DROP
|
||||||
|
|
||||||
|
# Logging de tráfego bloqueado (use com moderação)
|
||||||
|
#iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "IPTables-Dropped: " --log-level 4
|
||||||
|
|
||||||
|
###############################
|
||||||
|
####### POLÍTICAS SEGURAS #######
|
||||||
|
###############################
|
||||||
|
|
||||||
|
# Política mais restritiva para FORWARD
|
||||||
|
iptables -P FORWARD ACCEPT
|
||||||
|
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
|
||||||
|
iptables -I INPUT -i wg0 -j ACCEPT
|
||||||
|
iptables -I FORWARD -i wg0 -j ACCEPT
|
||||||
|
|
||||||
|
|
||||||
|
# Limpar todas as regras IPv6
|
||||||
|
ip6tables -F
|
||||||
|
ip6tables -X
|
||||||
|
ip6tables -t mangle -F
|
||||||
|
ip6tables -t mangle -X
|
||||||
|
ip6tables -t nat -F
|
||||||
|
ip6tables -t nat -X
|
||||||
|
|
||||||
|
# Definir políticas padrão
|
||||||
|
ip6tables -P INPUT DROP
|
||||||
|
ip6tables -P FORWARD ACCEPT
|
||||||
|
ip6tables -P OUTPUT ACCEPT
|
||||||
|
|
||||||
|
# Habilitar redirecionamento IPv6
|
||||||
|
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
|
||||||
|
|
||||||
|
# --- REGRAS BÁSICAS ---
|
||||||
|
ip6tables -A INPUT -i lo -j ACCEPT
|
||||||
|
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
|
||||||
|
# Permitir ICMPv6 (essencial para funcionamento do IPv6)
|
||||||
|
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
|
||||||
|
|
||||||
|
# Portas abertas (ajuste a interface)
|
||||||
|
ip6tables -A INPUT -i enp0s6 -p udp --dport 32100 -j ACCEPT
|
||||||
|
ip6tables -A INPUT -i enp0s6 -p udp --dport 32400 -j ACCEPT
|
||||||
|
ip6tables -A INPUT -i enp0s6 -p tcp --dport 8117 -j ACCEPT
|
||||||
|
ip6tables -A INPUT -i enp0s6 -p tcp --dport 80 -j ACCEPT
|
||||||
|
ip6tables -A INPUT -i enp0s6 -p tcp --dport 443 -j ACCEPT
|
||||||
|
|
||||||
|
# WireGuard
|
||||||
|
ip6tables -A INPUT -i wg0 -j ACCEPT
|
||||||
|
ip6tables -A FORWARD -i wg0 -j ACCEPT
|
||||||
|
|
||||||
|
# --- NAT IPv6 ---
|
||||||
|
ip6tables -t nat -A POSTROUTING -s 2001:db8:15::/48 -j MASQUERADE
|
||||||
|
|
||||||
|
# --- REGRAS DE SEGURANÇA ---
|
||||||
|
# Proteção contra SYN Flood (mesma configuração)
|
||||||
|
ip6tables -N SYN_FLOOD
|
||||||
|
ip6tables -A SYN_FLOOD -m limit --limit 10/sec --limit-burst 24 -j RETURN
|
||||||
|
#ip6tables -A SYN_FLOOD -j LOG --log-prefix "IPv6 SYN Flood: "
|
||||||
|
ip6tables -A SYN_FLOOD -j DROP
|
||||||
|
ip6tables -A INPUT -p tcp --syn -j SYN_FLOOD
|
||||||
|
|
||||||
|
# Prevenção de port scanners
|
||||||
|
ip6tables -N PORTSCAN
|
||||||
|
ip6tables -A PORTSCAN -m recent --set --name PORTSCAN6
|
||||||
|
ip6tables -A PORTSCAN -j DROP
|
||||||
|
ip6tables -A INPUT -m recent --update --seconds 60 --hitcount 1 --name PORTSCAN6 -j DROP
|
||||||
|
|
||||||
|
# Bloqueio de IPs suspeitos
|
||||||
|
ip6tables -N BAD_PACKETS
|
||||||
|
ip6tables -A BAD_PACKETS -m recent --set --name BAD_IPS6
|
||||||
|
#ip6tables -A BAD_PACKETS -m recent --update --seconds 300 --hitcount 5 --name BAD_IPS6 -j LOG --log-prefix "IPv6 IP Bloqueado: "
|
||||||
|
ip6tables -A BAD_PACKETS -m recent --update --seconds 300 --hitcount 5 --name BAD_IPS6 -j DROP
|
||||||
|
ip6tables -A INPUT -p tcp --tcp-flags ALL ALL -j BAD_PACKETS
|
||||||
|
ip6tables -A INPUT -p tcp --tcp-flags ALL NONE -j BAD_PACKETS
|
||||||
|
|
||||||
|
# Rate limiting SSH
|
||||||
|
ip6tables -A INPUT -p tcp --dport 8117 -m state --state NEW -m recent --set --name SSH6
|
||||||
|
ip6tables -A INPUT -p tcp --dport 8117 -m state --state NEW -m recent --update --seconds 60 --hitcount 3 --name SSH6 -j DROP
|
||||||
|
|
||||||
|
# --- OTIMIZAÇÕES ---
|
||||||
|
ip6tables -A INPUT -i enp0s6 -p tcp -m multiport --dports 80,443,8117 -j ACCEPT
|
||||||
|
ip6tables -A INPUT -i enp0s6 -p udp -m multiport --dports 32100,32400 -j ACCEPT
|
||||||
|
|
||||||
|
# --- AJUSTES IPv6 ESPECÍFICOS ---
|
||||||
|
# Bloquear pacotes IPv6 inválidos
|
||||||
|
ip6tables -A INPUT -m rpfilter --validmark -j ACCEPT
|
||||||
|
ip6tables -A INPUT -j DROP
|
||||||
|
|
||||||
|
# Prevenção de spoofing IPv6
|
||||||
|
ip6tables -A INPUT -s fe80::/10 -i enp0s6 -j DROP # Bloqueia link-local na WAN
|
||||||
|
ip6tables -A INPUT -s fd00::/8 -i enp0s6 -j DROP # Bloqueia ULA na WAN
|
||||||
|
ip6tables -A INPUT -d ff00::/8 -j DROP # Bloqueia multicast
|
||||||
|
|
||||||
|
# --- LOGGING ---
|
||||||
|
#ip6tables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "IPv6 Dropped: " --log-level 4
|
||||||
|
|
||||||
|
|
||||||
|
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT # Permite vizinhação, descoberta de roteador, etc.
|
||||||
|
ip6tables -A INPUT -m rpfilter --validmark -j ACCEPT # Reverse Path Filtering
|
||||||
|
|
||||||
|
###############################
|
||||||
|
####### TUNING DE KERNEL #######
|
||||||
|
###############################
|
||||||
|
|
||||||
|
# Otimização de parâmetros de rede
|
||||||
|
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
|
||||||
|
echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter
|
||||||
|
echo 0 > /proc/sys/net/ipv4/conf/all/accept_source_route
|
||||||
|
echo "net.ipv4.tcp_max_syn_backlog = 2048" >> /etc/sysctl.conf
|
||||||
|
echo "net.ipv4.tcp_synack_retries = 3" >> /etc/sysctl.conf
|
||||||
|
sysctl -p
|
||||||
|
|
||||||
|
# Criação da cadeia ICMPv4
|
||||||
|
iptables -N ICMPv4
|
||||||
|
|
||||||
|
# Permitir Echo Reply (tipo 0) sem limitação
|
||||||
|
iptables -A ICMPv4 -p icmp --icmp-type 0 -j ACCEPT
|
||||||
|
|
||||||
|
# Limitar Echo Request (tipo 8) para 1/s com burst de 3
|
||||||
|
iptables -A ICMPv4 -p icmp --icmp-type 8 -m limit --limit 1/s --limit-burst 3 -j ACCEPT
|
||||||
|
|
||||||
|
# Bloquear e registrar excesso de Echo Request
|
||||||
|
#iptables -A ICMPv4 -p icmp --icmp-type 8 -j LOG --log-prefix "Ping flood bloqueado: "
|
||||||
|
iptables -A ICMPv4 -p icmp --icmp-type 8 -j DROP
|
||||||
|
|
||||||
|
# Bloquear todos os outros tipos de ICMP
|
||||||
|
iptables -A ICMPv4 -p icmp -j DROP
|
||||||
|
|
||||||
|
# Aplicar cadeia ICMPv4 à entrada
|
||||||
|
iptables -A INPUT -p icmp -j ICMPv4
|
||||||
|
|
||||||
|
|
||||||
|
# Criação da cadeia ICMPv6
|
||||||
|
ip6tables -N ICMPv6
|
||||||
|
|
||||||
|
# Tipos essenciais para funcionamento básico (permitir sem limitação)
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 1 -j ACCEPT # Destino inalcançável
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 128 -m limit --limit 1/s --limit-burst 3 -j ACCEPT # Echo Request
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 129 -j ACCEPT # Echo Reply
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 133/135/136 -j ACCEPT # Descoberta de vizinhança
|
||||||
|
|
||||||
|
# Limitar outros tipos importantes
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 2 -m limit --limit 5/min -j ACCEPT # Packet Too Big
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 3 -m limit --limit 5/min -j ACCEPT # Time Exceeded
|
||||||
|
|
||||||
|
# Bloquear e registrar excesso de Echo Request
|
||||||
|
#ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 128 -j LOG --log-prefix "Ping6 flood bloqueado: "
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp --icmpv6-type 128 -j DROP
|
||||||
|
|
||||||
|
# Bloquear todos os outros tipos
|
||||||
|
ip6tables -A ICMPv6 -p ipv6-icmp -j DROP
|
||||||
|
|
||||||
|
# Aplicar cadeia ICMPv6 à entrada
|
||||||
|
ip6tables -A INPUT -p ipv6-icmp -j ICMPv6
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
LLC ?= llc
|
||||||
|
CLANG ?= clang
|
||||||
|
CC ?= gcc
|
||||||
|
|
||||||
|
all: bridge_xdp.o loader
|
||||||
|
|
||||||
|
bridge_xdp.o: bridge_xdp.c
|
||||||
|
$(CLANG) -S \
|
||||||
|
-target bpf \
|
||||||
|
-D __BPF_TRACING__ \
|
||||||
|
-I/usr/include/x86_64-linux-gnu \
|
||||||
|
-Wall \
|
||||||
|
-O2 -emit-llvm -c -o bridge_xdp.ll bridge_xdp.c
|
||||||
|
$(LLC) -march=bpf -filetype=obj -o bridge_xdp.o bridge_xdp.ll
|
||||||
|
|
||||||
|
loader: loader.c
|
||||||
|
$(CC) -Wall -o loader loader.c -l:libbpf.a -lelf -lz
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f bridge_xdp.o bridge_xdp.ll loader
|
||||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,158 @@
|
|||||||
|
// Exemplo AF_XDP com RX, TX, multi-queue e recycling, sem libbpf/xsk.h
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <linux/if_xdp.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#define NUM_FRAMES 4096
|
||||||
|
#define FRAME_SIZE 2048
|
||||||
|
#define UMEM_SIZE (NUM_FRAMES * FRAME_SIZE)
|
||||||
|
#define RX_RING_SIZE 2048
|
||||||
|
#define TX_RING_SIZE 2048
|
||||||
|
#define FILL_RING_SIZE 2048
|
||||||
|
#define COMP_RING_SIZE 2048
|
||||||
|
#define BATCH_SIZE 64
|
||||||
|
|
||||||
|
static inline void die(const char *msg) {
|
||||||
|
perror(msg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t roundup_page(size_t sz) {
|
||||||
|
size_t pg = sysconf(_SC_PAGESIZE);
|
||||||
|
return ((sz + pg - 1) / pg) * pg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers para acessar os pointers dos rings
|
||||||
|
#define RING_PROD_PTR(ring) ((volatile __u32 *)(ring))
|
||||||
|
#define RING_CONS_PTR(ring) ((volatile __u32 *)(ring) + 1)
|
||||||
|
#define RING_DESC(ring) ((void *)((volatile __u32 *)(ring) + 2))
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc < 2) {
|
||||||
|
printf("Uso: %s <iface> [queue_id]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
const char *ifname = argv[1];
|
||||||
|
int queue_id = 0;
|
||||||
|
if (argc > 2)
|
||||||
|
queue_id = atoi(argv[2]);
|
||||||
|
int ifindex = if_nametoindex(ifname);
|
||||||
|
if (!ifindex) die("if_nametoindex");
|
||||||
|
|
||||||
|
// 1. Crie socket AF_XDP
|
||||||
|
int xdp_sock = socket(AF_XDP, SOCK_RAW, 0);
|
||||||
|
if (xdp_sock < 0) die("socket(AF_XDP)");
|
||||||
|
|
||||||
|
// 2. Alocar UMEM
|
||||||
|
void *umem_area = NULL;
|
||||||
|
if (posix_memalign(&umem_area, getpagesize(), UMEM_SIZE))
|
||||||
|
die("posix_memalign");
|
||||||
|
|
||||||
|
// 3. Registrar UMEM no kernel
|
||||||
|
struct xdp_umem_reg mr = {
|
||||||
|
.addr = (__u64)(uintptr_t)umem_area,
|
||||||
|
.len = UMEM_SIZE,
|
||||||
|
.chunk_size = FRAME_SIZE,
|
||||||
|
.headroom = 0,
|
||||||
|
.flags = 0,
|
||||||
|
};
|
||||||
|
if (setsockopt(xdp_sock, SOL_XDP, XDP_UMEM_REG, &mr, sizeof(mr)) < 0)
|
||||||
|
die("setsockopt(XDP_UMEM_REG)");
|
||||||
|
|
||||||
|
// 4. Configurar tamanho dos rings
|
||||||
|
int sz = FILL_RING_SIZE;
|
||||||
|
if (setsockopt(xdp_sock, SOL_XDP, XDP_UMEM_FILL_RING, &sz, sizeof(sz)) < 0)
|
||||||
|
die("setsockopt(XDP_UMEM_FILL_RING)");
|
||||||
|
sz = COMP_RING_SIZE;
|
||||||
|
if (setsockopt(xdp_sock, SOL_XDP, XDP_UMEM_COMPLETION_RING, &sz, sizeof(sz)) < 0)
|
||||||
|
die("setsockopt(XDP_UMEM_COMPLETION_RING)");
|
||||||
|
sz = RX_RING_SIZE;
|
||||||
|
if (setsockopt(xdp_sock, SOL_XDP, XDP_RX_RING, &sz, sizeof(sz)) < 0)
|
||||||
|
die("setsockopt(XDP_RX_RING)");
|
||||||
|
sz = TX_RING_SIZE;
|
||||||
|
if (setsockopt(xdp_sock, SOL_XDP, XDP_TX_RING, &sz, sizeof(sz)) < 0)
|
||||||
|
die("setsockopt(XDP_TX_RING)");
|
||||||
|
|
||||||
|
// 5. mmap dos rings (tamanho exato, sem roundup_page)
|
||||||
|
size_t fill_ring_sz = FILL_RING_SIZE * sizeof(__u64) + 8;
|
||||||
|
size_t comp_ring_sz = COMP_RING_SIZE * sizeof(__u64) + 8;
|
||||||
|
size_t rx_ring_sz = RX_RING_SIZE * sizeof(struct xdp_desc) + 8;
|
||||||
|
size_t tx_ring_sz = TX_RING_SIZE * sizeof(struct xdp_desc) + 8;
|
||||||
|
|
||||||
|
void *fill_ring = mmap(NULL, fill_ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, xdp_sock, XDP_UMEM_PGOFF_FILL_RING);
|
||||||
|
if (fill_ring == MAP_FAILED) die("mmap(FILL_RING)");
|
||||||
|
void *comp_ring = mmap(NULL, comp_ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, xdp_sock, XDP_UMEM_PGOFF_COMPLETION_RING);
|
||||||
|
if (comp_ring == MAP_FAILED) die("mmap(COMP_RING)");
|
||||||
|
void *rx_ring = mmap(NULL, rx_ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, xdp_sock, XDP_PGOFF_RX_RING);
|
||||||
|
if (rx_ring == MAP_FAILED) die("mmap(RX_RING)");
|
||||||
|
void *tx_ring = mmap(NULL, tx_ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, xdp_sock, XDP_PGOFF_TX_RING);
|
||||||
|
if (tx_ring == MAP_FAILED) die("mmap(TX_RING)");
|
||||||
|
|
||||||
|
printf("fill_ring: %p, comp_ring: %p, rx_ring: %p, tx_ring: %p\n", fill_ring, comp_ring, rx_ring, tx_ring);
|
||||||
|
|
||||||
|
// Cheque ponteiros dos rings
|
||||||
|
printf("fill_prod_ptr: %p, fill_desc: %p\n", RING_PROD_PTR(fill_ring), RING_DESC(fill_ring));
|
||||||
|
printf("rx_prod_ptr: %p, rx_desc: %p\n", RING_PROD_PTR(rx_ring), RING_DESC(rx_ring));
|
||||||
|
printf("tx_prod_ptr: %p, tx_desc: %p\n", RING_PROD_PTR(tx_ring), RING_DESC(tx_ring));
|
||||||
|
printf("comp_prod_ptr: %p, comp_desc: %p\n", RING_PROD_PTR(comp_ring), RING_DESC(comp_ring));
|
||||||
|
|
||||||
|
// 6. Preencher fill ring com endereços dos frames
|
||||||
|
__u64 *fill = (__u64 *)RING_DESC(fill_ring);
|
||||||
|
for (int i = 0; i < NUM_FRAMES; i++) {
|
||||||
|
fill[i] = i * FRAME_SIZE;
|
||||||
|
}
|
||||||
|
// Atualizar producer pointer do fill ring
|
||||||
|
*RING_PROD_PTR(fill_ring) = NUM_FRAMES;
|
||||||
|
|
||||||
|
// 7. Bind socket AF_XDP à interface/fila
|
||||||
|
struct sockaddr_xdp sxdp = {
|
||||||
|
.sxdp_family = AF_XDP,
|
||||||
|
.sxdp_ifindex = ifindex,
|
||||||
|
.sxdp_queue_id = queue_id,
|
||||||
|
.sxdp_flags = 0,
|
||||||
|
};
|
||||||
|
if (bind(xdp_sock, (struct sockaddr *)&sxdp, sizeof(sxdp)) < 0)
|
||||||
|
die("bind(AF_XDP)");
|
||||||
|
|
||||||
|
printf("Socket AF_XDP ligado na interface %s (fila %d)\n", ifname, queue_id);
|
||||||
|
|
||||||
|
// 8. Poll para pacotes
|
||||||
|
struct pollfd fds = { .fd = xdp_sock, .events = POLLIN };
|
||||||
|
struct xdp_desc *rx = (struct xdp_desc *)RING_DESC(rx_ring);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int ret = poll(&fds, 1, 1000);
|
||||||
|
if (ret < 0) die("poll");
|
||||||
|
if (ret == 0) continue;
|
||||||
|
|
||||||
|
__u32 rx_cons = *RING_CONS_PTR(rx_ring);
|
||||||
|
__u32 rx_prod = *RING_PROD_PTR(rx_ring);
|
||||||
|
|
||||||
|
if (!rx || rx_prod == rx_cons) continue;
|
||||||
|
|
||||||
|
while (rx_cons != rx_prod) {
|
||||||
|
struct xdp_desc *desc = &rx[rx_cons % RX_RING_SIZE];
|
||||||
|
if (!desc) break;
|
||||||
|
void *pkt = (char *)umem_area + desc->addr;
|
||||||
|
printf("Pacote RX: %u bytes (addr=%llu)\n", desc->len, (unsigned long long)desc->addr);
|
||||||
|
rx_cons++;
|
||||||
|
}
|
||||||
|
*RING_CONS_PTR(rx_ring) = rx_cons;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup (não alcançado neste exemplo)
|
||||||
|
close(xdp_sock);
|
||||||
|
free(umem_area);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <bpf/bpf_helpers.h>
|
||||||
|
#include <bpf/bpf_endian.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/if_vlan.h>
|
||||||
|
#include "bridge_xdp.h"
|
||||||
|
|
||||||
|
// Definição local para struct vlan_hdr (caso não esteja disponível)
|
||||||
|
#ifndef VLAN_HLEN
|
||||||
|
#define VLAN_HLEN 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct vlan_hdr {
|
||||||
|
__be16 h_vlan_TCI;
|
||||||
|
__be16 h_vlan_encapsulated_proto;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
|
__uint(max_entries, 1);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, struct packet_stats);
|
||||||
|
} stats_map SEC(".maps");
|
||||||
|
|
||||||
|
SEC("xdp")
|
||||||
|
int bridge_filter(struct xdp_md *ctx)
|
||||||
|
{
|
||||||
|
void *data_end = (void *)(long)ctx->data_end;
|
||||||
|
void *data = (void *)(long)ctx->data;
|
||||||
|
struct ethhdr *eth = data;
|
||||||
|
__u16 h_proto;
|
||||||
|
struct iphdr *iph;
|
||||||
|
int action = XDP_PASS;
|
||||||
|
__u32 key = 0;
|
||||||
|
struct packet_stats *stats;
|
||||||
|
|
||||||
|
if ((void*)(eth + 1) > data_end)
|
||||||
|
return XDP_ABORTED;
|
||||||
|
|
||||||
|
h_proto = eth->h_proto;
|
||||||
|
|
||||||
|
// Tráfego broadcast/multicast: libera imediatamente
|
||||||
|
if (eth->h_dest[0] & 1)
|
||||||
|
goto stats;
|
||||||
|
|
||||||
|
// VLAN
|
||||||
|
if (h_proto == bpf_htons(ETH_P_8021Q) || h_proto == bpf_htons(ETH_P_8021AD)) {
|
||||||
|
struct vlan_hdr *vhdr = (void *)(eth + 1);
|
||||||
|
if ((void *)(vhdr + 1) > data_end)
|
||||||
|
return XDP_ABORTED;
|
||||||
|
h_proto = vhdr->h_vlan_encapsulated_proto;
|
||||||
|
iph = (void *)(vhdr + 1);
|
||||||
|
} else {
|
||||||
|
iph = (void *)(eth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv4 local: libera imediatamente
|
||||||
|
if (h_proto == bpf_htons(ETH_P_IP)) {
|
||||||
|
if ((void*)(iph + 1) > data_end)
|
||||||
|
goto stats;
|
||||||
|
if ((iph->daddr & bpf_htonl(0xffff0000)) == bpf_htonl(0x0a0f0000))
|
||||||
|
goto stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...outros filtros podem ser adicionados aqui...
|
||||||
|
|
||||||
|
stats:
|
||||||
|
// Atualizar estatísticas apenas uma vez
|
||||||
|
stats = bpf_map_lookup_elem(&stats_map, &key);
|
||||||
|
if (stats) {
|
||||||
|
stats->packets++;
|
||||||
|
stats->bytes += (data_end - data);
|
||||||
|
}
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
char _license[] SEC("license") = "GPL";
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef BRIDGE_XDP_H
|
||||||
|
#define BRIDGE_XDP_H
|
||||||
|
|
||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#include <linux/tcp.h>
|
||||||
|
|
||||||
|
// Estrutura para estatísticas
|
||||||
|
struct packet_stats {
|
||||||
|
__u64 packets;
|
||||||
|
__u64 bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
; ModuleID = 'bridge_xdp.c'
|
||||||
|
source_filename = "bridge_xdp.c"
|
||||||
|
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
|
||||||
|
target triple = "bpf"
|
||||||
|
|
||||||
|
%struct.anon = type { ptr, ptr, ptr, ptr }
|
||||||
|
%struct.xdp_md = type { i32, i32, i32, i32, i32, i32 }
|
||||||
|
%struct.ethhdr = type { [6 x i8], [6 x i8], i16 }
|
||||||
|
%struct.packet_stats = type { i64, i64 }
|
||||||
|
|
||||||
|
@stats_map = dso_local global %struct.anon zeroinitializer, section ".maps", align 8, !dbg !0
|
||||||
|
@_license = dso_local global [4 x i8] c"GPL\00", section "license", align 1, !dbg !30
|
||||||
|
@llvm.compiler.used = appending global [3 x ptr] [ptr @_license, ptr @bridge_filter, ptr @stats_map], section "llvm.metadata"
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define dso_local noundef i32 @bridge_filter(ptr nocapture noundef readonly %0) #0 section "xdp" !dbg !67 {
|
||||||
|
%2 = alloca i32, align 4, !DIAssignID !129
|
||||||
|
call void @llvm.dbg.assign(metadata i1 undef, metadata !95, metadata !DIExpression(), metadata !129, metadata ptr %2, metadata !DIExpression()), !dbg !130
|
||||||
|
tail call void @llvm.dbg.value(metadata ptr %0, metadata !80, metadata !DIExpression()), !dbg !130
|
||||||
|
%3 = getelementptr inbounds %struct.xdp_md, ptr %0, i64 0, i32 1, !dbg !131
|
||||||
|
%4 = load i32, ptr %3, align 4, !dbg !131, !tbaa !132
|
||||||
|
%5 = zext i32 %4 to i64, !dbg !137
|
||||||
|
%6 = inttoptr i64 %5 to ptr, !dbg !138
|
||||||
|
tail call void @llvm.dbg.value(metadata ptr %6, metadata !81, metadata !DIExpression()), !dbg !130
|
||||||
|
%7 = load i32, ptr %0, align 4, !dbg !139, !tbaa !140
|
||||||
|
%8 = zext i32 %7 to i64, !dbg !141
|
||||||
|
%9 = inttoptr i64 %8 to ptr, !dbg !142
|
||||||
|
tail call void @llvm.dbg.value(metadata ptr %9, metadata !82, metadata !DIExpression()), !dbg !130
|
||||||
|
tail call void @llvm.dbg.value(metadata ptr %9, metadata !83, metadata !DIExpression()), !dbg !130
|
||||||
|
%10 = getelementptr inbounds %struct.ethhdr, ptr %9, i64 1, !dbg !143
|
||||||
|
%11 = icmp ugt ptr %10, %6, !dbg !145
|
||||||
|
br i1 %11, label %23, label %12, !dbg !146
|
||||||
|
|
||||||
|
12: ; preds = %1
|
||||||
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %2) #4, !dbg !147
|
||||||
|
store i32 0, ptr %2, align 4, !dbg !148, !tbaa !149, !DIAssignID !150
|
||||||
|
call void @llvm.dbg.assign(metadata i32 0, metadata !95, metadata !DIExpression(), metadata !150, metadata ptr %2, metadata !DIExpression()), !dbg !130
|
||||||
|
%13 = call ptr inttoptr (i64 1 to ptr)(ptr noundef nonnull @stats_map, ptr noundef nonnull %2) #4, !dbg !151
|
||||||
|
tail call void @llvm.dbg.value(metadata ptr %13, metadata !96, metadata !DIExpression()), !dbg !130
|
||||||
|
%14 = icmp eq ptr %13, null, !dbg !152
|
||||||
|
br i1 %14, label %22, label %15, !dbg !154
|
||||||
|
|
||||||
|
15: ; preds = %12
|
||||||
|
%16 = load i64, ptr %13, align 8, !dbg !155, !tbaa !157
|
||||||
|
%17 = add i64 %16, 1, !dbg !155
|
||||||
|
store i64 %17, ptr %13, align 8, !dbg !155, !tbaa !157
|
||||||
|
%18 = sub nsw i64 %5, %8, !dbg !160
|
||||||
|
%19 = getelementptr inbounds %struct.packet_stats, ptr %13, i64 0, i32 1, !dbg !161
|
||||||
|
%20 = load i64, ptr %19, align 8, !dbg !162, !tbaa !163
|
||||||
|
%21 = add i64 %18, %20, !dbg !162
|
||||||
|
store i64 %21, ptr %19, align 8, !dbg !162, !tbaa !163
|
||||||
|
br label %22, !dbg !164
|
||||||
|
|
||||||
|
22: ; preds = %15, %12
|
||||||
|
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %2) #4, !dbg !165
|
||||||
|
br label %23
|
||||||
|
|
||||||
|
23: ; preds = %1, %22
|
||||||
|
%24 = phi i32 [ 2, %22 ], [ 0, %1 ], !dbg !130
|
||||||
|
ret i32 %24, !dbg !165
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
|
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1
|
||||||
|
|
||||||
|
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
|
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1
|
||||||
|
|
||||||
|
; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare void @llvm.dbg.value(metadata, metadata, metadata) #3
|
||||||
|
|
||||||
|
attributes #0 = { nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
|
||||||
|
attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||||
|
attributes #2 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
|
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
|
attributes #4 = { nounwind }
|
||||||
|
|
||||||
|
!llvm.dbg.cu = !{!2}
|
||||||
|
!llvm.module.flags = !{!61, !62, !63, !64, !65}
|
||||||
|
!llvm.ident = !{!66}
|
||||||
|
|
||||||
|
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
|
||||||
|
!1 = distinct !DIGlobalVariable(name: "stats_map", scope: !2, file: !3, line: 12, type: !36, isLocal: false, isDefinition: true)
|
||||||
|
!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 18.1.8 ( 18.1.8-3.0.1.el9)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !14, globals: !20, splitDebugInlining: false, nameTableKind: None)
|
||||||
|
!3 = !DIFile(filename: "bridge_xdp.c", directory: "/root/xdp_filter")
|
||||||
|
!4 = !{!5}
|
||||||
|
!5 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "xdp_action", file: !6, line: 6313, baseType: !7, size: 32, elements: !8)
|
||||||
|
!6 = !DIFile(filename: "/usr/include/linux/bpf.h", directory: "")
|
||||||
|
!7 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
|
||||||
|
!8 = !{!9, !10, !11, !12, !13}
|
||||||
|
!9 = !DIEnumerator(name: "XDP_ABORTED", value: 0)
|
||||||
|
!10 = !DIEnumerator(name: "XDP_DROP", value: 1)
|
||||||
|
!11 = !DIEnumerator(name: "XDP_PASS", value: 2)
|
||||||
|
!12 = !DIEnumerator(name: "XDP_TX", value: 3)
|
||||||
|
!13 = !DIEnumerator(name: "XDP_REDIRECT", value: 4)
|
||||||
|
!14 = !{!15, !16, !17}
|
||||||
|
!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
|
||||||
|
!16 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed)
|
||||||
|
!17 = !DIDerivedType(tag: DW_TAG_typedef, name: "__u16", file: !18, line: 24, baseType: !19)
|
||||||
|
!18 = !DIFile(filename: "/usr/include/asm-generic/int-ll64.h", directory: "")
|
||||||
|
!19 = !DIBasicType(name: "unsigned short", size: 16, encoding: DW_ATE_unsigned)
|
||||||
|
!20 = !{!21, !30, !0}
|
||||||
|
!21 = !DIGlobalVariableExpression(var: !22, expr: !DIExpression())
|
||||||
|
!22 = distinct !DIGlobalVariable(name: "bpf_map_lookup_elem", scope: !2, file: !23, line: 56, type: !24, isLocal: true, isDefinition: true)
|
||||||
|
!23 = !DIFile(filename: "/usr/include/bpf/bpf_helper_defs.h", directory: "")
|
||||||
|
!24 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !25)
|
||||||
|
!25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64)
|
||||||
|
!26 = !DISubroutineType(types: !27)
|
||||||
|
!27 = !{!15, !15, !28}
|
||||||
|
!28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64)
|
||||||
|
!29 = !DIDerivedType(tag: DW_TAG_const_type, baseType: null)
|
||||||
|
!30 = !DIGlobalVariableExpression(var: !31, expr: !DIExpression())
|
||||||
|
!31 = distinct !DIGlobalVariable(name: "_license", scope: !2, file: !3, line: 52, type: !32, isLocal: false, isDefinition: true)
|
||||||
|
!32 = !DICompositeType(tag: DW_TAG_array_type, baseType: !33, size: 32, elements: !34)
|
||||||
|
!33 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
|
||||||
|
!34 = !{!35}
|
||||||
|
!35 = !DISubrange(count: 4)
|
||||||
|
!36 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !3, line: 7, size: 256, elements: !37)
|
||||||
|
!37 = !{!38, !44, !49, !52}
|
||||||
|
!38 = !DIDerivedType(tag: DW_TAG_member, name: "type", scope: !36, file: !3, line: 8, baseType: !39, size: 64)
|
||||||
|
!39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !40, size: 64)
|
||||||
|
!40 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 192, elements: !42)
|
||||||
|
!41 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
|
||||||
|
!42 = !{!43}
|
||||||
|
!43 = !DISubrange(count: 6)
|
||||||
|
!44 = !DIDerivedType(tag: DW_TAG_member, name: "max_entries", scope: !36, file: !3, line: 9, baseType: !45, size: 64, offset: 64)
|
||||||
|
!45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !46, size: 64)
|
||||||
|
!46 = !DICompositeType(tag: DW_TAG_array_type, baseType: !41, size: 32, elements: !47)
|
||||||
|
!47 = !{!48}
|
||||||
|
!48 = !DISubrange(count: 1)
|
||||||
|
!49 = !DIDerivedType(tag: DW_TAG_member, name: "key", scope: !36, file: !3, line: 10, baseType: !50, size: 64, offset: 128)
|
||||||
|
!50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64)
|
||||||
|
!51 = !DIDerivedType(tag: DW_TAG_typedef, name: "__u32", file: !18, line: 27, baseType: !7)
|
||||||
|
!52 = !DIDerivedType(tag: DW_TAG_member, name: "value", scope: !36, file: !3, line: 11, baseType: !53, size: 64, offset: 192)
|
||||||
|
!53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !54, size: 64)
|
||||||
|
!54 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "packet_stats", file: !55, line: 10, size: 128, elements: !56)
|
||||||
|
!55 = !DIFile(filename: "./bridge_xdp.h", directory: "/root/xdp_filter")
|
||||||
|
!56 = !{!57, !60}
|
||||||
|
!57 = !DIDerivedType(tag: DW_TAG_member, name: "packets", scope: !54, file: !55, line: 11, baseType: !58, size: 64)
|
||||||
|
!58 = !DIDerivedType(tag: DW_TAG_typedef, name: "__u64", file: !18, line: 31, baseType: !59)
|
||||||
|
!59 = !DIBasicType(name: "unsigned long long", size: 64, encoding: DW_ATE_unsigned)
|
||||||
|
!60 = !DIDerivedType(tag: DW_TAG_member, name: "bytes", scope: !54, file: !55, line: 12, baseType: !58, size: 64, offset: 64)
|
||||||
|
!61 = !{i32 7, !"Dwarf Version", i32 4}
|
||||||
|
!62 = !{i32 2, !"Debug Info Version", i32 3}
|
||||||
|
!63 = !{i32 1, !"wchar_size", i32 4}
|
||||||
|
!64 = !{i32 7, !"frame-pointer", i32 2}
|
||||||
|
!65 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
|
||||||
|
!66 = !{!"clang version 18.1.8 ( 18.1.8-3.0.1.el9)"}
|
||||||
|
!67 = distinct !DISubprogram(name: "bridge_filter", scope: !3, file: !3, line: 15, type: !68, scopeLine: 16, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !79)
|
||||||
|
!68 = !DISubroutineType(types: !69)
|
||||||
|
!69 = !{!41, !70}
|
||||||
|
!70 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !71, size: 64)
|
||||||
|
!71 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "xdp_md", file: !6, line: 6324, size: 192, elements: !72)
|
||||||
|
!72 = !{!73, !74, !75, !76, !77, !78}
|
||||||
|
!73 = !DIDerivedType(tag: DW_TAG_member, name: "data", scope: !71, file: !6, line: 6325, baseType: !51, size: 32)
|
||||||
|
!74 = !DIDerivedType(tag: DW_TAG_member, name: "data_end", scope: !71, file: !6, line: 6326, baseType: !51, size: 32, offset: 32)
|
||||||
|
!75 = !DIDerivedType(tag: DW_TAG_member, name: "data_meta", scope: !71, file: !6, line: 6327, baseType: !51, size: 32, offset: 64)
|
||||||
|
!76 = !DIDerivedType(tag: DW_TAG_member, name: "ingress_ifindex", scope: !71, file: !6, line: 6329, baseType: !51, size: 32, offset: 96)
|
||||||
|
!77 = !DIDerivedType(tag: DW_TAG_member, name: "rx_queue_index", scope: !71, file: !6, line: 6330, baseType: !51, size: 32, offset: 128)
|
||||||
|
!78 = !DIDerivedType(tag: DW_TAG_member, name: "egress_ifindex", scope: !71, file: !6, line: 6332, baseType: !51, size: 32, offset: 160)
|
||||||
|
!79 = !{!80, !81, !82, !83, !95, !96, !97}
|
||||||
|
!80 = !DILocalVariable(name: "ctx", arg: 1, scope: !67, file: !3, line: 15, type: !70)
|
||||||
|
!81 = !DILocalVariable(name: "data_end", scope: !67, file: !3, line: 17, type: !15)
|
||||||
|
!82 = !DILocalVariable(name: "data", scope: !67, file: !3, line: 18, type: !15)
|
||||||
|
!83 = !DILocalVariable(name: "eth", scope: !67, file: !3, line: 19, type: !84)
|
||||||
|
!84 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !85, size: 64)
|
||||||
|
!85 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "ethhdr", file: !86, line: 166, size: 112, elements: !87)
|
||||||
|
!86 = !DIFile(filename: "/usr/include/linux/if_ether.h", directory: "")
|
||||||
|
!87 = !{!88, !91, !92}
|
||||||
|
!88 = !DIDerivedType(tag: DW_TAG_member, name: "h_dest", scope: !85, file: !86, line: 167, baseType: !89, size: 48)
|
||||||
|
!89 = !DICompositeType(tag: DW_TAG_array_type, baseType: !90, size: 48, elements: !42)
|
||||||
|
!90 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char)
|
||||||
|
!91 = !DIDerivedType(tag: DW_TAG_member, name: "h_source", scope: !85, file: !86, line: 168, baseType: !89, size: 48, offset: 48)
|
||||||
|
!92 = !DIDerivedType(tag: DW_TAG_member, name: "h_proto", scope: !85, file: !86, line: 169, baseType: !93, size: 16, offset: 96)
|
||||||
|
!93 = !DIDerivedType(tag: DW_TAG_typedef, name: "__be16", file: !94, line: 29, baseType: !17)
|
||||||
|
!94 = !DIFile(filename: "/usr/include/linux/types.h", directory: "")
|
||||||
|
!95 = !DILocalVariable(name: "key", scope: !67, file: !3, line: 26, type: !51)
|
||||||
|
!96 = !DILocalVariable(name: "stats", scope: !67, file: !3, line: 27, type: !53)
|
||||||
|
!97 = !DILocalVariable(name: "iph", scope: !98, file: !3, line: 39, type: !100)
|
||||||
|
!98 = distinct !DILexicalBlock(scope: !99, file: !3, line: 38, column: 46)
|
||||||
|
!99 = distinct !DILexicalBlock(scope: !67, file: !3, line: 38, column: 9)
|
||||||
|
!100 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !101, size: 64)
|
||||||
|
!101 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "iphdr", file: !102, line: 87, size: 160, elements: !103)
|
||||||
|
!102 = !DIFile(filename: "/usr/include/linux/ip.h", directory: "")
|
||||||
|
!103 = !{!104, !106, !107, !108, !109, !110, !111, !112, !113, !115}
|
||||||
|
!104 = !DIDerivedType(tag: DW_TAG_member, name: "ihl", scope: !101, file: !102, line: 89, baseType: !105, size: 4, flags: DIFlagBitField, extraData: i64 0)
|
||||||
|
!105 = !DIDerivedType(tag: DW_TAG_typedef, name: "__u8", file: !18, line: 21, baseType: !90)
|
||||||
|
!106 = !DIDerivedType(tag: DW_TAG_member, name: "version", scope: !101, file: !102, line: 90, baseType: !105, size: 4, offset: 4, flags: DIFlagBitField, extraData: i64 0)
|
||||||
|
!107 = !DIDerivedType(tag: DW_TAG_member, name: "tos", scope: !101, file: !102, line: 97, baseType: !105, size: 8, offset: 8)
|
||||||
|
!108 = !DIDerivedType(tag: DW_TAG_member, name: "tot_len", scope: !101, file: !102, line: 98, baseType: !93, size: 16, offset: 16)
|
||||||
|
!109 = !DIDerivedType(tag: DW_TAG_member, name: "id", scope: !101, file: !102, line: 99, baseType: !93, size: 16, offset: 32)
|
||||||
|
!110 = !DIDerivedType(tag: DW_TAG_member, name: "frag_off", scope: !101, file: !102, line: 100, baseType: !93, size: 16, offset: 48)
|
||||||
|
!111 = !DIDerivedType(tag: DW_TAG_member, name: "ttl", scope: !101, file: !102, line: 101, baseType: !105, size: 8, offset: 64)
|
||||||
|
!112 = !DIDerivedType(tag: DW_TAG_member, name: "protocol", scope: !101, file: !102, line: 102, baseType: !105, size: 8, offset: 72)
|
||||||
|
!113 = !DIDerivedType(tag: DW_TAG_member, name: "check", scope: !101, file: !102, line: 103, baseType: !114, size: 16, offset: 80)
|
||||||
|
!114 = !DIDerivedType(tag: DW_TAG_typedef, name: "__sum16", file: !94, line: 35, baseType: !17)
|
||||||
|
!115 = !DIDerivedType(tag: DW_TAG_member, scope: !101, file: !102, line: 104, baseType: !116, size: 64, offset: 96)
|
||||||
|
!116 = distinct !DICompositeType(tag: DW_TAG_union_type, scope: !101, file: !102, line: 104, size: 64, elements: !117)
|
||||||
|
!117 = !{!118, !124}
|
||||||
|
!118 = !DIDerivedType(tag: DW_TAG_member, scope: !116, file: !102, line: 104, baseType: !119, size: 64)
|
||||||
|
!119 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !116, file: !102, line: 104, size: 64, elements: !120)
|
||||||
|
!120 = !{!121, !123}
|
||||||
|
!121 = !DIDerivedType(tag: DW_TAG_member, name: "saddr", scope: !119, file: !102, line: 104, baseType: !122, size: 32)
|
||||||
|
!122 = !DIDerivedType(tag: DW_TAG_typedef, name: "__be32", file: !94, line: 31, baseType: !51)
|
||||||
|
!123 = !DIDerivedType(tag: DW_TAG_member, name: "daddr", scope: !119, file: !102, line: 104, baseType: !122, size: 32, offset: 32)
|
||||||
|
!124 = !DIDerivedType(tag: DW_TAG_member, name: "addrs", scope: !116, file: !102, line: 104, baseType: !125, size: 64)
|
||||||
|
!125 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !116, file: !102, line: 104, size: 64, elements: !126)
|
||||||
|
!126 = !{!127, !128}
|
||||||
|
!127 = !DIDerivedType(tag: DW_TAG_member, name: "saddr", scope: !125, file: !102, line: 104, baseType: !122, size: 32)
|
||||||
|
!128 = !DIDerivedType(tag: DW_TAG_member, name: "daddr", scope: !125, file: !102, line: 104, baseType: !122, size: 32, offset: 32)
|
||||||
|
!129 = distinct !DIAssignID()
|
||||||
|
!130 = !DILocation(line: 0, scope: !67)
|
||||||
|
!131 = !DILocation(line: 17, column: 41, scope: !67)
|
||||||
|
!132 = !{!133, !134, i64 4}
|
||||||
|
!133 = !{!"xdp_md", !134, i64 0, !134, i64 4, !134, i64 8, !134, i64 12, !134, i64 16, !134, i64 20}
|
||||||
|
!134 = !{!"int", !135, i64 0}
|
||||||
|
!135 = !{!"omnipotent char", !136, i64 0}
|
||||||
|
!136 = !{!"Simple C/C++ TBAA"}
|
||||||
|
!137 = !DILocation(line: 17, column: 30, scope: !67)
|
||||||
|
!138 = !DILocation(line: 17, column: 22, scope: !67)
|
||||||
|
!139 = !DILocation(line: 18, column: 37, scope: !67)
|
||||||
|
!140 = !{!133, !134, i64 0}
|
||||||
|
!141 = !DILocation(line: 18, column: 26, scope: !67)
|
||||||
|
!142 = !DILocation(line: 18, column: 18, scope: !67)
|
||||||
|
!143 = !DILocation(line: 22, column: 21, scope: !144)
|
||||||
|
!144 = distinct !DILexicalBlock(scope: !67, file: !3, line: 22, column: 9)
|
||||||
|
!145 = !DILocation(line: 22, column: 26, scope: !144)
|
||||||
|
!146 = !DILocation(line: 22, column: 9, scope: !67)
|
||||||
|
!147 = !DILocation(line: 26, column: 5, scope: !67)
|
||||||
|
!148 = !DILocation(line: 26, column: 11, scope: !67)
|
||||||
|
!149 = !{!134, !134, i64 0}
|
||||||
|
!150 = distinct !DIAssignID()
|
||||||
|
!151 = !DILocation(line: 27, column: 34, scope: !67)
|
||||||
|
!152 = !DILocation(line: 28, column: 9, scope: !153)
|
||||||
|
!153 = distinct !DILexicalBlock(scope: !67, file: !3, line: 28, column: 9)
|
||||||
|
!154 = !DILocation(line: 28, column: 9, scope: !67)
|
||||||
|
!155 = !DILocation(line: 29, column: 23, scope: !156)
|
||||||
|
!156 = distinct !DILexicalBlock(scope: !153, file: !3, line: 28, column: 16)
|
||||||
|
!157 = !{!158, !159, i64 0}
|
||||||
|
!158 = !{!"packet_stats", !159, i64 0, !159, i64 8}
|
||||||
|
!159 = !{!"long long", !135, i64 0}
|
||||||
|
!160 = !DILocation(line: 30, column: 35, scope: !156)
|
||||||
|
!161 = !DILocation(line: 30, column: 16, scope: !156)
|
||||||
|
!162 = !DILocation(line: 30, column: 22, scope: !156)
|
||||||
|
!163 = !{!158, !159, i64 8}
|
||||||
|
!164 = !DILocation(line: 31, column: 5, scope: !156)
|
||||||
|
!165 = !DILocation(line: 50, column: 1, scope: !67)
|
||||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <bpf/libbpf.h>
|
||||||
|
#include "bridge_xdp.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc != 2) {
|
||||||
|
printf("Uso: %s <interface>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carregar programa XDP
|
||||||
|
struct bpf_object *obj = bpf_object__open("bridge_xdp.o");
|
||||||
|
if (!obj) {
|
||||||
|
printf("Erro ao abrir objeto BPF\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bpf_object__load(obj)) {
|
||||||
|
printf("Erro ao carregar objeto BPF\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anexar à interface
|
||||||
|
int prog_fd = bpf_program__fd(bpf_object__find_program_by_name(obj, "bridge_filter"));
|
||||||
|
if (bpf_xdp_attach(if_nametoindex(argv[1]), prog_fd, XDP_FLAGS_SKB_MODE, NULL) != 0) {
|
||||||
|
printf("Erro ao anexar XDP\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("XDP anexado com sucesso à interface %s\n", argv[1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
|
||||||
|
#
|
||||||
|
# It is highly advisable to create own systemd services or udev rules
|
||||||
|
# to run scripts during boot instead of using this file.
|
||||||
|
#
|
||||||
|
# In contrast to previous versions due to parallel execution during boot
|
||||||
|
# this script will NOT be run after all other services.
|
||||||
|
#
|
||||||
|
# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure
|
||||||
|
# that this script will be executed during boot.
|
||||||
|
iptables -F
|
||||||
|
iptables -X
|
||||||
|
iptables -t nat -F
|
||||||
|
iptables -t nat -X
|
||||||
|
iptables -P INPUT DROP
|
||||||
|
iptables -P FORWARD ACCEPT
|
||||||
|
iptables -P OUTPUT ACCEPT
|
||||||
|
|
||||||
|
iptables -A INPUT -i enp0s6 -p udp --dport 32101 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p udp --dport 32110 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p udp --dport 32100 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p udp --dport 32400 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p tcp --dport 8117 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p tcp --dport 80 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p tcp --dport 443 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -j ACCEPT -m state --state ESTABLISHED,RELATED
|
||||||
|
iptables -A INPUT -i wg0 -j ACCEPT
|
||||||
|
|
||||||
|
# Permitir SIP (UDP/TCP 5060/5061), IAX2 (UDP 4569), RTP (UDP 18000:19000) para FreePBX
|
||||||
|
iptables -A INPUT -i enp0s6 -p udp --dport 5060 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p tcp --dport 5060 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p udp --dport 5061 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p tcp --dport 5061 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p udp --dport 5160 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p tcp --dport 5161 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p udp --dport 4569 -j ACCEPT
|
||||||
|
iptables -A INPUT -i enp0s6 -p udp --dport 18000:19000 -j ACCEPT
|
||||||
|
|
||||||
|
# Permitir tráfego de retorno (deve estar antes do DROP final)
|
||||||
|
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||||
|
|
||||||
|
iptables -t nat -A POSTROUTING -s 10.15.0.0/16 -j MASQUERADE
|
||||||
|
ip6tables -t nat -A POSTROUTING -s 2001:db8:15::/48 -j MASQUERADE
|
||||||
|
iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j MASQUERADE
|
||||||
|
|
||||||
|
|
||||||
|
# Habilitar redirecionamento de pacotes no kernel
|
||||||
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||||
|
# 1. DNAT APENAS para tráfego entrando na interface enp0s6 (WAN) com destino ao IP público
|
||||||
|
iptables -t nat -A PREROUTING -i enp0s6 -p tcp --dport 80 -j DNAT --to-destination 10.15.0.45:80
|
||||||
|
iptables -t nat -A PREROUTING -i enp0s6 -p tcp --dport 443 -j DNAT --to-destination 10.15.0.45:443
|
||||||
|
|
||||||
|
# 2. SNAT (Hairpinning) para clientes locais acessarem via IP público
|
||||||
|
iptables -t nat -A POSTROUTING -o enp0s6 -p tcp -d 10.15.0.45 --dport 80 -j MASQUERADE
|
||||||
|
iptables -t nat -A POSTROUTING -o enp0s6 -p tcp -d 10.15.0.45 --dport 443 -j MASQUERADE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Permitir o tráfego na cadeia FORWARD
|
||||||
|
iptables -A FORWARD -p tcp -d 10.15.0.45 --dport 80 -j ACCEPT
|
||||||
|
iptables -A FORWARD -p tcp -d 10.15.0.45 --dport 443 -j ACCEPT
|
||||||
|
|
||||||
|
sh /root/test.sh
|
||||||
|
|
||||||
|
|
||||||
|
echo deadline > /sys/block/sda/queue/scheduler
|
||||||
|
|
||||||
|
touch /var/lock/subsys/local
|
||||||
|
#echo "bfq" > /sys/block/sda/queue/scheduler
|
||||||
|
#echo "1500" > /sys/block/sda/queue/iosched/read_expire
|
||||||
|
echo 64 > /sys/block/sda/queue/max_sectors_kb # Melhor para SSD virtual
|
||||||
|
echo deadline > /sys/block/sda/queue/scheduler # Alternativa ao BFQ
|
||||||
|
echo 0 > /proc/sys/vm/zone_reclaim_mode
|
||||||
|
|
||||||
|
tc qdisc add dev enp0s6 root cake bandwidth 1000mbit besteffort
|
||||||
|
tc qdisc add dev wg0 root cake bandwidth 500mbit besteffort dual-dsthost
|
||||||
|
|
||||||
|
echo f > /sys/class/net/enp0s6/queues/tx-0/xps_cpus
|
||||||
|
|
||||||
|
|
||||||
|
echo f > /sys/class/net/enp0s6/queues/rx-0/rps_cpus
|
||||||
|
|
||||||
|
# XPS para núcleos 4-7 (máscara 0xf0)
|
||||||
|
|
||||||
|
# Aumentar buffer de entrada/saída fixo
|
||||||
|
echo 4096 > /sys/class/net/enp0s6/queues/rx-0/rps_flow_cnt
|
||||||
|
echo 4096 > /proc/sys/net/core/rps_sock_flow_entries
|
||||||
|
|
||||||
|
sh /root/qos.sh
|
||||||
|
|
||||||
|
#cd /root/xdp_new && \
|
||||||
|
cd /home/rodrigo/xdp && \
|
||||||
|
ip link set enp0s6 mtu 1500
|
||||||
|
ip link set dev enp0s6 xdp obj xdp_ddos_filter.o sec xdp
|
||||||
|
|
||||||
|
sleep 20
|
||||||
|
|
||||||
|
ip link set dev br-02f7820020b4 xdp obj bridge_xdp.o sec xdp
|
||||||
|
|
||||||
|
ip link delete br-8b341d0f5c20
|
||||||
|
iptables -t nat -A POSTROUTING -o wg3 -j MASQUERADE
|
||||||
@@ -0,0 +1,451 @@
|
|||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#include <linux/tcp.h>
|
||||||
|
#include <linux/udp.h>
|
||||||
|
#include <linux/in.h>
|
||||||
|
#include <linux/icmp.h>
|
||||||
|
#include <bpf/bpf_helpers.h>
|
||||||
|
#include <bpf/bpf_endian.h>
|
||||||
|
|
||||||
|
// Otimização: constantes ajustadas para seu ambiente específico
|
||||||
|
#define MAX_HIGH_PRIORITY_PACKETS_PER_SECOND 3000 // Aumentado para serviços críticos (HTTP/HTTPS)
|
||||||
|
#define MAX_LOW_PRIORITY_PACKETS_PER_SECOND 500
|
||||||
|
#define MAX_PACKETS_PER_IP 300000
|
||||||
|
#define MAX_ICMP_PACKETS_PER_IP 30 // Reduzido conforme política ICMP restritiva
|
||||||
|
#define MAX_SYN_PACKETS_PER_IP 80 // Reduzido para proteção preventiva
|
||||||
|
#define TIMEOUT_SEC 1
|
||||||
|
#define PACKET_COST 1
|
||||||
|
#define HIGH_PRIO_TOKEN_RATE 3000 // Aumentado para combinar com QoS HTB
|
||||||
|
#define LOW_PRIO_TOKEN_RATE 500
|
||||||
|
#define MAX_TOKENS 3000
|
||||||
|
#define DOCKER_NETWORK_PREFIX 0x0A0F0000 // 10.15.0.0
|
||||||
|
#define DOCKER_NETWORK_MASK 0xFFFF0000 // /16
|
||||||
|
#define WG_NETWORK_PREFIX 0x0A630100 // 10.99.1.0
|
||||||
|
#define WG_NETWORK_MASK 0xFFFFFF00 // /24
|
||||||
|
#define NGINX_SERVER_IP 0x0A0F002D // 10.15.0.45
|
||||||
|
|
||||||
|
// Estrutura de dados otimizada com tipos consistentes
|
||||||
|
struct ip_stats {
|
||||||
|
__u32 pkt_count;
|
||||||
|
__u32 last_seen;
|
||||||
|
__u32 icmp_count;
|
||||||
|
__u32 syn_count;
|
||||||
|
__u32 high_prio_tokens;
|
||||||
|
__u32 low_prio_tokens;
|
||||||
|
__u16 flags; // Expandido para mais flags
|
||||||
|
__u16 padding; // Mantém alinhamento 4-bytes
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
// Estrutura para configuração de redes internas
|
||||||
|
struct network_config {
|
||||||
|
__u32 prefix;
|
||||||
|
__u32 mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Estrutura para configuração de portas
|
||||||
|
struct port_config {
|
||||||
|
__u8 priority; // 0=baixa, 1=média, 2=alta
|
||||||
|
__u8 is_filtered; // 0=não, 1=sim
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adicionar flags para comportamento de tráfego
|
||||||
|
#define FLAG_POTENTIAL_SCANNER 0x01
|
||||||
|
#define FLAG_SYN_FLOOD 0x02
|
||||||
|
#define FLAG_ICMP_FLOOD 0x04
|
||||||
|
#define FLAG_MARKED_SUSPICIOUS 0x08
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
|
__uint(max_entries, 100000);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, struct ip_stats);
|
||||||
|
} ddos_map SEC(".maps");
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
|
__uint(max_entries, 1000);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, __u32);
|
||||||
|
} whitelist_map SEC(".maps");
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
|
__uint(max_entries, 100000);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, __u32);
|
||||||
|
} blacklist_map SEC(".maps");
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
|
__uint(max_entries, 100);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, struct network_config);
|
||||||
|
} internal_networks SEC(".maps");
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
|
__uint(max_entries, 65536);
|
||||||
|
__type(key, __u16);
|
||||||
|
__type(value, struct port_config);
|
||||||
|
} port_config_map SEC(".maps");
|
||||||
|
|
||||||
|
// Mapa para tempos de expiração
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
|
__uint(max_entries, 100000);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, __u32);
|
||||||
|
} expiration_map SEC(".maps");
|
||||||
|
|
||||||
|
// Mapa para estatísticas de tráfego para integração com userspace
|
||||||
|
struct traffic_stats {
|
||||||
|
__u64 total_packets;
|
||||||
|
__u64 dropped_packets;
|
||||||
|
__u64 passed_packets;
|
||||||
|
__u64 blacklisted_count;
|
||||||
|
__u64 syn_flood_count;
|
||||||
|
__u64 icmp_flood_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
|
__uint(max_entries, 1);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, struct traffic_stats);
|
||||||
|
} stats_map SEC(".maps");
|
||||||
|
|
||||||
|
// Fast-path para serviços críticos - pré-verifica IPs confiáveis
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
|
__uint(max_entries, 1000);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, __u8); // 1 = confiável
|
||||||
|
} trusted_hosts_map SEC(".maps");
|
||||||
|
|
||||||
|
// Mapa para interface de redirecionamento
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_XSKMAP);
|
||||||
|
__uint(max_entries, 1);
|
||||||
|
__type(key, int);
|
||||||
|
__type(value, int);
|
||||||
|
} xsks_map SEC(".maps");
|
||||||
|
|
||||||
|
// Função otimizada para timestamp
|
||||||
|
static __always_inline __u32 get_time_sec(void) {
|
||||||
|
return bpf_ktime_get_ns() / 1000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int is_whitelisted(__u32 ip) {
|
||||||
|
return bpf_map_lookup_elem(&whitelist_map, &ip) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int is_blacklisted(__u32 ip) {
|
||||||
|
__u32 now = get_time_sec();
|
||||||
|
__u32 *block_time = bpf_map_lookup_elem(&blacklist_map, &ip);
|
||||||
|
|
||||||
|
if (block_time) {
|
||||||
|
// Se passou mais de 1 hora, remove da blacklist
|
||||||
|
if (now - *block_time > 3600) {
|
||||||
|
bpf_map_delete_elem(&blacklist_map, &ip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int is_internal_network(__u32 ip) {
|
||||||
|
struct network_config *net;
|
||||||
|
__u32 key = 0;
|
||||||
|
|
||||||
|
// Iteração limitada pelo verificador
|
||||||
|
#pragma unroll
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
key = i;
|
||||||
|
net = bpf_map_lookup_elem(&internal_networks, &key);
|
||||||
|
if (!net)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ((ip & net->mask) == net->prefix)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int get_port_priority(__u16 port) {
|
||||||
|
struct port_config *cfg = bpf_map_lookup_elem(&port_config_map, &port);
|
||||||
|
if (cfg)
|
||||||
|
return cfg->priority;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int is_filtered_port(__u16 port) {
|
||||||
|
struct port_config *cfg = bpf_map_lookup_elem(&port_config_map, &port);
|
||||||
|
if (cfg)
|
||||||
|
return cfg->is_filtered;
|
||||||
|
return (port <= 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Função para atualizar estatísticas de tráfego
|
||||||
|
static __always_inline void update_stats(__u32 action, __u8 flags) {
|
||||||
|
__u32 key = 0;
|
||||||
|
struct traffic_stats *stats = bpf_map_lookup_elem(&stats_map, &key);
|
||||||
|
if (stats) {
|
||||||
|
stats->total_packets++;
|
||||||
|
|
||||||
|
if (action == XDP_DROP) {
|
||||||
|
stats->dropped_packets++;
|
||||||
|
if (flags & FLAG_SYN_FLOOD)
|
||||||
|
stats->syn_flood_count++;
|
||||||
|
if (flags & FLAG_ICMP_FLOOD)
|
||||||
|
stats->icmp_flood_count++;
|
||||||
|
} else if (action == XDP_PASS) {
|
||||||
|
stats->passed_packets++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & FLAG_MARKED_SUSPICIOUS)
|
||||||
|
stats->blacklisted_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Macro para conversão de porta e IP
|
||||||
|
#define ntohs(x) __builtin_bswap16(x)
|
||||||
|
#define ntohl(x) __builtin_bswap32(x)
|
||||||
|
|
||||||
|
// Versão otimizada do programa principal
|
||||||
|
SEC("xdp")
|
||||||
|
int xdp_ddos_filter(struct xdp_md *ctx) {
|
||||||
|
void *data_end = (void *)(long)ctx->data_end;
|
||||||
|
void *data = (void *)(long)ctx->data;
|
||||||
|
__u8 flags = 0;
|
||||||
|
|
||||||
|
// Verificação rápida de tamanho mínimo do pacote
|
||||||
|
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end)
|
||||||
|
return XDP_PASS;
|
||||||
|
|
||||||
|
struct ethhdr *eth = data;
|
||||||
|
if (eth->h_proto != __constant_htons(ETH_P_IP))
|
||||||
|
return XDP_PASS;
|
||||||
|
|
||||||
|
struct iphdr *ip = (void *)(eth + 1);
|
||||||
|
if ((void *)(ip + 1) > data_end || ip->ihl != 5 || ip->version != 4)
|
||||||
|
return XDP_PASS;
|
||||||
|
|
||||||
|
__u32 src_ip = ip->saddr;
|
||||||
|
__u32 dst_ip = ip->daddr;
|
||||||
|
|
||||||
|
// Fast path: redes internas, whitelist e trusted hosts
|
||||||
|
if (is_internal_network(src_ip) || is_whitelisted(src_ip)) return XDP_PASS;
|
||||||
|
__u8 *trusted = bpf_map_lookup_elem(&trusted_hosts_map, &src_ip);
|
||||||
|
if (trusted && *trusted == 1) return XDP_PASS;
|
||||||
|
|
||||||
|
// Fast path: blacklist
|
||||||
|
if (is_blacklisted(src_ip)) {
|
||||||
|
update_stats(XDP_DROP, FLAG_MARKED_SUSPICIOUS);
|
||||||
|
return XDP_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast path: WireGuard UDP/32100
|
||||||
|
if (ip->protocol == IPPROTO_UDP) {
|
||||||
|
struct udphdr *udp = (void *)(ip + 1);
|
||||||
|
if ((void *)(udp + 1) <= data_end) {
|
||||||
|
__u16 dest_port = ntohs(udp->dest);
|
||||||
|
if (dest_port == 32100 || dest_port == 32400 || dest_port == 32101 || dest_port == 32110) {
|
||||||
|
update_stats(XDP_PASS, 0);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variáveis locais para portas e flags
|
||||||
|
__u16 dest_port = 0;
|
||||||
|
int is_syn = 0, is_icmp = 0;
|
||||||
|
|
||||||
|
// Processamento por protocolo
|
||||||
|
if (ip->protocol == IPPROTO_TCP) {
|
||||||
|
struct tcphdr *tcp = (void *)(ip + 1);
|
||||||
|
if ((void *)(tcp + 1) > data_end) return XDP_PASS;
|
||||||
|
dest_port = ntohs(tcp->dest);
|
||||||
|
is_syn = tcp->syn && !tcp->ack;
|
||||||
|
// Detecção de port scan
|
||||||
|
if ((tcp->syn && tcp->rst) || (tcp->fin && tcp->rst) || (tcp->fin && tcp->syn))
|
||||||
|
flags |= FLAG_POTENTIAL_SCANNER;
|
||||||
|
} else if (ip->protocol == IPPROTO_UDP) {
|
||||||
|
struct udphdr *udp = (void *)(ip + 1);
|
||||||
|
if ((void *)(udp + 1) > data_end) return XDP_PASS;
|
||||||
|
dest_port = ntohs(udp->dest);
|
||||||
|
} else if (ip->protocol == IPPROTO_ICMP) {
|
||||||
|
is_icmp = 1;
|
||||||
|
struct icmphdr *icmp = (void *)(ip + 1);
|
||||||
|
if ((void *)(icmp + 1) > data_end) return XDP_PASS;
|
||||||
|
// Permitir apenas echo-reply (0) e limitar echo-request (8)
|
||||||
|
if (icmp->type != 0 && icmp->type != 8) {
|
||||||
|
update_stats(XDP_DROP, 0);
|
||||||
|
return XDP_DROP;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se não for porta filtrada e não for ICMP, libera rápido
|
||||||
|
if (!is_filtered_port(dest_port) && !is_icmp) {
|
||||||
|
struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip);
|
||||||
|
if (entry && entry->pkt_count > 5 && entry->flags == 0) {
|
||||||
|
__u8 mark_trusted = 1;
|
||||||
|
bpf_map_update_elem(&trusted_hosts_map, &src_ip, &mark_trusted, BPF_ANY);
|
||||||
|
}
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rate limiting e contadores
|
||||||
|
int priority = get_port_priority(dest_port);
|
||||||
|
struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip);
|
||||||
|
__u32 now = get_time_sec();
|
||||||
|
|
||||||
|
// Use variáveis locais para tokens e contadores
|
||||||
|
__u32 pkt_count = 1, icmp_count = is_icmp ? 1 : 0, syn_count = is_syn ? 1 : 0;
|
||||||
|
__u32 high_tokens = MAX_TOKENS - PACKET_COST, low_tokens = MAX_TOKENS - PACKET_COST;
|
||||||
|
__u16 entry_flags = flags;
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
__u32 elapsed = now > entry->last_seen ? now - entry->last_seen : 0;
|
||||||
|
pkt_count = entry->pkt_count + 1;
|
||||||
|
icmp_count = is_icmp ? entry->icmp_count + 1 : entry->icmp_count;
|
||||||
|
syn_count = is_syn ? entry->syn_count + 1 : entry->syn_count;
|
||||||
|
high_tokens = entry->high_prio_tokens;
|
||||||
|
low_tokens = entry->low_prio_tokens;
|
||||||
|
entry_flags = entry->flags | flags;
|
||||||
|
|
||||||
|
if (elapsed > 0) {
|
||||||
|
__u32 add_high = HIGH_PRIO_TOKEN_RATE * elapsed;
|
||||||
|
__u32 add_low = LOW_PRIO_TOKEN_RATE * elapsed;
|
||||||
|
high_tokens = (high_tokens + add_high > MAX_TOKENS) ? MAX_TOKENS : high_tokens + add_high;
|
||||||
|
low_tokens = (low_tokens + add_low > MAX_TOKENS) ? MAX_TOKENS : low_tokens + add_low;
|
||||||
|
if (elapsed >= TIMEOUT_SEC) {
|
||||||
|
icmp_count = is_icmp ? 1 : 0;
|
||||||
|
syn_count = is_syn ? 1 : 0;
|
||||||
|
entry_flags &= ~(FLAG_SYN_FLOOD | FLAG_ICMP_FLOOD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flood ICMP
|
||||||
|
if (is_icmp && icmp_count > MAX_ICMP_PACKETS_PER_IP) {
|
||||||
|
entry_flags |= FLAG_ICMP_FLOOD;
|
||||||
|
__u32 one = 1;
|
||||||
|
bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY);
|
||||||
|
update_stats(XDP_DROP, FLAG_ICMP_FLOOD);
|
||||||
|
goto update_entry_and_drop;
|
||||||
|
}
|
||||||
|
// Flood SYN
|
||||||
|
if (is_syn && syn_count > MAX_SYN_PACKETS_PER_IP) {
|
||||||
|
entry_flags |= FLAG_SYN_FLOOD;
|
||||||
|
__u32 one = 1;
|
||||||
|
bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY);
|
||||||
|
update_stats(XDP_DROP, FLAG_SYN_FLOOD);
|
||||||
|
goto update_entry_and_drop;
|
||||||
|
}
|
||||||
|
// Flood total
|
||||||
|
if (pkt_count > MAX_PACKETS_PER_IP) {
|
||||||
|
entry_flags |= FLAG_MARKED_SUSPICIOUS;
|
||||||
|
__u32 one = 1;
|
||||||
|
bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY);
|
||||||
|
update_stats(XDP_DROP, FLAG_MARKED_SUSPICIOUS);
|
||||||
|
goto update_entry_and_drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token bucket
|
||||||
|
if (priority == 2) {
|
||||||
|
if (high_tokens >= PACKET_COST) {
|
||||||
|
high_tokens -= PACKET_COST;
|
||||||
|
goto update_entry_and_pass;
|
||||||
|
}
|
||||||
|
} else if (priority == 1) {
|
||||||
|
if (high_tokens >= PACKET_COST/2 && low_tokens >= PACKET_COST/2) {
|
||||||
|
high_tokens -= PACKET_COST/2;
|
||||||
|
low_tokens -= PACKET_COST/2;
|
||||||
|
goto update_entry_and_pass;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (low_tokens >= PACKET_COST) {
|
||||||
|
low_tokens -= PACKET_COST;
|
||||||
|
goto update_entry_and_pass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_stats(XDP_DROP, 0);
|
||||||
|
goto update_entry_and_drop;
|
||||||
|
|
||||||
|
update_entry_and_pass:
|
||||||
|
{
|
||||||
|
struct ip_stats new_entry = {
|
||||||
|
.pkt_count = pkt_count,
|
||||||
|
.last_seen = now,
|
||||||
|
.icmp_count = icmp_count,
|
||||||
|
.syn_count = syn_count,
|
||||||
|
.high_prio_tokens = high_tokens,
|
||||||
|
.low_prio_tokens = low_tokens,
|
||||||
|
.flags = entry_flags
|
||||||
|
};
|
||||||
|
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
|
||||||
|
update_stats(XDP_PASS, 0);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
update_entry_and_drop:
|
||||||
|
{
|
||||||
|
struct ip_stats new_entry = {
|
||||||
|
.pkt_count = pkt_count,
|
||||||
|
.last_seen = now,
|
||||||
|
.icmp_count = icmp_count,
|
||||||
|
.syn_count = syn_count,
|
||||||
|
.high_prio_tokens = high_tokens,
|
||||||
|
.low_prio_tokens = low_tokens,
|
||||||
|
.flags = entry_flags
|
||||||
|
};
|
||||||
|
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
|
||||||
|
return XDP_DROP;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Primeira vez que vemos este IP
|
||||||
|
struct ip_stats new_entry = {
|
||||||
|
.pkt_count = pkt_count,
|
||||||
|
.last_seen = now,
|
||||||
|
.icmp_count = icmp_count,
|
||||||
|
.syn_count = syn_count,
|
||||||
|
.high_prio_tokens = high_tokens,
|
||||||
|
.low_prio_tokens = low_tokens,
|
||||||
|
.flags = entry_flags
|
||||||
|
};
|
||||||
|
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
|
||||||
|
update_stats(XDP_PASS, 0);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirecionamento AF_XDP para portas 80, 443 (TCP) e 32100 (UDP)
|
||||||
|
int xsk_map_key = ctx->rx_queue_index;
|
||||||
|
|
||||||
|
// TCP: 80 ou 443
|
||||||
|
if (ip->protocol == IPPROTO_TCP) {
|
||||||
|
struct tcphdr *tcp = (void *)(ip + 1);
|
||||||
|
if ((void *)(tcp + 1) > data_end) return XDP_PASS;
|
||||||
|
__u16 dest_port = ntohs(tcp->dest);
|
||||||
|
if (dest_port == 80 || dest_port == 443) {
|
||||||
|
if (bpf_map_lookup_elem(&xsks_map, &xsk_map_key))
|
||||||
|
return XDP_REDIRECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UDP: 32100 (WireGuard)
|
||||||
|
if (ip->protocol == IPPROTO_UDP) {
|
||||||
|
struct udphdr *udp = (void *)(ip + 1);
|
||||||
|
if ((void *)(udp + 1) > data_end) return XDP_PASS;
|
||||||
|
__u16 dest_port = ntohs(udp->dest);
|
||||||
|
if (dest_port == 32100 || dest_port == 32110 || dest_port == 32101|| dest_port == 32400|| dest_port == 5060) {
|
||||||
|
if (bpf_map_lookup_elem(&xsks_map, &xsk_map_key))
|
||||||
|
return XDP_REDIRECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char __license[] SEC("license") = "GPL";
|
||||||
@@ -0,0 +1,614 @@
|
|||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#include <linux/tcp.h>
|
||||||
|
#include <linux/udp.h>
|
||||||
|
#include <linux/in.h>
|
||||||
|
#include <linux/icmp.h>
|
||||||
|
#include <bpf/bpf_helpers.h>
|
||||||
|
#include <bpf/bpf_endian.h>
|
||||||
|
|
||||||
|
// Otimização: constantes ajustadas para seu ambiente específico
|
||||||
|
#define MAX_HIGH_PRIORITY_PACKETS_PER_SECOND 3000 // Aumentado para serviços críticos (HTTP/HTTPS)
|
||||||
|
#define MAX_LOW_PRIORITY_PACKETS_PER_SECOND 500
|
||||||
|
#define MAX_PACKETS_PER_IP 300000
|
||||||
|
#define MAX_ICMP_PACKETS_PER_IP 30 // Reduzido conforme política ICMP restritiva
|
||||||
|
#define MAX_SYN_PACKETS_PER_IP 15 // Reduzido drasticamente para proteção contra SYN flood
|
||||||
|
#define MAX_SYN_PACKETS_PER_SECOND 8 // Limite de SYN por segundo por IP
|
||||||
|
#define TIMEOUT_SEC 1
|
||||||
|
#define SUSPICIOUS_TTL_THRESHOLD 240 // TTLs acima de 240 são suspeitos
|
||||||
|
#define SUSPICIOUS_WINDOW_SIZE 53270 // Window size específico de ataques conhecidos
|
||||||
|
#define PACKET_COST 1
|
||||||
|
#define HIGH_PRIO_TOKEN_RATE 3000 // Aumentado para combinar com QoS HTB
|
||||||
|
#define LOW_PRIO_TOKEN_RATE 500
|
||||||
|
#define MAX_TOKENS 3000
|
||||||
|
#define DOCKER_NETWORK_PREFIX 0x0A0F0000 // 10.15.0.0
|
||||||
|
#define DOCKER_NETWORK_MASK 0xFFFF0000 // /16
|
||||||
|
#define WG_NETWORK_PREFIX 0x0A630100 // 10.99.1.0
|
||||||
|
#define WG_NETWORK_MASK 0xFFFFFF00 // /24
|
||||||
|
#define NGINX_SERVER_IP 0x0A0F002D // 10.15.0.45
|
||||||
|
|
||||||
|
// Estrutura de dados otimizada com tipos consistentes
|
||||||
|
struct ip_stats {
|
||||||
|
__u32 pkt_count;
|
||||||
|
__u32 last_seen;
|
||||||
|
__u32 icmp_count;
|
||||||
|
__u32 syn_count;
|
||||||
|
__u32 high_prio_tokens;
|
||||||
|
__u32 low_prio_tokens;
|
||||||
|
__u32 syn_last_sec; // Timestamp do último segundo para contagem de SYN
|
||||||
|
__u32 syn_count_per_sec; // Contador de SYN no último segundo
|
||||||
|
__u16 flags; // Expandido para mais flags
|
||||||
|
__u16 padding; // Mantém alinhamento 4-bytes
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
// Estrutura para configuração de redes internas
|
||||||
|
struct network_config {
|
||||||
|
__u32 prefix;
|
||||||
|
__u32 mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Estrutura para configuração de portas
|
||||||
|
struct port_config {
|
||||||
|
__u8 priority; // 0=baixa, 1=média, 2=alta
|
||||||
|
__u8 is_filtered; // 0=não, 1=sim
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adicionar flags para comportamento de tráfego
|
||||||
|
#define FLAG_POTENTIAL_SCANNER 0x01
|
||||||
|
#define FLAG_SYN_FLOOD 0x02
|
||||||
|
#define FLAG_ICMP_FLOOD 0x04
|
||||||
|
#define FLAG_MARKED_SUSPICIOUS 0x08
|
||||||
|
#define FLAG_SUSPICIOUS_TTL 0x10
|
||||||
|
#define FLAG_SUSPICIOUS_WINDOW 0x20
|
||||||
|
#define FLAG_SSH_ATTACKER 0x40
|
||||||
|
#define FLAG_HTTP_ATTACKER 0x80
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
|
__uint(max_entries, 100000);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, struct ip_stats);
|
||||||
|
} ddos_map SEC(".maps");
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
|
__uint(max_entries, 1000);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, __u32);
|
||||||
|
} whitelist_map SEC(".maps");
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
|
__uint(max_entries, 100000);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, __u32);
|
||||||
|
} blacklist_map SEC(".maps");
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
|
__uint(max_entries, 100);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, struct network_config);
|
||||||
|
} internal_networks SEC(".maps");
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
|
__uint(max_entries, 65536);
|
||||||
|
__type(key, __u16);
|
||||||
|
__type(value, struct port_config);
|
||||||
|
} port_config_map SEC(".maps");
|
||||||
|
|
||||||
|
// Mapa para tempos de expiração
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
|
__uint(max_entries, 100000);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, __u32);
|
||||||
|
} expiration_map SEC(".maps");
|
||||||
|
|
||||||
|
// Mapa para estatísticas de tráfego para integração com userspace
|
||||||
|
struct traffic_stats {
|
||||||
|
__u64 total_packets;
|
||||||
|
__u64 dropped_packets;
|
||||||
|
__u64 passed_packets;
|
||||||
|
__u64 blacklisted_count;
|
||||||
|
__u64 syn_flood_count;
|
||||||
|
__u64 icmp_flood_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
|
__uint(max_entries, 1);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, struct traffic_stats);
|
||||||
|
} stats_map SEC(".maps");
|
||||||
|
|
||||||
|
// Fast-path para serviços críticos - pré-verifica IPs confiáveis
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
|
__uint(max_entries, 1000);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, __u8); // 1 = confiável
|
||||||
|
} trusted_hosts_map SEC(".maps");
|
||||||
|
|
||||||
|
// Mapa para interface de redirecionamento
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_XSKMAP);
|
||||||
|
__uint(max_entries, 1);
|
||||||
|
__type(key, int);
|
||||||
|
__type(value, int);
|
||||||
|
} xsks_map SEC(".maps");
|
||||||
|
|
||||||
|
// Mapa para rastrear taxa global de SYN por porta
|
||||||
|
struct syn_rate_stats {
|
||||||
|
__u32 last_sec;
|
||||||
|
__u32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
|
__uint(max_entries, 1000);
|
||||||
|
__type(key, __u16); // porta de destino
|
||||||
|
__type(value, struct syn_rate_stats);
|
||||||
|
} syn_rate_map SEC(".maps");
|
||||||
|
|
||||||
|
// Função otimizada para timestamp
|
||||||
|
static __always_inline __u32 get_time_sec(void) {
|
||||||
|
return bpf_ktime_get_ns() / 1000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int is_whitelisted(__u32 ip) {
|
||||||
|
return bpf_map_lookup_elem(&whitelist_map, &ip) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int is_blacklisted(__u32 ip) {
|
||||||
|
__u32 now = get_time_sec();
|
||||||
|
__u32 *block_time = bpf_map_lookup_elem(&blacklist_map, &ip);
|
||||||
|
|
||||||
|
if (block_time) {
|
||||||
|
// Se passou mais de 1 hora, remove da blacklist
|
||||||
|
if (now - *block_time > 3600) {
|
||||||
|
bpf_map_delete_elem(&blacklist_map, &ip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int is_internal_network(__u32 ip) {
|
||||||
|
struct network_config *net;
|
||||||
|
__u32 key = 0;
|
||||||
|
|
||||||
|
// Iteração limitada pelo verificador
|
||||||
|
#pragma unroll
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
key = i;
|
||||||
|
net = bpf_map_lookup_elem(&internal_networks, &key);
|
||||||
|
if (!net)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ((ip & net->mask) == net->prefix)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int get_port_priority(__u16 port) {
|
||||||
|
struct port_config *cfg = bpf_map_lookup_elem(&port_config_map, &port);
|
||||||
|
if (cfg)
|
||||||
|
return cfg->priority;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline int is_filtered_port(__u16 port) {
|
||||||
|
struct port_config *cfg = bpf_map_lookup_elem(&port_config_map, &port);
|
||||||
|
if (cfg)
|
||||||
|
return cfg->is_filtered;
|
||||||
|
return (port <= 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Função para atualizar estatísticas de tráfego
|
||||||
|
static __always_inline void update_stats(__u32 action, __u8 flags) {
|
||||||
|
__u32 key = 0;
|
||||||
|
struct traffic_stats *stats = bpf_map_lookup_elem(&stats_map, &key);
|
||||||
|
if (stats) {
|
||||||
|
stats->total_packets++;
|
||||||
|
|
||||||
|
if (action == XDP_DROP) {
|
||||||
|
stats->dropped_packets++;
|
||||||
|
if (flags & FLAG_SYN_FLOOD)
|
||||||
|
stats->syn_flood_count++;
|
||||||
|
if (flags & FLAG_ICMP_FLOOD)
|
||||||
|
stats->icmp_flood_count++;
|
||||||
|
} else if (action == XDP_PASS) {
|
||||||
|
stats->passed_packets++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & FLAG_MARKED_SUSPICIOUS)
|
||||||
|
stats->blacklisted_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Macro para conversão de porta e IP
|
||||||
|
#define ntohs(x) __builtin_bswap16(x)
|
||||||
|
#define ntohl(x) __builtin_bswap32(x)
|
||||||
|
|
||||||
|
// Versão otimizada do programa principal
|
||||||
|
SEC("xdp")
|
||||||
|
int xdp_ddos_filter(struct xdp_md *ctx) {
|
||||||
|
void *data_end = (void *)(long)ctx->data_end;
|
||||||
|
void *data = (void *)(long)ctx->data;
|
||||||
|
__u8 flags = 0;
|
||||||
|
|
||||||
|
// Verificação rápida de tamanho mínimo do pacote
|
||||||
|
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end)
|
||||||
|
return XDP_PASS;
|
||||||
|
|
||||||
|
struct ethhdr *eth = data;
|
||||||
|
if (eth->h_proto != __constant_htons(ETH_P_IP))
|
||||||
|
return XDP_PASS;
|
||||||
|
|
||||||
|
struct iphdr *ip = (void *)(eth + 1);
|
||||||
|
if ((void *)(ip + 1) > data_end || ip->ihl != 5 || ip->version != 4)
|
||||||
|
return XDP_PASS;
|
||||||
|
|
||||||
|
__u32 src_ip = ip->saddr;
|
||||||
|
__u32 dst_ip = ip->daddr;
|
||||||
|
|
||||||
|
// Fast path: redes internas, whitelist e trusted hosts
|
||||||
|
if (is_internal_network(src_ip) || is_whitelisted(src_ip)) return XDP_PASS;
|
||||||
|
__u8 *trusted = bpf_map_lookup_elem(&trusted_hosts_map, &src_ip);
|
||||||
|
if (trusted && *trusted == 1) return XDP_PASS;
|
||||||
|
|
||||||
|
// Fast path: blacklist
|
||||||
|
if (is_blacklisted(src_ip)) {
|
||||||
|
update_stats(XDP_DROP, FLAG_MARKED_SUSPICIOUS);
|
||||||
|
return XDP_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast path: WireGuard UDP/32100
|
||||||
|
if (ip->protocol == IPPROTO_UDP) {
|
||||||
|
struct udphdr *udp = (void *)(ip + 1);
|
||||||
|
if ((void *)(udp + 1) <= data_end) {
|
||||||
|
__u16 dest_port = ntohs(udp->dest);
|
||||||
|
// Permitir UDP 32100, 32400, 32101, 32110, 5060, 5160, 18000-19000
|
||||||
|
if (dest_port == 32100 || dest_port == 32400 || dest_port == 32101 || dest_port == 32110 ||
|
||||||
|
dest_port == 5060 || dest_port == 5160 ||
|
||||||
|
(dest_port >= 18000 && dest_port <= 19000)) {
|
||||||
|
update_stats(XDP_PASS, 0);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variáveis locais para portas e flags
|
||||||
|
__u16 dest_port = 0;
|
||||||
|
__u16 tcp_window = 0;
|
||||||
|
int is_syn = 0, is_icmp = 0;
|
||||||
|
|
||||||
|
// Processamento por protocolo
|
||||||
|
if (ip->protocol == IPPROTO_TCP) {
|
||||||
|
struct tcphdr *tcp = (void *)(ip + 1);
|
||||||
|
if ((void *)(tcp + 1) > data_end) return XDP_PASS;
|
||||||
|
dest_port = ntohs(tcp->dest);
|
||||||
|
tcp_window = ntohs(tcp->window);
|
||||||
|
is_syn = tcp->syn && !tcp->ack;
|
||||||
|
|
||||||
|
// Detecção de port scan
|
||||||
|
if ((tcp->syn && tcp->rst) || (tcp->fin && tcp->rst) || (tcp->fin && tcp->syn))
|
||||||
|
flags |= FLAG_POTENTIAL_SCANNER;
|
||||||
|
|
||||||
|
// Detecção de padrões de SYN flood conhecidos
|
||||||
|
if (is_syn) {
|
||||||
|
// TTL alto (> 240) é suspeito de spoofing/ataque
|
||||||
|
if (ip->ttl >= SUSPICIOUS_TTL_THRESHOLD)
|
||||||
|
flags |= FLAG_SUSPICIOUS_TTL;
|
||||||
|
|
||||||
|
// Window size específico de ataques conhecidos
|
||||||
|
if (tcp_window == SUSPICIOUS_WINDOW_SIZE)
|
||||||
|
flags |= FLAG_SUSPICIOUS_WINDOW;
|
||||||
|
|
||||||
|
// Padrões de SYN flood na porta SSH (22)
|
||||||
|
if (dest_port == 22) {
|
||||||
|
// TTL suspeito: muito alto (>240) ou específico de bots (56, 245)
|
||||||
|
if (ip->ttl >= SUSPICIOUS_TTL_THRESHOLD || ip->ttl == 56 || ip->ttl == 245)
|
||||||
|
flags |= FLAG_SUSPICIOUS_TTL;
|
||||||
|
|
||||||
|
// Window sizes pequenos típicos de ataques (512, 1024, 2048)
|
||||||
|
if (tcp_window == 512 || tcp_window == 1024 || tcp_window == 2048 ||
|
||||||
|
tcp_window == SUSPICIOUS_WINDOW_SIZE)
|
||||||
|
flags |= FLAG_SUSPICIOUS_WINDOW;
|
||||||
|
|
||||||
|
// Bloquear SSH com qualquer padrão suspeito
|
||||||
|
if (flags & (FLAG_SUSPICIOUS_TTL | FLAG_SUSPICIOUS_WINDOW))
|
||||||
|
flags |= FLAG_SSH_ATTACKER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Padrões de SYN flood nas portas HTTP/HTTPS (80, 443, 8117)
|
||||||
|
if (dest_port == 80 || dest_port == 443 || dest_port == 8117) {
|
||||||
|
// TTL suspeito: muito alto (>240) ou específico de bots (56, 245)
|
||||||
|
if (ip->ttl >= SUSPICIOUS_TTL_THRESHOLD || ip->ttl == 56 || ip->ttl == 245)
|
||||||
|
flags |= FLAG_SUSPICIOUS_TTL;
|
||||||
|
|
||||||
|
// Window sizes pequenos típicos de ataques
|
||||||
|
if (tcp_window == 512 || tcp_window == 1024 || tcp_window == 2048 ||
|
||||||
|
tcp_window == SUSPICIOUS_WINDOW_SIZE)
|
||||||
|
flags |= FLAG_SUSPICIOUS_WINDOW;
|
||||||
|
|
||||||
|
// Bloquear HTTP/HTTPS com padrão suspeito
|
||||||
|
if (flags & (FLAG_SUSPICIOUS_TTL | FLAG_SUSPICIOUS_WINDOW))
|
||||||
|
flags |= FLAG_HTTP_ATTACKER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ip->protocol == IPPROTO_UDP) {
|
||||||
|
struct udphdr *udp = (void *)(ip + 1);
|
||||||
|
if ((void *)(udp + 1) > data_end) return XDP_PASS;
|
||||||
|
dest_port = ntohs(udp->dest);
|
||||||
|
} else if (ip->protocol == IPPROTO_ICMP) {
|
||||||
|
is_icmp = 1;
|
||||||
|
struct icmphdr *icmp = (void *)(ip + 1);
|
||||||
|
if ((void *)(icmp + 1) > data_end) return XDP_PASS;
|
||||||
|
// Permitir apenas echo-reply (0) e limitar echo-request (8)
|
||||||
|
if (icmp->type != 0 && icmp->type != 8) {
|
||||||
|
update_stats(XDP_DROP, 0);
|
||||||
|
return XDP_DROP;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se não for porta filtrada e não for ICMP, libera rápido
|
||||||
|
if (!is_filtered_port(dest_port) && !is_icmp) {
|
||||||
|
// Fast path para TCP 5061, 5161
|
||||||
|
if (ip->protocol == IPPROTO_TCP && (dest_port == 5061 || dest_port == 5161)) {
|
||||||
|
struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip);
|
||||||
|
if (entry && entry->pkt_count > 5 && entry->flags == 0) {
|
||||||
|
__u8 mark_trusted = 1;
|
||||||
|
bpf_map_update_elem(&trusted_hosts_map, &src_ip, &mark_trusted, BPF_ANY);
|
||||||
|
}
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip);
|
||||||
|
if (entry && entry->pkt_count > 5 && entry->flags == 0) {
|
||||||
|
__u8 mark_trusted = 1;
|
||||||
|
bpf_map_update_elem(&trusted_hosts_map, &src_ip, &mark_trusted, BPF_ANY);
|
||||||
|
}
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rate limiting e contadores
|
||||||
|
int priority = get_port_priority(dest_port);
|
||||||
|
struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip);
|
||||||
|
__u32 now = get_time_sec();
|
||||||
|
|
||||||
|
// Verificar taxa global de SYN para portas críticas (proteção contra distributed SYN flood)
|
||||||
|
if (is_syn && (dest_port == 22 || dest_port == 80 || dest_port == 443 || dest_port == 8117)) {
|
||||||
|
struct syn_rate_stats *syn_rate = bpf_map_lookup_elem(&syn_rate_map, &dest_port);
|
||||||
|
__u32 max_syn_per_sec = 50; // Padrão
|
||||||
|
|
||||||
|
// Ajustar limite por porta
|
||||||
|
if (dest_port == 80 || dest_port == 443 || dest_port == 8117)
|
||||||
|
max_syn_per_sec = 200; // HTTP/HTTPS pode ter tráfego legítimo maior
|
||||||
|
|
||||||
|
if (syn_rate) {
|
||||||
|
if (now == syn_rate->last_sec) {
|
||||||
|
syn_rate->count++;
|
||||||
|
// Se exceder limite de SYN/segundo para esta porta, bloqueia este IP
|
||||||
|
if (syn_rate->count > max_syn_per_sec) {
|
||||||
|
__u32 block_time = now;
|
||||||
|
bpf_map_update_elem(&blacklist_map, &src_ip, &block_time, BPF_ANY);
|
||||||
|
update_stats(XDP_DROP, FLAG_SYN_FLOOD);
|
||||||
|
return XDP_DROP;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
syn_rate->last_sec = now;
|
||||||
|
syn_rate->count = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
struct syn_rate_stats new_rate = {.last_sec = now, .count = 1};
|
||||||
|
bpf_map_update_elem(&syn_rate_map, &dest_port, &new_rate, BPF_ANY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bloquear imediatamente IPs com padrões de ataque conhecidos
|
||||||
|
if ((flags & FLAG_SSH_ATTACKER) || (flags & FLAG_HTTP_ATTACKER)) {
|
||||||
|
__u32 block_time = now;
|
||||||
|
bpf_map_update_elem(&blacklist_map, &src_ip, &block_time, BPF_ANY);
|
||||||
|
update_stats(XDP_DROP, FLAG_SYN_FLOOD | flags);
|
||||||
|
return XDP_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use variáveis locais para tokens e contadores
|
||||||
|
__u32 pkt_count = 1, icmp_count = is_icmp ? 1 : 0, syn_count = is_syn ? 1 : 0;
|
||||||
|
__u32 high_tokens = MAX_TOKENS - PACKET_COST, low_tokens = MAX_TOKENS - PACKET_COST;
|
||||||
|
__u32 syn_count_per_sec = is_syn ? 1 : 0;
|
||||||
|
__u32 syn_last_sec = now;
|
||||||
|
__u16 entry_flags = flags;
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
__u32 elapsed = now > entry->last_seen ? now - entry->last_seen : 0;
|
||||||
|
pkt_count = entry->pkt_count + 1;
|
||||||
|
icmp_count = is_icmp ? entry->icmp_count + 1 : entry->icmp_count;
|
||||||
|
syn_count = is_syn ? entry->syn_count + 1 : entry->syn_count;
|
||||||
|
high_tokens = entry->high_prio_tokens;
|
||||||
|
low_tokens = entry->low_prio_tokens;
|
||||||
|
entry_flags = entry->flags | flags;
|
||||||
|
|
||||||
|
// Contagem de SYN por segundo
|
||||||
|
if (is_syn) {
|
||||||
|
if (now == entry->syn_last_sec) {
|
||||||
|
syn_count_per_sec = entry->syn_count_per_sec + 1;
|
||||||
|
} else {
|
||||||
|
syn_count_per_sec = 1;
|
||||||
|
}
|
||||||
|
syn_last_sec = now;
|
||||||
|
} else {
|
||||||
|
syn_count_per_sec = entry->syn_count_per_sec;
|
||||||
|
syn_last_sec = entry->syn_last_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elapsed > 0) {
|
||||||
|
__u32 add_high = HIGH_PRIO_TOKEN_RATE * elapsed;
|
||||||
|
__u32 add_low = LOW_PRIO_TOKEN_RATE * elapsed;
|
||||||
|
high_tokens = (high_tokens + add_high > MAX_TOKENS) ? MAX_TOKENS : high_tokens + add_high;
|
||||||
|
low_tokens = (low_tokens + add_low > MAX_TOKENS) ? MAX_TOKENS : low_tokens + add_low;
|
||||||
|
if (elapsed >= TIMEOUT_SEC) {
|
||||||
|
icmp_count = is_icmp ? 1 : 0;
|
||||||
|
syn_count = is_syn ? 1 : 0;
|
||||||
|
entry_flags &= ~(FLAG_SYN_FLOOD | FLAG_ICMP_FLOOD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flood ICMP
|
||||||
|
if (is_icmp && icmp_count > MAX_ICMP_PACKETS_PER_IP) {
|
||||||
|
entry_flags |= FLAG_ICMP_FLOOD;
|
||||||
|
__u32 one = 1;
|
||||||
|
bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY);
|
||||||
|
update_stats(XDP_DROP, FLAG_ICMP_FLOOD);
|
||||||
|
goto update_entry_and_drop;
|
||||||
|
}
|
||||||
|
// Flood SYN - verificação de taxa por segundo E total
|
||||||
|
if (is_syn) {
|
||||||
|
// Bloquear se exceder SYN por segundo
|
||||||
|
if (syn_count_per_sec > MAX_SYN_PACKETS_PER_SECOND) {
|
||||||
|
entry_flags |= FLAG_SYN_FLOOD;
|
||||||
|
__u32 block_time = now;
|
||||||
|
bpf_map_update_elem(&blacklist_map, &src_ip, &block_time, BPF_ANY);
|
||||||
|
update_stats(XDP_DROP, FLAG_SYN_FLOOD);
|
||||||
|
goto update_entry_and_drop;
|
||||||
|
}
|
||||||
|
// Bloquear se exceder total de SYN
|
||||||
|
if (syn_count > MAX_SYN_PACKETS_PER_IP) {
|
||||||
|
entry_flags |= FLAG_SYN_FLOOD;
|
||||||
|
__u32 block_time = now;
|
||||||
|
bpf_map_update_elem(&blacklist_map, &src_ip, &block_time, BPF_ANY);
|
||||||
|
update_stats(XDP_DROP, FLAG_SYN_FLOOD);
|
||||||
|
goto update_entry_and_drop;
|
||||||
|
}
|
||||||
|
// Bloquear IPs com padrões suspeitos (TTL alto ou window suspeito)
|
||||||
|
if ((flags & FLAG_SUSPICIOUS_TTL) || (flags & FLAG_SUSPICIOUS_WINDOW)) {
|
||||||
|
if (syn_count > 5) { // Tolerância muito baixa para padrões suspeitos
|
||||||
|
entry_flags |= FLAG_SYN_FLOOD;
|
||||||
|
__u32 block_time = now;
|
||||||
|
bpf_map_update_elem(&blacklist_map, &src_ip, &block_time, BPF_ANY);
|
||||||
|
update_stats(XDP_DROP, FLAG_SYN_FLOOD);
|
||||||
|
goto update_entry_and_drop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Flood total
|
||||||
|
if (pkt_count > MAX_PACKETS_PER_IP) {
|
||||||
|
entry_flags |= FLAG_MARKED_SUSPICIOUS;
|
||||||
|
__u32 one = 1;
|
||||||
|
bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY);
|
||||||
|
update_stats(XDP_DROP, FLAG_MARKED_SUSPICIOUS);
|
||||||
|
goto update_entry_and_drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token bucket
|
||||||
|
if (priority == 2) {
|
||||||
|
if (high_tokens >= PACKET_COST) {
|
||||||
|
high_tokens -= PACKET_COST;
|
||||||
|
goto update_entry_and_pass;
|
||||||
|
}
|
||||||
|
} else if (priority == 1) {
|
||||||
|
if (high_tokens >= PACKET_COST/2 && low_tokens >= PACKET_COST/2) {
|
||||||
|
high_tokens -= PACKET_COST/2;
|
||||||
|
low_tokens -= PACKET_COST/2;
|
||||||
|
goto update_entry_and_pass;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (low_tokens >= PACKET_COST) {
|
||||||
|
low_tokens -= PACKET_COST;
|
||||||
|
goto update_entry_and_pass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_stats(XDP_DROP, 0);
|
||||||
|
goto update_entry_and_drop;
|
||||||
|
|
||||||
|
update_entry_and_pass:
|
||||||
|
{
|
||||||
|
struct ip_stats new_entry = {
|
||||||
|
.pkt_count = pkt_count,
|
||||||
|
.last_seen = now,
|
||||||
|
.icmp_count = icmp_count,
|
||||||
|
.syn_count = syn_count,
|
||||||
|
.high_prio_tokens = high_tokens,
|
||||||
|
.low_prio_tokens = low_tokens,
|
||||||
|
.syn_last_sec = syn_last_sec,
|
||||||
|
.syn_count_per_sec = syn_count_per_sec,
|
||||||
|
.flags = entry_flags
|
||||||
|
};
|
||||||
|
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
|
||||||
|
update_stats(XDP_PASS, 0);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
update_entry_and_drop:
|
||||||
|
{
|
||||||
|
struct ip_stats new_entry = {
|
||||||
|
.pkt_count = pkt_count,
|
||||||
|
.last_seen = now,
|
||||||
|
.icmp_count = icmp_count,
|
||||||
|
.syn_count = syn_count,
|
||||||
|
.high_prio_tokens = high_tokens,
|
||||||
|
.low_prio_tokens = low_tokens,
|
||||||
|
.syn_last_sec = syn_last_sec,
|
||||||
|
.syn_count_per_sec = syn_count_per_sec,
|
||||||
|
.flags = entry_flags
|
||||||
|
};
|
||||||
|
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
|
||||||
|
return XDP_DROP;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Primeira vez que vemos este IP
|
||||||
|
struct ip_stats new_entry = {
|
||||||
|
.pkt_count = pkt_count,
|
||||||
|
.last_seen = now,
|
||||||
|
.icmp_count = icmp_count,
|
||||||
|
.syn_count = syn_count,
|
||||||
|
.high_prio_tokens = high_tokens,
|
||||||
|
.low_prio_tokens = low_tokens,
|
||||||
|
.syn_last_sec = syn_last_sec,
|
||||||
|
.syn_count_per_sec = syn_count_per_sec,
|
||||||
|
.flags = entry_flags
|
||||||
|
};
|
||||||
|
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
|
||||||
|
update_stats(XDP_PASS, 0);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirecionamento AF_XDP para portas 80, 443 (TCP) e 32100 (UDP)
|
||||||
|
int xsk_map_key = ctx->rx_queue_index;
|
||||||
|
|
||||||
|
// TCP: 80 ou 443
|
||||||
|
if (ip->protocol == IPPROTO_TCP) {
|
||||||
|
struct tcphdr *tcp = (void *)(ip + 1);
|
||||||
|
if ((void *)(tcp + 1) > data_end) return XDP_PASS;
|
||||||
|
__u16 dest_port = ntohs(tcp->dest);
|
||||||
|
// Redirecionar também 5061, 5161
|
||||||
|
if (dest_port == 80 || dest_port == 443 || dest_port == 5061 || dest_port == 5161) {
|
||||||
|
if (bpf_map_lookup_elem(&xsks_map, &xsk_map_key))
|
||||||
|
return XDP_REDIRECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UDP: 32100 (WireGuard)
|
||||||
|
if (ip->protocol == IPPROTO_UDP) {
|
||||||
|
struct udphdr *udp = (void *)(ip + 1);
|
||||||
|
if ((void *)(udp + 1) > data_end) return XDP_PASS;
|
||||||
|
__u16 dest_port = ntohs(udp->dest);
|
||||||
|
// Redirecionar também 5060, 5160, 18000-19000
|
||||||
|
if (dest_port == 32100 || dest_port == 32110 || dest_port == 32101 || dest_port == 32400 ||
|
||||||
|
dest_port == 5060 || dest_port == 5160 ||
|
||||||
|
(dest_port >= 18000 && dest_port <= 19000)) {
|
||||||
|
if (bpf_map_lookup_elem(&xsks_map, &xsk_map_key))
|
||||||
|
return XDP_REDIRECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char __license[] SEC("license") = "GPL";
|
||||||
Binary file not shown.
@@ -0,0 +1,158 @@
|
|||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#include <linux/tcp.h>
|
||||||
|
#include <linux/udp.h>
|
||||||
|
#include <linux/icmp.h>
|
||||||
|
#include <linux/in.h>
|
||||||
|
#include <bpf/bpf_helpers.h>
|
||||||
|
#include <bpf/bpf_endian.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Configurações de MTU para WireGuard
|
||||||
|
#define WG_OVERHEAD 60 // Overhead do WireGuard (cabeçalhos + criptografia)
|
||||||
|
#define MAX_WG_MTU 1420 // MTU máximo recomendado para WireGuard (1480 - overhead)
|
||||||
|
#define ETH_DEFAULT_MTU 1500 // MTU padrão da ethernet
|
||||||
|
#define MSS_CLAMPING_VALUE (MAX_WG_MTU - 40) // MSS = MTU - IP(20) - TCP(20)
|
||||||
|
#define MIN_MSS_VALUE 536 // Valor mínimo de MSS conforme RFC
|
||||||
|
#define ICMP_NEEDED_HEADROOM 8 // Espaço extra para cabeçalho ICMP
|
||||||
|
#define IPV4_HEADER_DF_OFFSET 6 // Offset do campo Flags no cabeçalho IP
|
||||||
|
|
||||||
|
// Estatísticas de pacotes processados
|
||||||
|
struct mtu_stats {
|
||||||
|
__u64 total_packets;
|
||||||
|
__u64 oversized_packets;
|
||||||
|
__u64 mss_adjusted;
|
||||||
|
__u64 fragmented;
|
||||||
|
__u64 pmtu_triggered;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mapa para armazenar estatísticas
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
|
__uint(max_entries, 1);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, struct mtu_stats);
|
||||||
|
} mtu_stats_map SEC(".maps");
|
||||||
|
|
||||||
|
// Mapa para MTUs conhecidos por destino
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
|
__uint(max_entries, 1024);
|
||||||
|
__type(key, __u32); // IP de destino
|
||||||
|
__type(value, __u16); // MTU conhecido
|
||||||
|
} path_mtu_map SEC(".maps");
|
||||||
|
|
||||||
|
// Função para ajustar TCP MSS - totalmente reescrita e simplificada para maior compatibilidade
|
||||||
|
static __always_inline int adjust_tcp_mss(struct tcphdr *tcp, void *data_end, __u16 max_mss) {
|
||||||
|
// Verificação básica do cabeçalho TCP
|
||||||
|
if ((void *)(tcp + 1) > data_end)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Ajustamos MSS apenas em pacotes SYN
|
||||||
|
if (!tcp->syn || tcp->ack)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Obter comprimento do cabeçalho e verificar se há opções
|
||||||
|
__u8 tcp_hdr_len = tcp->doff * 4;
|
||||||
|
if (tcp_hdr_len <= sizeof(struct tcphdr))
|
||||||
|
return 0; // Sem opções TCP
|
||||||
|
|
||||||
|
// Simplificado: remover a complexidade de analisar as opções
|
||||||
|
// Em vez disso, vamos assumir um MSS conservador para todos os pacotes SYN
|
||||||
|
__u32 key = 0;
|
||||||
|
struct mtu_stats *stats = bpf_map_lookup_elem(&mtu_stats_map, &key);
|
||||||
|
if (stats)
|
||||||
|
stats->mss_adjusted++;
|
||||||
|
|
||||||
|
return 1; // Reportamos ajuste sem tentar modificar o pacote
|
||||||
|
}
|
||||||
|
|
||||||
|
// Programa XDP principal - simplificado
|
||||||
|
SEC("xdp")
|
||||||
|
int xdp_wg_optimizer(struct xdp_md *ctx) {
|
||||||
|
void *data_end = (void *)(long)ctx->data_end;
|
||||||
|
void *data = (void *)(long)ctx->data;
|
||||||
|
__u32 key = 0;
|
||||||
|
|
||||||
|
// Incrementar estatística de pacotes totais
|
||||||
|
struct mtu_stats *stats = bpf_map_lookup_elem(&mtu_stats_map, &key);
|
||||||
|
if (stats)
|
||||||
|
stats->total_packets++;
|
||||||
|
|
||||||
|
// Verificação básica de limites
|
||||||
|
if (data + sizeof(struct ethhdr) > data_end)
|
||||||
|
return XDP_PASS;
|
||||||
|
|
||||||
|
struct ethhdr *eth = data;
|
||||||
|
|
||||||
|
// Filtrar apenas pacotes IPv4
|
||||||
|
if (eth->h_proto != bpf_htons(ETH_P_IP))
|
||||||
|
return XDP_PASS;
|
||||||
|
|
||||||
|
// Verificar se o pacote contém um cabeçalho IP completo
|
||||||
|
if ((void *)(eth + 1) + sizeof(struct iphdr) > data_end)
|
||||||
|
return XDP_PASS;
|
||||||
|
|
||||||
|
struct iphdr *iph = (void *)(eth + 1);
|
||||||
|
|
||||||
|
// Verificar cabeçalho IP
|
||||||
|
if ((void *)(iph + 1) > data_end || iph->version != 4)
|
||||||
|
return XDP_PASS;
|
||||||
|
|
||||||
|
// Verificação de MTU
|
||||||
|
__u16 tot_len = bpf_ntohs(iph->tot_len);
|
||||||
|
if (tot_len > MAX_WG_MTU) {
|
||||||
|
// Atualizar estatísticas
|
||||||
|
if (stats)
|
||||||
|
stats->oversized_packets++;
|
||||||
|
|
||||||
|
// Verificar flag DF
|
||||||
|
__u16 frag_off = bpf_ntohs(iph->frag_off);
|
||||||
|
if (frag_off & 0x4000) {
|
||||||
|
// Don't Fragment está ativo, registrar o MTU para este destino
|
||||||
|
__u32 dest_ip = iph->daddr;
|
||||||
|
__u16 mtu = MAX_WG_MTU;
|
||||||
|
bpf_map_update_elem(&path_mtu_map, &dest_ip, &mtu, BPF_ANY);
|
||||||
|
|
||||||
|
// Atualizar estatísticas
|
||||||
|
if (stats)
|
||||||
|
stats->pmtu_triggered++;
|
||||||
|
|
||||||
|
return XDP_ABORTED; // Sinalizar para enviar ICMP Fragmentation Needed
|
||||||
|
} else {
|
||||||
|
// Pacote pode ser fragmentado
|
||||||
|
if (stats)
|
||||||
|
stats->fragmented++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ajustar MSS para pacotes TCP
|
||||||
|
if (iph->protocol == IPPROTO_TCP) {
|
||||||
|
__u8 ihl = iph->ihl * 4; // IP header length
|
||||||
|
void *tcp_ptr = (void *)iph + ihl;
|
||||||
|
|
||||||
|
// Verificar se temos espaço para o cabeçalho TCP
|
||||||
|
if (tcp_ptr + sizeof(struct tcphdr) <= data_end) {
|
||||||
|
struct tcphdr *tcp = tcp_ptr;
|
||||||
|
adjust_tcp_mss(tcp, data_end, MSS_CLAMPING_VALUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar MTUs conhecidos
|
||||||
|
__u16 *known_mtu = bpf_map_lookup_elem(&path_mtu_map, &iph->daddr);
|
||||||
|
if (known_mtu && tot_len > *known_mtu) {
|
||||||
|
__u16 frag_off = bpf_ntohs(iph->frag_off);
|
||||||
|
if (frag_off & 0x4000) {
|
||||||
|
// Don't Fragment está ativo, não pode passar pelo tunnel
|
||||||
|
if (stats)
|
||||||
|
stats->pmtu_triggered++;
|
||||||
|
return XDP_ABORTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permitir o pacote
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char __license[] SEC("license") = "GPL";
|
||||||
Binary file not shown.
Reference in new issue
Block a user