From 6081ec0c1cfe0100e2ecdedc91f22ff6f4861c57 Mon Sep 17 00:00:00 2001 From: Rodrigo Martins Date: Wed, 26 Feb 2025 19:17:06 -0300 Subject: [PATCH] commit --- compile_xdp.sh | 68 ---------- dnat_rules.txt | 3 - nat_config.py | 256 ----------------------------------- simple_xdp_filter.c | 93 ------------- snat_networks.txt | 2 - tc_nat_setup.sh | 116 ---------------- verify_nat.sh | 80 ----------- xdp_accelerated_nat.c | 216 ----------------------------- xdp_nat_tools.py | 306 ------------------------------------------ 9 files changed, 1140 deletions(-) delete mode 100644 compile_xdp.sh delete mode 100644 dnat_rules.txt delete mode 100644 nat_config.py delete mode 100644 simple_xdp_filter.c delete mode 100644 snat_networks.txt delete mode 100644 tc_nat_setup.sh delete mode 100644 verify_nat.sh delete mode 100644 xdp_accelerated_nat.c delete mode 100644 xdp_nat_tools.py diff --git a/compile_xdp.sh b/compile_xdp.sh deleted file mode 100644 index 1301cd2..0000000 --- a/compile_xdp.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash - -# Script simplificado para compilar e carregar programas XDP - -# Interface a ser utilizada -IFACE=${1:-"enp0s6"} -MODE=${2:-"generic"} # generic é mais compatível; native é mais rápido -TYPE=${3:-"simple"} # simple ou accelerated - -echo "Compilando e carregando programa XDP" -echo "Interface: $IFACE" -echo "Modo: $MODE" -echo "Tipo: $TYPE" - -# Compilar o programa correto -if [ "$TYPE" == "simple" ]; then - echo "=== Compilando programa simple_xdp_filter ===" - clang -O2 -Wall -target bpf -g -D__TARGET_ARCH_arm64 -c simple_xdp_filter.c -o simple_xdp_filter.o -I/usr/include/bpf -I/usr/include/ - - if [ $? -ne 0 ]; then - echo "Erro na compilação!" - exit 1 - fi - - PROG_FILE="simple_xdp_filter.o" -else - echo "=== Compilando programa xdp_accelerated_nat ===" - clang -O2 -Wall -target bpf -g -D__TARGET_ARCH_arm64 -c xdp_accelerated_nat.c -o xdp_accelerated_nat.o -I/usr/include/bpf -I/usr/include/ - - if [ $? -ne 0 ]; then - echo "Erro na compilação!" - exit 1 - fi - - PROG_FILE="xdp_accelerated_nat.o" -fi - -# Remover qualquer programa XDP existente -echo "Removendo programas XDP existentes da interface $IFACE" -ip link set dev $IFACE xdp off - -# Carregar o programa -echo "Carregando programa $PROG_FILE na interface $IFACE (modo $MODE)..." -ip link set dev $IFACE xdp obj $PROG_FILE section xdp mode $MODE - -if [ $? -ne 0 ]; then - echo "Erro ao carregar programa XDP." - echo "Tentando modo alternativo (skb)..." - ip link set dev $IFACE xdp obj $PROG_FILE section xdp mode skb - - if [ $? -ne 0 ]; then - echo "Falha também no modo skb." - echo "Verifique se seu kernel suporta XDP." - exit 1 - fi -fi - -echo "Programa XDP carregado com sucesso!" -echo "Verificando status:" -ip link show dev $IFACE | grep xdp - -echo "Use 'bpftool map dump' para ver os mapas" - -if [ "$TYPE" == "simple" ]; then - # Mostrar estatísticas iniciais - echo "Estatísticas iniciais:" - bpftool map dump name stats_map -fi diff --git a/dnat_rules.txt b/dnat_rules.txt deleted file mode 100644 index ab29c49..0000000 --- a/dnat_rules.txt +++ /dev/null @@ -1,3 +0,0 @@ -# Formato: -tcp 10.0.0.87:80 10.15.0.45:80 -tcp 10.0.0.87:443 10.15.0.45:443 \ No newline at end of file diff --git a/nat_config.py b/nat_config.py deleted file mode 100644 index 2983b5c..0000000 --- a/nat_config.py +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys -import socket -import struct -import json -import subprocess -import argparse -import ipaddress -import time -import re - -def run_cmd(cmd, show_output=False): - """Executa um comando shell e retorna sua saída""" - if show_output: - print(f"Executando: {cmd}") - - result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - if result.returncode != 0: - print(f"Erro ao executar: {cmd}") - print(f"Saída de erro: {result.stderr}") - return None - return result.stdout.strip() - -def find_map_ids_by_name(map_name): - """Encontra todos os IDs de mapas BPF com um determinado nome""" - output = run_cmd("bpftool map list") - if not output: - return [] - - map_ids = [] - for line in output.split('\n'): - if f"name {map_name}" in line: - match = re.search(r'^(\d+):', line) - if match: - map_ids.append(match.group(1)) - - return map_ids - -def find_map_id(map_name): - """Encontra o primeiro ID de um mapa BPF pelo nome""" - map_ids = find_map_ids_by_name(map_name) - return map_ids[0] if map_ids else None - -def clean_old_maps(): - """Limpa todos os mapas antigos com nomes relacionados a NAT""" - map_names = ["dnat_port_map", "snat_networks", "hairpin_map"] - count = 0 - - for map_name in map_names: - map_ids = find_map_ids_by_name(map_name) - for map_id in map_ids: - print(f"Limpando mapa antigo: {map_name} (ID: {map_id})") - run_cmd(f"bpftool map dump id {map_id} -j | grep -o '\"key\": \"[^\"]*\"' | " - f"sed 's/\"key\": \"\\([^\"]*\\)\"/\\1/g' | while read key; do " - f"[ ! -z \"$key\" ] && bpftool map delete id {map_id} key hex $key; done") - count += 1 - - return count - -def load_tc_program(interface, object_file="tc_nat.o"): - """Carrega programas TC para interface especificada""" - print(f"Carregando programas TC para interface {interface}...") - - # Compilar programa BPF - print("Compilando programa TC BPF...") - cmd = f"clang -O2 -Wall -target bpf -g -D__TARGET_ARCH_arm64 -c tc_nat.c -o {object_file} -I/usr/include/bpf -I/usr/include/" - if run_cmd(cmd) is None: - return False - - # Remover programas existentes - run_cmd(f"tc filter del dev {interface} ingress 2>/dev/null") - run_cmd(f"tc filter del dev {interface} egress 2>/dev/null") - run_cmd(f"tc qdisc del dev {interface} clsact 2>/dev/null") - - # Configurar qdisc - run_cmd(f"tc qdisc add dev {interface} clsact") - - # Carregar programas TC - ingress_cmd = f"tc filter add dev {interface} ingress prio 1 handle 1 bpf da obj {object_file} sec tc_ingress" - egress_cmd = f"tc filter add dev {interface} egress prio 1 handle 1 bpf da obj {object_file} sec tc_egress" - - if run_cmd(ingress_cmd, show_output=True) is None or run_cmd(egress_cmd, show_output=True) is None: - return False - - print("Programas TC carregados com sucesso!") - print("Aguardando criação dos mapas...") - time.sleep(1) - return True - -def setup_dnat_rule_hex(map_id, external_port, internal_ip, internal_port): - """Configura uma regra DNAT usando strings hexadecimais diretamente""" - # Converter porta para hex (little-endian) - port_hex = f"{external_port & 0xff:02x} {(external_port >> 8) & 0xff:02x}" - - # Converter IP para hex (formato de rede) - octets = list(map(int, internal_ip.split('.'))) - ip_hex = f"{octets[3]:02x} {octets[2]:02x} {octets[1]:02x} {octets[0]:02x}" - - # Converter porta interna para hex (little-endian) - int_port_hex = f"{internal_port & 0xff:02x} {(internal_port >> 8) & 0xff:02x}" - - # Valor: IP (4 bytes) + porta (2 bytes) + padding (2 bytes) - value_hex = f"{ip_hex} {int_port_hex} 00 00" - - # Update map usando bpftool com valores hex - cmd = f"bpftool map update id {map_id} key hex {port_hex} value hex {value_hex}" - success = run_cmd(cmd, show_output=True) is not None - - if success: - print(f"Regra DNAT configurada: {external_port} -> {internal_ip}:{internal_port}") - else: - print(f"Falha ao configurar regra DNAT: {external_port} -> {internal_ip}:{internal_port}") - - return success - -def setup_snat_network_hex(map_id, network): - """Configura uma rede para SNAT usando strings hexadecimais diretamente""" - # Parse da rede (exemplo: "10.15.0.0/16") - ip, prefix = network.split('/') - prefix = int(prefix) - - if prefix != 16: - print(f"AVISO: Rede {network} não é /16. Apenas redes /16 são totalmente suportadas.") - - # Extrair octetos do IP - octets = list(map(int, ip.split('.'))) - - # Para /16, montamos o prefixo - network_hex = f"00 00 {octets[1]:02x} {octets[0]:02x}" - - # Valor é 1 byte: habilitado - value_hex = "01" # 1 = habilitado - - # Update map usando bpftool com valores hex - cmd = f"bpftool map update id {map_id} key hex {network_hex} value hex {value_hex}" - success = run_cmd(cmd, show_output=True) is not None - - if success: - print(f"Rede SNAT configurada: {network}") - else: - print(f"Falha ao configurar rede SNAT: {network}") - - return success - -def clear_map(map_id): - """Limpa todas as entradas de um mapa BPF usando comandos diretos""" - print(f"Limpando mapa ID {map_id}...") - - # Verificar se o mapa existe - if run_cmd(f"bpftool map show id {map_id}") is None: - print(f" Mapa ID {map_id} não encontrado") - return False - - # Obter conteúdo do mapa - map_content = run_cmd(f"bpftool map dump id {map_id}") - if not map_content or "key:" not in map_content: - print(" Mapa já está vazio") - return True - - # Usar abordagem direta para limpar - run_cmd(f"echo '{map_content}' | grep -o 'key: [0-9a-fx ]*' | sed 's/key: //g' | while read -r key; do [ ! -z \"$key\" ] && bpftool map delete id {map_id} key hex $key; done") - - # Verificar se o mapa está vazio - map_content = run_cmd(f"bpftool map dump id {map_id}") - if not map_content or "key:" not in map_content: - print(" Mapa limpo com sucesso") - return True - else: - print(" Não foi possível limpar completamente o mapa") - return False - -def main(): - parser = argparse.ArgumentParser(description="Configuração de NAT BPF para Docker") - parser.add_argument("interface", help="Interface de rede externa (ex: enp0s6)") - parser.add_argument("--target", default="10.15.0.45", help="IP do container alvo (ex: 10.15.0.45)") - parser.add_argument("--networks", default="10.15.0.0/16,172.17.0.0/16", - help="Redes para SNAT, separadas por vírgulas (ex: 10.15.0.0/16,172.17.0.0/16)") - parser.add_argument("--http-port", type=int, default=80, help="Porta HTTP para redirecionar") - parser.add_argument("--https-port", type=int, default=443, help="Porta HTTPS para redirecionar") - parser.add_argument("--reload", action="store_true", help="Recarregar o programa TC") - parser.add_argument("--clean", action="store_true", help="Limpar mapas antigos antes de configurar") - - args = parser.parse_args() - - print("=== Configuração de NAT BPF para Docker ===") - print(f"Interface: {args.interface}") - print(f"Container alvo: {args.target}") - print(f"Redes para SNAT: {args.networks}") - print(f"Portas: HTTP={args.http_port}, HTTPS={args.https_port}") - print("") - - # Limpar mapas antigos se solicitado - if args.clean: - print("Limpando mapas antigos...") - count = clean_old_maps() - print(f"Total de {count} mapas antigos foram limpos.") - - # Carregar programa TC se necessário - if args.reload: - if not load_tc_program(args.interface): - print("Falha ao carregar programas TC. Abortando.") - return 1 - - # Encontrar IDs dos mapas - dnat_map_id = find_map_id("dnat_port_map") - snat_map_id = find_map_id("snat_networks") - - if not dnat_map_id or not snat_map_id: - print("Mapas BPF não encontrados. Carregando programas TC...") - if not load_tc_program(args.interface): - print("Falha ao carregar programas TC. Abortando.") - return 1 - - # Tentar novamente após carregar os programas - dnat_map_id = find_map_id("dnat_port_map") - snat_map_id = find_map_id("snat_networks") - - if not dnat_map_id or not snat_map_id: - print("Ainda não foi possível encontrar os mapas. Verifique o carregamento dos programas TC.") - return 1 - - print(f"IDs dos mapas: DNAT={dnat_map_id}, SNAT={snat_map_id}") - - # Limpar mapas existentes - clear_map(dnat_map_id) - clear_map(snat_map_id) - - # Configurar regras DNAT usando método hexadecimal direto - setup_dnat_rule_hex(dnat_map_id, args.http_port, args.target, args.http_port) - setup_dnat_rule_hex(dnat_map_id, args.https_port, args.target, args.https_port) - - # Configurar redes SNAT - for network in args.networks.split(','): - setup_snat_network_hex(snat_map_id, network.strip()) - - # Habilitar IP forwarding - print("Habilitando IP forwarding...") - with open("/proc/sys/net/ipv4/ip_forward", "w") as f: - f.write("1") - - # Verificar configuração final - print("\nConfiguração final:") - print("Regras DNAT:") - os.system(f"bpftool map dump id {dnat_map_id}") - print("\nRedes SNAT:") - os.system(f"bpftool map dump id {snat_map_id}") - - print("\nNAT BPF configurado com sucesso!") - print("Para ver logs em tempo real: cat /sys/kernel/debug/tracing/trace_pipe") - - return 0 - -if __name__ == "__main__": - sys.exit(main()) diff --git a/simple_xdp_filter.c b/simple_xdp_filter.c deleted file mode 100644 index 2c565d4..0000000 --- a/simple_xdp_filter.c +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -// Definir arquitetura ARM64 explicitamente -#ifndef __TARGET_ARCH_arm64 -#define __TARGET_ARCH_arm64 -#endif - -// Mapa para redes internas (para SNAT) -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 16); - __type(key, __u32); // Prefixo de rede (/16) - __type(value, __u8); // Flag (1=ativo) -} snat_networks SEC(".maps"); - -// Mapa para regras DNAT -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 64); - __type(key, __u16); // Porta destino - __type(value, __u32); // IP interno -} dnat_rules SEC(".maps"); - -// Estatísticas -struct { - __uint(type, BPF_MAP_TYPE_ARRAY); - __uint(max_entries, 4); - __type(key, __u32); - __type(value, __u64); -} stats_map SEC(".maps"); - -// Incrementar contador de estatísticas -static __always_inline void increment_counter(__u32 index) { - __u64 *counter; - counter = bpf_map_lookup_elem(&stats_map, &index); - if (counter) - (*counter)++; -} - -// Programa XDP simplificado para inspeção de tráfego -SEC("xdp") -int simple_xdp_filter(struct xdp_md *ctx) { - void *data = (void *)(long)ctx->data; - void *data_end = (void *)(long)ctx->data_end; - struct ethhdr *eth = data; - - // Verificação básica de limites - if (data + sizeof(struct ethhdr) > data_end) - return XDP_PASS; - - // Verificar se é um pacote IPv4 - if (eth->h_proto != bpf_htons(ETH_P_IP)) - return XDP_PASS; - - struct iphdr *ip = (void *)(eth + 1); - if ((void *)(ip + 1) > data_end) - return XDP_PASS; - - // Incrementar contador de pacotes processados - increment_counter(0); - - // Verificar se é um pacote TCP ou UDP - if (ip->protocol == IPPROTO_TCP) { - struct tcphdr *tcp = (void *)(ip + 1); - if ((void *)(tcp + 1) > data_end) - return XDP_PASS; - - // Contar pacotes TCP - increment_counter(1); - - // Verificar se é tráfego HTTPS (porta 443) - __u16 dport = bpf_ntohs(tcp->dest); - if (dport == 443) { - increment_counter(3); // Contar pacotes HTTPS - } - - } else if (ip->protocol == IPPROTO_UDP) { - // Contar pacotes UDP - increment_counter(2); - } - - // Permitir que todos os pacotes passem - return XDP_PASS; -} - -char _license[] SEC("license") = "GPL"; diff --git a/snat_networks.txt b/snat_networks.txt deleted file mode 100644 index ca3c2f7..0000000 --- a/snat_networks.txt +++ /dev/null @@ -1,2 +0,0 @@ -10.15.0.0/16 -172.17.0.0/16 \ No newline at end of file diff --git a/tc_nat_setup.sh b/tc_nat_setup.sh deleted file mode 100644 index fc3c918..0000000 --- a/tc_nat_setup.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash - -# Script para configurar NAT BPF com TC - -# Parâmetros -INTERFACE=${1:-"enp0s6"} -TARGET_IP=${2:-"10.15.0.45"} -GATEWAY_IP=${3:-"10.0.0.87"} - -echo "=== Configurando NAT BPF com TC ===" -echo "Interface: $INTERFACE" -echo "IP alvo (container): $TARGET_IP" -echo "Gateway IP: $GATEWAY_IP" - -# Compilar programa TC BPF -echo "Compilando programa TC BPF..." -clang -O2 -Wall -target bpf -g -D__TARGET_ARCH_arm64 -c tc_nat.c -o tc_nat.o -I/usr/include/bpf -I/usr/include/ - -if [ $? -ne 0 ]; then - echo "Erro na compilação do programa TC!" - exit 1 -fi - -# Verificar seções no arquivo objeto BPF -echo "Verificando seções no arquivo objeto..." -llvm-objdump -h tc_nat.o - -# Remover programas TC existentes na interface -echo "Removendo programas TC existentes na interface $INTERFACE..." -tc filter del dev $INTERFACE ingress 2>/dev/null -tc filter del dev $INTERFACE egress 2>/dev/null -tc qdisc del dev $INTERFACE clsact 2>/dev/null - -# Configurar qdisc -echo "Configurando qdisc no $INTERFACE..." -tc qdisc add dev $INTERFACE clsact - -# Carregar programas TC -echo "Carregando programa TC ingress..." -tc filter add dev $INTERFACE ingress prio 1 handle 1 bpf da obj tc_nat.o sec tc_ingress -if [ $? -ne 0 ]; then - echo "Erro ao carregar o programa ingress!" - exit 1 -fi - -echo "Carregando programa TC egress..." -tc filter add dev $INTERFACE egress prio 1 handle 1 bpf da obj tc_nat.o sec tc_egress -if [ $? -ne 0 ]; then - echo "Erro ao carregar o programa egress!" - exit 1 -fi - -# Encontrar IDs dos mapas -echo "Encontrando IDs dos mapas BPF..." -DNAT_MAP_ID=$(bpftool map list | grep -m1 "name dnat_port_map" | awk '{print $1}' | tr -d ':') -SNAT_MAP_ID=$(bpftool map list | grep -m1 "name snat_networks" | awk '{print $1}' | tr -d ':') - -if [ -z "$DNAT_MAP_ID" ] || [ -z "$SNAT_MAP_ID" ]; then - echo "Não foi possível encontrar todos os mapas BPF necessários." - exit 1 -fi - -echo "IDs dos mapas: DNAT=$DNAT_MAP_ID, SNAT=$SNAT_MAP_ID" - -# Limpar mapas existentes -echo "Limpando mapas existentes..." -echo "Limpando DNAT..." -bpftool map dump id $DNAT_MAP_ID -j | grep -o '"key": [0-9]*' | awk '{print $2}' | \ -while read key; do - [ ! -z "$key" ] && bpftool map delete id $DNAT_MAP_ID key hex $(printf "%04x" $key | sed 's/\(..\)\(..\)/\2 \1/') -done - -echo "Limpando SNAT..." -bpftool map dump id $SNAT_MAP_ID -j | grep -o '"key": [0-9]*' | awk '{print $2}' | \ -while read key; do - [ ! -z "$key" ] && bpftool map delete id $SNAT_MAP_ID key hex $(printf "%08x" $key | sed 's/\(..\)\(..\)\(..\)\(..\)/\4 \3 \2 \1/') -done - -# Configurar regras DNAT -echo "Configurando regras DNAT..." - -# Função auxiliar para converter IP para valores em little-endian -ip_to_hex_le() { - local ip=$1 - IFS='.' read -r -a octets <<< "$ip" - printf "%02x %02x %02x %02x" "${octets[3]}" "${octets[2]}" "${octets[1]}" "${octets[0]}" -} - -# Função auxiliar para converter porta para little-endian -port_to_hex_le() { - local port=$1 - printf "%02x %02x" $((port & 0xFF)) $(((port >> 8) & 0xFF)) -} - -# Obter valores em hex -TARGET_IP_HEX=$(ip_to_hex_le $TARGET_IP) - -# Configurar redirecionamento HTTP -echo "Configurando redirecionamento HTTP (porta 80)..." -bpftool map update id $DNAT_MAP_ID key hex "$(port_to_hex_le 80)" value hex "$TARGET_IP_HEX $(port_to_hex_le 80) 00 00" - -# Configurar redirecionamento HTTPS -echo "Configurando redirecionamento HTTPS (porta 443)..." -bpftool map update id $DNAT_MAP_ID key hex "$(port_to_hex_le 443)" value hex "$TARGET_IP_HEX $(port_to_hex_le 443) 00 00" - -# Configurar redes para SNAT -echo "Configurando redes para SNAT..." -bpftool map update id $SNAT_MAP_ID key hex "00 00 0f 0a" value hex "01" # 10.15.0.0/16 -bpftool map update id $SNAT_MAP_ID key hex "00 00 11 ac" value hex "01" # 172.17.0.0/16 - -# Habilitar IP forwarding -echo "Habilitando ip_forward..." -echo 1 > /proc/sys/net/ipv4/ip_forward - -echo "NAT BPF configurado com sucesso. Utilize 'tc filter show dev $INTERFACE ingress' para verificar." -echo "Para ver logs: cat /sys/kernel/debug/tracing/trace_pipe" diff --git a/verify_nat.sh b/verify_nat.sh deleted file mode 100644 index 88cc1d1..0000000 --- a/verify_nat.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash - -# Script para verificar se o NAT está funcionando corretamente - -INTERFACE=${1:-"enp0s6"} -TARGET_IP=${2:-"10.15.0.45"} - -echo "=== Verificando Configuração NAT ===" -echo "Interface: $INTERFACE" -echo "Container alvo: $TARGET_IP" - -# Verificar se os programas TC estão carregados -echo -e "\n[Programas TC]" -tc filter show dev $INTERFACE ingress | grep -i bpf -tc filter show dev $INTERFACE egress | grep -i bpf - -# Verificar mapas BPF -echo -e "\n[Mapas BPF]" -DNAT_MAP_ID=$(bpftool map list | grep -m1 "name dnat_port_map" | awk '{print $1}' | tr -d ':') -SNAT_MAP_ID=$(bpftool map list | grep -m1 "name snat_networks" | awk '{print $1}' | tr -d ':') - -if [ -n "$DNAT_MAP_ID" ]; then - echo "Mapa DNAT (ID: $DNAT_MAP_ID):" - bpftool map dump id $DNAT_MAP_ID -else - echo "Mapa DNAT não encontrado" -fi - -if [ -n "$SNAT_MAP_ID" ]; then - echo -e "\nMapa SNAT (ID: $SNAT_MAP_ID):" - bpftool map dump id $SNAT_MAP_ID -else - echo -e "\nMapa SNAT não encontrado" -fi - -# Verificar IP forwarding -echo -e "\n[IP Forwarding]" -IP_FWD=$(cat /proc/sys/net/ipv4/ip_forward) -echo "IP forwarding: $IP_FWD" -if [ "$IP_FWD" != "1" ]; then - echo "AVISO: IP forwarding está desativado. Ative com:" - echo " echo 1 > /proc/sys/net/ipv4/ip_forward" -fi - -# Verificar conectividade com o alvo -echo -e "\n[Conectividade]" -ping -c 1 -W 1 $TARGET_IP > /dev/null -if [ $? -eq 0 ]; then - echo "Ping para $TARGET_IP: OK" -else - echo "Ping para $TARGET_IP: FALHOU" -fi - -# Testar HTTP/HTTPS -echo -e "\n[Teste HTTP]" -curl -s -m 2 --head http://$TARGET_IP > /dev/null -if [ $? -eq 0 ]; then - echo "Acesso HTTP direto para $TARGET_IP: OK" -else - echo "Acesso HTTP direto para $TARGET_IP: FALHOU" -fi - -# Obter IP externo -EXTERNAL_IP=$(ip -br addr show $INTERFACE | awk '{print $3}' | cut -d'/' -f1) -echo -e "\n[Teste NAT]" -echo "IP externo: $EXTERNAL_IP" -curl -s -m 2 --head http://$EXTERNAL_IP > /dev/null -if [ $? -eq 0 ]; then - echo "Acesso HTTP via NAT ($EXTERNAL_IP): OK" -else - echo "Acesso HTTP via NAT ($EXTERNAL_IP): FALHOU" -fi - -# Verificar logs recentes -echo -e "\n[Logs Recentes]" -echo "Últimas 5 linhas do trace_pipe (pode estar vazio se não houver atividade recente):" -cat /sys/kernel/debug/tracing/trace_pipe | head -n 5 - -echo -e "\nPara monitorar logs em tempo real: cat /sys/kernel/debug/tracing/trace_pipe" -echo "Para visualizar conexões atuais: ss -tan" diff --git a/xdp_accelerated_nat.c b/xdp_accelerated_nat.c deleted file mode 100644 index 6526f21..0000000 --- a/xdp_accelerated_nat.c +++ /dev/null @@ -1,216 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -// Definir arquitetura ARM64 explicitamente -#ifndef __TARGET_ARCH_arm64 -#define __TARGET_ARCH_arm64 -#endif - -// Definições para contornar erros de compilação -#ifndef __section -#define __section(NAME) \ - __attribute__((section(NAME), used)) -#endif - -// Substituir o SEC original pela versão compatível com ARM64 -#undef SEC -#define SEC(NAME) __section(NAME) - -/* Estrutura para conexões NAT */ -struct nat_conn { - __u32 orig_src_ip; // IP de origem original - __u16 orig_src_port; // Porta de origem original - __u32 orig_dst_ip; // IP de destino original - __u16 orig_dst_port; // Porta de destino original - __u32 new_src_ip; // Novo IP de origem (para SNAT) - __u16 new_src_port; // Nova porta de origem (para SNAT) - __u32 new_dst_ip; // Novo IP de destino (para DNAT) - __u16 new_dst_port; // Nova porta de destino (para DNAT) - __u8 proto; // Protocolo (TCP/UDP) - __u8 state; // Estado da conexão (1=ativo) - __u64 last_update; // Timestamp da última atualização -}; - -/* Chave para consulta rápida de DNAT em pacotes de entrada */ -struct dnat_key { - __u32 dst_ip; - __u16 dst_port; - __u8 proto; - __u8 padding; // Adicionado para alinhamento em ARM64 -} __attribute__((packed)); - -/* Chave para busca de conexões de saída */ -struct conn_key { - __u32 src_ip; - __u16 src_port; - __u32 dst_ip; - __u16 dst_port; - __u8 proto; - __u8 padding; // Adicionado para alinhamento em ARM64 -} __attribute__((packed)); - -/* Mapas BPF para NAT */ -struct { - __uint(type, BPF_MAP_TYPE_HASH); // Alterado de LRU_HASH para HASH para maior compatibilidade - __uint(max_entries, 65536); - __type(key, struct conn_key); - __type(value, struct nat_conn); -} conn_track_map SEC(".maps"); - -/* Mapa para regras DNAT estáticas */ -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 1024); - __type(key, struct dnat_key); - __type(value, struct nat_conn); -} dnat_rules SEC(".maps"); - -/* Mapa para redes que devem ter SNAT aplicado */ -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 256); - __type(key, __u32); // Prefixo de rede (/16) - __type(value, __u8); // Flag (1=ativo) -} snat_networks SEC(".maps"); - -/* Mapa para estatísticas */ -struct nat_stats { - __u64 processed; // Pacotes processados - __u64 snat_hits; // Acertos de SNAT - __u64 dnat_hits; // Acertos de DNAT - __u64 new_conns; // Novas conexões - __u64 errors; // Erros -}; - -struct { - __uint(type, BPF_MAP_TYPE_ARRAY); - __uint(max_entries, 1); - __type(key, __u32); - __type(value, struct nat_stats); -} stats_map SEC(".maps"); - -/* Configuração para IP do gateway (usado para SNAT) */ -struct { - __uint(type, BPF_MAP_TYPE_ARRAY); - __uint(max_entries, 1); - __type(key, __u32); - __type(value, __u32); -} gateway_ip_map SEC(".maps"); - -/* Função para calcular IPv4 checksum - simplificada para evitar loops */ -static __always_inline __u16 ip_checksum(struct iphdr *ip) { - __u32 sum = 0; - - // Para ARM64, calcular o checksum sem depender de loops - __u16 *iph = (__u16 *)ip; - - // Zeramos o campo checksum do IP header - ip->check = 0; - - // Calculamos a soma de palavras de 16 bits do header IP - // O tamanho padrão do header IP é 20 bytes (5 palavras de 32 bits ou 10 de 16 bits) - sum += iph[0]; - sum += iph[1]; - sum += iph[2]; - sum += iph[3]; - sum += iph[4]; - sum += iph[5]; - sum += iph[6]; - sum += iph[7]; - sum += iph[8]; - sum += iph[9]; - - // Dobrar 32-bit para 16-bit para obter o checksum final - sum = (sum & 0xFFFF) + (sum >> 16); - sum = (sum & 0xFFFF) + (sum >> 16); - - return ~sum; // Complemento de 1 -} - -/* Função simplificada para atualizar checksum */ -static __always_inline __u16 update_checksum(__u16 old_checksum, __u32 old_value, __u32 new_value) { - __u32 sum = ~((__u32)old_checksum) & 0xFFFF; - sum += ~(old_value >> 16) & 0xFFFF; - sum += ~old_value & 0xFFFF; - sum += (new_value >> 16) & 0xFFFF; - sum += new_value & 0xFFFF; - sum = (sum & 0xFFFF) + (sum >> 16); - sum = (sum & 0xFFFF) + (sum >> 16); - return ~sum & 0xFFFF; -} - -/* Função para atualizar estatísticas */ -static __always_inline void update_stats(__u32 stat_type) { - __u32 key = 0; - struct nat_stats *stats; - - stats = bpf_map_lookup_elem(&stats_map, &key); - if (stats) { - if (stat_type == 0) stats->processed++; - else if (stat_type == 1) stats->snat_hits++; - else if (stat_type == 2) stats->dnat_hits++; - else if (stat_type == 3) stats->new_conns++; - else if (stat_type == 4) stats->errors++; - } -} - -/* Função para verificar e aplicar SNAT para redes internas */ -static __always_inline int should_apply_snat(__u32 src_ip) { - // Extrair o prefixo /16 - __u32 prefix = src_ip & 0xFFFF0000; - __u8 *flag; - - flag = bpf_map_lookup_elem(&snat_networks, &prefix); - return (flag && *flag == 1) ? 1 : 0; -} - -/* Programa principal XDP para aceleração de NAT */ -SEC("xdp") -int xdp_accelerate_nat(struct xdp_md *ctx) { - void *data = (void *)(long)ctx->data; - void *data_end = (void *)(long)ctx->data_end; - - // Verificação básica de limites - if (data + sizeof(struct ethhdr) > data_end) - return XDP_PASS; - - struct ethhdr *eth = data; - - // Só processa IPv4 - if (eth->h_proto != bpf_htons(ETH_P_IP)) - return XDP_PASS; - - struct iphdr *ip = (void *)(eth + 1); - if ((void *)(ip + 1) > data_end) - return XDP_PASS; - - // Ignorar pacotes não-TCP/UDP por enquanto - if (ip->protocol != IPPROTO_TCP && ip->protocol != IPPROTO_UDP) - return XDP_PASS; - - // Incrementar contador de pacotes processados - update_stats(0); - - // Restante do código simplificado para compatibilidade ARM64 - // No caso de XDP modo genérico, vamos deixar a maioria do processamento - // NAT para o TC/iptables padrão para garantir compatibilidade - - // Aplicar SNAT básico para rede interna - if (should_apply_snat(ip->saddr)) { - update_stats(1); // SNAT hit - // Permitir que o TC/iptables faça o trabalho real de SNAT - return XDP_PASS; - } - - // Apenas passar pacotes normalmente - return XDP_PASS; -} - -/* Simplificação extrema para compatibilidade */ -char _license[] SEC("license") = "GPL"; diff --git a/xdp_nat_tools.py b/xdp_nat_tools.py deleted file mode 100644 index 9a67f9a..0000000 --- a/xdp_nat_tools.py +++ /dev/null @@ -1,306 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys -import time -import socket -import struct -import argparse -import subprocess -import ipaddress -from bcc import BPF - -# Constantes -DEFAULT_INTERFACE = "enp0s6" -DEFAULT_GATEWAY_IP = "10.0.0.87" -DNAT_RULES_FILE = "dnat_rules.txt" -SNAT_NETWORKS_FILE = "snat_networks.txt" - -def run_cmd(cmd, show_output=False): - """Executa um comando shell e retorna sua saída""" - if show_output: - print(f"Executando: {cmd}") - - result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - if result.returncode != 0: - print(f"Erro ao executar: {cmd}") - print(f"Saída de erro: {result.stderr}") - return None - return result.stdout.strip() - -def ip_to_int(ip_str): - """Converte um endereço IP em inteiro.""" - return struct.unpack("!I", socket.inet_aton(ip_str))[0] - -def int_to_ip(ip_int): - """Converte um inteiro em endereço IP.""" - return socket.inet_ntoa(struct.pack("!I", ip_int)) - -def compile_xdp_program(): - """Compila o programa XDP.""" - print("Compilando o programa XDP...") - # Corrigido para incluir diretórios de inclusão e target para ARM64 - cmd = "clang -O2 -Wall -target bpf -g -D__TARGET_ARCH_arm64 -c xdp_accelerated_nat.c -o xdp_accelerated_nat.o -I/usr/include/bpf -I/usr/include/" - return run_cmd(cmd, True) is not None - -def attach_xdp_program(interface, program_path): - """Anexa o programa XDP à interface.""" - print(f"Anexando programa XDP à interface {interface}...") - # Adicionando a flag --force para substituir qualquer programa existente - # Usando 'generic' como modo XDP para maior compatibilidade - cmd = f"ip link set dev {interface} xdp obj {program_path} sec xdp" - return run_cmd(cmd, True) is not None - -def detach_xdp_program(interface): - """Remove o programa XDP da interface.""" - print(f"Removendo programa XDP da interface {interface}...") - cmd = f"ip link set dev {interface} xdp off" - return run_cmd(cmd, True) is not None - -def load_dnat_rules(file_path): - """Carrega regras DNAT de um arquivo.""" - rules = [] - - try: - with open(file_path, 'r') as f: - for line in f: - line = line.strip() - if not line or line.startswith('#'): - continue - - # Formato: proto dst_ip:dst_port new_ip:new_port - # Exemplo: tcp 10.0.0.87:80 10.15.0.45:80 - parts = line.split() - if len(parts) != 3: - print(f"Formato inválido na linha: {line}") - continue - - proto_name, dst, new = parts - - # Parse proto - if proto_name.lower() == "tcp": - proto = socket.IPPROTO_TCP - elif proto_name.lower() == "udp": - proto = socket.IPPROTO_UDP - else: - print(f"Protocolo inválido: {proto_name}") - continue - - # Parse IP e portas - try: - dst_ip, dst_port = dst.split(':') - new_ip, new_port = new.split(':') - - rule = { - 'proto': proto, - 'dst_ip': dst_ip, - 'dst_port': int(dst_port), - 'new_ip': new_ip, - 'new_port': int(new_port) - } - rules.append(rule) - except ValueError: - print(f"Erro ao analisar IP/porta na linha: {line}") - continue - - print(f"Carregadas {len(rules)} regras DNAT") - return rules - except FileNotFoundError: - print(f"Arquivo de regras DNAT não encontrado: {file_path}") - return [] - -def load_snat_networks(file_path): - """Carrega redes SNAT de um arquivo.""" - networks = [] - - try: - with open(file_path, 'r') as f: - for line in f: - line = line.strip() - if not line or line.startswith('#'): - continue - - # Formato: rede/prefixo - # Exemplo: 10.15.0.0/16 - try: - network = ipaddress.ip_network(line) - networks.append(str(network)) - except ValueError: - print(f"Rede inválida na linha: {line}") - continue - - print(f"Carregadas {len(networks)} redes SNAT") - return networks - except FileNotFoundError: - print(f"Arquivo de redes SNAT não encontrado: {file_path}") - return [] - -def update_gateway_ip(bpf_obj, gateway_ip): - """Atualiza o IP do gateway no mapa BPF.""" - try: - ip_int = ip_to_int(gateway_ip) - key = ctypes.c_uint32(0) - value = ctypes.c_uint32(ip_int) - bpf_obj["gateway_ip_map"][key] = value - print(f"IP do gateway configurado: {gateway_ip}") - except Exception as e: - print(f"Erro ao configurar IP do gateway: {e}") - -def update_dnat_rules(bpf_obj, rules): - """Atualiza regras DNAT no mapa BPF.""" - try: - dnat_map = bpf_obj["dnat_rules"] - - # Limpar mapa existente - for k in list(dnat_map.keys()): - del dnat_map[k] - - # Inserir novas regras - for rule in rules: - key = dnat_map.Key() - key.dst_ip = ip_to_int(rule['dst_ip']) - key.dst_port = rule['dst_port'] - key.proto = rule['proto'] - - value = dnat_map.Value() - value.new_dst_ip = ip_to_int(rule['new_ip']) - value.new_dst_port = rule['new_port'] - - dnat_map[key] = value - - print(f"{len(rules)} regras DNAT atualizadas") - except Exception as e: - print(f"Erro ao atualizar regras DNAT: {e}") - -def update_snat_networks(bpf_obj, networks): - """Atualiza redes SNAT no mapa BPF.""" - try: - snat_map = bpf_obj["snat_networks"] - - # Limpar mapa existente - for k in list(snat_map.keys()): - del snat_map[k] - - # Inserir novas redes - for net_str in networks: - network = ipaddress.ip_network(net_str) - if network.prefixlen != 16: - print(f"Aviso: Rede {net_str} não é /16. Pode não funcionar corretamente.") - - # Extrair prefixo /16 - prefix = int(network.network_address) & 0xFFFF0000 - key = ctypes.c_uint32(prefix) - value = ctypes.c_uint8(1) - - snat_map[key] = value - - print(f"{len(networks)} redes SNAT atualizadas") - except Exception as e: - print(f"Erro ao atualizar redes SNAT: {e}") - -def print_stats(bpf_obj): - """Imprime estatísticas de NAT.""" - stats = bpf_obj["stats_map"][0] - print("\nEstatísticas de NAT:") - print(f" Pacotes processados: {stats.processed}") - print(f" Acertos de SNAT: {stats.snat_hits}") - print(f" Acertos de DNAT: {stats.dnat_hits}") - print(f" Novas conexões: {stats.new_conns}") - print(f" Erros: {stats.errors}") - -def print_connections(bpf_obj): - """Imprime conexões ativas.""" - conn_map = bpf_obj["conn_track_map"] - print("\nConexões ativas:") - - count = 0 - for k, v in conn_map.items(): - proto_name = "TCP" if k.proto == socket.IPPROTO_TCP else "UDP" - src_ip = int_to_ip(k.src_ip) - dst_ip = int_to_ip(k.dst_ip) - - # Mostrar apenas as 20 primeiras conexões - if count < 20: - print(f" {proto_name}: {src_ip}:{k.src_port} -> {dst_ip}:{k.dst_port}") - count += 1 - - if count > 20: - print(f" ... e mais {count - 20} conexões") - print(f" Total: {count} conexões ativas") - -def main(): - parser = argparse.ArgumentParser(description='Ferramenta para configuração de aceleração NAT com XDP') - parser.add_argument('-i', '--interface', default=DEFAULT_INTERFACE, help=f'Interface de rede (padrão: {DEFAULT_INTERFACE})') - parser.add_argument('-g', '--gateway', default=DEFAULT_GATEWAY_IP, help=f'IP do gateway para SNAT (padrão: {DEFAULT_GATEWAY_IP})') - parser.add_argument('--compile', action='store_true', help='Compilar e carregar o programa XDP') - parser.add_argument('--unload', action='store_true', help='Descarregar o programa XDP') - parser.add_argument('--stats', action='store_true', help='Mostrar estatísticas') - parser.add_argument('--monitor', action='store_true', help='Monitorar estatísticas continuamente') - - args = parser.parse_args() - - # Descarregar programa - if args.unload: - detach_xdp_program(args.interface) - sys.exit(0) - - # Compilar e carregar programa - if args.compile: - if not compile_xdp_program(): - print("Falha ao compilar o programa XDP. Verifique os erros acima.") - sys.exit(1) - - # Desanexar qualquer programa existente - detach_xdp_program(args.interface) - - # Anexar o novo programa - print("Tentando anexar o programa XDP à interface...") - if not attach_xdp_program(args.interface, "xdp_accelerated_nat.o"): - print("Falha ao anexar o programa XDP à interface") - # Tentar modo alternativo - print("Tentando modo alternativo (generic)...") - if not run_cmd(f"ip link set dev {args.interface} xdp obj xdp_accelerated_nat.o section xdp mode generic", True): - print("Todas as tentativas de carregamento falharam. Verifique se o kernel suporta XDP.") - sys.exit(1) - - # Carregar o objeto BPF para interagir com o programa carregado - try: - b = BPF(obj="xdp_accelerated_nat.o") - except Exception as e: - print(f"Erro ao carregar o objeto BPF: {e}") - sys.exit(1) - - # Carregar regras DNAT e redes SNAT - dnat_rules = load_dnat_rules(DNAT_RULES_FILE) - snat_networks = load_snat_networks(SNAT_NETWORKS_FILE) - - # Atualizar configurações - update_gateway_ip(b, args.gateway) - if dnat_rules: - update_dnat_rules(b, dnat_rules) - if snat_networks: - update_snat_networks(b, snat_networks) - - # Mostrar estatísticas - if args.stats: - print_stats(b) - print_connections(b) - - # Monitorar estatísticas - if args.monitor: - try: - print("Monitorando estatísticas (Ctrl+C para sair)...") - while True: - os.system('clear') - print_stats(b) - print_connections(b) - time.sleep(1) - except KeyboardInterrupt: - print("\nMonitoramento interrompido.") - - print("\nPrograma XDP NAT acelerado em execução.") - print("Use '--stats' para visualizar estatísticas.") - print("Use '--unload' para descarregar o programa.") - -if __name__ == "__main__": - main()