From 4bc2651ef2a66a3a9eb1c6ed9e6c1d485e7ade8e Mon Sep 17 00:00:00 2001 From: ibrahimsql Date: Sun, 10 Aug 2025 19:44:12 +0300 Subject: [PATCH] . --- Makefile | 4 +- ebpf/Makefile | 175 +++++++++++ ebpf/include/zapret_ebpf.h | 285 ++++++++++++++++++ ebpf/loader/ebpf_manager.c | 467 ++++++++++++++++++++++++++++++ ebpf/loader/performance_monitor.c | 458 +++++++++++++++++++++++++++++ ebpf/loader/zapret_loader.c | 378 ++++++++++++++++++++++++ ebpf/src/packet_fragment.bpf.c | 271 +++++++++++++++++ ebpf/src/quic_filter.bpf.c | 265 +++++++++++++++++ ebpf/src/sni_encrypt.bpf.c | 298 +++++++++++++++++++ ebpf/src/tls_fingerprint.bpf.c | 166 +++++++++++ ebpf/src/zapret_filter.bpf.c | 369 +++++++++++++++++++++++ zapret_config.conf | 92 ++++++ 12 files changed, 3226 insertions(+), 2 deletions(-) create mode 100644 ebpf/Makefile create mode 100644 ebpf/include/zapret_ebpf.h create mode 100644 ebpf/loader/ebpf_manager.c create mode 100644 ebpf/loader/performance_monitor.c create mode 100644 ebpf/loader/zapret_loader.c create mode 100644 ebpf/src/packet_fragment.bpf.c create mode 100644 ebpf/src/quic_filter.bpf.c create mode 100644 ebpf/src/sni_encrypt.bpf.c create mode 100644 ebpf/src/tls_fingerprint.bpf.c create mode 100644 ebpf/src/zapret_filter.bpf.c create mode 100644 zapret_config.conf diff --git a/Makefile b/Makefile index 5f6cfc0..efc9032 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -DIRS := nfq tpws ip2net mdig -DIRS_MAC := tpws ip2net mdig +DIRS := nfq tpws ip2net mdig ebpf +DIRS_MAC := tpws ip2net mdig ebpf TGT := binaries/my all: clean diff --git a/ebpf/Makefile b/ebpf/Makefile new file mode 100644 index 0000000..7713e79 --- /dev/null +++ b/ebpf/Makefile @@ -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 ' > 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" \ No newline at end of file diff --git a/ebpf/include/zapret_ebpf.h b/ebpf/include/zapret_ebpf.h new file mode 100644 index 0000000..3ad093a --- /dev/null +++ b/ebpf/include/zapret_ebpf.h @@ -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 +#include + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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__ */ \ No newline at end of file diff --git a/ebpf/loader/ebpf_manager.c b/ebpf/loader/ebpf_manager.c new file mode 100644 index 0000000..36c1f53 --- /dev/null +++ b/ebpf/loader/ebpf_manager.c @@ -0,0 +1,467 @@ +/* + * Zapret eBPF Manager + * High-level interface for eBPF program management and integration + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/ebpf/loader/performance_monitor.c b/ebpf/loader/performance_monitor.c new file mode 100644 index 0000000..b80b8f0 --- /dev/null +++ b/ebpf/loader/performance_monitor.c @@ -0,0 +1,458 @@ +/* + * Zapret Performance Monitor + * Performance monitoring and optimization for eBPF packet filtering + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/ebpf/loader/zapret_loader.c b/ebpf/loader/zapret_loader.c new file mode 100644 index 0000000..dd4a373 --- /dev/null +++ b/ebpf/loader/zapret_loader.c @@ -0,0 +1,378 @@ +/* + * Zapret eBPF Loader and Manager + * User-space program to load and manage eBPF programs + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#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 +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/ebpf/src/packet_fragment.bpf.c b/ebpf/src/packet_fragment.bpf.c new file mode 100644 index 0000000..909bf81 --- /dev/null +++ b/ebpf/src/packet_fragment.bpf.c @@ -0,0 +1,271 @@ +/* + * Packet Fragmentation eBPF Program + * Strategic TCP segmentation and IP fragmentation for DPI evasion + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/ebpf/src/quic_filter.bpf.c b/ebpf/src/quic_filter.bpf.c new file mode 100644 index 0000000..72684f2 --- /dev/null +++ b/ebpf/src/quic_filter.bpf.c @@ -0,0 +1,265 @@ +/* + * QUIC Protocol Filter eBPF Program + * QUIC/HTTP3 and DNS over QUIC processing + */ + +#include +#include +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/ebpf/src/sni_encrypt.bpf.c b/ebpf/src/sni_encrypt.bpf.c new file mode 100644 index 0000000..2ee59db --- /dev/null +++ b/ebpf/src/sni_encrypt.bpf.c @@ -0,0 +1,298 @@ +/* + * SNI Encryption eBPF Program + * ECH (Encrypted ClientHello) and ESNI support for TLS privacy + */ + +#include +#include +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/ebpf/src/tls_fingerprint.bpf.c b/ebpf/src/tls_fingerprint.bpf.c new file mode 100644 index 0000000..15fa842 --- /dev/null +++ b/ebpf/src/tls_fingerprint.bpf.c @@ -0,0 +1,166 @@ +/* + * TLS Fingerprint Randomization eBPF Program + * JA3/JA3S spoofing and browser fingerprint simulation + */ + +#include +#include +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/ebpf/src/zapret_filter.bpf.c b/ebpf/src/zapret_filter.bpf.c new file mode 100644 index 0000000..013a719 --- /dev/null +++ b/ebpf/src/zapret_filter.bpf.c @@ -0,0 +1,369 @@ +/* + * Zapret eBPF Main Filter Program + * Packet filtering with DPI evasion techniques + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/zapret_config.conf b/zapret_config.conf new file mode 100644 index 0000000..ef972a6 --- /dev/null +++ b/zapret_config.conf @@ -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 \ No newline at end of file