mirror of https://github.com/bol-van/zapret/
12 changed files with 3226 additions and 2 deletions
@ -0,0 +1,175 @@ |
|||
# Zapret eBPF Makefile
|
|||
# eBPF implementation for DPI evasion
|
|||
|
|||
# Compiler and tools
|
|||
CC := clang |
|||
LLC := llc |
|||
OPT := opt |
|||
DIS := llvm-dis |
|||
OBJDUMP := llvm-objdump |
|||
STRIP := llvm-strip |
|||
|
|||
# Directories
|
|||
SRC_DIR := src |
|||
LOADER_DIR := loader |
|||
INCLUDE_DIR := include |
|||
LIBBPF_DIR := libbpf |
|||
BUILD_DIR := build |
|||
OBJ_DIR := $(BUILD_DIR)/obj |
|||
BIN_DIR := $(BUILD_DIR)/bin |
|||
|
|||
# Platform detection
|
|||
UNAME_S := $(shell uname -s) |
|||
ifeq ($(UNAME_S),Linux) |
|||
PLATFORM := linux |
|||
EBPF_SUPPORTED := yes |
|||
CFLAGS := -O2 -g -Wall -Wextra |
|||
EBPF_CFLAGS := -O2 -g -target bpf -D__TARGET_ARCH_x86_64 -D__KERNEL__ -D__ASM_SYSREG_H |
|||
INCLUDES := -I$(INCLUDE_DIR) -I$(LIBBPF_DIR)/src -I/usr/include |
|||
LDFLAGS := -L$(LIBBPF_DIR)/src -lbpf -lelf -lz |
|||
else ifeq ($(UNAME_S),Darwin) |
|||
PLATFORM := macos |
|||
EBPF_SUPPORTED := no |
|||
CFLAGS := -O2 -g -Wall -Wextra -D__APPLE__ -D__MACH__ -DMACOS_BUILD |
|||
EBPF_CFLAGS := -O2 -g -D__APPLE__ -D__MACH__ -DMACOS_BUILD |
|||
INCLUDES := -I$(INCLUDE_DIR) -I/usr/local/include |
|||
LDFLAGS := -L/usr/local/lib |
|||
else |
|||
PLATFORM := unknown |
|||
EBPF_SUPPORTED := no |
|||
CFLAGS := -O2 -g -Wall -Wextra |
|||
EBPF_CFLAGS := -O2 -g |
|||
INCLUDES := -I$(INCLUDE_DIR) |
|||
LDFLAGS := |
|||
endif |
|||
|
|||
# Legacy compatibility
|
|||
LLVM_STRIP ?= $(STRIP) |
|||
CLANG ?= $(CC) |
|||
LIBBPF_OBJ = $(LIBBPF_DIR)/src/libbpf.a |
|||
BPF_CFLAGS = $(EBPF_CFLAGS) |
|||
|
|||
# eBPF program sources
|
|||
EBPF_SRCS = src/zapret_filter.bpf.c \
|
|||
src/tls_fingerprint.bpf.c \
|
|||
src/quic_filter.bpf.c \
|
|||
src/packet_fragment.bpf.c \
|
|||
src/sni_encrypt.bpf.c |
|||
|
|||
# eBPF object files
|
|||
EBPF_OBJS = $(EBPF_SRCS:.bpf.c=.bpf.o) |
|||
|
|||
# User-space loader sources
|
|||
LOADER_SRCS = loader/zapret_loader.c \
|
|||
loader/ebpf_manager.c \
|
|||
loader/performance_monitor.c |
|||
|
|||
# User-space loader objects
|
|||
LOADER_OBJS = $(LOADER_SRCS:.c=.o) |
|||
|
|||
# Target executables
|
|||
TARGETS = zapret_ebpf_loader |
|||
|
|||
.PHONY: all clean install libbpf ebpf_programs user_space demo |
|||
|
|||
ifeq ($(EBPF_SUPPORTED),yes) |
|||
all: libbpf $(TARGETS) |
|||
@echo "Build complete for $(PLATFORM) platform with eBPF support" |
|||
else |
|||
all: demo |
|||
@echo "Build complete for $(PLATFORM) platform (demo mode - no eBPF support)" |
|||
endif |
|||
|
|||
# Build libbpf (Linux only)
|
|||
ifeq ($(EBPF_SUPPORTED),yes) |
|||
libbpf: $(LIBBPF_OBJ) |
|||
|
|||
$(LIBBPF_OBJ): |
|||
@if [ ! -d "$(LIBBPF_DIR)" ]; then \
|
|||
echo "Cloning libbpf..."; \
|
|||
git clone --depth 1 https://github.com/libbpf/libbpf.git $(LIBBPF_DIR); \
|
|||
fi |
|||
$(MAKE) -C $(LIBBPF_DIR)/src |
|||
else |
|||
libbpf: |
|||
@echo "SKIP libbpf (not supported on $(PLATFORM))" |
|||
|
|||
$(LIBBPF_OBJ): |
|||
@touch $@ |
|||
endif |
|||
|
|||
# Compile eBPF programs (Linux only)
|
|||
ifeq ($(EBPF_SUPPORTED),yes) |
|||
%.bpf.o: %.bpf.c |
|||
$(CLANG) $(BPF_CFLAGS) $(INCLUDES) -c $< -o $@ |
|||
$(LLVM_STRIP) -g $@ |
|||
else |
|||
%.bpf.o: %.bpf.c |
|||
@echo "SKIP $@ (eBPF not supported on $(PLATFORM))" |
|||
@touch $@ |
|||
endif |
|||
|
|||
# Compile user-space loader
|
|||
%.o: %.c |
|||
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ |
|||
|
|||
# Link the main loader
|
|||
ifeq ($(EBPF_SUPPORTED),yes) |
|||
zapret_ebpf_loader: $(EBPF_OBJS) $(LOADER_OBJS) $(LIBBPF_OBJ) |
|||
$(CC) $(CFLAGS) $(LOADER_OBJS) $(LIBBPF_OBJ) -lelf -lz -o $@ |
|||
else |
|||
zapret_ebpf_loader: $(LOADER_OBJS) |
|||
$(CC) $(CFLAGS) $(LOADER_OBJS) $(LDFLAGS) -DDEMO_MODE -o $@ |
|||
endif |
|||
|
|||
# Demo target for non-Linux platforms
|
|||
demo: zapret_demo |
|||
|
|||
zapret_demo: |
|||
@echo "CC zapret_demo (demo mode)" |
|||
@echo '#include <stdio.h>' > demo.c |
|||
@echo 'int main() { printf("Zapret demo for $(PLATFORM)\\n"); return 0; }' >> demo.c |
|||
@$(CC) $(CFLAGS) demo.c -o zapret_demo |
|||
@rm -f demo.c |
|||
|
|||
install: all |
|||
cp $(TARGETS) /usr/local/bin/ |
|||
cp $(EBPF_OBJS) /usr/local/share/zapret/ebpf/ |
|||
|
|||
clean: |
|||
rm -f $(EBPF_OBJS) $(LOADER_OBJS) $(TARGETS) |
|||
@if [ -d "$(LIBBPF_DIR)" ]; then \
|
|||
$(MAKE) -C $(LIBBPF_DIR)/src clean; \
|
|||
fi |
|||
|
|||
distclean: clean |
|||
rm -rf $(LIBBPF_DIR) |
|||
|
|||
# Development targets
|
|||
format: |
|||
clang-format -i $(LOADER_SRCS) include/*.h |
|||
|
|||
check: |
|||
@echo "Checking eBPF programs..." |
|||
@for prog in $(EBPF_OBJS); do \
|
|||
echo "Verifying $$prog"; \
|
|||
bpftool prog load $$prog /sys/fs/bpf/test_$$prog 2>/dev/null && \
|
|||
bpftool prog del pinned /sys/fs/bpf/test_$$prog || true; \
|
|||
done |
|||
|
|||
# macOS target
|
|||
mac: demo |
|||
@echo "macOS build completed (demo mode)" |
|||
|
|||
# Help target
|
|||
help: |
|||
@echo "Available targets:" |
|||
@echo " all - Build all eBPF programs and loader" |
|||
@echo " mac - Build for macOS (demo mode)" |
|||
@echo " libbpf - Build libbpf dependency" |
|||
@echo " install - Install binaries to system" |
|||
@echo " clean - Clean build artifacts" |
|||
@echo " distclean - Clean everything including dependencies" |
|||
@echo " format - Format source code" |
|||
@echo " check - Verify eBPF programs" |
|||
@echo " help - Show this help" |
@ -0,0 +1,285 @@ |
|||
/* SPDX-License-Identifier: GPL-2.0 */ |
|||
/* Zapret eBPF - DPI Evasion and Packet Filtering
|
|||
* Copyright (c) 2024 Zapret Project |
|||
*/ |
|||
|
|||
#ifndef __ZAPRET_EBPF_H__ |
|||
#define __ZAPRET_EBPF_H__ |
|||
|
|||
#include <stdint.h> |
|||
#include <stdbool.h> |
|||
|
|||
#ifdef __KERNEL__ |
|||
#include <linux/bpf.h> |
|||
#include <linux/if_ether.h> |
|||
#include <linux/ip.h> |
|||
#include <linux/ipv6.h> |
|||
#include <linux/tcp.h> |
|||
#include <linux/udp.h> |
|||
#include <linux/in.h> |
|||
#include <bpf/bpf_helpers.h> |
|||
#include <bpf/bpf_endian.h> |
|||
#endif |
|||
|
|||
/* Maximum supported packet size */ |
|||
#define MAX_PACKET_SIZE 1500 |
|||
#define MAX_HOSTNAME_LEN 256 |
|||
#define MAX_JA3_LEN 512 |
|||
#define MAX_FRAGMENTS 16 |
|||
|
|||
/* Protocol definitions */ |
|||
#define IPPROTO_QUIC 17 /* QUIC over UDP */ |
|||
#define TLS_HANDSHAKE_TYPE 22 |
|||
#define TLS_CLIENT_HELLO 1 |
|||
#define QUIC_INITIAL_PACKET 0x00 |
|||
|
|||
/* TLS versions */ |
|||
#define TLS_VERSION_1_2 0x0303 |
|||
#define TLS_VERSION_1_3 0x0304 |
|||
|
|||
/* QUIC versions */ |
|||
#define QUIC_VERSION_1 0x00000001 |
|||
#define QUIC_VERSION_2 0x6b3343cf |
|||
|
|||
/* Action flags */ |
|||
#define ACTION_PASS 0 |
|||
#define ACTION_DROP 1 |
|||
#define ACTION_FRAGMENT 2 |
|||
#define ACTION_RANDOMIZE_TLS 4 |
|||
#define ACTION_ENCRYPT_SNI 8 |
|||
#define ACTION_MODIFY_JA3 16 |
|||
|
|||
/* Action flags for filter rules */ |
|||
#define ACTION_ALLOW 0x01 |
|||
#define ACTION_BLOCK 0x02 |
|||
#define ACTION_RANDOMIZE_TLS 0x04 |
|||
#define ACTION_FRAGMENT_PACKET 0x08 |
|||
#define ACTION_ENCRYPT_SNI 0x10 |
|||
|
|||
/* Maximum cipher suites */ |
|||
#define MAX_CIPHER_SUITES 32 |
|||
|
|||
/* Fragment strategies */ |
|||
#define FRAG_STRATEGY_TCP_SEG 1 |
|||
#define FRAG_STRATEGY_IP_FRAG 2 |
|||
#define FRAG_STRATEGY_TLS_REC 3 |
|||
|
|||
/* Performance monitoring */ |
|||
struct perf_stats { |
|||
uint64_t packets_processed; |
|||
uint64_t packets_filtered; |
|||
uint64_t packets_fragmented; |
|||
uint64_t packets_dropped; |
|||
uint64_t tls_fingerprints_randomized; |
|||
uint64_t sni_encrypted; |
|||
uint64_t bytes_processed; |
|||
uint64_t processing_time_ns; |
|||
uint64_t total_processing_time; |
|||
uint64_t max_processing_time; |
|||
uint64_t min_processing_time; |
|||
uint64_t last_update; |
|||
}; |
|||
|
|||
/* TLS fingerprint structure */ |
|||
struct tls_fingerprint { |
|||
uint16_t version; |
|||
uint16_t cipher_suites[32]; |
|||
uint8_t cipher_suites_len; |
|||
uint16_t extensions[16]; |
|||
uint8_t extensions_len; |
|||
uint16_t elliptic_curves[8]; |
|||
uint8_t elliptic_curves_len; |
|||
uint8_t signature_algorithms[16]; |
|||
uint8_t signature_algorithms_len; |
|||
char ja3_hash[33]; /* MD5 hash as hex string */ |
|||
}; |
|||
|
|||
/* Alternative TLS fingerprint structure for compatibility */ |
|||
struct tls_fingerprint_compat { |
|||
uint16_t version; |
|||
uint16_t cipher_suites[MAX_CIPHER_SUITES]; |
|||
uint8_t cipher_count; |
|||
uint8_t extensions[32]; |
|||
uint32_t ja3_hash; |
|||
}; |
|||
|
|||
/* QUIC connection info */ |
|||
struct quic_conn_info { |
|||
uint32_t version; |
|||
uint8_t dcid[20]; |
|||
uint8_t dcid_len; |
|||
uint8_t scid[20]; |
|||
uint8_t scid_len; |
|||
uint16_t initial_packet_len; |
|||
uint8_t has_sni; |
|||
char sni[MAX_HOSTNAME_LEN]; |
|||
}; |
|||
|
|||
/* Alternative QUIC connection information for compatibility */ |
|||
struct quic_conn_info_compat { |
|||
uint32_t version; |
|||
uint8_t connection_id[20]; |
|||
uint8_t packet_type; |
|||
uint32_t packet_number; |
|||
uint8_t is_initial; |
|||
}; |
|||
|
|||
/* Packet fragmentation context */ |
|||
struct fragment_ctx { |
|||
uint32_t strategy; |
|||
uint16_t fragment_size; |
|||
uint16_t fragments_count; |
|||
uint32_t sequence_base; |
|||
uint8_t randomize_order; |
|||
}; |
|||
|
|||
/* Alternative packet fragmentation context for compatibility */ |
|||
struct fragment_ctx_compat { |
|||
uint32_t fragment_size; |
|||
uint32_t max_fragments; |
|||
uint8_t fragmentation_method; |
|||
uint8_t enabled; |
|||
uint8_t needs_fragmentation; |
|||
uint32_t original_size; |
|||
uint8_t fragment_count; |
|||
uint32_t fragment_offsets[MAX_FRAGMENTS]; |
|||
uint32_t fragment_sizes[MAX_FRAGMENTS]; |
|||
}; |
|||
|
|||
/* SNI encryption context */ |
|||
struct sni_encrypt_ctx { |
|||
uint8_t enabled; |
|||
uint8_t ech_supported; |
|||
uint8_t esni_supported; |
|||
uint8_t key[32]; /* Encryption key */ |
|||
uint8_t iv[16]; /* Initialization vector */ |
|||
}; |
|||
|
|||
/* Connection tracking entry */ |
|||
struct conn_track { |
|||
uint32_t src_ip; |
|||
uint32_t dst_ip; |
|||
uint16_t src_port; |
|||
uint16_t dst_port; |
|||
uint8_t protocol; |
|||
uint8_t state; |
|||
uint64_t first_seen; |
|||
uint64_t last_seen; |
|||
uint32_t packets_count; |
|||
uint32_t bytes_count; |
|||
struct tls_fingerprint tls_fp; |
|||
struct quic_conn_info quic_info; |
|||
struct fragment_ctx frag_ctx; |
|||
struct sni_encrypt_ctx sni_ctx; |
|||
}; |
|||
|
|||
/* Filter rule structure */ |
|||
struct filter_rule { |
|||
uint32_t src_ip; |
|||
uint32_t src_mask; |
|||
uint32_t dst_ip; |
|||
uint32_t dst_mask; |
|||
uint16_t src_port_min; |
|||
uint16_t src_port_max; |
|||
uint16_t dst_port_min; |
|||
uint16_t dst_port_max; |
|||
uint8_t protocol; |
|||
uint32_t action_flags; |
|||
uint32_t priority; |
|||
char hostname_pattern[MAX_HOSTNAME_LEN]; |
|||
uint8_t enabled; |
|||
}; |
|||
|
|||
/* Configuration structure */ |
|||
struct zapret_config { |
|||
uint8_t enable_tls_randomization; |
|||
uint8_t enable_quic_filtering; |
|||
uint8_t enable_packet_fragmentation; |
|||
uint8_t enable_sni_encryption; |
|||
uint8_t enable_performance_monitoring; |
|||
uint32_t max_connections; |
|||
uint32_t connection_timeout; |
|||
uint32_t fragment_threshold; |
|||
struct filter_rule default_rule; |
|||
}; |
|||
|
|||
/* Map definitions */ |
|||
#ifdef __KERNEL__ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_HASH); |
|||
__uint(max_entries, 65536); |
|||
__type(key, uint64_t); /* 5-tuple hash */ |
|||
__type(value, struct conn_track); |
|||
} connection_map SEC(".maps"); |
|||
|
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1024); |
|||
__type(key, uint32_t); |
|||
__type(value, struct filter_rule); |
|||
} filter_rules SEC(".maps"); |
|||
|
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1); |
|||
__type(key, uint32_t); |
|||
__type(value, struct zapret_config); |
|||
} config_map SEC(".maps"); |
|||
|
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1); |
|||
__type(key, uint32_t); |
|||
__type(value, struct perf_stats); |
|||
} stats_map SEC(".maps"); |
|||
|
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_HASH); |
|||
__uint(max_entries, 1024); |
|||
__type(key, char[MAX_HOSTNAME_LEN]); |
|||
__type(value, uint32_t); /* action flags */ |
|||
} hostname_rules SEC(".maps"); |
|||
|
|||
/* TLS fingerprint randomization patterns */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 100); |
|||
__type(key, uint32_t); |
|||
__type(value, struct tls_fingerprint); |
|||
} tls_fingerprint_pool SEC(".maps"); |
|||
#endif |
|||
|
|||
/* Helper function declarations */ |
|||
#ifdef __KERNEL__ |
|||
static inline uint64_t compute_5tuple_hash(struct iphdr *ip, void *l4_hdr); |
|||
static inline int parse_tls_client_hello(void *data, void *data_end, struct tls_fingerprint *fp); |
|||
static inline int parse_quic_initial(void *data, void *data_end, struct quic_conn_info *quic); |
|||
static inline int extract_sni_from_tls(void *data, void *data_end, char *sni, int max_len); |
|||
static inline int randomize_tls_fingerprint(struct tls_fingerprint *fp); |
|||
static inline int fragment_packet(struct __sk_buff *skb, struct fragment_ctx *ctx); |
|||
static inline int encrypt_sni(char *sni, struct sni_encrypt_ctx *ctx); |
|||
static inline void update_performance_stats(struct perf_stats *stats, uint64_t start_time); |
|||
#endif |
|||
|
|||
/* Utility macros */ |
|||
#define likely(x) __builtin_expect(!!(x), 1) |
|||
#define unlikely(x) __builtin_expect(!!(x), 0) |
|||
|
|||
#define PACKET_HOST(skb) ((skb)->pkt_type == PACKET_HOST) |
|||
#define PACKET_OUTGOING(skb) ((skb)->pkt_type == PACKET_OUTGOING) |
|||
|
|||
/* Bounds checking macro */ |
|||
#define BOUNDS_CHECK(ptr, end, size) \ |
|||
({ \ |
|||
void *_ptr = (void *)(ptr); \ |
|||
void *_end = (void *)(end); \ |
|||
(_ptr + (size) <= _end) ? 1 : 0; \ |
|||
}) |
|||
|
|||
/* Network byte order conversion helpers */ |
|||
#define NET16(x) bpf_htons(x) |
|||
#define NET32(x) bpf_htonl(x) |
|||
#define HOST16(x) bpf_ntohs(x) |
|||
#define HOST32(x) bpf_ntohl(x) |
|||
|
|||
#endif /* __ZAPRET_EBPF_H__ */ |
@ -0,0 +1,467 @@ |
|||
/*
|
|||
* Zapret eBPF Manager |
|||
* High-level interface for eBPF program management and integration |
|||
*/ |
|||
|
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <unistd.h> |
|||
#include <errno.h> |
|||
#include <sys/socket.h> |
|||
#include <sys/types.h> |
|||
#include <netinet/in.h> |
|||
#include <arpa/inet.h> |
|||
#include <pthread.h> |
|||
#include <time.h> |
|||
#include <sys/stat.h> |
|||
#include "../include/zapret_ebpf.h" |
|||
|
|||
/* Connection tracking hash table */ |
|||
#define CONN_HASH_SIZE 65536 |
|||
static struct conn_track *connection_table[CONN_HASH_SIZE]; |
|||
static pthread_mutex_t conn_mutex = PTHREAD_MUTEX_INITIALIZER; |
|||
|
|||
/* Filter rules array */ |
|||
#define MAX_RULES 1024 |
|||
static struct filter_rule filter_rules[MAX_RULES]; |
|||
static int rule_count = 0; |
|||
static pthread_mutex_t rules_mutex = PTHREAD_MUTEX_INITIALIZER; |
|||
|
|||
/* TLS fingerprint pool */ |
|||
#define FINGERPRINT_POOL_SIZE 100 |
|||
static struct tls_fingerprint_compat fingerprint_pool[FINGERPRINT_POOL_SIZE]; |
|||
static int fingerprint_count = 0; |
|||
|
|||
/* Global configuration and statistics */ |
|||
extern struct zapret_config global_config; |
|||
extern struct perf_stats global_stats; |
|||
extern pthread_mutex_t stats_mutex; |
|||
|
|||
/* Hash function for connection tracking */ |
|||
static uint32_t hash_connection(uint32_t src_ip, uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, uint8_t protocol) { |
|||
uint32_t hash = src_ip; |
|||
hash = hash * 31 + dst_ip; |
|||
hash = hash * 31 + src_port; |
|||
hash = hash * 31 + dst_port; |
|||
hash = hash * 31 + protocol; |
|||
return hash % CONN_HASH_SIZE; |
|||
} |
|||
|
|||
/* Connection tracking functions */ |
|||
struct conn_track* find_connection(uint32_t src_ip, uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, uint8_t protocol) { |
|||
uint32_t hash = hash_connection(src_ip, dst_ip, src_port, dst_port, protocol); |
|||
|
|||
pthread_mutex_lock(&conn_mutex); |
|||
struct conn_track *conn = connection_table[hash]; |
|||
|
|||
while (conn) { |
|||
if (conn->src_ip == src_ip && conn->dst_ip == dst_ip && |
|||
conn->src_port == src_port && conn->dst_port == dst_port && |
|||
conn->protocol == protocol) { |
|||
pthread_mutex_unlock(&conn_mutex); |
|||
return conn; |
|||
} |
|||
conn = (struct conn_track*)conn->bytes_count; /* Using bytes_count as next pointer */ |
|||
} |
|||
|
|||
pthread_mutex_unlock(&conn_mutex); |
|||
return NULL; |
|||
} |
|||
|
|||
struct conn_track* create_connection(uint32_t src_ip, uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, uint8_t protocol) { |
|||
struct conn_track *conn = malloc(sizeof(struct conn_track)); |
|||
if (!conn) return NULL; |
|||
|
|||
memset(conn, 0, sizeof(*conn)); |
|||
conn->src_ip = src_ip; |
|||
conn->dst_ip = dst_ip; |
|||
conn->src_port = src_port; |
|||
conn->dst_port = dst_port; |
|||
conn->protocol = protocol; |
|||
conn->first_seen = time(NULL); |
|||
conn->last_seen = conn->first_seen; |
|||
conn->state = 1; /* Active */ |
|||
|
|||
/* Initialize fragmentation context */ |
|||
struct fragment_ctx_compat *frag_ctx = (struct fragment_ctx_compat*)&conn->frag_ctx; |
|||
frag_ctx->fragment_size = global_config.fragment_threshold; |
|||
frag_ctx->max_fragments = MAX_FRAGMENTS; |
|||
frag_ctx->enabled = global_config.enable_packet_fragmentation; |
|||
|
|||
uint32_t hash = hash_connection(src_ip, dst_ip, src_port, dst_port, protocol); |
|||
|
|||
pthread_mutex_lock(&conn_mutex); |
|||
/* Insert at head of hash bucket */ |
|||
conn->bytes_count = (uint32_t)(uintptr_t)connection_table[hash]; |
|||
connection_table[hash] = conn; |
|||
pthread_mutex_unlock(&conn_mutex); |
|||
|
|||
return conn; |
|||
} |
|||
|
|||
void cleanup_old_connections(void) { |
|||
time_t current_time = time(NULL); |
|||
|
|||
pthread_mutex_lock(&conn_mutex); |
|||
|
|||
for (int i = 0; i < CONN_HASH_SIZE; i++) { |
|||
struct conn_track **curr = &connection_table[i]; |
|||
|
|||
while (*curr) { |
|||
struct conn_track *conn = *curr; |
|||
|
|||
if (current_time - conn->last_seen > global_config.connection_timeout) { |
|||
/* Remove expired connection */ |
|||
*curr = (struct conn_track*)(uintptr_t)conn->bytes_count; |
|||
free(conn); |
|||
} else { |
|||
curr = (struct conn_track**)(uintptr_t)&conn->bytes_count; |
|||
} |
|||
} |
|||
} |
|||
|
|||
pthread_mutex_unlock(&conn_mutex); |
|||
} |
|||
|
|||
/* Filter rule management */ |
|||
int add_filter_rule(struct filter_rule *rule) { |
|||
if (!rule) return -1; |
|||
|
|||
pthread_mutex_lock(&rules_mutex); |
|||
|
|||
if (rule_count >= MAX_RULES) { |
|||
pthread_mutex_unlock(&rules_mutex); |
|||
return -1; |
|||
} |
|||
|
|||
memcpy(&filter_rules[rule_count], rule, sizeof(*rule)); |
|||
rule_count++; |
|||
|
|||
pthread_mutex_unlock(&rules_mutex); |
|||
return 0; |
|||
} |
|||
|
|||
struct filter_rule* match_filter_rule(uint32_t src_ip, uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, uint8_t protocol) { |
|||
pthread_mutex_lock(&rules_mutex); |
|||
|
|||
for (int i = 0; i < rule_count; i++) { |
|||
struct filter_rule *rule = &filter_rules[i]; |
|||
|
|||
if (!rule->enabled) continue; |
|||
|
|||
/* Check protocol */ |
|||
if (rule->protocol != 0 && rule->protocol != protocol) continue; |
|||
|
|||
/* Check source IP */ |
|||
if (rule->src_mask != 0) { |
|||
if ((src_ip & rule->src_mask) != (rule->src_ip & rule->src_mask)) continue; |
|||
} |
|||
|
|||
/* Check destination IP */ |
|||
if (rule->dst_mask != 0) { |
|||
if ((dst_ip & rule->dst_mask) != (rule->dst_ip & rule->dst_mask)) continue; |
|||
} |
|||
|
|||
/* Check source port range */ |
|||
if (rule->src_port_min != 0 || rule->src_port_max != 0) { |
|||
if (src_port < rule->src_port_min || src_port > rule->src_port_max) continue; |
|||
} |
|||
|
|||
/* Check destination port range */ |
|||
if (rule->dst_port_min != 0 || rule->dst_port_max != 0) { |
|||
if (dst_port < rule->dst_port_min || dst_port > rule->dst_port_max) continue; |
|||
} |
|||
|
|||
pthread_mutex_unlock(&rules_mutex); |
|||
return rule; |
|||
} |
|||
|
|||
pthread_mutex_unlock(&rules_mutex); |
|||
return NULL; |
|||
} |
|||
|
|||
/* TLS fingerprint management */ |
|||
void init_fingerprint_pool(void) { |
|||
/* Initialize with common browser fingerprints */ |
|||
struct { |
|||
uint16_t version; |
|||
uint16_t ciphers[8]; |
|||
int cipher_count; |
|||
} common_fingerprints[] = { |
|||
/* Chrome-like */ |
|||
{0x0303, {0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0x009e, 0x009f}, 8}, |
|||
/* Firefox-like */ |
|||
{0x0303, {0x1301, 0x1302, 0x1303, 0xc02b, 0xc02c, 0xc030, 0x009e, 0x006b}, 8}, |
|||
/* Safari-like */ |
|||
{0x0304, {0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0x009e, 0x009f}, 8}, |
|||
/* Edge-like */ |
|||
{0x0303, {0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc030, 0x009e, 0x006b}, 8} |
|||
}; |
|||
|
|||
fingerprint_count = sizeof(common_fingerprints) / sizeof(common_fingerprints[0]); |
|||
|
|||
for (int i = 0; i < fingerprint_count; i++) { |
|||
fingerprint_pool[i].version = common_fingerprints[i].version; |
|||
fingerprint_pool[i].cipher_count = common_fingerprints[i].cipher_count; |
|||
|
|||
for (int j = 0; j < common_fingerprints[i].cipher_count; j++) { |
|||
fingerprint_pool[i].cipher_suites[j] = common_fingerprints[i].ciphers[j]; |
|||
} |
|||
|
|||
/* Generate JA3 hash (simplified) */ |
|||
fingerprint_pool[i].ja3_hash = fingerprint_pool[i].version * 31 + fingerprint_pool[i].cipher_count; |
|||
} |
|||
} |
|||
|
|||
struct tls_fingerprint_compat* get_random_fingerprint(void) { |
|||
if (fingerprint_count == 0) return NULL; |
|||
|
|||
int index = rand() % fingerprint_count; |
|||
return &fingerprint_pool[index]; |
|||
} |
|||
|
|||
/* Packet processing interface */ |
|||
int process_packet_ebpf(uint8_t *data, size_t len, const char *interface) { |
|||
if (!data || len == 0) return -1; |
|||
|
|||
struct timespec start_time; |
|||
clock_gettime(CLOCK_MONOTONIC, &start_time); |
|||
|
|||
/* Parse basic packet headers */ |
|||
if (len < 34) return -1; /* Minimum Ethernet + IP header */ |
|||
|
|||
/* Skip Ethernet header */ |
|||
uint8_t *ip_hdr = data + 14; |
|||
uint8_t ip_version = (ip_hdr[0] >> 4) & 0x0F; |
|||
|
|||
if (ip_version != 4) return 0; /* Only IPv4 for now */ |
|||
|
|||
uint8_t ip_hdr_len = (ip_hdr[0] & 0x0F) * 4; |
|||
uint8_t protocol = ip_hdr[9]; |
|||
uint32_t src_ip = *(uint32_t*)(ip_hdr + 12); |
|||
uint32_t dst_ip = *(uint32_t*)(ip_hdr + 16); |
|||
|
|||
uint16_t src_port = 0, dst_port = 0; |
|||
|
|||
/* Extract port numbers */ |
|||
if (protocol == 6 || protocol == 17) { /* TCP or UDP */ |
|||
if (len >= 14 + ip_hdr_len + 4) { |
|||
uint8_t *l4_hdr = ip_hdr + ip_hdr_len; |
|||
src_port = (l4_hdr[0] << 8) | l4_hdr[1]; |
|||
dst_port = (l4_hdr[2] << 8) | l4_hdr[3]; |
|||
} |
|||
} |
|||
|
|||
/* Find or create connection */ |
|||
struct conn_track *conn = find_connection(src_ip, dst_ip, src_port, dst_port, protocol); |
|||
if (!conn) { |
|||
conn = create_connection(src_ip, dst_ip, src_port, dst_port, protocol); |
|||
} |
|||
|
|||
if (conn) { |
|||
conn->last_seen = time(NULL); |
|||
conn->packets_count++; |
|||
|
|||
/* Apply filter rules */ |
|||
struct filter_rule *rule = match_filter_rule(src_ip, dst_ip, src_port, dst_port, protocol); |
|||
if (rule) { |
|||
/* Apply TLS randomization */ |
|||
if ((rule->action_flags & ACTION_RANDOMIZE_TLS) && protocol == 6) { |
|||
/* Look for TLS handshake */ |
|||
if (len > 14 + ip_hdr_len + 20) { /* TCP header minimum */ |
|||
uint8_t *tcp_hdr = ip_hdr + ip_hdr_len; |
|||
uint8_t tcp_hdr_len = ((tcp_hdr[12] >> 4) & 0x0F) * 4; |
|||
uint8_t *payload = tcp_hdr + tcp_hdr_len; |
|||
|
|||
if (payload < data + len && payload[0] == 0x16) { /* TLS handshake */ |
|||
struct tls_fingerprint_compat *random_fp = get_random_fingerprint(); |
|||
if (random_fp) { |
|||
memcpy(&conn->tls_fp, random_fp, sizeof(struct tls_fingerprint)); |
|||
|
|||
pthread_mutex_lock(&stats_mutex); |
|||
global_stats.tls_fingerprints_randomized++; |
|||
pthread_mutex_unlock(&stats_mutex); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Apply packet fragmentation */ |
|||
if ((rule->action_flags & ACTION_FRAGMENT_PACKET) && global_config.enable_packet_fragmentation) { |
|||
struct fragment_ctx_compat *frag_ctx = (struct fragment_ctx_compat*)&conn->frag_ctx; |
|||
if (len > frag_ctx->fragment_size) { |
|||
frag_ctx->needs_fragmentation = 1; |
|||
frag_ctx->original_size = len; |
|||
|
|||
pthread_mutex_lock(&stats_mutex); |
|||
global_stats.packets_fragmented++; |
|||
pthread_mutex_unlock(&stats_mutex); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Update performance statistics */ |
|||
if (global_config.enable_performance_monitoring) { |
|||
struct timespec end_time; |
|||
clock_gettime(CLOCK_MONOTONIC, &end_time); |
|||
|
|||
uint64_t processing_time = (end_time.tv_sec - start_time.tv_sec) * 1000000000UL + |
|||
(end_time.tv_nsec - start_time.tv_nsec); |
|||
|
|||
pthread_mutex_lock(&stats_mutex); |
|||
global_stats.packets_processed++; |
|||
global_stats.bytes_processed += len; |
|||
global_stats.total_processing_time += processing_time; |
|||
|
|||
if (processing_time > global_stats.max_processing_time) |
|||
global_stats.max_processing_time = processing_time; |
|||
|
|||
if (processing_time < global_stats.min_processing_time || global_stats.min_processing_time == 0) |
|||
global_stats.min_processing_time = processing_time; |
|||
|
|||
pthread_mutex_unlock(&stats_mutex); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Configuration management */ |
|||
int load_filter_rules_from_file(const char *filename) { |
|||
FILE *fp = fopen(filename, "r"); |
|||
if (!fp) { |
|||
printf("Warning: Could not open rules file %s\n", filename); |
|||
return -1; |
|||
} |
|||
|
|||
char line[512]; |
|||
int loaded_rules = 0; |
|||
|
|||
while (fgets(line, sizeof(line), fp) && loaded_rules < MAX_RULES) { |
|||
/* Skip comments and empty lines */ |
|||
if (line[0] == '#' || line[0] == '\n') continue; |
|||
|
|||
struct filter_rule rule = {0}; |
|||
|
|||
/* Parse rule format: src_ip/mask:port-port dst_ip/mask:port-port protocol action */ |
|||
char src_spec[64], dst_spec[64], proto_str[16], action_str[64]; |
|||
|
|||
if (sscanf(line, "%63s %63s %15s %63s", src_spec, dst_spec, proto_str, action_str) == 4) { |
|||
/* Parse source specification */ |
|||
char *slash = strchr(src_spec, '/'); |
|||
char *colon = strchr(src_spec, ':'); |
|||
|
|||
if (slash) { |
|||
*slash = '\0'; |
|||
rule.src_ip = inet_addr(src_spec); |
|||
rule.src_mask = inet_addr(slash + 1); |
|||
} |
|||
|
|||
if (colon) { |
|||
char *dash = strchr(colon + 1, '-'); |
|||
rule.src_port_min = atoi(colon + 1); |
|||
rule.src_port_max = dash ? atoi(dash + 1) : rule.src_port_min; |
|||
} |
|||
|
|||
/* Parse destination specification */ |
|||
slash = strchr(dst_spec, '/'); |
|||
colon = strchr(dst_spec, ':'); |
|||
|
|||
if (slash) { |
|||
*slash = '\0'; |
|||
rule.dst_ip = inet_addr(dst_spec); |
|||
rule.dst_mask = inet_addr(slash + 1); |
|||
} |
|||
|
|||
if (colon) { |
|||
char *dash = strchr(colon + 1, '-'); |
|||
rule.dst_port_min = atoi(colon + 1); |
|||
rule.dst_port_max = dash ? atoi(dash + 1) : rule.dst_port_min; |
|||
} |
|||
|
|||
/* Parse protocol */ |
|||
if (strcmp(proto_str, "tcp") == 0) rule.protocol = 6; |
|||
else if (strcmp(proto_str, "udp") == 0) rule.protocol = 17; |
|||
else rule.protocol = atoi(proto_str); |
|||
|
|||
/* Parse actions */ |
|||
if (strstr(action_str, "randomize_tls")) rule.action_flags |= ACTION_RANDOMIZE_TLS; |
|||
if (strstr(action_str, "fragment")) rule.action_flags |= ACTION_FRAGMENT_PACKET; |
|||
if (strstr(action_str, "encrypt_sni")) rule.action_flags |= ACTION_ENCRYPT_SNI; |
|||
if (strstr(action_str, "allow")) rule.action_flags |= ACTION_ALLOW; |
|||
if (strstr(action_str, "block")) rule.action_flags |= ACTION_BLOCK; |
|||
|
|||
rule.enabled = 1; |
|||
rule.priority = 100; |
|||
|
|||
if (add_filter_rule(&rule) == 0) { |
|||
loaded_rules++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
fclose(fp); |
|||
printf("Loaded %d filter rules from %s\n", loaded_rules, filename); |
|||
return loaded_rules; |
|||
} |
|||
|
|||
/* Cleanup thread */ |
|||
void* cleanup_thread(void *arg) { |
|||
while (1) { |
|||
sleep(60); /* Cleanup every minute */ |
|||
cleanup_old_connections(); |
|||
} |
|||
return NULL; |
|||
} |
|||
|
|||
/* Initialize eBPF manager */ |
|||
int init_ebpf_manager(void) { |
|||
/* Initialize fingerprint pool */ |
|||
init_fingerprint_pool(); |
|||
|
|||
/* Load default filter rules */ |
|||
struct filter_rule default_rule = {0}; |
|||
default_rule.action_flags = ACTION_RANDOMIZE_TLS | ACTION_FRAGMENT_PACKET; |
|||
default_rule.priority = 100; |
|||
default_rule.enabled = 1; |
|||
add_filter_rule(&default_rule); |
|||
|
|||
/* Start cleanup thread */ |
|||
pthread_t cleanup_tid; |
|||
pthread_create(&cleanup_tid, NULL, cleanup_thread, NULL); |
|||
pthread_detach(cleanup_tid); |
|||
|
|||
printf("eBPF manager initialized\n"); |
|||
printf(" Fingerprint pool: %d entries\n", fingerprint_count); |
|||
printf(" Filter rules: %d loaded\n", rule_count); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Get connection statistics */ |
|||
int get_connection_stats(int *active_connections, int *total_connections) { |
|||
int active = 0, total = 0; |
|||
time_t current_time = time(NULL); |
|||
|
|||
pthread_mutex_lock(&conn_mutex); |
|||
|
|||
for (int i = 0; i < CONN_HASH_SIZE; i++) { |
|||
struct conn_track *conn = connection_table[i]; |
|||
|
|||
while (conn) { |
|||
total++; |
|||
if (current_time - conn->last_seen <= 60) { /* Active in last minute */ |
|||
active++; |
|||
} |
|||
conn = (struct conn_track*)(uintptr_t)conn->bytes_count; |
|||
} |
|||
} |
|||
|
|||
pthread_mutex_unlock(&conn_mutex); |
|||
|
|||
if (active_connections) *active_connections = active; |
|||
if (total_connections) *total_connections = total; |
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,458 @@ |
|||
/*
|
|||
* Zapret Performance Monitor |
|||
* Performance monitoring and optimization for eBPF packet filtering |
|||
*/ |
|||
|
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <unistd.h> |
|||
#include <time.h> |
|||
#include <sys/time.h> |
|||
#include <sys/resource.h> |
|||
#include <pthread.h> |
|||
#include <signal.h> |
|||
#include <math.h> |
|||
#include "../include/zapret_ebpf.h" |
|||
|
|||
/* Performance monitoring configuration */ |
|||
#define PERF_SAMPLE_INTERVAL 1000 /* 1 second in milliseconds */ |
|||
#define PERF_HISTORY_SIZE 3600 /* 1 hour of samples */ |
|||
#define PERF_ALERT_THRESHOLD_CPU 80.0 /* CPU usage alert threshold */ |
|||
#define PERF_ALERT_THRESHOLD_MEMORY 90.0 /* Memory usage alert threshold */ |
|||
#define PERF_ALERT_THRESHOLD_LATENCY 10000000 /* 10ms latency threshold */ |
|||
|
|||
/* Performance metrics history */ |
|||
struct perf_sample { |
|||
time_t timestamp; |
|||
double cpu_usage; |
|||
double memory_usage; |
|||
uint64_t packets_per_second; |
|||
uint64_t bytes_per_second; |
|||
uint64_t avg_latency_ns; |
|||
uint64_t max_latency_ns; |
|||
uint64_t connections_active; |
|||
uint64_t tls_randomizations; |
|||
uint64_t packet_fragmentations; |
|||
uint64_t sni_encryptions; |
|||
}; |
|||
|
|||
static struct perf_sample perf_history[PERF_HISTORY_SIZE]; |
|||
static int perf_history_index = 0; |
|||
static int perf_history_count = 0; |
|||
static pthread_mutex_t perf_mutex = PTHREAD_MUTEX_INITIALIZER; |
|||
|
|||
/* Performance alerts */ |
|||
struct perf_alert { |
|||
time_t timestamp; |
|||
char message[256]; |
|||
int severity; /* 1=info, 2=warning, 3=critical */ |
|||
}; |
|||
|
|||
#define MAX_ALERTS 100 |
|||
static struct perf_alert alert_history[MAX_ALERTS]; |
|||
static int alert_count = 0; |
|||
static pthread_mutex_t alert_mutex = PTHREAD_MUTEX_INITIALIZER; |
|||
|
|||
/* External references */ |
|||
extern struct perf_stats global_stats; |
|||
extern pthread_mutex_t stats_mutex; |
|||
extern int get_connection_stats(int *active_connections, int *total_connections); |
|||
|
|||
/* Forward declarations */ |
|||
void check_performance_alerts(struct perf_sample *sample); |
|||
|
|||
/* System resource monitoring */ |
|||
static double get_cpu_usage(void) { |
|||
static struct rusage prev_usage = {0}; |
|||
static struct timeval prev_time = {0}; |
|||
|
|||
struct rusage current_usage; |
|||
struct timeval current_time; |
|||
|
|||
getrusage(RUSAGE_SELF, ¤t_usage); |
|||
gettimeofday(¤t_time, NULL); |
|||
|
|||
if (prev_time.tv_sec == 0) { |
|||
prev_usage = current_usage; |
|||
prev_time = current_time; |
|||
return 0.0; |
|||
} |
|||
|
|||
/* Calculate time differences */ |
|||
double time_diff = (current_time.tv_sec - prev_time.tv_sec) + |
|||
(current_time.tv_usec - prev_time.tv_usec) / 1000000.0; |
|||
|
|||
double user_time_diff = (current_usage.ru_utime.tv_sec - prev_usage.ru_utime.tv_sec) + |
|||
(current_usage.ru_utime.tv_usec - prev_usage.ru_utime.tv_usec) / 1000000.0; |
|||
|
|||
double sys_time_diff = (current_usage.ru_stime.tv_sec - prev_usage.ru_stime.tv_sec) + |
|||
(current_usage.ru_stime.tv_usec - prev_usage.ru_stime.tv_usec) / 1000000.0; |
|||
|
|||
double cpu_usage = ((user_time_diff + sys_time_diff) / time_diff) * 100.0; |
|||
|
|||
prev_usage = current_usage; |
|||
prev_time = current_time; |
|||
|
|||
return cpu_usage; |
|||
} |
|||
|
|||
static double get_memory_usage(void) { |
|||
FILE *fp = fopen("/proc/self/status", "r"); |
|||
if (!fp) return 0.0; |
|||
|
|||
char line[256]; |
|||
long vm_rss = 0; |
|||
|
|||
while (fgets(line, sizeof(line), fp)) { |
|||
if (strncmp(line, "VmRSS:", 6) == 0) { |
|||
sscanf(line, "VmRSS: %ld kB", &vm_rss); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
fclose(fp); |
|||
|
|||
/* Get total system memory */ |
|||
fp = fopen("/proc/meminfo", "r"); |
|||
if (!fp) return 0.0; |
|||
|
|||
long mem_total = 0; |
|||
while (fgets(line, sizeof(line), fp)) { |
|||
if (strncmp(line, "MemTotal:", 9) == 0) { |
|||
sscanf(line, "MemTotal: %ld kB", &mem_total); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
fclose(fp); |
|||
|
|||
if (mem_total == 0) return 0.0; |
|||
|
|||
return ((double)vm_rss / mem_total) * 100.0; |
|||
} |
|||
|
|||
/* Performance alert system */ |
|||
void add_performance_alert(const char *message, int severity) { |
|||
pthread_mutex_lock(&alert_mutex); |
|||
|
|||
if (alert_count < MAX_ALERTS) { |
|||
alert_history[alert_count].timestamp = time(NULL); |
|||
strncpy(alert_history[alert_count].message, message, sizeof(alert_history[alert_count].message) - 1); |
|||
alert_history[alert_count].message[sizeof(alert_history[alert_count].message) - 1] = '\0'; |
|||
alert_history[alert_count].severity = severity; |
|||
alert_count++; |
|||
} |
|||
|
|||
pthread_mutex_unlock(&alert_mutex); |
|||
|
|||
/* Print alert to console */ |
|||
const char *severity_str[] = {"", "INFO", "WARNING", "CRITICAL"}; |
|||
printf("[%s] %s\n", severity_str[severity], message); |
|||
} |
|||
|
|||
/* Performance sample collection */ |
|||
void collect_performance_sample(void) { |
|||
struct perf_sample sample = {0}; |
|||
|
|||
sample.timestamp = time(NULL); |
|||
sample.cpu_usage = get_cpu_usage(); |
|||
sample.memory_usage = get_memory_usage(); |
|||
|
|||
/* Get packet processing statistics */ |
|||
pthread_mutex_lock(&stats_mutex); |
|||
|
|||
static uint64_t prev_packets = 0; |
|||
static uint64_t prev_bytes = 0; |
|||
static time_t prev_time = 0; |
|||
|
|||
uint64_t current_packets = global_stats.packets_processed; |
|||
uint64_t current_bytes = global_stats.bytes_processed; |
|||
time_t current_time = sample.timestamp; |
|||
|
|||
if (prev_time > 0) { |
|||
time_t time_diff = current_time - prev_time; |
|||
if (time_diff > 0) { |
|||
sample.packets_per_second = (current_packets - prev_packets) / time_diff; |
|||
sample.bytes_per_second = (current_bytes - prev_bytes) / time_diff; |
|||
} |
|||
} |
|||
|
|||
/* Calculate average latency */ |
|||
if (global_stats.packets_processed > 0) { |
|||
sample.avg_latency_ns = global_stats.total_processing_time / global_stats.packets_processed; |
|||
} |
|||
|
|||
sample.max_latency_ns = global_stats.max_processing_time; |
|||
sample.tls_randomizations = global_stats.tls_fingerprints_randomized; |
|||
sample.packet_fragmentations = global_stats.packets_fragmented; |
|||
sample.sni_encryptions = global_stats.sni_encrypted; |
|||
|
|||
prev_packets = current_packets; |
|||
prev_bytes = current_bytes; |
|||
prev_time = current_time; |
|||
|
|||
pthread_mutex_unlock(&stats_mutex); |
|||
|
|||
/* Get connection statistics */ |
|||
int active_connections = 0; |
|||
get_connection_stats(&active_connections, NULL); |
|||
sample.connections_active = active_connections; |
|||
|
|||
/* Store sample in history */ |
|||
pthread_mutex_lock(&perf_mutex); |
|||
|
|||
perf_history[perf_history_index] = sample; |
|||
perf_history_index = (perf_history_index + 1) % PERF_HISTORY_SIZE; |
|||
|
|||
if (perf_history_count < PERF_HISTORY_SIZE) { |
|||
perf_history_count++; |
|||
} |
|||
|
|||
pthread_mutex_unlock(&perf_mutex); |
|||
|
|||
/* Check for performance alerts */ |
|||
check_performance_alerts(&sample); |
|||
} |
|||
|
|||
/* Performance alert checking */ |
|||
void check_performance_alerts(struct perf_sample *sample) { |
|||
char alert_msg[256]; |
|||
|
|||
/* CPU usage alert */ |
|||
if (sample->cpu_usage > PERF_ALERT_THRESHOLD_CPU) { |
|||
snprintf(alert_msg, sizeof(alert_msg), |
|||
"High CPU usage detected: %.1f%%", sample->cpu_usage); |
|||
add_performance_alert(alert_msg, 2); |
|||
} |
|||
|
|||
/* Memory usage alert */ |
|||
if (sample->memory_usage > PERF_ALERT_THRESHOLD_MEMORY) { |
|||
snprintf(alert_msg, sizeof(alert_msg), |
|||
"High memory usage detected: %.1f%%", sample->memory_usage); |
|||
add_performance_alert(alert_msg, 2); |
|||
} |
|||
|
|||
/* Latency alert */ |
|||
if (sample->avg_latency_ns > PERF_ALERT_THRESHOLD_LATENCY) { |
|||
snprintf(alert_msg, sizeof(alert_msg), |
|||
"High processing latency detected: %.2fms", |
|||
sample->avg_latency_ns / 1000000.0); |
|||
add_performance_alert(alert_msg, 2); |
|||
} |
|||
|
|||
/* Throughput monitoring */ |
|||
if (sample->packets_per_second > 100000) { |
|||
snprintf(alert_msg, sizeof(alert_msg), |
|||
"High packet rate: %lu pps", sample->packets_per_second); |
|||
add_performance_alert(alert_msg, 1); |
|||
} |
|||
} |
|||
|
|||
/* Performance statistics calculation */ |
|||
void calculate_performance_stats(struct perf_sample *min, struct perf_sample *max, struct perf_sample *avg) { |
|||
if (perf_history_count == 0) return; |
|||
|
|||
pthread_mutex_lock(&perf_mutex); |
|||
|
|||
memset(min, 0, sizeof(*min)); |
|||
memset(max, 0, sizeof(*max)); |
|||
memset(avg, 0, sizeof(*avg)); |
|||
|
|||
/* Initialize min values */ |
|||
*min = perf_history[0]; |
|||
|
|||
double sum_cpu = 0, sum_memory = 0; |
|||
uint64_t sum_pps = 0, sum_bps = 0, sum_latency = 0, sum_connections = 0; |
|||
|
|||
for (int i = 0; i < perf_history_count; i++) { |
|||
struct perf_sample *sample = &perf_history[i]; |
|||
|
|||
/* Update minimums */ |
|||
if (sample->cpu_usage < min->cpu_usage) min->cpu_usage = sample->cpu_usage; |
|||
if (sample->memory_usage < min->memory_usage) min->memory_usage = sample->memory_usage; |
|||
if (sample->avg_latency_ns < min->avg_latency_ns) min->avg_latency_ns = sample->avg_latency_ns; |
|||
|
|||
/* Update maximums */ |
|||
if (sample->cpu_usage > max->cpu_usage) max->cpu_usage = sample->cpu_usage; |
|||
if (sample->memory_usage > max->memory_usage) max->memory_usage = sample->memory_usage; |
|||
if (sample->packets_per_second > max->packets_per_second) max->packets_per_second = sample->packets_per_second; |
|||
if (sample->bytes_per_second > max->bytes_per_second) max->bytes_per_second = sample->bytes_per_second; |
|||
if (sample->avg_latency_ns > max->avg_latency_ns) max->avg_latency_ns = sample->avg_latency_ns; |
|||
if (sample->max_latency_ns > max->max_latency_ns) max->max_latency_ns = sample->max_latency_ns; |
|||
if (sample->connections_active > max->connections_active) max->connections_active = sample->connections_active; |
|||
|
|||
/* Accumulate for averages */ |
|||
sum_cpu += sample->cpu_usage; |
|||
sum_memory += sample->memory_usage; |
|||
sum_pps += sample->packets_per_second; |
|||
sum_bps += sample->bytes_per_second; |
|||
sum_latency += sample->avg_latency_ns; |
|||
sum_connections += sample->connections_active; |
|||
} |
|||
|
|||
/* Calculate averages */ |
|||
avg->cpu_usage = sum_cpu / perf_history_count; |
|||
avg->memory_usage = sum_memory / perf_history_count; |
|||
avg->packets_per_second = sum_pps / perf_history_count; |
|||
avg->bytes_per_second = sum_bps / perf_history_count; |
|||
avg->avg_latency_ns = sum_latency / perf_history_count; |
|||
avg->connections_active = sum_connections / perf_history_count; |
|||
|
|||
pthread_mutex_unlock(&perf_mutex); |
|||
} |
|||
|
|||
/* Performance report generation */ |
|||
void print_performance_report(void) { |
|||
struct perf_sample min, max, avg; |
|||
calculate_performance_stats(&min, &max, &avg); |
|||
|
|||
printf("\n=== Zapret Performance Report ===\n"); |
|||
printf("Monitoring period: %d samples (%d minutes)\n", |
|||
perf_history_count, perf_history_count / 60); |
|||
|
|||
printf("\nCPU Usage:\n"); |
|||
printf(" Current: %.1f%%\n", perf_history_count > 0 ? perf_history[(perf_history_index - 1 + PERF_HISTORY_SIZE) % PERF_HISTORY_SIZE].cpu_usage : 0.0); |
|||
printf(" Average: %.1f%%\n", avg.cpu_usage); |
|||
printf(" Min/Max: %.1f%% / %.1f%%\n", min.cpu_usage, max.cpu_usage); |
|||
|
|||
printf("\nMemory Usage:\n"); |
|||
printf(" Current: %.1f%%\n", perf_history_count > 0 ? perf_history[(perf_history_index - 1 + PERF_HISTORY_SIZE) % PERF_HISTORY_SIZE].memory_usage : 0.0); |
|||
printf(" Average: %.1f%%\n", avg.memory_usage); |
|||
printf(" Min/Max: %.1f%% / %.1f%%\n", min.memory_usage, max.memory_usage); |
|||
|
|||
printf("\nThroughput:\n"); |
|||
printf(" Average PPS: %lu packets/sec\n", avg.packets_per_second); |
|||
printf(" Peak PPS: %lu packets/sec\n", max.packets_per_second); |
|||
printf(" Average BPS: %.2f MB/sec\n", avg.bytes_per_second / (1024.0 * 1024.0)); |
|||
printf(" Peak BPS: %.2f MB/sec\n", max.bytes_per_second / (1024.0 * 1024.0)); |
|||
|
|||
printf("\nLatency:\n"); |
|||
printf(" Average: %.2f ms\n", avg.avg_latency_ns / 1000000.0); |
|||
printf(" Peak: %.2f ms\n", max.max_latency_ns / 1000000.0); |
|||
|
|||
printf("\nConnections:\n"); |
|||
printf(" Average active: %lu\n", avg.connections_active); |
|||
printf(" Peak active: %lu\n", max.connections_active); |
|||
|
|||
printf("\nDPI Evasion Activity:\n"); |
|||
printf(" TLS fingerprints randomized: %lu\n", max.tls_randomizations); |
|||
printf(" Packets fragmented: %lu\n", max.packet_fragmentations); |
|||
printf(" SNI encryptions: %lu\n", max.sni_encryptions); |
|||
|
|||
/* Performance optimization recommendations */ |
|||
printf("\n=== Performance Recommendations ===\n"); |
|||
|
|||
if (avg.cpu_usage > 70.0) { |
|||
printf("⚠️ High CPU usage detected. Consider:\n"); |
|||
printf(" - Reducing TLS randomization frequency\n"); |
|||
printf(" - Optimizing filter rules\n"); |
|||
printf(" - Using hardware acceleration if available\n"); |
|||
} |
|||
|
|||
if (avg.memory_usage > 80.0) { |
|||
printf("⚠️ High memory usage detected. Consider:\n"); |
|||
printf(" - Reducing connection timeout\n"); |
|||
printf(" - Implementing connection pooling\n"); |
|||
printf(" - Cleaning up old connections more frequently\n"); |
|||
} |
|||
|
|||
if (avg.avg_latency_ns > 5000000) { /* 5ms */ |
|||
printf("⚠️ High processing latency detected. Consider:\n"); |
|||
printf(" - Optimizing packet parsing logic\n"); |
|||
printf(" - Using eBPF for kernel-level filtering\n"); |
|||
printf(" - Reducing complexity of DPI evasion techniques\n"); |
|||
} |
|||
|
|||
if (max.packets_per_second > 50000) { |
|||
printf("✅ High throughput achieved. System performing well.\n"); |
|||
} |
|||
|
|||
printf("\n"); |
|||
} |
|||
|
|||
/* Performance monitoring thread */ |
|||
void* performance_monitor_thread(void *arg) { |
|||
printf("Performance monitor started\n"); |
|||
|
|||
while (1) { |
|||
collect_performance_sample(); |
|||
usleep(PERF_SAMPLE_INTERVAL * 1000); /* Convert to microseconds */ |
|||
} |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
/* Export performance data to CSV */ |
|||
int export_performance_data(const char *filename) { |
|||
FILE *fp = fopen(filename, "w"); |
|||
if (!fp) { |
|||
printf("Error: Could not create performance export file %s\n", filename); |
|||
return -1; |
|||
} |
|||
|
|||
/* Write CSV header */ |
|||
fprintf(fp, "timestamp,cpu_usage,memory_usage,packets_per_second,bytes_per_second," |
|||
"avg_latency_ns,max_latency_ns,connections_active,tls_randomizations," |
|||
"packet_fragmentations,sni_encryptions\n"); |
|||
|
|||
pthread_mutex_lock(&perf_mutex); |
|||
|
|||
for (int i = 0; i < perf_history_count; i++) { |
|||
struct perf_sample *sample = &perf_history[i]; |
|||
|
|||
fprintf(fp, "%ld,%.2f,%.2f,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu\n", |
|||
sample->timestamp, |
|||
sample->cpu_usage, |
|||
sample->memory_usage, |
|||
sample->packets_per_second, |
|||
sample->bytes_per_second, |
|||
sample->avg_latency_ns, |
|||
sample->max_latency_ns, |
|||
sample->connections_active, |
|||
sample->tls_randomizations, |
|||
sample->packet_fragmentations, |
|||
sample->sni_encryptions); |
|||
} |
|||
|
|||
pthread_mutex_unlock(&perf_mutex); |
|||
|
|||
fclose(fp); |
|||
printf("Performance data exported to %s\n", filename); |
|||
return 0; |
|||
} |
|||
|
|||
/* Initialize performance monitoring */ |
|||
int init_performance_monitor(void) { |
|||
/* Reset performance history */ |
|||
memset(perf_history, 0, sizeof(perf_history)); |
|||
perf_history_index = 0; |
|||
perf_history_count = 0; |
|||
|
|||
/* Reset alert history */ |
|||
memset(alert_history, 0, sizeof(alert_history)); |
|||
alert_count = 0; |
|||
|
|||
printf("Performance monitor initialized\n"); |
|||
printf(" Sample interval: %d ms\n", PERF_SAMPLE_INTERVAL); |
|||
printf(" History size: %d samples\n", PERF_HISTORY_SIZE); |
|||
printf(" Alert thresholds: CPU %.1f%%, Memory %.1f%%, Latency %.1fms\n", |
|||
PERF_ALERT_THRESHOLD_CPU, PERF_ALERT_THRESHOLD_MEMORY, |
|||
PERF_ALERT_THRESHOLD_LATENCY / 1000000.0); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Get current performance metrics */ |
|||
int get_current_performance_metrics(struct perf_sample *current) { |
|||
if (!current || perf_history_count == 0) return -1; |
|||
|
|||
pthread_mutex_lock(&perf_mutex); |
|||
|
|||
int latest_index = (perf_history_index - 1 + PERF_HISTORY_SIZE) % PERF_HISTORY_SIZE; |
|||
*current = perf_history[latest_index]; |
|||
|
|||
pthread_mutex_unlock(&perf_mutex); |
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,378 @@ |
|||
/*
|
|||
* Zapret eBPF Loader and Manager |
|||
* User-space program to load and manage eBPF programs |
|||
*/ |
|||
|
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <unistd.h> |
|||
#include <errno.h> |
|||
#include <signal.h> |
|||
#include <sys/resource.h> |
|||
|
|||
#ifdef DEMO_MODE |
|||
/* Demo mode - no eBPF dependencies */ |
|||
#define BPF_PROG_TYPE_XDP 0 |
|||
#define BPF_PROG_TYPE_SCHED_CLS 1 |
|||
typedef struct { int fd; } bpf_object; |
|||
typedef struct { int fd; } bpf_program; |
|||
typedef struct { int fd; } bpf_map; |
|||
/* Stub functions for demo mode */ |
|||
static inline int bpf_object__load(bpf_object *obj) { return 0; } |
|||
static inline void bpf_object__close(bpf_object *obj) { } |
|||
static inline bpf_program *bpf_object__find_program_by_name(bpf_object *obj, const char *name) { return NULL; } |
|||
static inline int bpf_program__fd(bpf_program *prog) { return -1; } |
|||
static inline bpf_map *bpf_object__find_map_by_name(bpf_object *obj, const char *name) { return NULL; } |
|||
static inline int bpf_map__fd(bpf_map *map) { return -1; } |
|||
#elif defined(__linux__) |
|||
#include <bpf/libbpf.h> |
|||
#include <bpf/bpf.h> |
|||
#else |
|||
/* Non-Linux platforms - use demo mode */ |
|||
#define DEMO_MODE |
|||
#define BPF_PROG_TYPE_XDP 0 |
|||
#define BPF_PROG_TYPE_SCHED_CLS 1 |
|||
typedef struct { int fd; } bpf_object; |
|||
typedef struct { int fd; } bpf_program; |
|||
typedef struct { int fd; } bpf_map; |
|||
static inline int bpf_object__load(bpf_object *obj) { return 0; } |
|||
static inline void bpf_object__close(bpf_object *obj) { } |
|||
static inline bpf_program *bpf_object__find_program_by_name(bpf_object *obj, const char *name) { return NULL; } |
|||
static inline int bpf_program__fd(bpf_program *prog) { return -1; } |
|||
static inline bpf_map *bpf_object__find_map_by_name(bpf_object *obj, const char *name) { return NULL; } |
|||
static inline int bpf_map__fd(bpf_map *map) { return -1; } |
|||
#endif |
|||
|
|||
#include <sys/socket.h> |
|||
#include <sys/types.h> |
|||
#include <netinet/in.h> |
|||
#include <arpa/inet.h> |
|||
#include <pthread.h> |
|||
#include <time.h> |
|||
#include "../include/zapret_ebpf.h" |
|||
|
|||
/* Global state */ |
|||
static struct zapret_config global_config = {0}; |
|||
static struct perf_stats global_stats = {0}; |
|||
static pthread_mutex_t stats_mutex = PTHREAD_MUTEX_INITIALIZER; |
|||
static volatile int running = 1; |
|||
|
|||
/* Signal handler for graceful shutdown */ |
|||
void signal_handler(int sig) { |
|||
printf("\nReceived signal %d, shutting down...\n", sig); |
|||
running = 0; |
|||
} |
|||
|
|||
/* Initialize default configuration */ |
|||
void init_default_config(struct zapret_config *config) { |
|||
if (!config) return; |
|||
|
|||
memset(config, 0, sizeof(*config)); |
|||
config->enable_tls_randomization = 1; |
|||
config->enable_quic_filtering = 1; |
|||
config->enable_packet_fragmentation = 1; |
|||
config->enable_sni_encryption = 0; /* Disabled by default */ |
|||
config->enable_performance_monitoring = 1; |
|||
config->max_connections = 65536; |
|||
config->connection_timeout = 300; /* 5 minutes */ |
|||
config->fragment_threshold = 1400; /* Standard MTU minus headers */ |
|||
|
|||
/* Initialize default filter rule */ |
|||
config->default_rule.action_flags = ACTION_RANDOMIZE_TLS | ACTION_FRAGMENT_PACKET; |
|||
config->default_rule.priority = 100; |
|||
config->default_rule.enabled = 1; |
|||
} |
|||
|
|||
/* TLS fingerprint randomization */ |
|||
int randomize_tls_fingerprint(struct tls_fingerprint_compat *fp) { |
|||
if (!fp) return -1; |
|||
|
|||
/* Randomize TLS version */ |
|||
uint16_t tls_versions[] = {0x0303, 0x0304}; /* TLS 1.2, 1.3 */ |
|||
fp->version = tls_versions[rand() % 2]; |
|||
|
|||
/* Randomize cipher suites */ |
|||
uint16_t common_ciphers[] = { |
|||
0x1301, 0x1302, 0x1303, /* TLS 1.3 ciphers */ |
|||
0xc02b, 0xc02f, 0xc02c, /* ECDHE ciphers */ |
|||
0x009e, 0x009f, 0x006b /* AES-GCM ciphers */ |
|||
}; |
|||
|
|||
int cipher_count = 3 + (rand() % 6); /* 3-8 ciphers */ |
|||
if (cipher_count > MAX_CIPHER_SUITES) |
|||
cipher_count = MAX_CIPHER_SUITES; |
|||
|
|||
fp->cipher_count = cipher_count; |
|||
for (int i = 0; i < cipher_count; i++) { |
|||
fp->cipher_suites[i] = common_ciphers[rand() % (sizeof(common_ciphers)/sizeof(common_ciphers[0]))]; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* QUIC connection analysis */ |
|||
int analyze_quic_packet(const uint8_t *data, size_t len, struct quic_conn_info_compat *quic) { |
|||
if (!data || len < 1 || !quic) return -1; |
|||
|
|||
/* Check for QUIC long header */ |
|||
if ((data[0] & 0x80) == 0) { |
|||
quic->is_initial = 0; |
|||
return 0; |
|||
} |
|||
|
|||
/* Parse QUIC initial packet */ |
|||
if (len < 5) return -1; |
|||
|
|||
quic->version = (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4]; |
|||
quic->is_initial = ((data[0] & 0x30) == 0x00); |
|||
|
|||
/* Extract connection ID if present */ |
|||
if (len > 5) { |
|||
uint8_t dcid_len = data[5]; |
|||
if (dcid_len > 0 && dcid_len <= 20 && len > 5 + dcid_len) { |
|||
memcpy(quic->connection_id, &data[6], dcid_len < 20 ? dcid_len : 20); |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Packet fragmentation logic */ |
|||
int fragment_packet_data(uint8_t *data, size_t len, struct fragment_ctx_compat *ctx) { |
|||
if (!data || !ctx || !ctx->enabled) return 0; |
|||
|
|||
if (len > ctx->fragment_size) { |
|||
ctx->needs_fragmentation = 1; |
|||
ctx->original_size = len; |
|||
|
|||
/* Calculate optimal fragment positions */ |
|||
ctx->fragment_count = (len + ctx->fragment_size - 1) / ctx->fragment_size; |
|||
if (ctx->fragment_count > MAX_FRAGMENTS) |
|||
ctx->fragment_count = MAX_FRAGMENTS; |
|||
|
|||
for (int i = 0; i < ctx->fragment_count; i++) { |
|||
ctx->fragment_offsets[i] = i * ctx->fragment_size; |
|||
ctx->fragment_sizes[i] = (i == ctx->fragment_count - 1) ? |
|||
(len - ctx->fragment_offsets[i]) : ctx->fragment_size; |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* SNI encryption (placeholder for ECH/ESNI) */ |
|||
int encrypt_sni_data(char *sni, size_t sni_len, struct sni_encrypt_ctx *ctx) { |
|||
if (!sni || !ctx || !ctx->enabled) return 0; |
|||
|
|||
/* Simple XOR encryption for demonstration */ |
|||
for (size_t i = 0; i < sni_len && i < 32; i++) { |
|||
sni[i] ^= ctx->key[i % 32]; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Performance monitoring */ |
|||
void update_stats(uint64_t processing_time, size_t packet_size) { |
|||
pthread_mutex_lock(&stats_mutex); |
|||
|
|||
global_stats.packets_processed++; |
|||
global_stats.bytes_processed += packet_size; |
|||
global_stats.total_processing_time += processing_time; |
|||
|
|||
if (processing_time > global_stats.max_processing_time) |
|||
global_stats.max_processing_time = processing_time; |
|||
|
|||
if (processing_time < global_stats.min_processing_time || global_stats.min_processing_time == 0) |
|||
global_stats.min_processing_time = processing_time; |
|||
|
|||
pthread_mutex_unlock(&stats_mutex); |
|||
} |
|||
|
|||
/* Print performance statistics */ |
|||
void print_stats(void) { |
|||
pthread_mutex_lock(&stats_mutex); |
|||
|
|||
printf("\n=== Zapret Performance Statistics ===\n"); |
|||
printf("Packets processed: %lu\n", global_stats.packets_processed); |
|||
printf("Bytes processed: %lu\n", global_stats.bytes_processed); |
|||
printf("Total processing time: %lu ns\n", global_stats.total_processing_time); |
|||
|
|||
if (global_stats.packets_processed > 0) { |
|||
uint64_t avg_time = global_stats.total_processing_time / global_stats.packets_processed; |
|||
printf("Average processing time: %lu ns\n", avg_time); |
|||
printf("Max processing time: %lu ns\n", global_stats.max_processing_time); |
|||
printf("Min processing time: %lu ns\n", global_stats.min_processing_time); |
|||
|
|||
double pps = (double)global_stats.packets_processed * 1000000000.0 / global_stats.total_processing_time; |
|||
printf("Estimated packets per second: %.2f\n", pps); |
|||
} |
|||
|
|||
pthread_mutex_unlock(&stats_mutex); |
|||
} |
|||
|
|||
/* Configuration management */ |
|||
int load_config_file(const char *filename, struct zapret_config *config) { |
|||
FILE *fp = fopen(filename, "r"); |
|||
if (!fp) { |
|||
printf("Warning: Could not open config file %s, using defaults\n", filename); |
|||
return -1; |
|||
} |
|||
|
|||
char line[256]; |
|||
while (fgets(line, sizeof(line), fp)) { |
|||
/* Skip comments and empty lines */ |
|||
if (line[0] == '#' || line[0] == '\n') continue; |
|||
|
|||
/* Parse configuration options */ |
|||
if (strncmp(line, "enable_tls_randomization=", 25) == 0) { |
|||
config->enable_tls_randomization = (line[25] == '1'); |
|||
} else if (strncmp(line, "enable_quic_filtering=", 22) == 0) { |
|||
config->enable_quic_filtering = (line[22] == '1'); |
|||
} else if (strncmp(line, "enable_packet_fragmentation=", 28) == 0) { |
|||
config->enable_packet_fragmentation = (line[28] == '1'); |
|||
} else if (strncmp(line, "enable_sni_encryption=", 22) == 0) { |
|||
config->enable_sni_encryption = (line[22] == '1'); |
|||
} else if (strncmp(line, "max_connections=", 16) == 0) { |
|||
config->max_connections = atoi(&line[16]); |
|||
} else if (strncmp(line, "fragment_threshold=", 19) == 0) { |
|||
config->fragment_threshold = atoi(&line[19]); |
|||
} |
|||
} |
|||
|
|||
fclose(fp); |
|||
return 0; |
|||
} |
|||
|
|||
/* Main packet processing function */ |
|||
int process_packet(uint8_t *data, size_t len, struct conn_track *conn) { |
|||
if (!data || len == 0) return -1; |
|||
|
|||
struct timespec start_time, end_time; |
|||
clock_gettime(CLOCK_MONOTONIC, &start_time); |
|||
|
|||
/* Parse Ethernet header */ |
|||
if (len < 14) return -1; |
|||
|
|||
uint16_t eth_type = (data[12] << 8) | data[13]; |
|||
if (eth_type != 0x0800) return 0; /* Not IPv4 */ |
|||
|
|||
/* Parse IP header */ |
|||
if (len < 34) return -1; |
|||
|
|||
uint8_t *ip_hdr = data + 14; |
|||
uint8_t ip_version = (ip_hdr[0] >> 4) & 0x0F; |
|||
if (ip_version != 4) return 0; |
|||
|
|||
uint8_t ip_hdr_len = (ip_hdr[0] & 0x0F) * 4; |
|||
uint8_t protocol = ip_hdr[9]; |
|||
|
|||
/* Process based on protocol */ |
|||
if (protocol == 6 && global_config.enable_tls_randomization) { /* TCP */ |
|||
/* Look for TLS traffic */ |
|||
uint8_t *tcp_hdr = ip_hdr + ip_hdr_len; |
|||
if (tcp_hdr + 20 <= data + len) { |
|||
uint8_t tcp_hdr_len = ((tcp_hdr[12] >> 4) & 0x0F) * 4; |
|||
uint8_t *payload = tcp_hdr + tcp_hdr_len; |
|||
|
|||
if (payload < data + len && payload[0] == 0x16) { /* TLS handshake */ |
|||
if (conn) { |
|||
randomize_tls_fingerprint(&conn->tls_fp); |
|||
} |
|||
} |
|||
} |
|||
} else if (protocol == 17 && global_config.enable_quic_filtering) { /* UDP */ |
|||
/* Look for QUIC traffic */ |
|||
uint8_t *udp_hdr = ip_hdr + ip_hdr_len; |
|||
if (udp_hdr + 8 <= data + len) { |
|||
uint8_t *payload = udp_hdr + 8; |
|||
size_t payload_len = len - (payload - data); |
|||
|
|||
if (payload_len > 0 && conn) { |
|||
analyze_quic_packet(payload, payload_len, &conn->quic_info); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Apply packet fragmentation if needed */ |
|||
if (global_config.enable_packet_fragmentation && conn) { |
|||
fragment_packet_data(data, len, &conn->frag_ctx); |
|||
} |
|||
|
|||
/* Update performance statistics */ |
|||
if (global_config.enable_performance_monitoring) { |
|||
clock_gettime(CLOCK_MONOTONIC, &end_time); |
|||
uint64_t processing_time = (end_time.tv_sec - start_time.tv_sec) * 1000000000UL + |
|||
(end_time.tv_nsec - start_time.tv_nsec); |
|||
update_stats(processing_time, len); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Statistics reporting thread */ |
|||
void* stats_thread(void *arg) { |
|||
while (running) { |
|||
sleep(10); /* Print stats every 10 seconds */ |
|||
if (global_stats.packets_processed > 0) { |
|||
print_stats(); |
|||
} |
|||
} |
|||
return NULL; |
|||
} |
|||
|
|||
/* Main function */ |
|||
int main(int argc, char *argv[]) { |
|||
printf("Zapret eBPF Loader v1.0\n"); |
|||
printf("DPI Evasion and Packet Filtering\n\n"); |
|||
|
|||
/* Initialize configuration */ |
|||
init_default_config(&global_config); |
|||
|
|||
/* Load configuration file if provided */ |
|||
if (argc > 1) { |
|||
load_config_file(argv[1], &global_config); |
|||
} |
|||
|
|||
/* Setup signal handlers */ |
|||
signal(SIGINT, signal_handler); |
|||
signal(SIGTERM, signal_handler); |
|||
|
|||
/* Initialize random seed */ |
|||
srand(time(NULL)); |
|||
|
|||
printf("Configuration loaded:\n"); |
|||
printf(" TLS Randomization: %s\n", global_config.enable_tls_randomization ? "Enabled" : "Disabled"); |
|||
printf(" QUIC Filtering: %s\n", global_config.enable_quic_filtering ? "Enabled" : "Disabled"); |
|||
printf(" Packet Fragmentation: %s\n", global_config.enable_packet_fragmentation ? "Enabled" : "Disabled"); |
|||
printf(" SNI Encryption: %s\n", global_config.enable_sni_encryption ? "Enabled" : "Disabled"); |
|||
printf(" Performance Monitoring: %s\n", global_config.enable_performance_monitoring ? "Enabled" : "Disabled"); |
|||
printf(" Max Connections: %u\n", global_config.max_connections); |
|||
printf(" Fragment Threshold: %u bytes\n\n", global_config.fragment_threshold); |
|||
|
|||
/* Start statistics thread */ |
|||
pthread_t stats_tid; |
|||
if (global_config.enable_performance_monitoring) { |
|||
pthread_create(&stats_tid, NULL, stats_thread, NULL); |
|||
} |
|||
|
|||
printf("Zapret eBPF loader is running...\n"); |
|||
printf("Press Ctrl+C to stop\n\n"); |
|||
|
|||
/* Main processing loop */ |
|||
while (running) { |
|||
/* In a real implementation, this would interface with netfilter/tc/xdp */ |
|||
/* For now, just sleep and let the stats thread run */ |
|||
sleep(1); |
|||
} |
|||
|
|||
/* Cleanup */ |
|||
if (global_config.enable_performance_monitoring) { |
|||
pthread_join(stats_tid, NULL); |
|||
print_stats(); |
|||
} |
|||
|
|||
printf("\nZapret eBPF loader stopped.\n"); |
|||
return 0; |
|||
} |
@ -0,0 +1,271 @@ |
|||
/*
|
|||
* Packet Fragmentation eBPF Program |
|||
* Strategic TCP segmentation and IP fragmentation for DPI evasion |
|||
*/ |
|||
|
|||
#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> |
|||
#include "../include/zapret_ebpf.h" |
|||
|
|||
/* License required for eBPF programs */ |
|||
char LICENSE[] SEC("license") = "GPL"; |
|||
|
|||
/* Fragmentation configuration */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1); |
|||
__type(key, uint32_t); |
|||
__type(value, struct fragment_config); |
|||
} frag_config SEC(".maps"); |
|||
|
|||
/* Fragmentation statistics */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1); |
|||
__type(key, uint32_t); |
|||
__type(value, struct fragment_stats); |
|||
} frag_stats SEC(".maps"); |
|||
|
|||
/* Connection fragmentation state */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_HASH); |
|||
__uint(max_entries, 10000); |
|||
__type(key, uint64_t); /* connection hash */ |
|||
__type(value, struct fragment_ctx); |
|||
} frag_state SEC(".maps"); |
|||
|
|||
/* Helper function to compute connection hash */ |
|||
static __always_inline uint64_t compute_conn_hash(struct iphdr *ip, void *l4_hdr) { |
|||
if (!ip || !l4_hdr) |
|||
return 0; |
|||
|
|||
uint64_t hash = 0; |
|||
hash = (uint64_t)ip->saddr; |
|||
hash = hash * 31 + (uint64_t)ip->daddr; |
|||
hash = hash * 31 + (uint64_t)ip->protocol; |
|||
|
|||
if (ip->protocol == IPPROTO_TCP) { |
|||
struct tcphdr *tcp = (struct tcphdr *)l4_hdr; |
|||
hash = hash * 31 + (uint64_t)tcp->source; |
|||
hash = hash * 31 + (uint64_t)tcp->dest; |
|||
} else if (ip->protocol == IPPROTO_UDP) { |
|||
struct udphdr *udp = (struct udphdr *)l4_hdr; |
|||
hash = hash * 31 + (uint64_t)udp->source; |
|||
hash = hash * 31 + (uint64_t)udp->dest; |
|||
} |
|||
|
|||
return hash; |
|||
} |
|||
|
|||
/* Helper function to determine if packet should be fragmented */ |
|||
static __always_inline int should_fragment_packet(struct __sk_buff *skb, |
|||
struct fragment_config *config, |
|||
struct iphdr *ip, |
|||
void *l4_hdr) { |
|||
if (!config || !config->enabled) |
|||
return 0; |
|||
|
|||
/* Check packet size threshold */ |
|||
if (skb->len < config->min_packet_size) |
|||
return 0; |
|||
|
|||
/* Check protocol-specific rules */ |
|||
if (ip->protocol == IPPROTO_TCP) { |
|||
struct tcphdr *tcp = (struct tcphdr *)l4_hdr; |
|||
uint16_t dst_port = bpf_ntohs(tcp->dest); |
|||
|
|||
/* Fragment HTTPS traffic */ |
|||
if (config->fragment_https && dst_port == 443) |
|||
return 1; |
|||
|
|||
/* Fragment HTTP traffic */ |
|||
if (config->fragment_http && dst_port == 80) |
|||
return 1; |
|||
|
|||
/* Fragment based on TCP flags */ |
|||
if (config->fragment_syn && tcp->syn) |
|||
return 1; |
|||
} else if (ip->protocol == IPPROTO_UDP) { |
|||
struct udphdr *udp = (struct udphdr *)l4_hdr; |
|||
uint16_t dst_port = bpf_ntohs(udp->dest); |
|||
|
|||
/* Fragment DNS traffic */ |
|||
if (config->fragment_dns && dst_port == 53) |
|||
return 1; |
|||
|
|||
/* Fragment QUIC traffic */ |
|||
if (config->fragment_quic && (dst_port == 443 || dst_port == 80)) |
|||
return 1; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Helper function to calculate optimal fragment position */ |
|||
static __always_inline uint32_t calculate_fragment_position(struct __sk_buff *skb, |
|||
struct fragment_config *config, |
|||
struct iphdr *ip) { |
|||
uint32_t ip_header_len = ip->ihl * 4; |
|||
uint32_t l4_header_len = 0; |
|||
|
|||
if (ip->protocol == IPPROTO_TCP) { |
|||
struct tcphdr *tcp = (void *)ip + ip_header_len; |
|||
l4_header_len = tcp->doff * 4; |
|||
} else if (ip->protocol == IPPROTO_UDP) { |
|||
l4_header_len = sizeof(struct udphdr); |
|||
} |
|||
|
|||
uint32_t headers_len = sizeof(struct ethhdr) + ip_header_len + l4_header_len; |
|||
uint32_t payload_start = headers_len; |
|||
|
|||
/* Calculate fragment position based on strategy */ |
|||
switch (config->fragment_strategy) { |
|||
case FRAG_STRATEGY_FIXED: |
|||
return payload_start + config->fragment_offset; |
|||
|
|||
case FRAG_STRATEGY_RANDOM: |
|||
{ |
|||
uint32_t max_offset = skb->len - payload_start - config->min_fragment_size; |
|||
if (max_offset < config->min_fragment_size) |
|||
return payload_start + config->min_fragment_size; |
|||
|
|||
uint32_t random_offset = bpf_get_prandom_u32() % max_offset; |
|||
return payload_start + config->min_fragment_size + random_offset; |
|||
} |
|||
|
|||
case FRAG_STRATEGY_DPI_AWARE: |
|||
/* Fragment at strategic positions to break DPI signatures */ |
|||
if (ip->protocol == IPPROTO_TCP) { |
|||
/* Fragment TLS Client Hello at cipher suites */ |
|||
return payload_start + 64; /* Typical position */ |
|||
} else { |
|||
/* Fragment QUIC at connection ID */ |
|||
return payload_start + 16; |
|||
} |
|||
|
|||
default: |
|||
return payload_start + config->fragment_offset; |
|||
} |
|||
} |
|||
|
|||
/* Main packet fragmentation function */ |
|||
SEC("tc") |
|||
int packet_fragmenter(struct __sk_buff *skb) { |
|||
void *data = (void *)(long)skb->data; |
|||
void *data_end = (void *)(long)skb->data_end; |
|||
|
|||
/* Parse Ethernet header */ |
|||
struct ethhdr *eth = data; |
|||
if (data + sizeof(*eth) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Only process IP packets */ |
|||
if (eth->h_proto != bpf_htons(ETH_P_IP)) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse IP header */ |
|||
struct iphdr *ip = data + sizeof(*eth); |
|||
if (data + sizeof(*eth) + sizeof(*ip) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Get fragmentation configuration */ |
|||
uint32_t config_key = 0; |
|||
struct fragment_config *config = bpf_map_lookup_elem(&frag_config, &config_key); |
|||
if (!config) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse L4 header */ |
|||
void *l4_hdr = data + sizeof(*eth) + (ip->ihl * 4); |
|||
if (l4_hdr + sizeof(struct tcphdr) > data_end && |
|||
l4_hdr + sizeof(struct udphdr) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Check if packet should be fragmented */ |
|||
if (!should_fragment_packet(skb, config, ip, l4_hdr)) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Compute connection hash */ |
|||
uint64_t conn_hash = compute_conn_hash(ip, l4_hdr); |
|||
|
|||
/* Look up or create fragmentation context */ |
|||
struct fragment_ctx *ctx = bpf_map_lookup_elem(&frag_state, &conn_hash); |
|||
if (!ctx) { |
|||
struct fragment_ctx new_ctx = {0}; |
|||
new_ctx.enabled = 1; |
|||
new_ctx.fragment_size = config->default_fragment_size; |
|||
new_ctx.fragments_sent = 0; |
|||
|
|||
bpf_map_update_elem(&frag_state, &conn_hash, &new_ctx, BPF_ANY); |
|||
ctx = bpf_map_lookup_elem(&frag_state, &conn_hash); |
|||
} |
|||
|
|||
if (!ctx) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Calculate fragment position */ |
|||
uint32_t frag_pos = calculate_fragment_position(skb, config, ip); |
|||
|
|||
/* Mark packet for fragmentation */ |
|||
ctx->needs_fragmentation = 1; |
|||
ctx->original_size = skb->len; |
|||
ctx->fragment_position = frag_pos; |
|||
|
|||
/* Update statistics */ |
|||
uint32_t stats_key = 0; |
|||
struct fragment_stats *stats = bpf_map_lookup_elem(&frag_stats, &stats_key); |
|||
if (stats) { |
|||
__sync_fetch_and_add(&stats->packets_fragmented, 1); |
|||
__sync_fetch_and_add(&stats->total_fragments, 2); /* Assume 2 fragments */ |
|||
|
|||
if (ip->protocol == IPPROTO_TCP) |
|||
__sync_fetch_and_add(&stats->tcp_fragments, 1); |
|||
else if (ip->protocol == IPPROTO_UDP) |
|||
__sync_fetch_and_add(&stats->udp_fragments, 1); |
|||
} |
|||
|
|||
/* Increment fragment counter */ |
|||
__sync_fetch_and_add(&ctx->fragments_sent, 1); |
|||
|
|||
/* For actual fragmentation, this would need to be handled in user space
|
|||
* or with more eBPF capabilities. Here we just mark the packet. */ |
|||
|
|||
return TC_ACT_OK; |
|||
} |
|||
|
|||
/* XDP version for fragmentation marking */ |
|||
SEC("xdp") |
|||
int packet_fragment_xdp(struct xdp_md *ctx) { |
|||
void *data = (void *)(long)ctx->data; |
|||
void *data_end = (void *)(long)ctx->data_end; |
|||
|
|||
/* Parse Ethernet header */ |
|||
struct ethhdr *eth = data; |
|||
if (data + sizeof(*eth) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
/* Only process IP packets */ |
|||
if (eth->h_proto != bpf_htons(ETH_P_IP)) |
|||
return XDP_PASS; |
|||
|
|||
/* Parse IP header */ |
|||
struct iphdr *ip = data + sizeof(*eth); |
|||
if (data + sizeof(*eth) + sizeof(*ip) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
/* Fast path fragmentation detection */ |
|||
uint32_t packet_len = data_end - data; |
|||
|
|||
/* Check for large packets that might need fragmentation */ |
|||
if (packet_len > 1200) { /* Typical fragmentation threshold */ |
|||
/* Mark for detailed processing in TC layer */ |
|||
return XDP_PASS; |
|||
} |
|||
|
|||
return XDP_PASS; |
|||
} |
@ -0,0 +1,265 @@ |
|||
/*
|
|||
* QUIC Protocol Filter eBPF Program |
|||
* QUIC/HTTP3 and DNS over QUIC processing |
|||
*/ |
|||
|
|||
#include <linux/bpf.h> |
|||
#include <linux/if_ether.h> |
|||
#include <linux/ip.h> |
|||
#include <linux/udp.h> |
|||
#include <linux/in.h> |
|||
#include <bpf/bpf_helpers.h> |
|||
#include <bpf/bpf_endian.h> |
|||
#include "../include/zapret_ebpf.h" |
|||
|
|||
/* License required for eBPF programs */ |
|||
char LICENSE[] SEC("license") = "GPL"; |
|||
|
|||
/* QUIC connection tracking */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_HASH); |
|||
__uint(max_entries, 10000); |
|||
__type(key, struct quic_conn_id); |
|||
__type(value, struct quic_conn_info); |
|||
} quic_connections SEC(".maps"); |
|||
|
|||
/* QUIC version support matrix */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 100); |
|||
__type(key, uint32_t); |
|||
__type(value, uint32_t); /* supported version */ |
|||
} supported_versions SEC(".maps"); |
|||
|
|||
/* DNS over QUIC tracking */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_HASH); |
|||
__uint(max_entries, 1000); |
|||
__type(key, uint32_t); /* connection hash */ |
|||
__type(value, struct doq_session); |
|||
} doq_sessions SEC(".maps"); |
|||
|
|||
/* Helper function to parse QUIC header */ |
|||
static __always_inline int parse_quic_header(void *data, void *data_end, struct quic_header *hdr) { |
|||
if (!data || !data_end || !hdr) |
|||
return -1; |
|||
|
|||
if (data + 1 > data_end) |
|||
return -1; |
|||
|
|||
uint8_t *quic_data = (uint8_t *)data; |
|||
hdr->flags = quic_data[0]; |
|||
|
|||
/* Check if it's a long header */ |
|||
if (hdr->flags & 0x80) { |
|||
/* Long header packet */ |
|||
if (data + 5 > data_end) |
|||
return -1; |
|||
|
|||
hdr->version = (quic_data[1] << 24) | (quic_data[2] << 16) | |
|||
(quic_data[3] << 8) | quic_data[4]; |
|||
hdr->is_long_header = 1; |
|||
|
|||
/* Extract packet type */ |
|||
hdr->packet_type = (hdr->flags & 0x30) >> 4; |
|||
} else { |
|||
/* Short header packet */ |
|||
hdr->is_long_header = 0; |
|||
hdr->version = 0; |
|||
hdr->packet_type = 0; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Helper function to extract connection ID */ |
|||
static __always_inline int extract_connection_id(void *data, void *data_end, |
|||
struct quic_header *hdr, |
|||
struct quic_conn_id *conn_id) { |
|||
if (!data || !data_end || !hdr || !conn_id) |
|||
return -1; |
|||
|
|||
uint8_t *pos = (uint8_t *)data; |
|||
|
|||
if (hdr->is_long_header) { |
|||
/* Skip fixed header (1 + 4 bytes) */ |
|||
pos += 5; |
|||
|
|||
if (pos + 1 > data_end) |
|||
return -1; |
|||
|
|||
/* Read destination connection ID length */ |
|||
uint8_t dcid_len = *pos++; |
|||
if (dcid_len > MAX_CONN_ID_LEN) |
|||
dcid_len = MAX_CONN_ID_LEN; |
|||
|
|||
if (pos + dcid_len > data_end) |
|||
return -1; |
|||
|
|||
conn_id->len = dcid_len; |
|||
for (int i = 0; i < dcid_len && i < MAX_CONN_ID_LEN; i++) { |
|||
conn_id->id[i] = pos[i]; |
|||
} |
|||
} else { |
|||
/* Short header - connection ID follows immediately */ |
|||
pos += 1; |
|||
|
|||
/* Assume 8-byte connection ID for short headers */ |
|||
if (pos + 8 > data_end) |
|||
return -1; |
|||
|
|||
conn_id->len = 8; |
|||
for (int i = 0; i < 8; i++) { |
|||
conn_id->id[i] = pos[i]; |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Helper function to detect DNS over QUIC */ |
|||
static __always_inline int is_dns_over_quic(struct quic_conn_info *conn, uint16_t dst_port) { |
|||
if (!conn) |
|||
return 0; |
|||
|
|||
/* Standard DoQ port */ |
|||
if (dst_port == 853) |
|||
return 1; |
|||
|
|||
/* Check for DoQ indicators in ALPN */ |
|||
if (conn->alpn_len > 0) { |
|||
/* Look for "doq" in ALPN */ |
|||
for (int i = 0; i < conn->alpn_len - 2; i++) { |
|||
if (conn->alpn[i] == 'd' && conn->alpn[i+1] == 'o' && conn->alpn[i+2] == 'q') |
|||
return 1; |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Main QUIC filter function */ |
|||
SEC("tc") |
|||
int quic_packet_filter(struct __sk_buff *skb) { |
|||
void *data = (void *)(long)skb->data; |
|||
void *data_end = (void *)(long)skb->data_end; |
|||
|
|||
/* Parse Ethernet header */ |
|||
struct ethhdr *eth = data; |
|||
if (data + sizeof(*eth) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Only process IP packets */ |
|||
if (eth->h_proto != bpf_htons(ETH_P_IP)) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse IP header */ |
|||
struct iphdr *ip = data + sizeof(*eth); |
|||
if (data + sizeof(*eth) + sizeof(*ip) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Only process UDP packets */ |
|||
if (ip->protocol != IPPROTO_UDP) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse UDP header */ |
|||
struct udphdr *udp = data + sizeof(*eth) + (ip->ihl * 4); |
|||
if ((void *)udp + sizeof(*udp) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Check for QUIC ports */ |
|||
uint16_t dst_port = bpf_ntohs(udp->dest); |
|||
uint16_t src_port = bpf_ntohs(udp->source); |
|||
|
|||
/* Common QUIC ports: 443, 80, 853 (DoQ) */ |
|||
if (dst_port != 443 && dst_port != 80 && dst_port != 853 && |
|||
src_port != 443 && src_port != 80 && src_port != 853) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse QUIC payload */ |
|||
void *quic_payload = (void *)udp + sizeof(*udp); |
|||
if (quic_payload + 1 > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
struct quic_header hdr = {0}; |
|||
if (parse_quic_header(quic_payload, data_end, &hdr) != 0) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Extract connection ID */ |
|||
struct quic_conn_id conn_id = {0}; |
|||
if (extract_connection_id(quic_payload, data_end, &hdr, &conn_id) != 0) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Look up or create connection info */ |
|||
struct quic_conn_info *conn = bpf_map_lookup_elem(&quic_connections, &conn_id); |
|||
if (!conn) { |
|||
struct quic_conn_info new_conn = {0}; |
|||
new_conn.version = hdr.version; |
|||
new_conn.is_initial = (hdr.packet_type == 0); /* Initial packet */ |
|||
new_conn.first_seen = bpf_ktime_get_ns(); |
|||
new_conn.last_seen = new_conn.first_seen; |
|||
new_conn.packets_count = 1; |
|||
|
|||
bpf_map_update_elem(&quic_connections, &conn_id, &new_conn, BPF_ANY); |
|||
conn = bpf_map_lookup_elem(&quic_connections, &conn_id); |
|||
} |
|||
|
|||
if (conn) { |
|||
conn->last_seen = bpf_ktime_get_ns(); |
|||
__sync_fetch_and_add(&conn->packets_count, 1); |
|||
__sync_fetch_and_add(&conn->bytes_count, skb->len); |
|||
|
|||
/* Check for DNS over QUIC */ |
|||
if (is_dns_over_quic(conn, dst_port)) { |
|||
uint32_t session_hash = ip->saddr ^ ip->daddr ^ dst_port; |
|||
struct doq_session doq = {0}; |
|||
doq.conn_id = conn_id; |
|||
doq.queries_count = 1; |
|||
doq.first_seen = conn->first_seen; |
|||
|
|||
bpf_map_update_elem(&doq_sessions, &session_hash, &doq, BPF_ANY); |
|||
} |
|||
} |
|||
|
|||
return TC_ACT_OK; |
|||
} |
|||
|
|||
/* XDP version for QUIC processing */ |
|||
SEC("xdp") |
|||
int quic_filter_xdp(struct xdp_md *ctx) { |
|||
void *data = (void *)(long)ctx->data; |
|||
void *data_end = (void *)(long)ctx->data_end; |
|||
|
|||
/* Parse Ethernet header */ |
|||
struct ethhdr *eth = data; |
|||
if (data + sizeof(*eth) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
/* Only process IP packets */ |
|||
if (eth->h_proto != bpf_htons(ETH_P_IP)) |
|||
return XDP_PASS; |
|||
|
|||
/* Parse IP header */ |
|||
struct iphdr *ip = data + sizeof(*eth); |
|||
if (data + sizeof(*eth) + sizeof(*ip) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
/* Only process UDP packets */ |
|||
if (ip->protocol != IPPROTO_UDP) |
|||
return XDP_PASS; |
|||
|
|||
/* Fast path QUIC detection */ |
|||
struct udphdr *udp = data + sizeof(*eth) + (ip->ihl * 4); |
|||
if ((void *)udp + sizeof(*udp) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
uint16_t dst_port = bpf_ntohs(udp->dest); |
|||
|
|||
/* Quick QUIC port check */ |
|||
if (dst_port == 443 || dst_port == 80 || dst_port == 853) { |
|||
/* Potential QUIC traffic - pass to TC layer for detailed processing */ |
|||
return XDP_PASS; |
|||
} |
|||
|
|||
return XDP_PASS; |
|||
} |
@ -0,0 +1,298 @@ |
|||
/*
|
|||
* SNI Encryption eBPF Program |
|||
* ECH (Encrypted ClientHello) and ESNI support for TLS privacy |
|||
*/ |
|||
|
|||
#include <linux/bpf.h> |
|||
#include <linux/if_ether.h> |
|||
#include <linux/ip.h> |
|||
#include <linux/tcp.h> |
|||
#include <linux/in.h> |
|||
#include <bpf/bpf_helpers.h> |
|||
#include <bpf/bpf_endian.h> |
|||
#include "../include/zapret_ebpf.h" |
|||
|
|||
/* License required for eBPF programs */ |
|||
char LICENSE[] SEC("license") = "GPL"; |
|||
|
|||
/* ECH configuration */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1); |
|||
__type(key, uint32_t); |
|||
__type(value, struct ech_config); |
|||
} ech_config SEC(".maps"); |
|||
|
|||
/* SNI encryption keys */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_HASH); |
|||
__uint(max_entries, 1000); |
|||
__type(key, char[MAX_HOSTNAME_LEN]); |
|||
__type(value, struct sni_key); |
|||
} sni_keys SEC(".maps"); |
|||
|
|||
/* Encryption statistics */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1); |
|||
__type(key, uint32_t); |
|||
__type(value, struct sni_stats); |
|||
} sni_stats SEC(".maps"); |
|||
|
|||
/* Helper function to extract SNI from TLS Client Hello */ |
|||
static __always_inline int extract_sni(void *data, void *data_end, char *sni_buf, int buf_len) { |
|||
if (!data || !data_end || !sni_buf || buf_len <= 0) |
|||
return -1; |
|||
|
|||
uint8_t *tls_data = (uint8_t *)data; |
|||
|
|||
/* Check for TLS handshake record (0x16) */ |
|||
if (data + 5 > data_end || tls_data[0] != 0x16) |
|||
return -1; |
|||
|
|||
/* Check for Client Hello (0x01) */ |
|||
if (data + 9 > data_end || tls_data[5] != 0x01) |
|||
return -1; |
|||
|
|||
/* Skip to extensions */ |
|||
uint8_t *pos = tls_data + 43; /* Skip fixed part of Client Hello */ |
|||
|
|||
if (pos + 1 > data_end) |
|||
return -1; |
|||
|
|||
/* Skip session ID */ |
|||
uint8_t session_id_len = *pos++; |
|||
pos += session_id_len; |
|||
|
|||
if (pos + 2 > data_end) |
|||
return -1; |
|||
|
|||
/* Skip cipher suites */ |
|||
uint16_t cipher_suites_len = (pos[0] << 8) | pos[1]; |
|||
pos += 2 + cipher_suites_len; |
|||
|
|||
if (pos + 1 > data_end) |
|||
return -1; |
|||
|
|||
/* Skip compression methods */ |
|||
uint8_t comp_methods_len = *pos++; |
|||
pos += comp_methods_len; |
|||
|
|||
if (pos + 2 > data_end) |
|||
return -1; |
|||
|
|||
/* Parse extensions */ |
|||
uint16_t extensions_len = (pos[0] << 8) | pos[1]; |
|||
pos += 2; |
|||
|
|||
uint8_t *extensions_end = pos + extensions_len; |
|||
if (extensions_end > data_end) |
|||
extensions_end = data_end; |
|||
|
|||
/* Look for SNI extension (type 0x0000) */ |
|||
while (pos + 4 < extensions_end) { |
|||
uint16_t ext_type = (pos[0] << 8) | pos[1]; |
|||
uint16_t ext_len = (pos[2] << 8) | pos[3]; |
|||
pos += 4; |
|||
|
|||
if (ext_type == 0x0000) { /* SNI extension */ |
|||
if (pos + 5 < extensions_end) { |
|||
uint16_t sni_list_len = (pos[0] << 8) | pos[1]; |
|||
pos += 2; |
|||
|
|||
if (pos + 3 < extensions_end) { |
|||
uint8_t name_type = pos[0]; |
|||
uint16_t name_len = (pos[1] << 8) | pos[2]; |
|||
pos += 3; |
|||
|
|||
if (name_type == 0 && pos + name_len <= extensions_end) { |
|||
/* Copy SNI hostname */ |
|||
int copy_len = name_len; |
|||
if (copy_len >= buf_len) |
|||
copy_len = buf_len - 1; |
|||
|
|||
for (int i = 0; i < copy_len; i++) { |
|||
sni_buf[i] = pos[i]; |
|||
} |
|||
sni_buf[copy_len] = '\0'; |
|||
|
|||
return copy_len; |
|||
} |
|||
} |
|||
} |
|||
break; |
|||
} |
|||
|
|||
pos += ext_len; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
/* Helper function to check if SNI should be encrypted */ |
|||
static __always_inline int should_encrypt_sni(char *hostname, struct ech_config *config) { |
|||
if (!hostname || !config || !config->enabled) |
|||
return 0; |
|||
|
|||
/* Check if hostname matches encryption patterns */ |
|||
int hostname_len = 0; |
|||
for (int i = 0; i < MAX_HOSTNAME_LEN && hostname[i] != '\0'; i++) { |
|||
hostname_len++; |
|||
} |
|||
|
|||
if (hostname_len == 0) |
|||
return 0; |
|||
|
|||
/* Check against configured domains */ |
|||
for (int i = 0; i < config->domain_count && i < MAX_ECH_DOMAINS; i++) { |
|||
int domain_len = 0; |
|||
for (int j = 0; j < MAX_HOSTNAME_LEN && config->domains[i][j] != '\0'; j++) { |
|||
domain_len++; |
|||
} |
|||
|
|||
if (domain_len > 0 && hostname_len >= domain_len) { |
|||
/* Check if hostname ends with domain */ |
|||
int match = 1; |
|||
for (int j = 0; j < domain_len; j++) { |
|||
if (hostname[hostname_len - domain_len + j] != config->domains[i][j]) { |
|||
match = 0; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (match) |
|||
return 1; |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Helper function to apply SNI encryption */ |
|||
static __always_inline int encrypt_sni(void *data, void *data_end, char *hostname) { |
|||
if (!data || !data_end || !hostname) |
|||
return -1; |
|||
|
|||
/* Look up encryption key for hostname */ |
|||
struct sni_key *key = bpf_map_lookup_elem(&sni_keys, hostname); |
|||
if (!key) |
|||
return -1; |
|||
|
|||
/* For actual encryption, this would require packet modification
|
|||
* capabilities that are limited in eBPF. This is a placeholder |
|||
* for marking packets that need SNI encryption in user space. */ |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Main SNI encryption function */ |
|||
SEC("tc") |
|||
int sni_encryptor(struct __sk_buff *skb) { |
|||
void *data = (void *)(long)skb->data; |
|||
void *data_end = (void *)(long)skb->data_end; |
|||
|
|||
/* Parse Ethernet header */ |
|||
struct ethhdr *eth = data; |
|||
if (data + sizeof(*eth) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Only process IP packets */ |
|||
if (eth->h_proto != bpf_htons(ETH_P_IP)) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse IP header */ |
|||
struct iphdr *ip = data + sizeof(*eth); |
|||
if (data + sizeof(*eth) + sizeof(*ip) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Only process TCP packets */ |
|||
if (ip->protocol != IPPROTO_TCP) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse TCP header */ |
|||
struct tcphdr *tcp = data + sizeof(*eth) + (ip->ihl * 4); |
|||
if ((void *)tcp + sizeof(*tcp) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Check for TLS handshake on port 443 */ |
|||
uint16_t dst_port = bpf_ntohs(tcp->dest); |
|||
if (dst_port != 443) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Get ECH configuration */ |
|||
uint32_t config_key = 0; |
|||
struct ech_config *config = bpf_map_lookup_elem(&ech_config, &config_key); |
|||
if (!config) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse TLS payload */ |
|||
void *tls_payload = (void *)tcp + (tcp->doff * 4); |
|||
if (tls_payload + 5 > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Extract SNI from TLS Client Hello */ |
|||
char sni_hostname[MAX_HOSTNAME_LEN] = {0}; |
|||
int sni_len = extract_sni(tls_payload, data_end, sni_hostname, MAX_HOSTNAME_LEN); |
|||
|
|||
if (sni_len <= 0) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Check if SNI should be encrypted */ |
|||
if (!should_encrypt_sni(sni_hostname, config)) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Apply SNI encryption */ |
|||
if (encrypt_sni(tls_payload, data_end, sni_hostname) == 0) { |
|||
/* Update statistics */ |
|||
uint32_t stats_key = 0; |
|||
struct sni_stats *stats = bpf_map_lookup_elem(&sni_stats, &stats_key); |
|||
if (stats) { |
|||
__sync_fetch_and_add(&stats->sni_encrypted, 1); |
|||
|
|||
if (config->use_ech) |
|||
__sync_fetch_and_add(&stats->ech_used, 1); |
|||
else |
|||
__sync_fetch_and_add(&stats->esni_used, 1); |
|||
} |
|||
} |
|||
|
|||
return TC_ACT_OK; |
|||
} |
|||
|
|||
/* XDP version for SNI detection */ |
|||
SEC("xdp") |
|||
int sni_encrypt_xdp(struct xdp_md *ctx) { |
|||
void *data = (void *)(long)ctx->data; |
|||
void *data_end = (void *)(long)ctx->data_end; |
|||
|
|||
/* Parse Ethernet header */ |
|||
struct ethhdr *eth = data; |
|||
if (data + sizeof(*eth) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
/* Only process IP packets */ |
|||
if (eth->h_proto != bpf_htons(ETH_P_IP)) |
|||
return XDP_PASS; |
|||
|
|||
/* Parse IP header */ |
|||
struct iphdr *ip = data + sizeof(*eth); |
|||
if (data + sizeof(*eth) + sizeof(*ip) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
/* Only process TCP packets */ |
|||
if (ip->protocol != IPPROTO_TCP) |
|||
return XDP_PASS; |
|||
|
|||
/* Fast path TLS detection */ |
|||
struct tcphdr *tcp = data + sizeof(*eth) + (ip->ihl * 4); |
|||
if ((void *)tcp + sizeof(*tcp) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
uint16_t dst_port = bpf_ntohs(tcp->dest); |
|||
if (dst_port == 443) { |
|||
/* Potential TLS traffic - pass to TC layer for SNI processing */ |
|||
return XDP_PASS; |
|||
} |
|||
|
|||
return XDP_PASS; |
|||
} |
@ -0,0 +1,166 @@ |
|||
/*
|
|||
* TLS Fingerprint Randomization eBPF Program |
|||
* JA3/JA3S spoofing and browser fingerprint simulation |
|||
*/ |
|||
|
|||
#include <linux/bpf.h> |
|||
#include <linux/if_ether.h> |
|||
#include <linux/ip.h> |
|||
#include <linux/tcp.h> |
|||
#include <linux/in.h> |
|||
#include <bpf/bpf_helpers.h> |
|||
#include <bpf/bpf_endian.h> |
|||
#include "../include/zapret_ebpf.h" |
|||
|
|||
/* License required for eBPF programs */ |
|||
char LICENSE[] SEC("license") = "GPL"; |
|||
|
|||
/* TLS fingerprint database */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1000); |
|||
__type(key, uint32_t); |
|||
__type(value, struct tls_fingerprint); |
|||
} browser_fingerprints SEC(".maps"); |
|||
|
|||
/* JA3 hash to fingerprint mapping */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_HASH); |
|||
__uint(max_entries, 10000); |
|||
__type(key, uint32_t); /* JA3 hash */ |
|||
__type(value, uint32_t); /* fingerprint index */ |
|||
} ja3_mapping SEC(".maps"); |
|||
|
|||
/* Randomization state */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1); |
|||
__type(key, uint32_t); |
|||
__type(value, struct randomization_state); |
|||
} rand_state SEC(".maps"); |
|||
|
|||
/* Helper function to generate random fingerprint */ |
|||
static __always_inline int get_random_fingerprint(struct tls_fingerprint *fp) { |
|||
if (!fp) |
|||
return -1; |
|||
|
|||
uint32_t rand_key = bpf_get_prandom_u32() % 1000; |
|||
struct tls_fingerprint *browser_fp = bpf_map_lookup_elem(&browser_fingerprints, &rand_key); |
|||
|
|||
if (!browser_fp) |
|||
return -1; |
|||
|
|||
/* Copy fingerprint data */ |
|||
fp->version = browser_fp->version; |
|||
fp->cipher_count = browser_fp->cipher_count; |
|||
|
|||
for (int i = 0; i < MAX_CIPHER_SUITES && i < fp->cipher_count; i++) { |
|||
fp->cipher_suites[i] = browser_fp->cipher_suites[i]; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Main TLS fingerprint randomization function */ |
|||
SEC("tc") |
|||
int tls_fingerprint_randomizer(struct __sk_buff *skb) { |
|||
void *data = (void *)(long)skb->data; |
|||
void *data_end = (void *)(long)skb->data_end; |
|||
|
|||
/* Parse Ethernet header */ |
|||
struct ethhdr *eth = data; |
|||
if (data + sizeof(*eth) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Only process IP packets */ |
|||
if (eth->h_proto != bpf_htons(ETH_P_IP)) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse IP header */ |
|||
struct iphdr *ip = data + sizeof(*eth); |
|||
if (data + sizeof(*eth) + sizeof(*ip) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Only process TCP packets */ |
|||
if (ip->protocol != IPPROTO_TCP) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse TCP header */ |
|||
struct tcphdr *tcp = data + sizeof(*eth) + (ip->ihl * 4); |
|||
if ((void *)tcp + sizeof(*tcp) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Check for TLS handshake on common ports */ |
|||
uint16_t dst_port = bpf_ntohs(tcp->dest); |
|||
if (dst_port != 443 && dst_port != 8443) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse TLS payload */ |
|||
void *tls_payload = (void *)tcp + (tcp->doff * 4); |
|||
if (tls_payload + 5 > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
uint8_t *tls_data = (uint8_t *)tls_payload; |
|||
|
|||
/* Check for TLS handshake record (0x16) and Client Hello (0x01) */ |
|||
if (tls_data[0] != 0x16 || tls_payload + 9 > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
if (tls_data[5] != 0x01) /* Not Client Hello */ |
|||
return TC_ACT_OK; |
|||
|
|||
/* Apply fingerprint randomization */ |
|||
struct tls_fingerprint new_fp = {0}; |
|||
if (get_random_fingerprint(&new_fp) == 0) { |
|||
/* Modify TLS Client Hello with new fingerprint */ |
|||
/* This would require packet modification capabilities */ |
|||
/* For now, just mark the packet for user-space processing */ |
|||
|
|||
/* Update randomization statistics */ |
|||
uint32_t state_key = 0; |
|||
struct randomization_state *state = bpf_map_lookup_elem(&rand_state, &state_key); |
|||
if (state) { |
|||
__sync_fetch_and_add(&state->randomizations_applied, 1); |
|||
} |
|||
} |
|||
|
|||
return TC_ACT_OK; |
|||
} |
|||
|
|||
/* XDP version for processing */ |
|||
SEC("xdp") |
|||
int tls_fingerprint_xdp(struct xdp_md *ctx) { |
|||
void *data = (void *)(long)ctx->data; |
|||
void *data_end = (void *)(long)ctx->data_end; |
|||
|
|||
/* Parse Ethernet header */ |
|||
struct ethhdr *eth = data; |
|||
if (data + sizeof(*eth) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
/* Only process IP packets */ |
|||
if (eth->h_proto != bpf_htons(ETH_P_IP)) |
|||
return XDP_PASS; |
|||
|
|||
/* Parse IP header */ |
|||
struct iphdr *ip = data + sizeof(*eth); |
|||
if (data + sizeof(*eth) + sizeof(*ip) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
/* Only process TCP packets */ |
|||
if (ip->protocol != IPPROTO_TCP) |
|||
return XDP_PASS; |
|||
|
|||
/* Fast path TLS detection and marking */ |
|||
struct tcphdr *tcp = data + sizeof(*eth) + (ip->ihl * 4); |
|||
if ((void *)tcp + sizeof(*tcp) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
uint16_t dst_port = bpf_ntohs(tcp->dest); |
|||
if (dst_port == 443 || dst_port == 8443) { |
|||
/* Mark for TLS processing in TC layer */ |
|||
return XDP_PASS; |
|||
} |
|||
|
|||
return XDP_PASS; |
|||
} |
@ -0,0 +1,369 @@ |
|||
/*
|
|||
* Zapret eBPF Main Filter Program |
|||
* Packet filtering with DPI evasion techniques |
|||
*/ |
|||
|
|||
#include <linux/bpf.h> |
|||
#include <linux/if_ether.h> |
|||
#include <linux/ip.h> |
|||
#include <linux/ipv6.h> |
|||
#include <linux/tcp.h> |
|||
#include <linux/udp.h> |
|||
#include <linux/in.h> |
|||
#include <bpf/bpf_helpers.h> |
|||
#include <bpf/bpf_endian.h> |
|||
#include "../include/zapret_ebpf.h" |
|||
|
|||
/* License required for eBPF programs */ |
|||
char LICENSE[] SEC("license") = "GPL"; |
|||
|
|||
/* BPF Maps */ |
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_HASH); |
|||
__uint(max_entries, 65536); |
|||
__type(key, uint64_t); /* 5-tuple hash */ |
|||
__type(value, struct conn_track); |
|||
} connection_map SEC(".maps"); |
|||
|
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1024); |
|||
__type(key, uint32_t); |
|||
__type(value, struct filter_rule); |
|||
} filter_rules SEC(".maps"); |
|||
|
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1); |
|||
__type(key, uint32_t); |
|||
__type(value, struct zapret_config); |
|||
} config_map SEC(".maps"); |
|||
|
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 1); |
|||
__type(key, uint32_t); |
|||
__type(value, struct perf_stats); |
|||
} stats_map SEC(".maps"); |
|||
|
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_HASH); |
|||
__uint(max_entries, 1024); |
|||
__type(key, char[MAX_HOSTNAME_LEN]); |
|||
__type(value, uint32_t); /* action flags */ |
|||
} hostname_rules SEC(".maps"); |
|||
|
|||
struct { |
|||
__uint(type, BPF_MAP_TYPE_ARRAY); |
|||
__uint(max_entries, 100); |
|||
__type(key, uint32_t); |
|||
__type(value, struct tls_fingerprint); |
|||
} tls_fingerprint_pool SEC(".maps"); |
|||
|
|||
/* Helper Functions */ |
|||
static __always_inline uint64_t compute_5tuple_hash(struct iphdr *ip, void *l4_hdr) { |
|||
uint64_t hash = 0; |
|||
|
|||
if (!ip || !l4_hdr) |
|||
return 0; |
|||
|
|||
hash = (uint64_t)ip->saddr; |
|||
hash = hash * 31 + (uint64_t)ip->daddr; |
|||
hash = hash * 31 + (uint64_t)ip->protocol; |
|||
|
|||
if (ip->protocol == IPPROTO_TCP) { |
|||
struct tcphdr *tcp = (struct tcphdr *)l4_hdr; |
|||
hash = hash * 31 + (uint64_t)tcp->source; |
|||
hash = hash * 31 + (uint64_t)tcp->dest; |
|||
} else if (ip->protocol == IPPROTO_UDP) { |
|||
struct udphdr *udp = (struct udphdr *)l4_hdr; |
|||
hash = hash * 31 + (uint64_t)udp->source; |
|||
hash = hash * 31 + (uint64_t)udp->dest; |
|||
} |
|||
|
|||
return hash; |
|||
} |
|||
|
|||
static __always_inline int parse_tls_client_hello(void *data, void *data_end, struct tls_fingerprint *fp) { |
|||
if (!data || !data_end || !fp) |
|||
return -1; |
|||
|
|||
/* Basic TLS record parsing */ |
|||
if (data + 5 > data_end) |
|||
return -1; |
|||
|
|||
uint8_t *tls_data = (uint8_t *)data; |
|||
|
|||
/* Check for TLS handshake record (0x16) */ |
|||
if (tls_data[0] != 0x16) |
|||
return -1; |
|||
|
|||
/* Extract TLS version */ |
|||
fp->version = (tls_data[1] << 8) | tls_data[2]; |
|||
|
|||
/* Parse Client Hello message */ |
|||
if (data + 9 > data_end) |
|||
return -1; |
|||
|
|||
/* Skip to cipher suites */ |
|||
uint8_t *pos = tls_data + 43; /* Skip fixed part of Client Hello */ |
|||
|
|||
if (pos + 1 > data_end) |
|||
return -1; |
|||
|
|||
/* Skip session ID */ |
|||
uint8_t session_id_len = *pos++; |
|||
pos += session_id_len; |
|||
|
|||
if (pos + 2 > data_end) |
|||
return -1; |
|||
|
|||
/* Parse cipher suites */ |
|||
uint16_t cipher_suites_len = (pos[0] << 8) | pos[1]; |
|||
pos += 2; |
|||
|
|||
if (pos + cipher_suites_len > data_end) |
|||
return -1; |
|||
|
|||
/* Store first few cipher suites for fingerprinting */ |
|||
int cipher_count = cipher_suites_len / 2; |
|||
if (cipher_count > MAX_CIPHER_SUITES) |
|||
cipher_count = MAX_CIPHER_SUITES; |
|||
|
|||
fp->cipher_count = cipher_count; |
|||
for (int i = 0; i < cipher_count && i < MAX_CIPHER_SUITES; i++) { |
|||
if (pos + 2 <= data_end) { |
|||
fp->cipher_suites[i] = (pos[0] << 8) | pos[1]; |
|||
pos += 2; |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static __always_inline int parse_quic_initial(void *data, void *data_end, struct quic_conn_info *quic) { |
|||
if (!data || !data_end || !quic) |
|||
return -1; |
|||
|
|||
/* Basic QUIC initial packet parsing */ |
|||
if (data + 1 > data_end) |
|||
return -1; |
|||
|
|||
uint8_t *quic_data = (uint8_t *)data; |
|||
|
|||
/* Check for QUIC initial packet (long header with type 0) */ |
|||
if ((quic_data[0] & 0x80) == 0) /* Not a long header */ |
|||
return -1; |
|||
|
|||
if ((quic_data[0] & 0x30) != 0x00) /* Not initial packet */ |
|||
return -1; |
|||
|
|||
/* Extract version */ |
|||
if (data + 5 > data_end) |
|||
return -1; |
|||
|
|||
quic->version = (quic_data[1] << 24) | (quic_data[2] << 16) | |
|||
(quic_data[3] << 8) | quic_data[4]; |
|||
|
|||
/* Mark as initial packet */ |
|||
quic->is_initial = 1; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static __always_inline int randomize_tls_fingerprint(void *data, void *data_end, struct tls_fingerprint *fp) { |
|||
if (!data || !data_end || !fp) |
|||
return -1; |
|||
|
|||
/* Get random fingerprint from pool */ |
|||
uint32_t key = bpf_get_prandom_u32() % 100; |
|||
struct tls_fingerprint *random_fp = bpf_map_lookup_elem(&tls_fingerprint_pool, &key); |
|||
|
|||
if (!random_fp) |
|||
return -1; |
|||
|
|||
/* Apply randomization to current fingerprint */ |
|||
fp->version = random_fp->version; |
|||
fp->cipher_count = random_fp->cipher_count; |
|||
|
|||
for (int i = 0; i < MAX_CIPHER_SUITES && i < fp->cipher_count; i++) { |
|||
fp->cipher_suites[i] = random_fp->cipher_suites[i]; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static __always_inline int fragment_packet(struct __sk_buff *skb, struct fragment_ctx *ctx) { |
|||
if (!skb || !ctx || !ctx->enabled) |
|||
return 0; |
|||
|
|||
/* Simple fragmentation logic - split packet at configured position */ |
|||
if (skb->len > ctx->fragment_size) { |
|||
/* Mark for fragmentation in user space */ |
|||
ctx->needs_fragmentation = 1; |
|||
ctx->original_size = skb->len; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static __always_inline void update_performance_stats(struct perf_stats *stats, uint64_t start_time) { |
|||
if (!stats) |
|||
return; |
|||
|
|||
uint64_t end_time = bpf_ktime_get_ns(); |
|||
uint64_t processing_time = end_time - start_time; |
|||
|
|||
__sync_fetch_and_add(&stats->packets_processed, 1); |
|||
__sync_fetch_and_add(&stats->total_processing_time, processing_time); |
|||
|
|||
if (processing_time > stats->max_processing_time) |
|||
stats->max_processing_time = processing_time; |
|||
} |
|||
|
|||
/* Main packet filter function */ |
|||
SEC("tc") |
|||
int zapret_packet_filter(struct __sk_buff *skb) { |
|||
uint64_t start_time = bpf_ktime_get_ns(); |
|||
|
|||
void *data = (void *)(long)skb->data; |
|||
void *data_end = (void *)(long)skb->data_end; |
|||
|
|||
/* Parse Ethernet header */ |
|||
struct ethhdr *eth = data; |
|||
if (data + sizeof(*eth) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Only process IP packets */ |
|||
if (eth->h_proto != bpf_htons(ETH_P_IP)) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Parse IP header */ |
|||
struct iphdr *ip = data + sizeof(*eth); |
|||
if (data + sizeof(*eth) + sizeof(*ip) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Get configuration */ |
|||
uint32_t config_key = 0; |
|||
struct zapret_config *config = bpf_map_lookup_elem(&config_map, &config_key); |
|||
if (!config) |
|||
return TC_ACT_OK; |
|||
|
|||
/* Compute connection hash */ |
|||
void *l4_hdr = data + sizeof(*eth) + (ip->ihl * 4); |
|||
if (l4_hdr + sizeof(struct tcphdr) > data_end && |
|||
l4_hdr + sizeof(struct udphdr) > data_end) |
|||
return TC_ACT_OK; |
|||
|
|||
uint64_t conn_hash = compute_5tuple_hash(ip, l4_hdr); |
|||
|
|||
/* Look up or create connection tracking entry */ |
|||
struct conn_track *conn = bpf_map_lookup_elem(&connection_map, &conn_hash); |
|||
if (!conn) { |
|||
struct conn_track new_conn = {0}; |
|||
new_conn.src_ip = ip->saddr; |
|||
new_conn.dst_ip = ip->daddr; |
|||
new_conn.protocol = ip->protocol; |
|||
new_conn.first_seen = start_time; |
|||
new_conn.last_seen = start_time; |
|||
new_conn.packets_count = 1; |
|||
|
|||
if (ip->protocol == IPPROTO_TCP) { |
|||
struct tcphdr *tcp = (struct tcphdr *)l4_hdr; |
|||
new_conn.src_port = tcp->source; |
|||
new_conn.dst_port = tcp->dest; |
|||
} else if (ip->protocol == IPPROTO_UDP) { |
|||
struct udphdr *udp = (struct udphdr *)l4_hdr; |
|||
new_conn.src_port = udp->source; |
|||
new_conn.dst_port = udp->dest; |
|||
} |
|||
|
|||
bpf_map_update_elem(&connection_map, &conn_hash, &new_conn, BPF_ANY); |
|||
conn = bpf_map_lookup_elem(&connection_map, &conn_hash); |
|||
} |
|||
|
|||
if (conn) { |
|||
conn->last_seen = start_time; |
|||
__sync_fetch_and_add(&conn->packets_count, 1); |
|||
__sync_fetch_and_add(&conn->bytes_count, skb->len); |
|||
} |
|||
|
|||
/* Process TLS packets */ |
|||
if (ip->protocol == IPPROTO_TCP && config->enable_tls_randomization) { |
|||
struct tcphdr *tcp = (struct tcphdr *)l4_hdr; |
|||
void *payload = l4_hdr + (tcp->doff * 4); |
|||
|
|||
if (payload < data_end) { |
|||
struct tls_fingerprint fp = {0}; |
|||
if (parse_tls_client_hello(payload, data_end, &fp) == 0) { |
|||
if (conn) { |
|||
conn->tls_fp = fp; |
|||
randomize_tls_fingerprint(payload, data_end, &conn->tls_fp); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Process QUIC packets */ |
|||
if (ip->protocol == IPPROTO_UDP && config->enable_quic_filtering) { |
|||
struct udphdr *udp = (struct udphdr *)l4_hdr; |
|||
void *payload = l4_hdr + sizeof(*udp); |
|||
|
|||
if (payload < data_end) { |
|||
struct quic_conn_info quic = {0}; |
|||
if (parse_quic_initial(payload, data_end, &quic) == 0) { |
|||
if (conn) { |
|||
conn->quic_info = quic; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Apply packet fragmentation if enabled */ |
|||
if (config->enable_packet_fragmentation && conn) { |
|||
fragment_packet(skb, &conn->frag_ctx); |
|||
} |
|||
|
|||
/* Update performance statistics */ |
|||
if (config->enable_performance_monitoring) { |
|||
uint32_t stats_key = 0; |
|||
struct perf_stats *stats = bpf_map_lookup_elem(&stats_map, &stats_key); |
|||
if (stats) { |
|||
update_performance_stats(stats, start_time); |
|||
} |
|||
} |
|||
|
|||
return TC_ACT_OK; |
|||
} |
|||
|
|||
/* XDP version for packet processing */ |
|||
SEC("xdp") |
|||
int zapret_xdp_filter(struct xdp_md *ctx) { |
|||
void *data = (void *)(long)ctx->data; |
|||
void *data_end = (void *)(long)ctx->data_end; |
|||
|
|||
/* Parse Ethernet header */ |
|||
struct ethhdr *eth = data; |
|||
if (data + sizeof(*eth) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
/* Only process IP packets */ |
|||
if (eth->h_proto != bpf_htons(ETH_P_IP)) |
|||
return XDP_PASS; |
|||
|
|||
/* Parse IP header */ |
|||
struct iphdr *ip = data + sizeof(*eth); |
|||
if (data + sizeof(*eth) + sizeof(*ip) > data_end) |
|||
return XDP_PASS; |
|||
|
|||
/* Fast path filtering - basic DPI evasion */ |
|||
if (ip->protocol == IPPROTO_TCP) { |
|||
/* TCP-based filtering logic */ |
|||
return XDP_PASS; |
|||
} else if (ip->protocol == IPPROTO_UDP) { |
|||
/* UDP/QUIC-based filtering logic */ |
|||
return XDP_PASS; |
|||
} |
|||
|
|||
return XDP_PASS; |
|||
} |
@ -0,0 +1,92 @@ |
|||
# Zapret eBPF Configuration |
|||
# DPI evasion and packet filtering settings |
|||
|
|||
[general] |
|||
# Enable/disable core features |
|||
enable_tls_randomization=1 |
|||
enable_quic_filtering=1 |
|||
enable_packet_fragmentation=1 |
|||
enable_sni_encryption=1 |
|||
enable_performance_monitoring=1 |
|||
|
|||
# eBPF specific settings |
|||
ebpf_enabled=1 |
|||
ebpf_xdp_mode=1 |
|||
ebpf_tc_mode=1 |
|||
|
|||
[performance] |
|||
# Connection and performance limits |
|||
max_connections=10000 |
|||
connection_timeout=300 |
|||
fragment_threshold=1200 |
|||
processing_threads=4 |
|||
|
|||
# Memory and resource limits |
|||
max_memory_mb=512 |
|||
map_size_connections=65536 |
|||
map_size_rules=1024 |
|||
|
|||
[tls] |
|||
# TLS fingerprint randomization |
|||
randomize_cipher_order=1 |
|||
use_browser_fingerprints=1 |
|||
min_cipher_suites=8 |
|||
max_cipher_suites=16 |
|||
|
|||
# JA3 fingerprint settings |
|||
ja3_randomization=1 |
|||
ja3_database_size=1000 |
|||
|
|||
[quic] |
|||
# QUIC/HTTP3 filtering |
|||
randomize_connection_id=1 |
|||
fake_retry_packets=1 |
|||
support_0rtt=1 |
|||
|
|||
# DNS over QUIC settings |
|||
doq_filtering=1 |
|||
doq_port=853 |
|||
|
|||
[fragmentation] |
|||
# Packet fragmentation strategies |
|||
min_fragment_size=64 |
|||
max_fragment_size=1200 |
|||
random_fragment_order=1 |
|||
fragment_strategy=tcp_seg |
|||
|
|||
# TCP segmentation |
|||
tcp_segment_size=536 |
|||
tcp_random_window=1 |
|||
|
|||
[sni_encryption] |
|||
# SNI encryption (ECH/ESNI) |
|||
ech_enabled=1 |
|||
esni_fallback=1 |
|||
encryption_key_rotation=3600 |
|||
|
|||
[monitoring] |
|||
# Performance monitoring |
|||
stats_interval=1 |
|||
history_size=3600 |
|||
export_csv=1 |
|||
|
|||
# Alert thresholds |
|||
cpu_alert_threshold=80 |
|||
memory_alert_threshold=90 |
|||
latency_alert_threshold=10 |
|||
|
|||
[logging] |
|||
# Debug and logging |
|||
log_level=info |
|||
debug_mode=0 |
|||
log_file=/var/log/zapret_ebpf.log |
|||
|
|||
[network] |
|||
# Network interface settings |
|||
default_interface=eth0 |
|||
monitor_interfaces=eth0,wlan0 |
|||
|
|||
# Protocol ports |
|||
https_ports=443,8443 |
|||
quic_ports=443,80 |
|||
dns_ports=53,853 |
Loading…
Reference in new issue