commit
This commit is contained in:
1 parent
6081ec0c1c
commit
dd38ec9f0e
2 files changed
+499
-223
No files matched your search
@@ -0,0 +1,323 @@
|
||||
#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>
|
||||
|
||||
#define MAX_HIGH_PRIORITY_PACKETS_PER_SECOND 2000 // Limite de pacotes por segundo para alta prioridade
|
||||
#define MAX_LOW_PRIORITY_PACKETS_PER_SECOND 500 // Limite de pacotes por segundo para baixa prioridade
|
||||
#define MAX_PACKETS_PER_IP 60000 // Limite de pacotes por IP antes de bloquear
|
||||
#define MAX_ICMP_PACKETS_PER_IP 50 // Limite de pacotes ICMP por IP antes de bloquear
|
||||
#define MAX_SYN_PACKETS_PER_IP 100 // Limite de pacotes SYN por IP antes de bloquear
|
||||
#define TIMEOUT_SEC 1 // 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 pkt_count; // Contador de pacotes
|
||||
__u64 icmp_count; // Contador de pacotes ICMP
|
||||
__u64 syn_count; // Contador de pacotes SYN
|
||||
__u64 last_seen; // Timestamp do último pacote
|
||||
__u64 high_priority_count; // Contador de pacotes de alta prioridade
|
||||
__u64 low_priority_count; // Contador de pacotes de baixa prioridade
|
||||
};
|
||||
|
||||
// Estruturas para NAT
|
||||
struct nat_entry {
|
||||
__u32 new_ip; // IP de destino após NAT
|
||||
__u16 new_port; // Porta de destino após NAT
|
||||
__u16 proto; // Protocolo (TCP/UDP)
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
// Modificar mapa DNAT para usar porta como chave
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 10000);
|
||||
__type(key, __u16); // Porta de destino
|
||||
__type(value, struct nat_entry); // Nova configuração
|
||||
} dnat_map SEC(".maps");
|
||||
|
||||
struct masquerade_key {
|
||||
__u32 src_ip; // IP de origem
|
||||
__u32 dst_ip; // IP de destino
|
||||
__u16 dst_port; // Porta de destino
|
||||
__u16 proto; // Protocolo
|
||||
} __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");
|
||||
|
||||
// Mapas adicionais para NAT
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 10000);
|
||||
__type(key, struct masquerade_key);
|
||||
__type(value, __u32); // IP mascarado
|
||||
} masquerade_map SEC(".maps");
|
||||
|
||||
// Adicionar estrutura para SNAT
|
||||
struct snat_entry {
|
||||
__u32 new_ip; // IP após NAT
|
||||
__u16 new_port; // Porta após NAT
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
// Adicionar mapa para SNAT
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 10000);
|
||||
__type(key, struct masquerade_key);
|
||||
__type(value, struct snat_entry);
|
||||
} snat_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
|
||||
};
|
||||
|
||||
// Adicionar 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");
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Função para verificar se a porta é de alta prioridade
|
||||
static __inline int is_high_priority_port(__u16 port) {
|
||||
return (port == 80 || port == 443 || port == 32100 || port == 32400);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Função para verificar se a porta deve ser filtrada
|
||||
static __inline int is_filtered_port(__u16 port) {
|
||||
// Intervalo de portas 1-1024
|
||||
if (port >= 1 && port <= 1024) {
|
||||
return 1;
|
||||
}
|
||||
// Portas específicas
|
||||
if (port == 8117 || port == 8085 || port == 8086 || port == 32100 || port == 32400) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Função para verificar se o pacote é destinado ao servidor e deve ser filtrado
|
||||
static __inline int is_destined_to_server(struct iphdr *ip, void *data_end) {
|
||||
if (ip->protocol == IPPROTO_TCP) {
|
||||
struct tcphdr *tcp = (void *)(ip + 1);
|
||||
if ((void *)(tcp + 1) > data_end)
|
||||
return 0;
|
||||
// Verifique se a porta está na lista de portas a serem filtradas
|
||||
__u16 dest_port = __constant_ntohs(tcp->dest);
|
||||
if (is_filtered_port(dest_port)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (ip->protocol == IPPROTO_UDP) {
|
||||
struct udphdr *udp = (void *)(ip + 1);
|
||||
if ((void *)(udp + 1) > data_end)
|
||||
return 0;
|
||||
// Verifique se a porta está na lista de portas a serem filtradas
|
||||
__u16 dest_port = __constant_ntohs(udp->dest);
|
||||
if (is_filtered_port(dest_port)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (ip->protocol == IPPROTO_ICMP) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
struct ethhdr *eth = data;
|
||||
if ((void *)(eth + 1) > data_end)
|
||||
return XDP_PASS;
|
||||
|
||||
// Verifica se é um pacote IP
|
||||
if (eth->h_proto != __constant_htons(ETH_P_IP))
|
||||
return XDP_PASS;
|
||||
|
||||
struct iphdr *ip = (void *)(eth + 1);
|
||||
if ((void *)(ip + 1) > data_end)
|
||||
return XDP_PASS;
|
||||
|
||||
// Verifica se o cabeçalho IP é válido
|
||||
if (ip->ihl != 5 || ip->version != 4)
|
||||
return XDP_PASS;
|
||||
|
||||
// Verifica se o IP está na lista de permissões
|
||||
__u32 src_ip = ip->saddr;
|
||||
if (is_whitelisted(src_ip)) {
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
// Verifica se o IP está na lista negra
|
||||
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";
|
||||
+176
-223
@@ -7,92 +7,45 @@
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
|
||||
#define MAX_HIGH_PRIORITY_PACKETS_PER_SECOND 2000 // Limite de pacotes por segundo para alta prioridade
|
||||
#define MAX_LOW_PRIORITY_PACKETS_PER_SECOND 500 // Limite de pacotes por segundo para baixa prioridade
|
||||
#define MAX_PACKETS_PER_IP 60000 // Limite de pacotes por IP antes de bloquear
|
||||
#define MAX_ICMP_PACKETS_PER_IP 50 // Limite de pacotes ICMP por IP antes de bloquear
|
||||
#define MAX_SYN_PACKETS_PER_IP 100 // Limite de pacotes SYN por IP antes de bloquear
|
||||
#define TIMEOUT_SEC 1 // Tempo para expiração do contador (em segundos)
|
||||
// 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
|
||||
|
||||
// Estrutura para armazenar contagem de pacotes e o último timestamp visto para um IP
|
||||
// Otimização: estrutura de dados mais compacta para reduzir acessos de memória
|
||||
struct ip_stats {
|
||||
__u64 pkt_count; // Contador de pacotes
|
||||
__u64 icmp_count; // Contador de pacotes ICMP
|
||||
__u64 syn_count; // Contador de pacotes SYN
|
||||
__u64 last_seen; // Timestamp do último pacote
|
||||
__u64 high_priority_count; // Contador de pacotes de alta prioridade
|
||||
__u64 low_priority_count; // Contador de pacotes de baixa prioridade
|
||||
};
|
||||
__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));
|
||||
|
||||
// Estruturas para NAT
|
||||
struct nat_entry {
|
||||
__u32 new_ip; // IP de destino após NAT
|
||||
__u16 new_port; // Porta de destino após NAT
|
||||
__u16 proto; // Protocolo (TCP/UDP)
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
// Modificar mapa DNAT para usar porta como chave
|
||||
// Otimização: usar PERCPU map para evitar contenção e locks
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 10000);
|
||||
__type(key, __u16); // Porta de destino
|
||||
__type(value, struct nat_entry); // Nova configuração
|
||||
} dnat_map SEC(".maps");
|
||||
|
||||
struct masquerade_key {
|
||||
__u32 src_ip; // IP de origem
|
||||
__u32 dst_ip; // IP de destino
|
||||
__u16 dst_port; // Porta de destino
|
||||
__u16 proto; // Protocolo
|
||||
} __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)
|
||||
__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");
|
||||
|
||||
// Mapa de hash para armazenar a blacklist
|
||||
// Otimização: usar LRU para blacklist para evitar crescimento infinito
|
||||
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)
|
||||
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||
__uint(max_entries, 100000);
|
||||
__type(key, __u32);
|
||||
__type(value, __u32);
|
||||
} blacklist_map SEC(".maps");
|
||||
|
||||
// Mapas adicionais para NAT
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 10000);
|
||||
__type(key, struct masquerade_key);
|
||||
__type(value, __u32); // IP mascarado
|
||||
} masquerade_map SEC(".maps");
|
||||
|
||||
// Adicionar estrutura para SNAT
|
||||
struct snat_entry {
|
||||
__u32 new_ip; // IP após NAT
|
||||
__u16 new_port; // Porta após NAT
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
// Adicionar mapa para SNAT
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 10000);
|
||||
__type(key, struct masquerade_key);
|
||||
__type(value, struct snat_entry);
|
||||
} snat_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
|
||||
};
|
||||
|
||||
// Adicionar mapa para interface de redirecionamento
|
||||
// Mapa para interface de redirecionamento
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_XSKMAP);
|
||||
__uint(max_entries, 1);
|
||||
@@ -100,15 +53,23 @@ struct {
|
||||
__type(value, int);
|
||||
} xsks_map SEC(".maps");
|
||||
|
||||
// Função auxiliar para obter o timestamp atual em nanossegundos
|
||||
static __inline __u64 get_time_ns(void) {
|
||||
return bpf_ktime_get_ns();
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
// 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;
|
||||
}
|
||||
@@ -116,59 +77,41 @@ static __inline int is_whitelisted(__u32 ip) {
|
||||
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;
|
||||
static __always_inline int is_blacklisted(__u32 ip) {
|
||||
return bpf_map_lookup_elem(&blacklist_map, &ip) != NULL;
|
||||
}
|
||||
|
||||
// Função para verificar se a porta é de alta prioridade
|
||||
static __inline int is_high_priority_port(__u16 port) {
|
||||
return (port == 80 || port == 443 || port == 32100 || port == 32400);
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Função para verificar se a porta deve ser filtrada
|
||||
static __inline int is_filtered_port(__u16 port) {
|
||||
// Intervalo de portas 1-1024
|
||||
if (port >= 1 && port <= 1024) {
|
||||
// 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);
|
||||
}
|
||||
// Portas específicas
|
||||
if (port == 8117 || port == 8085 || port == 8086 || port == 32100 || port == 32400) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Função para verificar se o pacote é destinado ao servidor e deve ser filtrado
|
||||
static __inline int is_destined_to_server(struct iphdr *ip, void *data_end) {
|
||||
if (ip->protocol == IPPROTO_TCP) {
|
||||
struct tcphdr *tcp = (void *)(ip + 1);
|
||||
if ((void *)(tcp + 1) > data_end)
|
||||
return 0;
|
||||
// Verifique se a porta está na lista de portas a serem filtradas
|
||||
__u16 dest_port = __constant_ntohs(tcp->dest);
|
||||
if (is_filtered_port(dest_port)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (ip->protocol == IPPROTO_UDP) {
|
||||
struct udphdr *udp = (void *)(ip + 1);
|
||||
if ((void *)(udp + 1) > data_end)
|
||||
return 0;
|
||||
// Verifique se a porta está na lista de portas a serem filtradas
|
||||
__u16 dest_port = __constant_ntohs(udp->dest);
|
||||
if (is_filtered_port(dest_port)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (ip->protocol == IPPROTO_ICMP) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Programa principal XDP
|
||||
@@ -177,147 +120,157 @@ int xdp_ddos_filter(struct xdp_md *ctx) {
|
||||
void *data_end = (void *)(long)ctx->data_end;
|
||||
void *data = (void *)(long)ctx->data;
|
||||
|
||||
struct ethhdr *eth = data;
|
||||
if ((void *)(eth + 1) > data_end)
|
||||
// Otimização: early return para pacotes muito pequenos
|
||||
if (__builtin_expect(data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end, 0))
|
||||
return XDP_PASS;
|
||||
|
||||
// Verifica se é um pacote IP
|
||||
if (eth->h_proto != __constant_htons(ETH_P_IP))
|
||||
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);
|
||||
if ((void *)(ip + 1) > data_end)
|
||||
|
||||
// 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;
|
||||
|
||||
// Verifica se o cabeçalho IP é válido
|
||||
if (ip->ihl != 5 || ip->version != 4)
|
||||
return XDP_PASS;
|
||||
|
||||
// Verifica se o IP está na lista de permissões
|
||||
// Otimização: early return para IPs na whitelist
|
||||
__u32 src_ip = ip->saddr;
|
||||
if (is_whitelisted(src_ip)) {
|
||||
if (__builtin_expect(is_whitelisted(src_ip), 0)) {
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
// Verifica se o IP está na lista negra
|
||||
if (is_blacklisted(src_ip)) {
|
||||
// Otimização: early drop para IPs bloqueados
|
||||
if (__builtin_expect(is_blacklisted(src_ip), 0)) {
|
||||
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;
|
||||
|
||||
// Identificar protocolo e porta para classificação
|
||||
__u16 dest_port = 0;
|
||||
struct tcphdr *tcp = NULL;
|
||||
int is_syn = 0;
|
||||
int is_icmp = 0;
|
||||
|
||||
// Verifica se é um pacote TCP ou UDP
|
||||
if (ip->protocol == IPPROTO_TCP) {
|
||||
tcp = (void *)(ip + 1);
|
||||
if ((void *)(tcp + 1) > data_end)
|
||||
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;
|
||||
|
||||
// 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)
|
||||
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;
|
||||
}
|
||||
|
||||
// Consulta o mapa para verificar se o IP já está registrado
|
||||
// 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) {
|
||||
__u64 now = get_time_ns();
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Reinicia o contador se o tempo decorrido for maior que um segundo
|
||||
if ((now - entry->last_seen) > TIMEOUT_SEC * 1000000000ULL) {
|
||||
entry->pkt_count = 0;
|
||||
// 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;
|
||||
entry->high_priority_count = 0;
|
||||
entry->low_priority_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Atualizar o último timestamp visto
|
||||
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
|
||||
// Atualizar contadores específicos
|
||||
entry->pkt_count++;
|
||||
entry->last_seen = now;
|
||||
|
||||
// Filtra pacotes ICMP
|
||||
if (ip->protocol == IPPROTO_ICMP) {
|
||||
if (is_icmp) {
|
||||
entry->icmp_count++;
|
||||
if (entry->icmp_count > MAX_ICMP_PACKETS_PER_IP) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Bloqueia o tráfego se o limite de pacotes for excedido
|
||||
if (entry->pkt_count > MAX_PACKETS_PER_IP) {
|
||||
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 {
|
||||
// Adiciona um novo IP ao mapa
|
||||
// Primeira vez que vemos este IP, inicializar com valores generosos
|
||||
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
|
||||
.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);
|
||||
}
|
||||
|
||||
bpf_map_update_elem(&ddos_map, &src_ip, &new_entry, BPF_ANY);
|
||||
return XDP_PASS;
|
||||
}
|
||||
}
|
||||
|
||||
char __license[] SEC("license") = "GPL";
|
||||
Reference in new issue
Block a user