diff --git a/ebpf-sockmap/bpf/sockmap.bpf.c b/ebpf-sockmap/bpf/sockmap.bpf.c index 9b8c8e8..1e25ac5 100644 --- a/ebpf-sockmap/bpf/sockmap.bpf.c +++ b/ebpf-sockmap/bpf/sockmap.bpf.c @@ -133,6 +133,48 @@ static __always_inline bool is_local_ip(__u32 ip_be) return false; } +// ───────────────────────────────────────────────────────── +// DECISÃO DE ESCOPO (depois de 4 rodadas de bugs encontrados e +// corrigidos — porta, dual-stack, loopback, tamanho — todos legítimos, +// mas o problema de fundo continuou aparecendo especificamente em +// tráfego HTTP via proxy: nginx↔Nextcloud, nginx↔Atlas, nginx↔OnlyOffice, +// sempre — "upstream prematurely closed connection" / timeout, mesmo em +// respostas pequenas, em serviços sem nenhuma relação entre si): +// +// Em TODOS os testes, sem uma única exceção, tráfego banco/cache +// (MariaDB, Redis) redirecionou correto e nunca corrompeu nada — nem em +// rajada, nem sob concorrência. Tráfego HTTP via proxy reverso +// (streaming, HTTP/2, keep-alive, chunked) é o único padrão onde o +// problema aparece, e é mais sensível a qualquer perturbação sutil no +// fluxo de bytes. +// +// Em vez de continuar caçando o bug exato do mecanismo de redirect do +// kernel, restringimos o REGISTRO no sockhash a conexões onde um dos +// lados é porta de banco/cache. Tráfego HTTP nunca entra no mapa — +// nunca é candidato a redirect, sempre usa o caminho normal de rede +// (comprovadamente 100% íntegro, inclusive em teste isolado de 500MB +// via nc). Perde-se a aceleração pra proxy HTTP; mantém-se pro tipo de +// tráfego (muitos round-trips pequenos de banco/cache) onde o ganho de +// latência é maior E onde já provamos que funciona sem risco. +// +// IMPORTANTE: key.sport/key.dport NÃO são o número literal da porta — +// já passam pela transformação bpf_htonl(porta)>>16 (ver +// extract_key_from_ops). Comparar direto com 3306/6379 nunca bateria. +// Os valores abaixo são o RESULTADO dessa mesma transformação aplicada +// às portas reais, verificados nos traces de produção desta sessão +// (ex: MariaDB sempre apareceu com dport/sport=59916, nunca 3306) e +// confirmados by matemática: bpf_htonl(3306)>>16 = 0xEA0C = 59916; +// bpf_htonl(6379)>>16 = 0xEB18 = 60184. +// ───────────────────────────────────────────────────────── +#define SOCKMAP_PORT_MARIADB_ENC 59916 // 3306 já transformado +#define SOCKMAP_PORT_REDIS_ENC 60184 // 6379 já transformado + +static __always_inline bool is_accelerated_key(struct sock_key *key) +{ + return key->sport == SOCKMAP_PORT_MARIADB_ENC || key->dport == SOCKMAP_PORT_MARIADB_ENC || + key->sport == SOCKMAP_PORT_REDIS_ENC || key->dport == SOCKMAP_PORT_REDIS_ENC; +} + // ───────────────────────────────────────────────────────── // Extrai a chave de conexão a partir do contexto sock_ops // (usado quando uma conexão TCP é estabelecida) @@ -216,6 +258,12 @@ int bpf_sockops_handler(struct bpf_sock_ops *skops) return 0; } + // ── Escopo: só banco/cache (ver comentário em is_accelerated_key) ── + if (!is_accelerated_key(&key)) { + stats_inc(3); + return 0; + } + if (skops->family == SOCKMAP_AF_INET6) bpf_printk("REG via IPv6 mapeado: op=%u sip=%x dip=%x", op, key.sip4, key.dip4); @@ -323,6 +371,14 @@ int bpf_msg_redirect(struct sk_msg_md *msg) key.dport = bpf_htonl(msg->local_port) >> 16; key.family = SOCKMAP_AF_INET; // normalizado, ver extract_key_from_ops + // Cinto e suspensório: mesmo que o registro já esteja restrito a + // banco/cache, confere de novo aqui — garante que nenhum tráfego + // HTTP tenta redirect mesmo que uma entrada antiga/inesperada exista. + if (!is_accelerated_key(&key)) { + stats_inc(2); + return SK_PASS; + } + // Tenta redirecionar diretamente para o socket par. int ret = bpf_msg_redirect_hash(msg, &sock_hash, &key, BPF_F_INGRESS); if (ret == SK_PASS) {