diff --git a/ebpf-sockmap/bpf/sockmap.bpf.c b/ebpf-sockmap/bpf/sockmap.bpf.c index f19a30a..211fcdc 100644 --- a/ebpf-sockmap/bpf/sockmap.bpf.c +++ b/ebpf-sockmap/bpf/sockmap.bpf.c @@ -97,13 +97,30 @@ static __always_inline bool is_local_ip(__u32 ip_be) // Extrai a chave de conexão a partir do contexto sock_ops // (usado quando uma conexão TCP é estabelecida) // ───────────────────────────────────────────────────────── +// BUG CORRIGIDO: o código antigo misturava dois domínios de codificação +// de porta sem converter os dois pro mesmo formato. +// - ops->local_port vem em HOST byte order (puro, ex: 6379) +// - ops->remote_port vem com o valor em network byte order alojado nos +// 16 bits SUPERIORES de um campo de 32 bits — não é um u32 comum em +// network order, então bpf_ntohl() sozinho faz um byte-swap completo +// de 32 bits e produz um número sem relação com a porta real. +// A comparação `sport == local_port do outro lado` só funciona se AMBOS +// os valores estiverem no MESMO domínio. Em vez de tentar decifrar o +// valor "verdadeiro" da porta (o que exigiria confiar numa convenção que +// não dá pra validar sem testar em kernel real), a correção normaliza os +// dois pro domínio de remote_port: extrai remote_port com ">> 16" (sem +// ntohl) e converte local_port PRA esse mesmo domínio com +// "bpf_htonl(local_port) >> 16". Assim os dois lados de uma conexão +// calculam exatamente o mesmo valor pro mesmo par de portas, não importa +// qual seja a codificação exata do kernel — o que importa é que os dois +// membros do par usem a MESMA transformação simétrica. 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->sport = bpf_htonl(ops->local_port) >> 16; + key->dport = ops->remote_port >> 16; key->family = ops->family; } @@ -201,11 +218,15 @@ 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} + // + // Mesma normalização de porta usada em extract_key_from_ops (mesmo + // domínio dos dois lados): remote_port só com ">> 16", local_port + // convertido pra esse domínio via "bpf_htonl(...) >> 16". 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.sport = msg->remote_port >> 16; + key.dport = bpf_htonl(msg->local_port) >> 16; key.family = msg->family; // Tenta redirecionar diretamente para o socket par. diff --git a/rc.local b/rc.local index 170a0ca..f914fb8 100755 --- a/rc.local +++ b/rc.local @@ -52,4 +52,12 @@ ip link set dev wg0 xdp obj /home/rodrigo/server-mv/xdp/xdp_wg_mtu_optimizer.o s # então esse link precisa ser reaplicado se a rede "dockernet" for recriada. ip link set dev br-02f7820020b4 xdp obj /home/rodrigo/server-mv/xdp/bridge_xdp.o sec xdp -cd /home/docker/ebpf-sockmap/ && make load +# eBPF sockmap: DESATIVADO DE PROPÓSITO. Causou timeout/connection reset +# em conexões container-a-container (nginx->redis/mariadb/anatomia-web), +# incluindo depois de corrigir o bug de NAT (masquerade sem -o enp0s6, +# já corrigido em nftables.conf) — ou seja, o bug é na lógica de +# match/redirect do sk_msg em bpf/sockmap.bpf.c, não só no NAT. Não +# reativar em produção sem antes rastrear com bpf_printk num ambiente +# de teste. Ganho é só performance (bypass de socket local entre +# containers no mesmo host); o site funciona normalmente sem isso. +# cd /home/docker/ebpf-sockmap/ && make load