commit
This commit is contained in:
1 parent
d9412a360d
commit
6081ec0c1c
9 files changed
-1140
No files matched your search
@@ -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
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Formato: <protocolo> <ip_destino:porta_destino> <novo_ip:nova_porta>
|
|
||||||
tcp 10.0.0.87:80 10.15.0.45:80
|
|
||||||
tcp 10.0.0.87:443 10.15.0.45:443
|
|
||||||
-256
@@ -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())
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#include <linux/bpf.h>
|
|
||||||
#include <linux/if_ether.h>
|
|
||||||
#include <linux/ip.h>
|
|
||||||
#include <linux/tcp.h>
|
|
||||||
#include <linux/udp.h>
|
|
||||||
#include <linux/in.h>
|
|
||||||
#include <bpf/bpf_helpers.h>
|
|
||||||
#include <bpf/bpf_endian.h>
|
|
||||||
|
|
||||||
// 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";
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
10.15.0.0/16
|
|
||||||
172.17.0.0/16
|
|
||||||
-116
@@ -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"
|
|
||||||
@@ -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"
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
#include <linux/bpf.h>
|
|
||||||
#include <linux/if_ether.h>
|
|
||||||
#include <linux/ip.h>
|
|
||||||
#include <linux/tcp.h>
|
|
||||||
#include <linux/udp.h>
|
|
||||||
#include <linux/in.h>
|
|
||||||
#include <bpf/bpf_helpers.h>
|
|
||||||
#include <bpf/bpf_endian.h>
|
|
||||||
|
|
||||||
// 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";
|
|
||||||
@@ -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()
|
|
||||||
Reference in new issue
Block a user