This commit is contained in:
rodrigo committed 2025-02-26 19:32:54 -03:00
1 parent dd38ec9f0e
commit 6d94766293
3 files changed
+517 -345

No files matched your search

+276
View File
@@ -0,0 +1,276 @@
#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>
// Otimização: melhorar constantes para ajuste fino
#define MAX_HIGH_PRIORITY_PACKETS_PER_SECOND 2000
#define MAX_LOW_PRIORITY_PACKETS_PER_SECOND 500
#define MAX_PACKETS_PER_IP 60000
#define MAX_ICMP_PACKETS_PER_IP 50
#define MAX_SYN_PACKETS_PER_IP 100
#define TIMEOUT_SEC 1
#define PACKET_COST 1 // Custo padrão por pacote
#define HIGH_PRIO_TOKEN_RATE 2000 // Tokens/segundo para alta prioridade
#define LOW_PRIO_TOKEN_RATE 500 // Tokens/segundo para baixa prioridade
#define MAX_TOKENS 2000 // Máximo de tokens acumulados
// Otimização: estrutura de dados mais compacta para reduzir acessos de memória
struct ip_stats {
__u32 pkt_count; // Contador de pacotes total
__u32 last_seen; // Último timestamp em segundos (não nanossegundos)
__u16 icmp_count; // Contador ICMP (reduzido para 16 bits)
__u16 syn_count; // Contador SYN (reduzido para 16 bits)
__u16 high_prio_tokens; // Tokens disponíveis para alta prioridade
__u16 low_prio_tokens; // Tokens disponíveis para baixa prioridade
} __attribute__((packed));
// Otimização: usar PERCPU map para evitar contenção e locks
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
__uint(max_entries, 100000);
__type(key, __u32); // IP address
__type(value, struct ip_stats);
} ddos_map SEC(".maps");
// Otimização: usar LRU para blacklist para evitar crescimento infinito
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 100000);
__type(key, __u32);
__type(value, __u32);
} blacklist_map SEC(".maps");
// Mapa para interface de redirecionamento
struct {
__uint(type, BPF_MAP_TYPE_XSKMAP);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
} xsks_map SEC(".maps");
// Otimização: usar lista de permissões fixa e compacta
static const __u32 whitelist[] = {
0x0A000001, // 10.0.0.1
0x0A630101, // 10.99.1.1
0x0a630102, // 10.99.1.2
0x0a630104 // 10.99.1.4
};
// Otimização: função inline para obter o timestamp em segundos em vez de nanossegundos
static __always_inline __u32 get_time_sec(void) {
return bpf_ktime_get_ns() / 1000000000;
}
// Otimização: usar always_inline e __builtin_expect para funções críticas
static __always_inline int is_whitelisted(__u32 ip) {
#pragma unroll
for (int i = 0; i < sizeof(whitelist) / sizeof(whitelist[0]); i++) {
if (ip == whitelist[i]) {
return 1;
}
}
return 0;
}
static __always_inline int is_blacklisted(__u32 ip) {
return bpf_map_lookup_elem(&blacklist_map, &ip) != NULL;
}
// Otimização: usar switch-case em vez de verificações múltiplas para portas
static __always_inline int get_port_priority(__u16 port) {
switch (port) {
case 80: // HTTP
case 443: // HTTPS
case 32100: // Serviço de alta prioridade 1
case 32400: // Serviço de alta prioridade 2
return 2; // Alta prioridade
case 22: // SSH
case 25: // SMTP
case 53: // DNS
return 1; // Média prioridade
default:
return 0; // Baixa prioridade
}
}
// Otimização: função de verificação de porta mais eficiente
static __always_inline int is_filtered_port(__u16 port) {
// Portas importantes usam verificação direta
switch (port) {
case 8117:
case 8085:
case 8086:
case 32100:
case 32400:
return 1;
default:
// Verificação de intervalo apenas para outras portas
return (port <= 1024);
}
}
// Programa principal XDP
SEC("xdp")
int xdp_ddos_filter(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
// Otimização: early return para pacotes muito pequenos
if (__builtin_expect(data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end, 0))
return XDP_PASS;
struct ethhdr *eth = data;
// Otimização: early return para pacotes não-IP
if (__builtin_expect(eth->h_proto != __constant_htons(ETH_P_IP), 0))
return XDP_PASS;
struct iphdr *ip = (void *)(eth + 1);
// Otimização: verificar o cabeçalho IP de forma mais eficiente
if (__builtin_expect((void *)(ip + 1) > data_end ||
ip->ihl != 5 ||
ip->version != 4, 0))
return XDP_PASS;
// Otimização: early return para IPs na whitelist
__u32 src_ip = ip->saddr;
if (__builtin_expect(is_whitelisted(src_ip), 0)) {
return XDP_PASS;
}
// Otimização: early drop para IPs bloqueados
if (__builtin_expect(is_blacklisted(src_ip), 0)) {
return XDP_DROP;
}
// Identificar protocolo e porta para classificação
__u16 dest_port = 0;
int is_syn = 0;
int is_icmp = 0;
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = (void *)(ip + 1);
if (__builtin_expect((void *)(tcp + 1) > data_end, 0))
return XDP_PASS;
dest_port = __constant_ntohs(tcp->dest);
is_syn = tcp->syn && !tcp->ack;
} else if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp = (void *)(ip + 1);
if (__builtin_expect((void *)(udp + 1) > data_end, 0))
return XDP_PASS;
dest_port = __constant_ntohs(udp->dest);
} else if (ip->protocol == IPPROTO_ICMP) {
is_icmp = 1;
} else {
// Outros protocolos passam sem filtro adicional
return XDP_PASS;
}
// Otimização: early return se não for para portas filtradas
if (__builtin_expect(!is_filtered_port(dest_port) && !is_icmp, 1))
return XDP_PASS;
// Prioridade do pacote com base na porta
int priority = get_port_priority(dest_port);
// Obter estatísticas atuais do IP
struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip);
__u32 now = get_time_sec();
if (entry) {
// Otimização: cálculo de tempo decorrido mais eficiente
__u32 elapsed = 0;
if (__builtin_expect(now > entry->last_seen, 1)) {
elapsed = now - entry->last_seen;
}
// Implementação de token bucket para cada prioridade
if (elapsed > 0) {
// Adicionar tokens baseado no tempo decorrido, limitado ao máximo
__u16 new_high_tokens = entry->high_prio_tokens + (HIGH_PRIO_TOKEN_RATE * elapsed);
entry->high_prio_tokens = new_high_tokens > MAX_TOKENS ? MAX_TOKENS : new_high_tokens;
__u16 new_low_tokens = entry->low_prio_tokens + (LOW_PRIO_TOKEN_RATE * elapsed);
entry->low_prio_tokens = new_low_tokens > MAX_TOKENS ? MAX_TOKENS : new_low_tokens;
// Resetar contadores específicos de protocolo após 1 segundo
if (elapsed >= TIMEOUT_SEC) {
entry->icmp_count = 0;
entry->syn_count = 0;
}
}
// Atualizar o último timestamp visto
entry->last_seen = now;
// Atualizar contadores específicos
entry->pkt_count++;
if (is_icmp) {
entry->icmp_count++;
if (__builtin_expect(entry->icmp_count > MAX_ICMP_PACKETS_PER_IP, 0)) {
__u32 one = 1;
bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY);
return XDP_DROP;
}
}
if (is_syn) {
entry->syn_count++;
if (__builtin_expect(entry->syn_count > MAX_SYN_PACKETS_PER_IP, 0)) {
__u32 one = 1;
bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY);
return XDP_DROP;
}
}
// Otimização: verificação total de pacotes
if (__builtin_expect(entry->pkt_count > MAX_PACKETS_PER_IP, 0)) {
__u32 one = 1;
bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY);
return XDP_DROP;
}
// Verificar tokens disponíveis com base na prioridade
if (priority == 2) { // Alta prioridade
if (entry->high_prio_tokens >= PACKET_COST) {
entry->high_prio_tokens -= PACKET_COST;
return XDP_PASS;
}
} else { // Média e baixa prioridade
if (entry->low_prio_tokens >= PACKET_COST) {
entry->low_prio_tokens -= PACKET_COST;
return XDP_PASS;
}
}
// Sem tokens disponíveis para esta prioridade, descartar
return XDP_DROP;
} else {
// Primeira vez que vemos este IP, inicializar com valores generosos
struct ip_stats new_entry = {
.pkt_count = 1,
.last_seen = now,
.icmp_count = is_icmp ? 1 : 0,
.syn_count = is_syn ? 1 : 0,
.high_prio_tokens = MAX_TOKENS - PACKET_COST,
.low_prio_tokens = MAX_TOKENS - PACKET_COST
};
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
return XDP_PASS;
}
}
char __license[] SEC("license") = "GPL";
-280
View File
@@ -1,280 +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>
#define MAX_HIGH_PRIORITY_PACKETS_PER_SECOND 10000 // Limite de pacotes por segundo para alta prioridade
#define MAX_LOW_PRIORITY_PACKETS_PER_SECOND 2000 // Limite de pacotes por segundo para baixa prioridade
#define MAX_PACKETS_PER_IP 100000 // Limite de pacotes por IP antes de bloquear
#define MAX_ICMP_PACKETS_PER_IP 200 // Limite de pacotes ICMP por IP antes de bloquear
#define MAX_SYN_PACKETS_PER_IP 500 // Limite de pacotes SYN por IP antes de bloquear
#define TIMEOUT_SEC 5 // Tempo para expiração do contador (em segundos)
// Estrutura para armazenar contagem de pacotes e o último timestamp visto para um IP
struct ip_stats {
__u64 last_seen; // Campo mais acessado primeiro
__u32 pkt_count; // Reduzido para u32
__u16 high_priority_count;
__u16 low_priority_count;
__u16 icmp_count;
__u16 syn_count;
__u32 pad; // Padding para alinhamento de 8 bytes
} __attribute__((aligned(8)));
// Mapa de hash para armazenar estatísticas de IPs
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 100000); // Número máximo de entradas no mapa
__type(key, __u32); // Tipo da chave (endereço IP)
__type(value, struct ip_stats); // Tipo do valor (estatísticas do IP)
} ddos_map SEC(".maps");
// Mapa de hash para armazenar a blacklist
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 100000); // Número máximo de entradas no mapa
__type(key, __u32); // Tipo da chave (endereço IP)
__type(value, __u32); // Tipo do valor (dummy value)
} blacklist_map SEC(".maps");
// Lista de permissões estática (whitelist) de IPs confiáveis
const __u32 whitelist[] = {
0x0A000001, // 10.0.0.1
0x0A630101, // 192.168.0.1
0x0a630102,
0x0a630104
};
// Função auxiliar para obter o timestamp atual em nanossegundos
static __inline __u64 get_time_ns(void) {
return bpf_ktime_get_ns();
}
// Função para verificar se o IP está na lista de permissões
static __inline int is_whitelisted(__u32 ip) {
int i;
for (i = 0; i < sizeof(whitelist) / sizeof(whitelist[0]); i++) {
if (ip == whitelist[i]) {
return 1;
}
}
return 0;
}
// Função para verificar se o IP está na lista negra
static __inline int is_blacklisted(__u32 ip) {
__u32 *value = bpf_map_lookup_elem(&blacklist_map, &ip);
return value != NULL;
}
// Otimização: Função de verificação de porta simplificada usando switch
static __inline int is_high_priority_port(__u16 port) {
switch (port) {
case 32100:
case 80:
case 443:
case 3306:
case 5432:
case 8443:
return 1;
default:
return 0;
}
}
// Função para verificar se a porta é de baixa prioridade
static __inline int is_low_priority_port(__u16 port) {
return !is_high_priority_port(port);
}
// Otimização: Função de verificação de porta filtrada simplificada
static __inline int is_filtered_port(__u16 port) {
// Early return para portas privilegiadas
if (port <= 1024) return 1;
switch (port) {
case 32100:
case 8080:
case 8443:
case 10050:
case 10051:
case 8085:
case 8117:
return 1;
default:
return 0;
}
}
// Otimização: Função is_destined_to_server com early returns
static __inline int is_destined_to_server(struct iphdr *ip, void *data_end) {
// Early return para ICMP
if (ip->protocol == IPPROTO_ICMP) return 1;
__u16 dest_port;
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = (void *)(ip + 1);
if ((void *)(tcp + 1) > data_end)
return 0;
dest_port = __constant_ntohs(tcp->dest);
} else if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp = (void *)(ip + 1);
if ((void *)(udp + 1) > data_end)
return 0;
dest_port = __constant_ntohs(udp->dest);
} else {
return 0;
}
return is_filtered_port(dest_port);
}
// Programa principal XDP otimizado
SEC("xdp")
int xdp_ddos_filter(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
// Early returns para validação de cabeçalhos
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end)
return XDP_PASS;
if (eth->h_proto != __constant_htons(ETH_P_IP))
return XDP_PASS;
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end || ip->ihl != 5 || ip->version != 4)
return XDP_PASS;
__u32 src_ip = ip->saddr;
// Early return para IPs permitidos
if (is_whitelisted(src_ip))
return XDP_PASS;
// Early return para IPs bloqueados
if (is_blacklisted(src_ip))
return XDP_DROP;
// Verifica se o pacote é destinado ao servidor e deve ser filtrado
if (!is_destined_to_server(ip, data_end))
return XDP_PASS;
__u16 dest_port = 0;
struct tcphdr *tcp = NULL;
// Verifica se é um pacote TCP ou UDP
if (ip->protocol == IPPROTO_TCP) {
tcp = (void *)(ip + 1);
if ((void *)(tcp + 1) > data_end)
return XDP_PASS;
dest_port = __constant_ntohs(tcp->dest);
// Verifica se é um pacote SYN
if (tcp->syn && !tcp->ack) {
// Consulta o mapa para verificar se o IP já está registrado
struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip);
if (entry) {
__u64 now = get_time_ns();
// Reinicia o contador se o tempo decorrido for maior que um segundo
if ((now - entry->last_seen) > TIMEOUT_SEC * 1000000000ULL) {
entry->syn_count = 0;
entry->last_seen = now;
}
// Incrementa o contador de pacotes SYN
entry->syn_count++;
entry->last_seen = now;
// Bloqueia o tráfego se o limite de pacotes SYN for excedido
if (entry->syn_count > MAX_SYN_PACKETS_PER_IP) {
return XDP_DROP;
}
} else {
// Adiciona um novo IP ao mapa
struct ip_stats new_entry = {
.syn_count = 1,
.last_seen = get_time_ns()
};
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
}
}
} else if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp = (void *)(ip + 1);
if ((void *)(udp + 1) > data_end)
return XDP_PASS;
dest_port = __constant_ntohs(udp->dest);
}
// Consulta o mapa para verificar se o IP já está registrado
struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip);
if (entry) {
__u64 now = get_time_ns();
// Reinicia o contador se o tempo decorrido for maior que um segundo
if ((now - entry->last_seen) > TIMEOUT_SEC * 1000000000ULL) {
entry->pkt_count = 0;
entry->icmp_count = 0;
entry->syn_count = 0;
entry->high_priority_count = 0;
entry->low_priority_count = 0;
entry->last_seen = now;
}
// Rate limiting para pacotes de alta prioridade
if (is_high_priority_port(dest_port)) {
entry->high_priority_count++;
if (entry->high_priority_count > MAX_HIGH_PRIORITY_PACKETS_PER_SECOND) {
return XDP_DROP;
}
}
// Rate limiting para pacotes de baixa prioridade
if (is_low_priority_port(dest_port)) {
entry->low_priority_count++;
if (entry->low_priority_count > MAX_LOW_PRIORITY_PACKETS_PER_SECOND) {
return XDP_DROP;
}
}
// Incrementa o contador de pacotes
entry->pkt_count++;
entry->last_seen = now;
// Filtra pacotes ICMP
if (ip->protocol == IPPROTO_ICMP) {
entry->icmp_count++;
if (entry->icmp_count > MAX_ICMP_PACKETS_PER_IP) {
return XDP_DROP;
}
}
// Bloqueia o tráfego se o limite de pacotes for excedido
if (entry->pkt_count > MAX_PACKETS_PER_IP) {
return XDP_DROP;
}
} else {
// Adiciona um novo IP ao mapa
struct ip_stats new_entry = {
.pkt_count = 1,
.icmp_count = (ip->protocol == IPPROTO_ICMP) ? 1 : 0,
.syn_count = (ip->protocol == IPPROTO_TCP && tcp && tcp->syn && !tcp->ack) ? 1 : 0,
.last_seen = get_time_ns(),
.high_priority_count = is_high_priority_port(dest_port) ? 1 : 0,
.low_priority_count = is_low_priority_port(dest_port) ? 1 : 0
};
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
}
return XDP_PASS;
}
char __license[] SEC("license") = "GPL";
+241 -65
View File
@@ -4,40 +4,51 @@
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/udp.h> #include <linux/udp.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/icmp.h>
#include <bpf/bpf_helpers.h> #include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h> #include <bpf/bpf_endian.h>
// Otimização: melhorar constantes para ajuste fino // Otimização: constantes ajustadas para seu ambiente específico
#define MAX_HIGH_PRIORITY_PACKETS_PER_SECOND 2000 #define MAX_HIGH_PRIORITY_PACKETS_PER_SECOND 3000 // Aumentado para serviços críticos (HTTP/HTTPS)
#define MAX_LOW_PRIORITY_PACKETS_PER_SECOND 500 #define MAX_LOW_PRIORITY_PACKETS_PER_SECOND 500
#define MAX_PACKETS_PER_IP 60000 #define MAX_PACKETS_PER_IP 60000
#define MAX_ICMP_PACKETS_PER_IP 50 #define MAX_ICMP_PACKETS_PER_IP 30 // Reduzido conforme política ICMP restritiva
#define MAX_SYN_PACKETS_PER_IP 100 #define MAX_SYN_PACKETS_PER_IP 80 // Reduzido para proteção preventiva
#define TIMEOUT_SEC 1 #define TIMEOUT_SEC 1
#define PACKET_COST 1 // Custo padrão por pacote #define PACKET_COST 1
#define HIGH_PRIO_TOKEN_RATE 2000 // Tokens/segundo para alta prioridade #define HIGH_PRIO_TOKEN_RATE 3000 // Aumentado para combinar com QoS HTB
#define LOW_PRIO_TOKEN_RATE 500 // Tokens/segundo para baixa prioridade #define LOW_PRIO_TOKEN_RATE 500
#define MAX_TOKENS 2000 // Máximo de tokens acumulados #define MAX_TOKENS 3000
#define DOCKER_NETWORK_PREFIX 0x0A0F0000 // 10.15.0.0
#define DOCKER_NETWORK_MASK 0xFFFF0000 // /16
#define WG_NETWORK_PREFIX 0x0A630100 // 10.99.1.0
#define WG_NETWORK_MASK 0xFFFFFF00 // /24
#define NGINX_SERVER_IP 0x0A0F002D // 10.15.0.45
// Otimização: estrutura de dados mais compacta para reduzir acessos de memória // Estrutura de dados otimizada
struct ip_stats { struct ip_stats {
__u32 pkt_count; // Contador de pacotes total __u32 pkt_count;
__u32 last_seen; // Último timestamp em segundos (não nanossegundos) __u32 last_seen;
__u16 icmp_count; // Contador ICMP (reduzido para 16 bits) __u16 icmp_count;
__u16 syn_count; // Contador SYN (reduzido para 16 bits) __u16 syn_count;
__u16 high_prio_tokens; // Tokens disponíveis para alta prioridade __u16 high_prio_tokens;
__u16 low_prio_tokens; // Tokens disponíveis para baixa prioridade __u16 low_prio_tokens;
__u8 flags; // Flags para identificar comportamentos suspeitos
} __attribute__((packed)); } __attribute__((packed));
// Otimização: usar PERCPU map para evitar contenção e locks // Adicionar flags para comportamento de tráfego
#define FLAG_POTENTIAL_SCANNER 0x01
#define FLAG_SYN_FLOOD 0x02
#define FLAG_ICMP_FLOOD 0x04
#define FLAG_MARKED_SUSPICIOUS 0x08
struct { struct {
__uint(type, BPF_MAP_TYPE_PERCPU_HASH); __uint(type, BPF_MAP_TYPE_PERCPU_HASH);
__uint(max_entries, 100000); __uint(max_entries, 100000);
__type(key, __u32); // IP address __type(key, __u32);
__type(value, struct ip_stats); __type(value, struct ip_stats);
} ddos_map SEC(".maps"); } ddos_map SEC(".maps");
// Otimização: usar LRU para blacklist para evitar crescimento infinito
struct { struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 100000); __uint(max_entries, 100000);
@@ -45,6 +56,31 @@ struct {
__type(value, __u32); __type(value, __u32);
} blacklist_map SEC(".maps"); } blacklist_map SEC(".maps");
// Mapa para estatísticas de tráfego para integração com userspace
struct traffic_stats {
__u64 total_packets;
__u64 dropped_packets;
__u64 passed_packets;
__u64 blacklisted_count;
__u64 syn_flood_count;
__u64 icmp_flood_count;
};
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, struct traffic_stats);
} stats_map SEC(".maps");
// Fast-path para serviços críticos - pré-verifica IPs confiáveis
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 1000);
__type(key, __u32);
__type(value, __u8); // 1 = confiável
} trusted_hosts_map SEC(".maps");
// Mapa para interface de redirecionamento // Mapa para interface de redirecionamento
struct { struct {
__uint(type, BPF_MAP_TYPE_XSKMAP); __uint(type, BPF_MAP_TYPE_XSKMAP);
@@ -53,7 +89,7 @@ struct {
__type(value, int); __type(value, int);
} xsks_map SEC(".maps"); } xsks_map SEC(".maps");
// Otimização: usar lista de permissões fixa e compacta // Lista de permissões fixa
static const __u32 whitelist[] = { static const __u32 whitelist[] = {
0x0A000001, // 10.0.0.1 0x0A000001, // 10.0.0.1
0x0A630101, // 10.99.1.1 0x0A630101, // 10.99.1.1
@@ -61,12 +97,11 @@ static const __u32 whitelist[] = {
0x0a630104 // 10.99.1.4 0x0a630104 // 10.99.1.4
}; };
// Otimização: função inline para obter o timestamp em segundos em vez de nanossegundos // Função otimizada para timestamp
static __always_inline __u32 get_time_sec(void) { static __always_inline __u32 get_time_sec(void) {
return bpf_ktime_get_ns() / 1000000000; return bpf_ktime_get_ns() / 1000000000;
} }
// Otimização: usar always_inline e __builtin_expect para funções críticas
static __always_inline int is_whitelisted(__u32 ip) { static __always_inline int is_whitelisted(__u32 ip) {
#pragma unroll #pragma unroll
for (int i = 0; i < sizeof(whitelist) / sizeof(whitelist[0]); i++) { for (int i = 0; i < sizeof(whitelist) / sizeof(whitelist[0]); i++) {
@@ -81,75 +116,124 @@ static __always_inline int is_blacklisted(__u32 ip) {
return bpf_map_lookup_elem(&blacklist_map, &ip) != NULL; return bpf_map_lookup_elem(&blacklist_map, &ip) != NULL;
} }
// Otimização: usar switch-case em vez de verificações múltiplas para portas // Otimização: verificar se é rede interna para bypass
static __always_inline int is_internal_network(__u32 ip) {
// Verificar se é rede Docker
if ((ip & DOCKER_NETWORK_MASK) == DOCKER_NETWORK_PREFIX)
return 1;
// Verificar se é rede WireGuard
if ((ip & WG_NETWORK_MASK) == WG_NETWORK_PREFIX)
return 1;
return 0;
}
// Função otimizada para classificação de portas
static __always_inline int get_port_priority(__u16 port) { static __always_inline int get_port_priority(__u16 port) {
// Otimização: usar switch para fast lookup
switch (port) { switch (port) {
case 80: // HTTP case 80: // HTTP
case 443: // HTTPS case 443: // HTTPS
case 32100: // Serviço de alta prioridade 1 case 32100: // WireGuard
case 32400: // Serviço de alta prioridade 2 case 32400: // Outro serviço crítico
return 2; // Alta prioridade return 2; // Alta prioridade
case 22: // SSH case 8117: // SSH personalizado
case 25: // SMTP case 8085: // Serviço interno 1
case 53: // DNS case 8086: // Serviço interno 2
return 1; // Média prioridade return 1; // Média prioridade
default: default:
return 0; // Baixa prioridade return 0; // Baixa prioridade
} }
} }
// Otimização: função de verificação de porta mais eficiente // Função otimizada para detecção de serviços expostos
static __always_inline int is_filtered_port(__u16 port) { static __always_inline int is_filtered_port(__u16 port) {
// Portas importantes usam verificação direta // Portas críticas verificadas primeiro
switch (port) { switch (port) {
case 8117: case 80: // HTTP
case 8085: case 443: // HTTPS
case 8086: case 8117: // SSH personalizado
case 32100: case 8085: // Serviço interno 1
case 32400: case 8086: // Serviço interno 2
case 32100: // WireGuard
case 32400: // Outro serviço crítico
return 1; return 1;
default: }
// Verificação de intervalo apenas para outras portas
// Demais portas privilegiadas
return (port <= 1024); return (port <= 1024);
}
// Função para atualizar estatísticas de tráfego
static __always_inline void update_stats(__u32 action, __u8 flags) {
__u32 key = 0;
struct traffic_stats *stats = bpf_map_lookup_elem(&stats_map, &key);
if (stats) {
stats->total_packets++;
if (action == XDP_DROP) {
stats->dropped_packets++;
if (flags & FLAG_SYN_FLOOD)
stats->syn_flood_count++;
if (flags & FLAG_ICMP_FLOOD)
stats->icmp_flood_count++;
} else if (action == XDP_PASS) {
stats->passed_packets++;
}
if (flags & FLAG_MARKED_SUSPICIOUS)
stats->blacklisted_count++;
} }
} }
// Programa principal XDP // Versão otimizada do programa principal
SEC("xdp") SEC("xdp")
int xdp_ddos_filter(struct xdp_md *ctx) { int xdp_ddos_filter(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end; void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data; void *data = (void *)(long)ctx->data;
__u8 flags = 0;
// Otimização: early return para pacotes muito pequenos // Otimização: verificação de tamanho mínimo do pacote mais eficiente
if (__builtin_expect(data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end, 0)) if (__builtin_expect(data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end, 0))
return XDP_PASS; return XDP_PASS;
struct ethhdr *eth = data; struct ethhdr *eth = data;
// Otimização: early return para pacotes não-IP
if (__builtin_expect(eth->h_proto != __constant_htons(ETH_P_IP), 0)) if (__builtin_expect(eth->h_proto != __constant_htons(ETH_P_IP), 0))
return XDP_PASS; return XDP_PASS;
struct iphdr *ip = (void *)(eth + 1); struct iphdr *ip = (void *)(eth + 1);
if (__builtin_expect((void *)(ip + 1) > data_end || ip->ihl != 5 || ip->version != 4, 0))
// Otimização: verificar o cabeçalho IP de forma mais eficiente
if (__builtin_expect((void *)(ip + 1) > data_end ||
ip->ihl != 5 ||
ip->version != 4, 0))
return XDP_PASS; return XDP_PASS;
// Otimização: early return para IPs na whitelist
__u32 src_ip = ip->saddr; __u32 src_ip = ip->saddr;
__u32 dst_ip = ip->daddr;
// Fast path 1: Permitir tráfego de redes internas
if (__builtin_expect(is_internal_network(src_ip), 0)) {
return XDP_PASS;
}
// Fast path 2: Verificar whitelist estática
if (__builtin_expect(is_whitelisted(src_ip), 0)) { if (__builtin_expect(is_whitelisted(src_ip), 0)) {
return XDP_PASS; return XDP_PASS;
} }
// Otimização: early drop para IPs bloqueados // Fast path 3: Verificar IPs já confiáveis (hosts que já passaram por verificação)
__u8 *trusted = bpf_map_lookup_elem(&trusted_hosts_map, &src_ip);
if (__builtin_expect(trusted != NULL && *trusted == 1, 0)) {
return XDP_PASS;
}
// Fast path 4: Rejeitar IPs na blacklist
if (__builtin_expect(is_blacklisted(src_ip), 0)) { if (__builtin_expect(is_blacklisted(src_ip), 0)) {
update_stats(XDP_DROP, FLAG_MARKED_SUSPICIOUS);
return XDP_DROP; return XDP_DROP;
} }
// Identificar protocolo e porta para classificação // Ignorar pacotes que não são destinados ao nosso servidor NGINX ou serviços expostos
if (dst_ip != NGINX_SERVER_IP) {
// Verificar protocolo e porta para classificação de tráfego
__u16 dest_port = 0; __u16 dest_port = 0;
int is_syn = 0; int is_syn = 0;
int is_icmp = 0; int is_icmp = 0;
@@ -162,6 +246,11 @@ int xdp_ddos_filter(struct xdp_md *ctx) {
dest_port = __constant_ntohs(tcp->dest); dest_port = __constant_ntohs(tcp->dest);
is_syn = tcp->syn && !tcp->ack; is_syn = tcp->syn && !tcp->ack;
// Detecção de port scan - múltiplas flags ativas (inválido)
if ((tcp->syn && tcp->rst) || (tcp->fin && tcp->rst) || (tcp->fin && tcp->syn)) {
flags |= FLAG_POTENTIAL_SCANNER;
}
} else if (ip->protocol == IPPROTO_UDP) { } else if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp = (void *)(ip + 1); struct udphdr *udp = (void *)(ip + 1);
if (__builtin_expect((void *)(udp + 1) > data_end, 0)) if (__builtin_expect((void *)(udp + 1) > data_end, 0))
@@ -171,56 +260,78 @@ int xdp_ddos_filter(struct xdp_md *ctx) {
} else if (ip->protocol == IPPROTO_ICMP) { } else if (ip->protocol == IPPROTO_ICMP) {
is_icmp = 1; is_icmp = 1;
// Verificar tipo de ICMP
struct icmphdr *icmp = (void *)(ip + 1);
if (__builtin_expect((void *)(icmp + 1) > data_end, 0))
return XDP_PASS;
// Permitir apenas echo-reply (0) e limitar echo-request (8)
if (icmp->type != 0 && icmp->type != 8) {
update_stats(XDP_DROP, 0);
return XDP_DROP;
}
} else { } else {
// Outros protocolos passam sem filtro adicional // Outros protocolos passam sem filtro adicional
return XDP_PASS; return XDP_PASS;
} }
// Otimização: early return se não for para portas filtradas // Otimização: Se não for para portas filtradas, deixar passar
if (__builtin_expect(!is_filtered_port(dest_port) && !is_icmp, 1)) if (__builtin_expect(!is_filtered_port(dest_port) && !is_icmp, 1)) {
// Se for um cliente fazendo várias conexões legítimas, marcá-lo como confiável
struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip);
if (entry && entry->pkt_count > 5 && entry->flags == 0) {
__u8 mark_trusted = 1;
bpf_map_update_elem(&trusted_hosts_map, &src_ip, &mark_trusted, BPF_ANY);
}
return XDP_PASS; return XDP_PASS;
}
// Prioridade do pacote com base na porta // Prioridade do pacote
int priority = get_port_priority(dest_port); int priority = get_port_priority(dest_port);
// Obter estatísticas atuais do IP // Rate limiting baseado em tokens
struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip); struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip);
__u32 now = get_time_sec(); __u32 now = get_time_sec();
if (entry) { if (entry) {
// Otimização: cálculo de tempo decorrido mais eficiente // Cálculo de tokens disponíveis
__u32 elapsed = 0; __u32 elapsed = 0;
if (__builtin_expect(now > entry->last_seen, 1)) { if (__builtin_expect(now > entry->last_seen, 1)) {
elapsed = now - entry->last_seen; elapsed = now - entry->last_seen;
} }
// Implementação de token bucket para cada prioridade // Implementação de token bucket
if (elapsed > 0) { if (elapsed > 0) {
// Adicionar tokens baseado no tempo decorrido, limitado ao máximo // Adicionar tokens com limite máximo
__u16 new_high_tokens = entry->high_prio_tokens + (HIGH_PRIO_TOKEN_RATE * elapsed); __u16 new_high_tokens = entry->high_prio_tokens + (HIGH_PRIO_TOKEN_RATE * elapsed);
entry->high_prio_tokens = new_high_tokens > MAX_TOKENS ? MAX_TOKENS : new_high_tokens; entry->high_prio_tokens = new_high_tokens > MAX_TOKENS ? MAX_TOKENS : new_high_tokens;
__u16 new_low_tokens = entry->low_prio_tokens + (LOW_PRIO_TOKEN_RATE * elapsed); __u16 new_low_tokens = entry->low_prio_tokens + (LOW_PRIO_TOKEN_RATE * elapsed);
entry->low_prio_tokens = new_low_tokens > MAX_TOKENS ? MAX_TOKENS : new_low_tokens; entry->low_prio_tokens = new_low_tokens > MAX_TOKENS ? MAX_TOKENS : new_low_tokens;
// Resetar contadores específicos de protocolo após 1 segundo // Resetar contadores se passou tempo suficiente
if (elapsed >= TIMEOUT_SEC) { if (elapsed >= TIMEOUT_SEC) {
entry->icmp_count = 0; entry->icmp_count = 0;
entry->syn_count = 0; entry->syn_count = 0;
entry->flags &= ~(FLAG_SYN_FLOOD | FLAG_ICMP_FLOOD);
} }
} }
// Atualizar o último timestamp visto // Atualizar flags e contadores
entry->last_seen = now; entry->last_seen = now;
// Atualizar contadores específicos
entry->pkt_count++; entry->pkt_count++;
if (flags & FLAG_POTENTIAL_SCANNER) {
entry->flags |= FLAG_POTENTIAL_SCANNER;
}
if (is_icmp) { if (is_icmp) {
entry->icmp_count++; entry->icmp_count++;
if (__builtin_expect(entry->icmp_count > MAX_ICMP_PACKETS_PER_IP, 0)) { if (__builtin_expect(entry->icmp_count > MAX_ICMP_PACKETS_PER_IP, 0)) {
entry->flags |= FLAG_ICMP_FLOOD;
__u32 one = 1; __u32 one = 1;
bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY); bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY);
update_stats(XDP_DROP, FLAG_ICMP_FLOOD);
return XDP_DROP; return XDP_DROP;
} }
} }
@@ -228,49 +339,114 @@ int xdp_ddos_filter(struct xdp_md *ctx) {
if (is_syn) { if (is_syn) {
entry->syn_count++; entry->syn_count++;
if (__builtin_expect(entry->syn_count > MAX_SYN_PACKETS_PER_IP, 0)) { if (__builtin_expect(entry->syn_count > MAX_SYN_PACKETS_PER_IP, 0)) {
entry->flags |= FLAG_SYN_FLOOD;
__u32 one = 1; __u32 one = 1;
bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY); bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY);
update_stats(XDP_DROP, FLAG_SYN_FLOOD);
return XDP_DROP; return XDP_DROP;
} }
} }
// Otimização: verificação total de pacotes // Blacklist se exceder total de pacotes
if (__builtin_expect(entry->pkt_count > MAX_PACKETS_PER_IP, 0)) { if (__builtin_expect(entry->pkt_count > MAX_PACKETS_PER_IP, 0)) {
__u32 one = 1; __u32 one = 1;
entry->flags |= FLAG_MARKED_SUSPICIOUS;
bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY); bpf_map_update_elem(&blacklist_map, &src_ip, &one, BPF_ANY);
update_stats(XDP_DROP, FLAG_MARKED_SUSPICIOUS);
return XDP_DROP; return XDP_DROP;
} }
// Verificar tokens disponíveis com base na prioridade // Aplicar token bucket conforme prioridade
if (priority == 2) { // Alta prioridade if (priority == 2) { // Alta prioridade
if (entry->high_prio_tokens >= PACKET_COST) { if (entry->high_prio_tokens >= PACKET_COST) {
entry->high_prio_tokens -= PACKET_COST; entry->high_prio_tokens -= PACKET_COST;
update_stats(XDP_PASS, 0);
return XDP_PASS; return XDP_PASS;
} }
} else { // Média e baixa prioridade } else if (priority == 1) { // Média prioridade
// Média prioridade usa metade de cada bucket
if (entry->high_prio_tokens >= PACKET_COST/2 &&
entry->low_prio_tokens >= PACKET_COST/2) {
entry->high_prio_tokens -= PACKET_COST/2;
entry->low_prio_tokens -= PACKET_COST/2;
update_stats(XDP_PASS, 0);
return XDP_PASS;
}
} else { // Baixa prioridade
if (entry->low_prio_tokens >= PACKET_COST) { if (entry->low_prio_tokens >= PACKET_COST) {
entry->low_prio_tokens -= PACKET_COST; entry->low_prio_tokens -= PACKET_COST;
update_stats(XDP_PASS, 0);
return XDP_PASS; return XDP_PASS;
} }
} }
// Sem tokens disponíveis para esta prioridade, descartar update_stats(XDP_DROP, 0);
return XDP_DROP; return XDP_DROP;
} else { } else {
// Primeira vez que vemos este IP, inicializar com valores generosos // Primeira vez que vemos este IP
struct ip_stats new_entry = { struct ip_stats new_entry = {
.pkt_count = 1, .pkt_count = 1,
.last_seen = now, .last_seen = now,
.icmp_count = is_icmp ? 1 : 0, .icmp_count = is_icmp ? 1 : 0,
.syn_count = is_syn ? 1 : 0, .syn_count = is_syn ? 1 : 0,
.high_prio_tokens = MAX_TOKENS - PACKET_COST, .high_prio_tokens = MAX_TOKENS - PACKET_COST,
.low_prio_tokens = MAX_TOKENS - PACKET_COST .low_prio_tokens = MAX_TOKENS - PACKET_COST,
.flags = flags
}; };
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY); bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
update_stats(XDP_PASS, 0);
return XDP_PASS; return XDP_PASS;
} }
}
// Processar pacotes destinados ao servidor NGINX
if (dst_ip == NGINX_SERVER_IP) {
struct ip_stats *entry = bpf_map_lookup_elem(&ddos_map, &src_ip);
__u32 now = get_time_sec();
// Verificar cabeçalhos TCP para tráfego HTTP(S)
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = (void *)(ip + 1);
if (__builtin_expect((void *)(tcp + 1) > data_end, 0))
return XDP_PASS;
__u16 dest_port = __constant_ntohs(tcp->dest);
if (dest_port == 80 || dest_port == 443) {
// Lógica específica para proteção de HTTP/HTTPS
// Se for SYN, fazer rate limiting específico
if (tcp->syn && !tcp->ack) {
if (entry) {
entry->syn_count++;
if (entry->syn_count > MAX_SYN_PACKETS_PER_IP) {
update_stats(XDP_DROP, FLAG_SYN_FLOOD);
return XDP_DROP;
}
} else {
// Primeiro SYN deste IP
struct ip_stats new_entry = {
.pkt_count = 1,
.last_seen = now,
.syn_count = 1,
.high_prio_tokens = MAX_TOKENS - PACKET_COST,
.low_prio_tokens = MAX_TOKENS - PACKET_COST
};
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
}
}
// Permitir o pacote (processamento de entrada HTTP é prioritário)
update_stats(XDP_PASS, 0);
return XDP_PASS;
}
}
}
// Se chegou até aqui, passa o tráfego
update_stats(XDP_PASS, 0);
return XDP_PASS;
} }
char __license[] SEC("license") = "GPL"; char __license[] SEC("license") = "GPL";