first commit

This commit is contained in:
Rodrigo Bertoldo de Sousa Martins committed 2026-09-13 20:49:33 -03:00
commit 01d7cb1a80
34 files changed
+5723

No files matched your search

+82
View File
@@ -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 ""
+403
View File
@@ -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
+166
View File
@@ -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 ""