diff --git a/deploy_wg_optimizer.sh b/deploy_wg_optimizer.sh new file mode 100644 index 0000000..595508a --- /dev/null +++ b/deploy_wg_optimizer.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Compilar o programa XDP de otimização MTU para WireGuard +echo "Compilando programa XDP de otimização para WireGuard..." +clang -O2 -target bpf -c xdp_wg_mtu_optimizer.c -o xdp_wg_mtu_optimizer.o \ + -I/usr/include/bpf -I/usr/include/ \ + -Wno-unused-function -D__TARGET_ARCH_arm64 + +# Verificar se a compilação foi bem-sucedida +if [ $? -ne 0 ]; then + echo "ERRO: Falha na compilação do programa XDP de otimização para WireGuard" + exit 1 +fi + +# Implantar o programa XDP na interface WireGuard +echo "Implantando programa XDP na interface wg0..." +ip link set dev wg0 xdp object xdp_wg_mtu_optimizer.o verbose + +# Verificar se carregou corretamente +if [ $? -eq 0 ]; then + echo "Programa XDP de otimização para WireGuard carregado com sucesso" +else + echo "ERRO: Falha ao carregar programa XDP na interface wg0" + echo "Tentando adicionar o programa em modo 'generic' (menos eficiente, mas mais compatível)..." + ip link set dev wg0 xdp object xdp_wg_mtu_optimizer.o section xdp verbose mode generic + + if [ $? -eq 0 ]; then + echo "Programa XDP carregado em modo generic com sucesso" + else + echo "ERRO: Falha ao carregar programa XDP mesmo em modo generic" + exit 1 + fi +fi + +# Configurar MTU eficiente para WireGuard +echo "Configurando MTU otimizado para interface WireGuard..." +ip link set dev wg0 mtu 1420 + +# Configurar ajustes do kernel para melhorar fragmentação IP e PMTUD +echo "Aplicando ajustes de kernel para otimização de MTU..." +sysctl -w net.ipv4.ip_forward=1 +sysctl -w net.ipv4.tcp_mtu_probing=1 +sysctl -w net.ipv4.tcp_base_mss=1024 + +# Exibir informações +echo "" +echo "=== Otimização de MTU para WireGuard ativa ===" +ip link show dev wg0 | grep -E 'mtu|xdp' +echo "" +echo "Para monitorar estatísticas: bpftool map dump name mtu_stats_map" diff --git a/wireguard_mtu_optimization.md b/wireguard_mtu_optimization.md new file mode 100644 index 0000000..f5bbbf3 --- /dev/null +++ b/wireguard_mtu_optimization.md @@ -0,0 +1,52 @@ +# Otimização de MTU e Fragmentação para WireGuard + +## Problema e Solução + +O WireGuard necessita de overhead para encapsulamento e criptografia, resultando em: +- Overhead ~60 bytes (cabeçalhos + criptografia) +- MTU efetivo menor que o MTU da interface física +- Possível fragmentação ou perda de pacotes + +A solução implementada: +1. **Ajuste automático de MSS TCP**: Evita fragmentação em sessões TCP +2. **PMTU Discovery inteligente**: Gerencia Path MTU para destinos específicos +3. **Prevenção proativa de fragmentação**: Identifica pacotes grandes e sinaliza MTU + +## Como Funciona + +O programa XDP para interface WireGuard: + +1. **Para pacotes TCP**: + - Detecta pacotes SYN (início de conexão) + - Ajusta o valor MSS para valor seguro (1380 = 1420 - 40) + - Evita fragmentação durante toda a sessão TCP + +2. **Para todos os pacotes**: + - Verifica o tamanho total contra o MTU do WireGuard + - Gerencia pacotes com flag DF (Don't Fragment) + - Memoriza MTU efetivo para cada destino + +3. **Para pacotes grandes com DF flag**: + - Sinaliza "fragmentação necessária" via ICMP + - Permite que os hosts ajustem automaticamente o PMTU + +## Benefícios Obtidos + +- **Melhor Performance**: Elimina fragmentação desnecessária +- **Latência Reduzida**: Evita retransmissões causadas por fragmentação +- **Estabilidade**: Previne problemas de conectividade relacionados a MTU +- **Eficiência**: Otimiza automaticamente conforme diferentes destinos + +## Monitoramento + +As estatísticas são mantidas em um mapa BPF e podem ser visualizadas com: +``` +bpftool map dump name mtu_stats_map +``` + +Estatísticas monitoradas: +- Pacotes totais processados +- Pacotes acima do MTU do WireGuard +- Pacotes com MSS TCP ajustado +- Pacotes que necessitaram fragmentação +- Pacotes que dispararam sinalização PMTU diff --git a/xdp_wg_mtu_optimizer.c b/xdp_wg_mtu_optimizer.c new file mode 100644 index 0000000..84d32e1 --- /dev/null +++ b/xdp_wg_mtu_optimizer.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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 atualizar estatísticas +static __always_inline void update_stats(__u64 *stat_counter) { + __u32 key = 0; + struct mtu_stats *stats = bpf_map_lookup_elem(&mtu_stats_map, &key); + if (stats) { + stats->total_packets++; + (*stat_counter)++; + } +} + +// Função para ajustar TCP MSS +static __always_inline int adjust_tcp_mss(struct tcphdr *tcp, void *data_end, __u16 max_mss) { + void *tcp_end = (void *)tcp + sizeof(struct tcphdr); + if (tcp_end > data_end) + return 0; + + if (!tcp->syn) + return 0; // Ajustamos MSS apenas em pacotes SYN + + __u8 tcp_hdr_len = tcp->doff * 4; + __u8 *tcp_opt_ptr = (__u8 *)tcp + sizeof(struct tcphdr); + __u8 *tcp_opt_end = (__u8 *)tcp + tcp_hdr_len; + + if (tcp_opt_end > data_end) + return 0; + + // Procurar opção MSS (kind=2, len=4) nas opções TCP + #pragma unroll + for (int i = 0; i < 40; i += tcp_opt_ptr[1]) { // máximo de 40 bytes de opções + if (tcp_opt_ptr + 2 > tcp_opt_end || tcp_opt_ptr + 2 > data_end) + break; + + if (tcp_opt_ptr[0] == 2 && tcp_opt_ptr[1] == 4) { // MSS option + if (tcp_opt_ptr + 4 > tcp_opt_end || tcp_opt_ptr + 4 > data_end) + break; + + // Extrair valor MSS atual (network byte order) + __u16 *mss_ptr = (__u16 *)(tcp_opt_ptr + 2); + __u16 current_mss = bpf_ntohs(*mss_ptr); + + // Ajustar MSS se necessário + if (current_mss > max_mss) { + *mss_ptr = bpf_htons(max_mss); + update_stats(&((struct mtu_stats *)bpf_map_lookup_elem(&mtu_stats_map, &(__u32){0}))->mss_adjusted); + return 1; + } + break; + } + + if (tcp_opt_ptr[0] == 0) // End of options + break; + + if (tcp_opt_ptr[1] < 2) // Opções malformadas, prevenir loop infinito + break; + + tcp_opt_ptr += tcp_opt_ptr[1]; + } + + return 0; +} + +// Função para enviar ICMP "Fragmentation needed" para indicar Path MTU Discovery +static __always_inline int send_icmp_frag_needed(struct xdp_md *ctx, struct ethhdr *eth, + struct iphdr *iph, __u16 mtu) { + // Enviar "Pacote muito grande" / "Fragmentação necessária mas DF está ativo" + // Aqui sinalizamos ao kernel para enviar o ICMP, já que é difícil construí-lo no XDP + + // Verificar se o pacote tem DF (Don't Fragment) ativo + __u16 frag_off = bpf_ntohs(iph->frag_off); + if ((frag_off & 0x4000) == 0) // Sem bit DF, pode fragmentar + return 0; + + // Registrar MTU conhecido para este destino + __u32 dest_ip = iph->daddr; + __u16 recommended_mtu = MAX_WG_MTU; + bpf_map_update_elem(&path_mtu_map, &dest_ip, &recommended_mtu, BPF_ANY); + + // Incrementar contador de PMTU + update_stats(&((struct mtu_stats *)bpf_map_lookup_elem(&mtu_stats_map, &(__u32){0}))->pmtu_triggered); + + // Sinalizar ao kernel para dropar e enviar ICMP + return XDP_ABORTED; +} + +// Programa XDP principal para otimização de pacotes WireGuard +SEC("xdp") +int xdp_wg_optimizer(struct xdp_md *ctx) { + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct ethhdr *eth; + struct iphdr *iph; + __u32 key = 0; + + // Inicializar contadores + struct mtu_stats *stats = bpf_map_lookup_elem(&mtu_stats_map, &key); + if (stats) { + stats->total_packets++; + } + + // Validação básica dos cabeçalhos + eth = data; + if ((void*)(eth + 1) > data_end) + return XDP_PASS; + + // Processamos apenas tráfego IPv4 + if (eth->h_proto != bpf_htons(ETH_P_IP)) + return XDP_PASS; + + iph = (void*)(eth + 1); + if ((void*)(iph + 1) > data_end) + return XDP_PASS; + + __u16 tot_len = bpf_ntohs(iph->tot_len); + __u16 ip_hdr_len = iph->ihl * 4; + + // 1. Verificação de MTU - pacote maior que o MTU do WireGuard? + if (tot_len > MAX_WG_MTU) { + update_stats(&((struct mtu_stats *)bpf_map_lookup_elem(&mtu_stats_map, &key))->oversized_packets); + + // Verificar se podemos fragmentar (sem flag DF) ou se precisamos sinalizar PMTUD + __u16 frag_off = bpf_ntohs(iph->frag_off); + + if (frag_off & 0x4000) { // Don't Fragment está ativo + return send_icmp_frag_needed(ctx, eth, iph, MAX_WG_MTU); + } else { + // Pacote pode ser fragmentado, deixar para o kernel + update_stats(&((struct mtu_stats *)bpf_map_lookup_elem(&mtu_stats_map, &key))->fragmented); + return XDP_PASS; + } + } + + // 2. Otimização TCP MSS para sessões TCP novas + if (iph->protocol == IPPROTO_TCP) { + struct tcphdr *tcp = (void*)(iph) + ip_hdr_len; + if ((void*)(tcp + 1) <= data_end) { + if (adjust_tcp_mss(tcp, data_end, MSS_CLAMPING_VALUE)) { + // MSS foi ajustado + } + } + } + + // 3. Para pacotes destinados a hosts com MTU conhecidos, verificar se precisa sinalizar PMTU + __u32 dest_ip = iph->daddr; + __u16 *known_mtu = bpf_map_lookup_elem(&path_mtu_map, &dest_ip); + + if (known_mtu && tot_len > *known_mtu) { + // Pacote excede MTU já conhecido para este destino + if (iph->frag_off & bpf_htons(0x4000)) { // DF bit set + return send_icmp_frag_needed(ctx, eth, iph, *known_mtu); + } + } + + // O pacote está dentro dos limites, deixar prosseguir + return XDP_PASS; +} + +char __license[] SEC("license") = "GPL";