Browse Source

.

pull/1667/head
ibrahimsql 1 day ago
parent
commit
4bc2651ef2
  1. 4
      Makefile
  2. 175
      ebpf/Makefile
  3. 285
      ebpf/include/zapret_ebpf.h
  4. 467
      ebpf/loader/ebpf_manager.c
  5. 458
      ebpf/loader/performance_monitor.c
  6. 378
      ebpf/loader/zapret_loader.c
  7. 271
      ebpf/src/packet_fragment.bpf.c
  8. 265
      ebpf/src/quic_filter.bpf.c
  9. 298
      ebpf/src/sni_encrypt.bpf.c
  10. 166
      ebpf/src/tls_fingerprint.bpf.c
  11. 369
      ebpf/src/zapret_filter.bpf.c
  12. 92
      zapret_config.conf

4
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

175
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 <stdio.h>' > demo.c
@echo 'int main() { printf("Zapret demo for $(PLATFORM)\\n"); return 0; }' >> demo.c
@$(CC) $(CFLAGS) demo.c -o zapret_demo
@rm -f demo.c
install: all
cp $(TARGETS) /usr/local/bin/
cp $(EBPF_OBJS) /usr/local/share/zapret/ebpf/
clean:
rm -f $(EBPF_OBJS) $(LOADER_OBJS) $(TARGETS)
@if [ -d "$(LIBBPF_DIR)" ]; then \
$(MAKE) -C $(LIBBPF_DIR)/src clean; \
fi
distclean: clean
rm -rf $(LIBBPF_DIR)
# Development targets
format:
clang-format -i $(LOADER_SRCS) include/*.h
check:
@echo "Checking eBPF programs..."
@for prog in $(EBPF_OBJS); do \
echo "Verifying $$prog"; \
bpftool prog load $$prog /sys/fs/bpf/test_$$prog 2>/dev/null && \
bpftool prog del pinned /sys/fs/bpf/test_$$prog || true; \
done
# macOS target
mac: demo
@echo "macOS build completed (demo mode)"
# Help target
help:
@echo "Available targets:"
@echo " all - Build all eBPF programs and loader"
@echo " mac - Build for macOS (demo mode)"
@echo " libbpf - Build libbpf dependency"
@echo " install - Install binaries to system"
@echo " clean - Clean build artifacts"
@echo " distclean - Clean everything including dependencies"
@echo " format - Format source code"
@echo " check - Verify eBPF programs"
@echo " help - Show this help"

285
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 <stdint.h>
#include <stdbool.h>
#ifdef __KERNEL__
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#endif
/* Maximum supported packet size */
#define MAX_PACKET_SIZE 1500
#define MAX_HOSTNAME_LEN 256
#define MAX_JA3_LEN 512
#define MAX_FRAGMENTS 16
/* Protocol definitions */
#define IPPROTO_QUIC 17 /* QUIC over UDP */
#define TLS_HANDSHAKE_TYPE 22
#define TLS_CLIENT_HELLO 1
#define QUIC_INITIAL_PACKET 0x00
/* TLS versions */
#define TLS_VERSION_1_2 0x0303
#define TLS_VERSION_1_3 0x0304
/* QUIC versions */
#define QUIC_VERSION_1 0x00000001
#define QUIC_VERSION_2 0x6b3343cf
/* Action flags */
#define ACTION_PASS 0
#define ACTION_DROP 1
#define ACTION_FRAGMENT 2
#define ACTION_RANDOMIZE_TLS 4
#define ACTION_ENCRYPT_SNI 8
#define ACTION_MODIFY_JA3 16
/* Action flags for filter rules */
#define ACTION_ALLOW 0x01
#define ACTION_BLOCK 0x02
#define ACTION_RANDOMIZE_TLS 0x04
#define ACTION_FRAGMENT_PACKET 0x08
#define ACTION_ENCRYPT_SNI 0x10
/* Maximum cipher suites */
#define MAX_CIPHER_SUITES 32
/* Fragment strategies */
#define FRAG_STRATEGY_TCP_SEG 1
#define FRAG_STRATEGY_IP_FRAG 2
#define FRAG_STRATEGY_TLS_REC 3
/* Performance monitoring */
struct perf_stats {
uint64_t packets_processed;
uint64_t packets_filtered;
uint64_t packets_fragmented;
uint64_t packets_dropped;
uint64_t tls_fingerprints_randomized;
uint64_t sni_encrypted;
uint64_t bytes_processed;
uint64_t processing_time_ns;
uint64_t total_processing_time;
uint64_t max_processing_time;
uint64_t min_processing_time;
uint64_t last_update;
};
/* TLS fingerprint structure */
struct tls_fingerprint {
uint16_t version;
uint16_t cipher_suites[32];
uint8_t cipher_suites_len;
uint16_t extensions[16];
uint8_t extensions_len;
uint16_t elliptic_curves[8];
uint8_t elliptic_curves_len;
uint8_t signature_algorithms[16];
uint8_t signature_algorithms_len;
char ja3_hash[33]; /* MD5 hash as hex string */
};
/* Alternative TLS fingerprint structure for compatibility */
struct tls_fingerprint_compat {
uint16_t version;
uint16_t cipher_suites[MAX_CIPHER_SUITES];
uint8_t cipher_count;
uint8_t extensions[32];
uint32_t ja3_hash;
};
/* QUIC connection info */
struct quic_conn_info {
uint32_t version;
uint8_t dcid[20];
uint8_t dcid_len;
uint8_t scid[20];
uint8_t scid_len;
uint16_t initial_packet_len;
uint8_t has_sni;
char sni[MAX_HOSTNAME_LEN];
};
/* Alternative QUIC connection information for compatibility */
struct quic_conn_info_compat {
uint32_t version;
uint8_t connection_id[20];
uint8_t packet_type;
uint32_t packet_number;
uint8_t is_initial;
};
/* Packet fragmentation context */
struct fragment_ctx {
uint32_t strategy;
uint16_t fragment_size;
uint16_t fragments_count;
uint32_t sequence_base;
uint8_t randomize_order;
};
/* Alternative packet fragmentation context for compatibility */
struct fragment_ctx_compat {
uint32_t fragment_size;
uint32_t max_fragments;
uint8_t fragmentation_method;
uint8_t enabled;
uint8_t needs_fragmentation;
uint32_t original_size;
uint8_t fragment_count;
uint32_t fragment_offsets[MAX_FRAGMENTS];
uint32_t fragment_sizes[MAX_FRAGMENTS];
};
/* SNI encryption context */
struct sni_encrypt_ctx {
uint8_t enabled;
uint8_t ech_supported;
uint8_t esni_supported;
uint8_t key[32]; /* Encryption key */
uint8_t iv[16]; /* Initialization vector */
};
/* Connection tracking entry */
struct conn_track {
uint32_t src_ip;
uint32_t dst_ip;
uint16_t src_port;
uint16_t dst_port;
uint8_t protocol;
uint8_t state;
uint64_t first_seen;
uint64_t last_seen;
uint32_t packets_count;
uint32_t bytes_count;
struct tls_fingerprint tls_fp;
struct quic_conn_info quic_info;
struct fragment_ctx frag_ctx;
struct sni_encrypt_ctx sni_ctx;
};
/* Filter rule structure */
struct filter_rule {
uint32_t src_ip;
uint32_t src_mask;
uint32_t dst_ip;
uint32_t dst_mask;
uint16_t src_port_min;
uint16_t src_port_max;
uint16_t dst_port_min;
uint16_t dst_port_max;
uint8_t protocol;
uint32_t action_flags;
uint32_t priority;
char hostname_pattern[MAX_HOSTNAME_LEN];
uint8_t enabled;
};
/* Configuration structure */
struct zapret_config {
uint8_t enable_tls_randomization;
uint8_t enable_quic_filtering;
uint8_t enable_packet_fragmentation;
uint8_t enable_sni_encryption;
uint8_t enable_performance_monitoring;
uint32_t max_connections;
uint32_t connection_timeout;
uint32_t fragment_threshold;
struct filter_rule default_rule;
};
/* Map definitions */
#ifdef __KERNEL__
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 65536);
__type(key, uint64_t); /* 5-tuple hash */
__type(value, struct conn_track);
} connection_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1024);
__type(key, uint32_t);
__type(value, struct filter_rule);
} filter_rules SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, uint32_t);
__type(value, struct zapret_config);
} config_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, uint32_t);
__type(value, struct perf_stats);
} stats_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, char[MAX_HOSTNAME_LEN]);
__type(value, uint32_t); /* action flags */
} hostname_rules SEC(".maps");
/* TLS fingerprint randomization patterns */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 100);
__type(key, uint32_t);
__type(value, struct tls_fingerprint);
} tls_fingerprint_pool SEC(".maps");
#endif
/* Helper function declarations */
#ifdef __KERNEL__
static inline uint64_t compute_5tuple_hash(struct iphdr *ip, void *l4_hdr);
static inline int parse_tls_client_hello(void *data, void *data_end, struct tls_fingerprint *fp);
static inline int parse_quic_initial(void *data, void *data_end, struct quic_conn_info *quic);
static inline int extract_sni_from_tls(void *data, void *data_end, char *sni, int max_len);
static inline int randomize_tls_fingerprint(struct tls_fingerprint *fp);
static inline int fragment_packet(struct __sk_buff *skb, struct fragment_ctx *ctx);
static inline int encrypt_sni(char *sni, struct sni_encrypt_ctx *ctx);
static inline void update_performance_stats(struct perf_stats *stats, uint64_t start_time);
#endif
/* Utility macros */
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define PACKET_HOST(skb) ((skb)->pkt_type == PACKET_HOST)
#define PACKET_OUTGOING(skb) ((skb)->pkt_type == PACKET_OUTGOING)
/* Bounds checking macro */
#define BOUNDS_CHECK(ptr, end, size) \
({ \
void *_ptr = (void *)(ptr); \
void *_end = (void *)(end); \
(_ptr + (size) <= _end) ? 1 : 0; \
})
/* Network byte order conversion helpers */
#define NET16(x) bpf_htons(x)
#define NET32(x) bpf_htonl(x)
#define HOST16(x) bpf_ntohs(x)
#define HOST32(x) bpf_ntohl(x)
#endif /* __ZAPRET_EBPF_H__ */

467
ebpf/loader/ebpf_manager.c

@ -0,0 +1,467 @@
/*
* Zapret eBPF Manager
* High-level interface for eBPF program management and integration
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <time.h>
#include <sys/stat.h>
#include "../include/zapret_ebpf.h"
/* Connection tracking hash table */
#define CONN_HASH_SIZE 65536
static struct conn_track *connection_table[CONN_HASH_SIZE];
static pthread_mutex_t conn_mutex = PTHREAD_MUTEX_INITIALIZER;
/* Filter rules array */
#define MAX_RULES 1024
static struct filter_rule filter_rules[MAX_RULES];
static int rule_count = 0;
static pthread_mutex_t rules_mutex = PTHREAD_MUTEX_INITIALIZER;
/* TLS fingerprint pool */
#define FINGERPRINT_POOL_SIZE 100
static struct tls_fingerprint_compat fingerprint_pool[FINGERPRINT_POOL_SIZE];
static int fingerprint_count = 0;
/* Global configuration and statistics */
extern struct zapret_config global_config;
extern struct perf_stats global_stats;
extern pthread_mutex_t stats_mutex;
/* Hash function for connection tracking */
static uint32_t hash_connection(uint32_t src_ip, uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, uint8_t protocol) {
uint32_t hash = src_ip;
hash = hash * 31 + dst_ip;
hash = hash * 31 + src_port;
hash = hash * 31 + dst_port;
hash = hash * 31 + protocol;
return hash % CONN_HASH_SIZE;
}
/* Connection tracking functions */
struct conn_track* find_connection(uint32_t src_ip, uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, uint8_t protocol) {
uint32_t hash = hash_connection(src_ip, dst_ip, src_port, dst_port, protocol);
pthread_mutex_lock(&conn_mutex);
struct conn_track *conn = connection_table[hash];
while (conn) {
if (conn->src_ip == src_ip && conn->dst_ip == dst_ip &&
conn->src_port == src_port && conn->dst_port == dst_port &&
conn->protocol == protocol) {
pthread_mutex_unlock(&conn_mutex);
return conn;
}
conn = (struct conn_track*)conn->bytes_count; /* Using bytes_count as next pointer */
}
pthread_mutex_unlock(&conn_mutex);
return NULL;
}
struct conn_track* create_connection(uint32_t src_ip, uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, uint8_t protocol) {
struct conn_track *conn = malloc(sizeof(struct conn_track));
if (!conn) return NULL;
memset(conn, 0, sizeof(*conn));
conn->src_ip = src_ip;
conn->dst_ip = dst_ip;
conn->src_port = src_port;
conn->dst_port = dst_port;
conn->protocol = protocol;
conn->first_seen = time(NULL);
conn->last_seen = conn->first_seen;
conn->state = 1; /* Active */
/* Initialize fragmentation context */
struct fragment_ctx_compat *frag_ctx = (struct fragment_ctx_compat*)&conn->frag_ctx;
frag_ctx->fragment_size = global_config.fragment_threshold;
frag_ctx->max_fragments = MAX_FRAGMENTS;
frag_ctx->enabled = global_config.enable_packet_fragmentation;
uint32_t hash = hash_connection(src_ip, dst_ip, src_port, dst_port, protocol);
pthread_mutex_lock(&conn_mutex);
/* Insert at head of hash bucket */
conn->bytes_count = (uint32_t)(uintptr_t)connection_table[hash];
connection_table[hash] = conn;
pthread_mutex_unlock(&conn_mutex);
return conn;
}
void cleanup_old_connections(void) {
time_t current_time = time(NULL);
pthread_mutex_lock(&conn_mutex);
for (int i = 0; i < CONN_HASH_SIZE; i++) {
struct conn_track **curr = &connection_table[i];
while (*curr) {
struct conn_track *conn = *curr;
if (current_time - conn->last_seen > global_config.connection_timeout) {
/* Remove expired connection */
*curr = (struct conn_track*)(uintptr_t)conn->bytes_count;
free(conn);
} else {
curr = (struct conn_track**)(uintptr_t)&conn->bytes_count;
}
}
}
pthread_mutex_unlock(&conn_mutex);
}
/* Filter rule management */
int add_filter_rule(struct filter_rule *rule) {
if (!rule) return -1;
pthread_mutex_lock(&rules_mutex);
if (rule_count >= MAX_RULES) {
pthread_mutex_unlock(&rules_mutex);
return -1;
}
memcpy(&filter_rules[rule_count], rule, sizeof(*rule));
rule_count++;
pthread_mutex_unlock(&rules_mutex);
return 0;
}
struct filter_rule* match_filter_rule(uint32_t src_ip, uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, uint8_t protocol) {
pthread_mutex_lock(&rules_mutex);
for (int i = 0; i < rule_count; i++) {
struct filter_rule *rule = &filter_rules[i];
if (!rule->enabled) continue;
/* Check protocol */
if (rule->protocol != 0 && rule->protocol != protocol) continue;
/* Check source IP */
if (rule->src_mask != 0) {
if ((src_ip & rule->src_mask) != (rule->src_ip & rule->src_mask)) continue;
}
/* Check destination IP */
if (rule->dst_mask != 0) {
if ((dst_ip & rule->dst_mask) != (rule->dst_ip & rule->dst_mask)) continue;
}
/* Check source port range */
if (rule->src_port_min != 0 || rule->src_port_max != 0) {
if (src_port < rule->src_port_min || src_port > rule->src_port_max) continue;
}
/* Check destination port range */
if (rule->dst_port_min != 0 || rule->dst_port_max != 0) {
if (dst_port < rule->dst_port_min || dst_port > rule->dst_port_max) continue;
}
pthread_mutex_unlock(&rules_mutex);
return rule;
}
pthread_mutex_unlock(&rules_mutex);
return NULL;
}
/* TLS fingerprint management */
void init_fingerprint_pool(void) {
/* Initialize with common browser fingerprints */
struct {
uint16_t version;
uint16_t ciphers[8];
int cipher_count;
} common_fingerprints[] = {
/* Chrome-like */
{0x0303, {0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0x009e, 0x009f}, 8},
/* Firefox-like */
{0x0303, {0x1301, 0x1302, 0x1303, 0xc02b, 0xc02c, 0xc030, 0x009e, 0x006b}, 8},
/* Safari-like */
{0x0304, {0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0x009e, 0x009f}, 8},
/* Edge-like */
{0x0303, {0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc030, 0x009e, 0x006b}, 8}
};
fingerprint_count = sizeof(common_fingerprints) / sizeof(common_fingerprints[0]);
for (int i = 0; i < fingerprint_count; i++) {
fingerprint_pool[i].version = common_fingerprints[i].version;
fingerprint_pool[i].cipher_count = common_fingerprints[i].cipher_count;
for (int j = 0; j < common_fingerprints[i].cipher_count; j++) {
fingerprint_pool[i].cipher_suites[j] = common_fingerprints[i].ciphers[j];
}
/* Generate JA3 hash (simplified) */
fingerprint_pool[i].ja3_hash = fingerprint_pool[i].version * 31 + fingerprint_pool[i].cipher_count;
}
}
struct tls_fingerprint_compat* get_random_fingerprint(void) {
if (fingerprint_count == 0) return NULL;
int index = rand() % fingerprint_count;
return &fingerprint_pool[index];
}
/* Packet processing interface */
int process_packet_ebpf(uint8_t *data, size_t len, const char *interface) {
if (!data || len == 0) return -1;
struct timespec start_time;
clock_gettime(CLOCK_MONOTONIC, &start_time);
/* Parse basic packet headers */
if (len < 34) return -1; /* Minimum Ethernet + IP header */
/* Skip Ethernet header */
uint8_t *ip_hdr = data + 14;
uint8_t ip_version = (ip_hdr[0] >> 4) & 0x0F;
if (ip_version != 4) return 0; /* Only IPv4 for now */
uint8_t ip_hdr_len = (ip_hdr[0] & 0x0F) * 4;
uint8_t protocol = ip_hdr[9];
uint32_t src_ip = *(uint32_t*)(ip_hdr + 12);
uint32_t dst_ip = *(uint32_t*)(ip_hdr + 16);
uint16_t src_port = 0, dst_port = 0;
/* Extract port numbers */
if (protocol == 6 || protocol == 17) { /* TCP or UDP */
if (len >= 14 + ip_hdr_len + 4) {
uint8_t *l4_hdr = ip_hdr + ip_hdr_len;
src_port = (l4_hdr[0] << 8) | l4_hdr[1];
dst_port = (l4_hdr[2] << 8) | l4_hdr[3];
}
}
/* Find or create connection */
struct conn_track *conn = find_connection(src_ip, dst_ip, src_port, dst_port, protocol);
if (!conn) {
conn = create_connection(src_ip, dst_ip, src_port, dst_port, protocol);
}
if (conn) {
conn->last_seen = time(NULL);
conn->packets_count++;
/* Apply filter rules */
struct filter_rule *rule = match_filter_rule(src_ip, dst_ip, src_port, dst_port, protocol);
if (rule) {
/* Apply TLS randomization */
if ((rule->action_flags & ACTION_RANDOMIZE_TLS) && protocol == 6) {
/* Look for TLS handshake */
if (len > 14 + ip_hdr_len + 20) { /* TCP header minimum */
uint8_t *tcp_hdr = ip_hdr + ip_hdr_len;
uint8_t tcp_hdr_len = ((tcp_hdr[12] >> 4) & 0x0F) * 4;
uint8_t *payload = tcp_hdr + tcp_hdr_len;
if (payload < data + len && payload[0] == 0x16) { /* TLS handshake */
struct tls_fingerprint_compat *random_fp = get_random_fingerprint();
if (random_fp) {
memcpy(&conn->tls_fp, random_fp, sizeof(struct tls_fingerprint));
pthread_mutex_lock(&stats_mutex);
global_stats.tls_fingerprints_randomized++;
pthread_mutex_unlock(&stats_mutex);
}
}
}
}
/* Apply packet fragmentation */
if ((rule->action_flags & ACTION_FRAGMENT_PACKET) && global_config.enable_packet_fragmentation) {
struct fragment_ctx_compat *frag_ctx = (struct fragment_ctx_compat*)&conn->frag_ctx;
if (len > frag_ctx->fragment_size) {
frag_ctx->needs_fragmentation = 1;
frag_ctx->original_size = len;
pthread_mutex_lock(&stats_mutex);
global_stats.packets_fragmented++;
pthread_mutex_unlock(&stats_mutex);
}
}
}
}
/* Update performance statistics */
if (global_config.enable_performance_monitoring) {
struct timespec end_time;
clock_gettime(CLOCK_MONOTONIC, &end_time);
uint64_t processing_time = (end_time.tv_sec - start_time.tv_sec) * 1000000000UL +
(end_time.tv_nsec - start_time.tv_nsec);
pthread_mutex_lock(&stats_mutex);
global_stats.packets_processed++;
global_stats.bytes_processed += len;
global_stats.total_processing_time += processing_time;
if (processing_time > global_stats.max_processing_time)
global_stats.max_processing_time = processing_time;
if (processing_time < global_stats.min_processing_time || global_stats.min_processing_time == 0)
global_stats.min_processing_time = processing_time;
pthread_mutex_unlock(&stats_mutex);
}
return 0;
}
/* Configuration management */
int load_filter_rules_from_file(const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) {
printf("Warning: Could not open rules file %s\n", filename);
return -1;
}
char line[512];
int loaded_rules = 0;
while (fgets(line, sizeof(line), fp) && loaded_rules < MAX_RULES) {
/* Skip comments and empty lines */
if (line[0] == '#' || line[0] == '\n') continue;
struct filter_rule rule = {0};
/* Parse rule format: src_ip/mask:port-port dst_ip/mask:port-port protocol action */
char src_spec[64], dst_spec[64], proto_str[16], action_str[64];
if (sscanf(line, "%63s %63s %15s %63s", src_spec, dst_spec, proto_str, action_str) == 4) {
/* Parse source specification */
char *slash = strchr(src_spec, '/');
char *colon = strchr(src_spec, ':');
if (slash) {
*slash = '\0';
rule.src_ip = inet_addr(src_spec);
rule.src_mask = inet_addr(slash + 1);
}
if (colon) {
char *dash = strchr(colon + 1, '-');
rule.src_port_min = atoi(colon + 1);
rule.src_port_max = dash ? atoi(dash + 1) : rule.src_port_min;
}
/* Parse destination specification */
slash = strchr(dst_spec, '/');
colon = strchr(dst_spec, ':');
if (slash) {
*slash = '\0';
rule.dst_ip = inet_addr(dst_spec);
rule.dst_mask = inet_addr(slash + 1);
}
if (colon) {
char *dash = strchr(colon + 1, '-');
rule.dst_port_min = atoi(colon + 1);
rule.dst_port_max = dash ? atoi(dash + 1) : rule.dst_port_min;
}
/* Parse protocol */
if (strcmp(proto_str, "tcp") == 0) rule.protocol = 6;
else if (strcmp(proto_str, "udp") == 0) rule.protocol = 17;
else rule.protocol = atoi(proto_str);
/* Parse actions */
if (strstr(action_str, "randomize_tls")) rule.action_flags |= ACTION_RANDOMIZE_TLS;
if (strstr(action_str, "fragment")) rule.action_flags |= ACTION_FRAGMENT_PACKET;
if (strstr(action_str, "encrypt_sni")) rule.action_flags |= ACTION_ENCRYPT_SNI;
if (strstr(action_str, "allow")) rule.action_flags |= ACTION_ALLOW;
if (strstr(action_str, "block")) rule.action_flags |= ACTION_BLOCK;
rule.enabled = 1;
rule.priority = 100;
if (add_filter_rule(&rule) == 0) {
loaded_rules++;
}
}
}
fclose(fp);
printf("Loaded %d filter rules from %s\n", loaded_rules, filename);
return loaded_rules;
}
/* Cleanup thread */
void* cleanup_thread(void *arg) {
while (1) {
sleep(60); /* Cleanup every minute */
cleanup_old_connections();
}
return NULL;
}
/* Initialize eBPF manager */
int init_ebpf_manager(void) {
/* Initialize fingerprint pool */
init_fingerprint_pool();
/* Load default filter rules */
struct filter_rule default_rule = {0};
default_rule.action_flags = ACTION_RANDOMIZE_TLS | ACTION_FRAGMENT_PACKET;
default_rule.priority = 100;
default_rule.enabled = 1;
add_filter_rule(&default_rule);
/* Start cleanup thread */
pthread_t cleanup_tid;
pthread_create(&cleanup_tid, NULL, cleanup_thread, NULL);
pthread_detach(cleanup_tid);
printf("eBPF manager initialized\n");
printf(" Fingerprint pool: %d entries\n", fingerprint_count);
printf(" Filter rules: %d loaded\n", rule_count);
return 0;
}
/* Get connection statistics */
int get_connection_stats(int *active_connections, int *total_connections) {
int active = 0, total = 0;
time_t current_time = time(NULL);
pthread_mutex_lock(&conn_mutex);
for (int i = 0; i < CONN_HASH_SIZE; i++) {
struct conn_track *conn = connection_table[i];
while (conn) {
total++;
if (current_time - conn->last_seen <= 60) { /* Active in last minute */
active++;
}
conn = (struct conn_track*)(uintptr_t)conn->bytes_count;
}
}
pthread_mutex_unlock(&conn_mutex);
if (active_connections) *active_connections = active;
if (total_connections) *total_connections = total;
return 0;
}

458
ebpf/loader/performance_monitor.c

@ -0,0 +1,458 @@
/*
* Zapret Performance Monitor
* Performance monitoring and optimization for eBPF packet filtering
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <pthread.h>
#include <signal.h>
#include <math.h>
#include "../include/zapret_ebpf.h"
/* Performance monitoring configuration */
#define PERF_SAMPLE_INTERVAL 1000 /* 1 second in milliseconds */
#define PERF_HISTORY_SIZE 3600 /* 1 hour of samples */
#define PERF_ALERT_THRESHOLD_CPU 80.0 /* CPU usage alert threshold */
#define PERF_ALERT_THRESHOLD_MEMORY 90.0 /* Memory usage alert threshold */
#define PERF_ALERT_THRESHOLD_LATENCY 10000000 /* 10ms latency threshold */
/* Performance metrics history */
struct perf_sample {
time_t timestamp;
double cpu_usage;
double memory_usage;
uint64_t packets_per_second;
uint64_t bytes_per_second;
uint64_t avg_latency_ns;
uint64_t max_latency_ns;
uint64_t connections_active;
uint64_t tls_randomizations;
uint64_t packet_fragmentations;
uint64_t sni_encryptions;
};
static struct perf_sample perf_history[PERF_HISTORY_SIZE];
static int perf_history_index = 0;
static int perf_history_count = 0;
static pthread_mutex_t perf_mutex = PTHREAD_MUTEX_INITIALIZER;
/* Performance alerts */
struct perf_alert {
time_t timestamp;
char message[256];
int severity; /* 1=info, 2=warning, 3=critical */
};
#define MAX_ALERTS 100
static struct perf_alert alert_history[MAX_ALERTS];
static int alert_count = 0;
static pthread_mutex_t alert_mutex = PTHREAD_MUTEX_INITIALIZER;
/* External references */
extern struct perf_stats global_stats;
extern pthread_mutex_t stats_mutex;
extern int get_connection_stats(int *active_connections, int *total_connections);
/* Forward declarations */
void check_performance_alerts(struct perf_sample *sample);
/* System resource monitoring */
static double get_cpu_usage(void) {
static struct rusage prev_usage = {0};
static struct timeval prev_time = {0};
struct rusage current_usage;
struct timeval current_time;
getrusage(RUSAGE_SELF, &current_usage);
gettimeofday(&current_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;
}

378
ebpf/loader/zapret_loader.c

@ -0,0 +1,378 @@
/*
* Zapret eBPF Loader and Manager
* User-space program to load and manage eBPF programs
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/resource.h>
#ifdef DEMO_MODE
/* Demo mode - no eBPF dependencies */
#define BPF_PROG_TYPE_XDP 0
#define BPF_PROG_TYPE_SCHED_CLS 1
typedef struct { int fd; } bpf_object;
typedef struct { int fd; } bpf_program;
typedef struct { int fd; } bpf_map;
/* Stub functions for demo mode */
static inline int bpf_object__load(bpf_object *obj) { return 0; }
static inline void bpf_object__close(bpf_object *obj) { }
static inline bpf_program *bpf_object__find_program_by_name(bpf_object *obj, const char *name) { return NULL; }
static inline int bpf_program__fd(bpf_program *prog) { return -1; }
static inline bpf_map *bpf_object__find_map_by_name(bpf_object *obj, const char *name) { return NULL; }
static inline int bpf_map__fd(bpf_map *map) { return -1; }
#elif defined(__linux__)
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#else
/* Non-Linux platforms - use demo mode */
#define DEMO_MODE
#define BPF_PROG_TYPE_XDP 0
#define BPF_PROG_TYPE_SCHED_CLS 1
typedef struct { int fd; } bpf_object;
typedef struct { int fd; } bpf_program;
typedef struct { int fd; } bpf_map;
static inline int bpf_object__load(bpf_object *obj) { return 0; }
static inline void bpf_object__close(bpf_object *obj) { }
static inline bpf_program *bpf_object__find_program_by_name(bpf_object *obj, const char *name) { return NULL; }
static inline int bpf_program__fd(bpf_program *prog) { return -1; }
static inline bpf_map *bpf_object__find_map_by_name(bpf_object *obj, const char *name) { return NULL; }
static inline int bpf_map__fd(bpf_map *map) { return -1; }
#endif
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <time.h>
#include "../include/zapret_ebpf.h"
/* Global state */
static struct zapret_config global_config = {0};
static struct perf_stats global_stats = {0};
static pthread_mutex_t stats_mutex = PTHREAD_MUTEX_INITIALIZER;
static volatile int running = 1;
/* Signal handler for graceful shutdown */
void signal_handler(int sig) {
printf("\nReceived signal %d, shutting down...\n", sig);
running = 0;
}
/* Initialize default configuration */
void init_default_config(struct zapret_config *config) {
if (!config) return;
memset(config, 0, sizeof(*config));
config->enable_tls_randomization = 1;
config->enable_quic_filtering = 1;
config->enable_packet_fragmentation = 1;
config->enable_sni_encryption = 0; /* Disabled by default */
config->enable_performance_monitoring = 1;
config->max_connections = 65536;
config->connection_timeout = 300; /* 5 minutes */
config->fragment_threshold = 1400; /* Standard MTU minus headers */
/* Initialize default filter rule */
config->default_rule.action_flags = ACTION_RANDOMIZE_TLS | ACTION_FRAGMENT_PACKET;
config->default_rule.priority = 100;
config->default_rule.enabled = 1;
}
/* TLS fingerprint randomization */
int randomize_tls_fingerprint(struct tls_fingerprint_compat *fp) {
if (!fp) return -1;
/* Randomize TLS version */
uint16_t tls_versions[] = {0x0303, 0x0304}; /* TLS 1.2, 1.3 */
fp->version = tls_versions[rand() % 2];
/* Randomize cipher suites */
uint16_t common_ciphers[] = {
0x1301, 0x1302, 0x1303, /* TLS 1.3 ciphers */
0xc02b, 0xc02f, 0xc02c, /* ECDHE ciphers */
0x009e, 0x009f, 0x006b /* AES-GCM ciphers */
};
int cipher_count = 3 + (rand() % 6); /* 3-8 ciphers */
if (cipher_count > MAX_CIPHER_SUITES)
cipher_count = MAX_CIPHER_SUITES;
fp->cipher_count = cipher_count;
for (int i = 0; i < cipher_count; i++) {
fp->cipher_suites[i] = common_ciphers[rand() % (sizeof(common_ciphers)/sizeof(common_ciphers[0]))];
}
return 0;
}
/* QUIC connection analysis */
int analyze_quic_packet(const uint8_t *data, size_t len, struct quic_conn_info_compat *quic) {
if (!data || len < 1 || !quic) return -1;
/* Check for QUIC long header */
if ((data[0] & 0x80) == 0) {
quic->is_initial = 0;
return 0;
}
/* Parse QUIC initial packet */
if (len < 5) return -1;
quic->version = (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
quic->is_initial = ((data[0] & 0x30) == 0x00);
/* Extract connection ID if present */
if (len > 5) {
uint8_t dcid_len = data[5];
if (dcid_len > 0 && dcid_len <= 20 && len > 5 + dcid_len) {
memcpy(quic->connection_id, &data[6], dcid_len < 20 ? dcid_len : 20);
}
}
return 0;
}
/* Packet fragmentation logic */
int fragment_packet_data(uint8_t *data, size_t len, struct fragment_ctx_compat *ctx) {
if (!data || !ctx || !ctx->enabled) return 0;
if (len > ctx->fragment_size) {
ctx->needs_fragmentation = 1;
ctx->original_size = len;
/* Calculate optimal fragment positions */
ctx->fragment_count = (len + ctx->fragment_size - 1) / ctx->fragment_size;
if (ctx->fragment_count > MAX_FRAGMENTS)
ctx->fragment_count = MAX_FRAGMENTS;
for (int i = 0; i < ctx->fragment_count; i++) {
ctx->fragment_offsets[i] = i * ctx->fragment_size;
ctx->fragment_sizes[i] = (i == ctx->fragment_count - 1) ?
(len - ctx->fragment_offsets[i]) : ctx->fragment_size;
}
}
return 0;
}
/* SNI encryption (placeholder for ECH/ESNI) */
int encrypt_sni_data(char *sni, size_t sni_len, struct sni_encrypt_ctx *ctx) {
if (!sni || !ctx || !ctx->enabled) return 0;
/* Simple XOR encryption for demonstration */
for (size_t i = 0; i < sni_len && i < 32; i++) {
sni[i] ^= ctx->key[i % 32];
}
return 0;
}
/* Performance monitoring */
void update_stats(uint64_t processing_time, size_t packet_size) {
pthread_mutex_lock(&stats_mutex);
global_stats.packets_processed++;
global_stats.bytes_processed += packet_size;
global_stats.total_processing_time += processing_time;
if (processing_time > global_stats.max_processing_time)
global_stats.max_processing_time = processing_time;
if (processing_time < global_stats.min_processing_time || global_stats.min_processing_time == 0)
global_stats.min_processing_time = processing_time;
pthread_mutex_unlock(&stats_mutex);
}
/* Print performance statistics */
void print_stats(void) {
pthread_mutex_lock(&stats_mutex);
printf("\n=== Zapret Performance Statistics ===\n");
printf("Packets processed: %lu\n", global_stats.packets_processed);
printf("Bytes processed: %lu\n", global_stats.bytes_processed);
printf("Total processing time: %lu ns\n", global_stats.total_processing_time);
if (global_stats.packets_processed > 0) {
uint64_t avg_time = global_stats.total_processing_time / global_stats.packets_processed;
printf("Average processing time: %lu ns\n", avg_time);
printf("Max processing time: %lu ns\n", global_stats.max_processing_time);
printf("Min processing time: %lu ns\n", global_stats.min_processing_time);
double pps = (double)global_stats.packets_processed * 1000000000.0 / global_stats.total_processing_time;
printf("Estimated packets per second: %.2f\n", pps);
}
pthread_mutex_unlock(&stats_mutex);
}
/* Configuration management */
int load_config_file(const char *filename, struct zapret_config *config) {
FILE *fp = fopen(filename, "r");
if (!fp) {
printf("Warning: Could not open config file %s, using defaults\n", filename);
return -1;
}
char line[256];
while (fgets(line, sizeof(line), fp)) {
/* Skip comments and empty lines */
if (line[0] == '#' || line[0] == '\n') continue;
/* Parse configuration options */
if (strncmp(line, "enable_tls_randomization=", 25) == 0) {
config->enable_tls_randomization = (line[25] == '1');
} else if (strncmp(line, "enable_quic_filtering=", 22) == 0) {
config->enable_quic_filtering = (line[22] == '1');
} else if (strncmp(line, "enable_packet_fragmentation=", 28) == 0) {
config->enable_packet_fragmentation = (line[28] == '1');
} else if (strncmp(line, "enable_sni_encryption=", 22) == 0) {
config->enable_sni_encryption = (line[22] == '1');
} else if (strncmp(line, "max_connections=", 16) == 0) {
config->max_connections = atoi(&line[16]);
} else if (strncmp(line, "fragment_threshold=", 19) == 0) {
config->fragment_threshold = atoi(&line[19]);
}
}
fclose(fp);
return 0;
}
/* Main packet processing function */
int process_packet(uint8_t *data, size_t len, struct conn_track *conn) {
if (!data || len == 0) return -1;
struct timespec start_time, end_time;
clock_gettime(CLOCK_MONOTONIC, &start_time);
/* Parse Ethernet header */
if (len < 14) return -1;
uint16_t eth_type = (data[12] << 8) | data[13];
if (eth_type != 0x0800) return 0; /* Not IPv4 */
/* Parse IP header */
if (len < 34) return -1;
uint8_t *ip_hdr = data + 14;
uint8_t ip_version = (ip_hdr[0] >> 4) & 0x0F;
if (ip_version != 4) return 0;
uint8_t ip_hdr_len = (ip_hdr[0] & 0x0F) * 4;
uint8_t protocol = ip_hdr[9];
/* Process based on protocol */
if (protocol == 6 && global_config.enable_tls_randomization) { /* TCP */
/* Look for TLS traffic */
uint8_t *tcp_hdr = ip_hdr + ip_hdr_len;
if (tcp_hdr + 20 <= data + len) {
uint8_t tcp_hdr_len = ((tcp_hdr[12] >> 4) & 0x0F) * 4;
uint8_t *payload = tcp_hdr + tcp_hdr_len;
if (payload < data + len && payload[0] == 0x16) { /* TLS handshake */
if (conn) {
randomize_tls_fingerprint(&conn->tls_fp);
}
}
}
} else if (protocol == 17 && global_config.enable_quic_filtering) { /* UDP */
/* Look for QUIC traffic */
uint8_t *udp_hdr = ip_hdr + ip_hdr_len;
if (udp_hdr + 8 <= data + len) {
uint8_t *payload = udp_hdr + 8;
size_t payload_len = len - (payload - data);
if (payload_len > 0 && conn) {
analyze_quic_packet(payload, payload_len, &conn->quic_info);
}
}
}
/* Apply packet fragmentation if needed */
if (global_config.enable_packet_fragmentation && conn) {
fragment_packet_data(data, len, &conn->frag_ctx);
}
/* Update performance statistics */
if (global_config.enable_performance_monitoring) {
clock_gettime(CLOCK_MONOTONIC, &end_time);
uint64_t processing_time = (end_time.tv_sec - start_time.tv_sec) * 1000000000UL +
(end_time.tv_nsec - start_time.tv_nsec);
update_stats(processing_time, len);
}
return 0;
}
/* Statistics reporting thread */
void* stats_thread(void *arg) {
while (running) {
sleep(10); /* Print stats every 10 seconds */
if (global_stats.packets_processed > 0) {
print_stats();
}
}
return NULL;
}
/* Main function */
int main(int argc, char *argv[]) {
printf("Zapret eBPF Loader v1.0\n");
printf("DPI Evasion and Packet Filtering\n\n");
/* Initialize configuration */
init_default_config(&global_config);
/* Load configuration file if provided */
if (argc > 1) {
load_config_file(argv[1], &global_config);
}
/* Setup signal handlers */
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
/* Initialize random seed */
srand(time(NULL));
printf("Configuration loaded:\n");
printf(" TLS Randomization: %s\n", global_config.enable_tls_randomization ? "Enabled" : "Disabled");
printf(" QUIC Filtering: %s\n", global_config.enable_quic_filtering ? "Enabled" : "Disabled");
printf(" Packet Fragmentation: %s\n", global_config.enable_packet_fragmentation ? "Enabled" : "Disabled");
printf(" SNI Encryption: %s\n", global_config.enable_sni_encryption ? "Enabled" : "Disabled");
printf(" Performance Monitoring: %s\n", global_config.enable_performance_monitoring ? "Enabled" : "Disabled");
printf(" Max Connections: %u\n", global_config.max_connections);
printf(" Fragment Threshold: %u bytes\n\n", global_config.fragment_threshold);
/* Start statistics thread */
pthread_t stats_tid;
if (global_config.enable_performance_monitoring) {
pthread_create(&stats_tid, NULL, stats_thread, NULL);
}
printf("Zapret eBPF loader is running...\n");
printf("Press Ctrl+C to stop\n\n");
/* Main processing loop */
while (running) {
/* In a real implementation, this would interface with netfilter/tc/xdp */
/* For now, just sleep and let the stats thread run */
sleep(1);
}
/* Cleanup */
if (global_config.enable_performance_monitoring) {
pthread_join(stats_tid, NULL);
print_stats();
}
printf("\nZapret eBPF loader stopped.\n");
return 0;
}

271
ebpf/src/packet_fragment.bpf.c

@ -0,0 +1,271 @@
/*
* Packet Fragmentation eBPF Program
* Strategic TCP segmentation and IP fragmentation for DPI evasion
*/
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include "../include/zapret_ebpf.h"
/* License required for eBPF programs */
char LICENSE[] SEC("license") = "GPL";
/* Fragmentation configuration */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, uint32_t);
__type(value, struct fragment_config);
} frag_config SEC(".maps");
/* Fragmentation statistics */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, uint32_t);
__type(value, struct fragment_stats);
} frag_stats SEC(".maps");
/* Connection fragmentation state */
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10000);
__type(key, uint64_t); /* connection hash */
__type(value, struct fragment_ctx);
} frag_state SEC(".maps");
/* Helper function to compute connection hash */
static __always_inline uint64_t compute_conn_hash(struct iphdr *ip, void *l4_hdr) {
if (!ip || !l4_hdr)
return 0;
uint64_t hash = 0;
hash = (uint64_t)ip->saddr;
hash = hash * 31 + (uint64_t)ip->daddr;
hash = hash * 31 + (uint64_t)ip->protocol;
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = (struct tcphdr *)l4_hdr;
hash = hash * 31 + (uint64_t)tcp->source;
hash = hash * 31 + (uint64_t)tcp->dest;
} else if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp = (struct udphdr *)l4_hdr;
hash = hash * 31 + (uint64_t)udp->source;
hash = hash * 31 + (uint64_t)udp->dest;
}
return hash;
}
/* Helper function to determine if packet should be fragmented */
static __always_inline int should_fragment_packet(struct __sk_buff *skb,
struct fragment_config *config,
struct iphdr *ip,
void *l4_hdr) {
if (!config || !config->enabled)
return 0;
/* Check packet size threshold */
if (skb->len < config->min_packet_size)
return 0;
/* Check protocol-specific rules */
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = (struct tcphdr *)l4_hdr;
uint16_t dst_port = bpf_ntohs(tcp->dest);
/* Fragment HTTPS traffic */
if (config->fragment_https && dst_port == 443)
return 1;
/* Fragment HTTP traffic */
if (config->fragment_http && dst_port == 80)
return 1;
/* Fragment based on TCP flags */
if (config->fragment_syn && tcp->syn)
return 1;
} else if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp = (struct udphdr *)l4_hdr;
uint16_t dst_port = bpf_ntohs(udp->dest);
/* Fragment DNS traffic */
if (config->fragment_dns && dst_port == 53)
return 1;
/* Fragment QUIC traffic */
if (config->fragment_quic && (dst_port == 443 || dst_port == 80))
return 1;
}
return 0;
}
/* Helper function to calculate optimal fragment position */
static __always_inline uint32_t calculate_fragment_position(struct __sk_buff *skb,
struct fragment_config *config,
struct iphdr *ip) {
uint32_t ip_header_len = ip->ihl * 4;
uint32_t l4_header_len = 0;
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = (void *)ip + ip_header_len;
l4_header_len = tcp->doff * 4;
} else if (ip->protocol == IPPROTO_UDP) {
l4_header_len = sizeof(struct udphdr);
}
uint32_t headers_len = sizeof(struct ethhdr) + ip_header_len + l4_header_len;
uint32_t payload_start = headers_len;
/* Calculate fragment position based on strategy */
switch (config->fragment_strategy) {
case FRAG_STRATEGY_FIXED:
return payload_start + config->fragment_offset;
case FRAG_STRATEGY_RANDOM:
{
uint32_t max_offset = skb->len - payload_start - config->min_fragment_size;
if (max_offset < config->min_fragment_size)
return payload_start + config->min_fragment_size;
uint32_t random_offset = bpf_get_prandom_u32() % max_offset;
return payload_start + config->min_fragment_size + random_offset;
}
case FRAG_STRATEGY_DPI_AWARE:
/* Fragment at strategic positions to break DPI signatures */
if (ip->protocol == IPPROTO_TCP) {
/* Fragment TLS Client Hello at cipher suites */
return payload_start + 64; /* Typical position */
} else {
/* Fragment QUIC at connection ID */
return payload_start + 16;
}
default:
return payload_start + config->fragment_offset;
}
}
/* Main packet fragmentation function */
SEC("tc")
int packet_fragmenter(struct __sk_buff *skb) {
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
/* Parse Ethernet header */
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return TC_ACT_OK;
/* Only process IP packets */
if (eth->h_proto != bpf_htons(ETH_P_IP))
return TC_ACT_OK;
/* Parse IP header */
struct iphdr *ip = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip) > data_end)
return TC_ACT_OK;
/* Get fragmentation configuration */
uint32_t config_key = 0;
struct fragment_config *config = bpf_map_lookup_elem(&frag_config, &config_key);
if (!config)
return TC_ACT_OK;
/* Parse L4 header */
void *l4_hdr = data + sizeof(*eth) + (ip->ihl * 4);
if (l4_hdr + sizeof(struct tcphdr) > data_end &&
l4_hdr + sizeof(struct udphdr) > data_end)
return TC_ACT_OK;
/* Check if packet should be fragmented */
if (!should_fragment_packet(skb, config, ip, l4_hdr))
return TC_ACT_OK;
/* Compute connection hash */
uint64_t conn_hash = compute_conn_hash(ip, l4_hdr);
/* Look up or create fragmentation context */
struct fragment_ctx *ctx = bpf_map_lookup_elem(&frag_state, &conn_hash);
if (!ctx) {
struct fragment_ctx new_ctx = {0};
new_ctx.enabled = 1;
new_ctx.fragment_size = config->default_fragment_size;
new_ctx.fragments_sent = 0;
bpf_map_update_elem(&frag_state, &conn_hash, &new_ctx, BPF_ANY);
ctx = bpf_map_lookup_elem(&frag_state, &conn_hash);
}
if (!ctx)
return TC_ACT_OK;
/* Calculate fragment position */
uint32_t frag_pos = calculate_fragment_position(skb, config, ip);
/* Mark packet for fragmentation */
ctx->needs_fragmentation = 1;
ctx->original_size = skb->len;
ctx->fragment_position = frag_pos;
/* Update statistics */
uint32_t stats_key = 0;
struct fragment_stats *stats = bpf_map_lookup_elem(&frag_stats, &stats_key);
if (stats) {
__sync_fetch_and_add(&stats->packets_fragmented, 1);
__sync_fetch_and_add(&stats->total_fragments, 2); /* Assume 2 fragments */
if (ip->protocol == IPPROTO_TCP)
__sync_fetch_and_add(&stats->tcp_fragments, 1);
else if (ip->protocol == IPPROTO_UDP)
__sync_fetch_and_add(&stats->udp_fragments, 1);
}
/* Increment fragment counter */
__sync_fetch_and_add(&ctx->fragments_sent, 1);
/* For actual fragmentation, this would need to be handled in user space
* or with more eBPF capabilities. Here we just mark the packet. */
return TC_ACT_OK;
}
/* XDP version for fragmentation marking */
SEC("xdp")
int packet_fragment_xdp(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
/* Parse Ethernet header */
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return XDP_PASS;
/* Only process IP packets */
if (eth->h_proto != bpf_htons(ETH_P_IP))
return XDP_PASS;
/* Parse IP header */
struct iphdr *ip = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip) > data_end)
return XDP_PASS;
/* Fast path fragmentation detection */
uint32_t packet_len = data_end - data;
/* Check for large packets that might need fragmentation */
if (packet_len > 1200) { /* Typical fragmentation threshold */
/* Mark for detailed processing in TC layer */
return XDP_PASS;
}
return XDP_PASS;
}

265
ebpf/src/quic_filter.bpf.c

@ -0,0 +1,265 @@
/*
* QUIC Protocol Filter eBPF Program
* QUIC/HTTP3 and DNS over QUIC processing
*/
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include "../include/zapret_ebpf.h"
/* License required for eBPF programs */
char LICENSE[] SEC("license") = "GPL";
/* QUIC connection tracking */
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10000);
__type(key, struct quic_conn_id);
__type(value, struct quic_conn_info);
} quic_connections SEC(".maps");
/* QUIC version support matrix */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 100);
__type(key, uint32_t);
__type(value, uint32_t); /* supported version */
} supported_versions SEC(".maps");
/* DNS over QUIC tracking */
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1000);
__type(key, uint32_t); /* connection hash */
__type(value, struct doq_session);
} doq_sessions SEC(".maps");
/* Helper function to parse QUIC header */
static __always_inline int parse_quic_header(void *data, void *data_end, struct quic_header *hdr) {
if (!data || !data_end || !hdr)
return -1;
if (data + 1 > data_end)
return -1;
uint8_t *quic_data = (uint8_t *)data;
hdr->flags = quic_data[0];
/* Check if it's a long header */
if (hdr->flags & 0x80) {
/* Long header packet */
if (data + 5 > data_end)
return -1;
hdr->version = (quic_data[1] << 24) | (quic_data[2] << 16) |
(quic_data[3] << 8) | quic_data[4];
hdr->is_long_header = 1;
/* Extract packet type */
hdr->packet_type = (hdr->flags & 0x30) >> 4;
} else {
/* Short header packet */
hdr->is_long_header = 0;
hdr->version = 0;
hdr->packet_type = 0;
}
return 0;
}
/* Helper function to extract connection ID */
static __always_inline int extract_connection_id(void *data, void *data_end,
struct quic_header *hdr,
struct quic_conn_id *conn_id) {
if (!data || !data_end || !hdr || !conn_id)
return -1;
uint8_t *pos = (uint8_t *)data;
if (hdr->is_long_header) {
/* Skip fixed header (1 + 4 bytes) */
pos += 5;
if (pos + 1 > data_end)
return -1;
/* Read destination connection ID length */
uint8_t dcid_len = *pos++;
if (dcid_len > MAX_CONN_ID_LEN)
dcid_len = MAX_CONN_ID_LEN;
if (pos + dcid_len > data_end)
return -1;
conn_id->len = dcid_len;
for (int i = 0; i < dcid_len && i < MAX_CONN_ID_LEN; i++) {
conn_id->id[i] = pos[i];
}
} else {
/* Short header - connection ID follows immediately */
pos += 1;
/* Assume 8-byte connection ID for short headers */
if (pos + 8 > data_end)
return -1;
conn_id->len = 8;
for (int i = 0; i < 8; i++) {
conn_id->id[i] = pos[i];
}
}
return 0;
}
/* Helper function to detect DNS over QUIC */
static __always_inline int is_dns_over_quic(struct quic_conn_info *conn, uint16_t dst_port) {
if (!conn)
return 0;
/* Standard DoQ port */
if (dst_port == 853)
return 1;
/* Check for DoQ indicators in ALPN */
if (conn->alpn_len > 0) {
/* Look for "doq" in ALPN */
for (int i = 0; i < conn->alpn_len - 2; i++) {
if (conn->alpn[i] == 'd' && conn->alpn[i+1] == 'o' && conn->alpn[i+2] == 'q')
return 1;
}
}
return 0;
}
/* Main QUIC filter function */
SEC("tc")
int quic_packet_filter(struct __sk_buff *skb) {
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
/* Parse Ethernet header */
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return TC_ACT_OK;
/* Only process IP packets */
if (eth->h_proto != bpf_htons(ETH_P_IP))
return TC_ACT_OK;
/* Parse IP header */
struct iphdr *ip = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip) > data_end)
return TC_ACT_OK;
/* Only process UDP packets */
if (ip->protocol != IPPROTO_UDP)
return TC_ACT_OK;
/* Parse UDP header */
struct udphdr *udp = data + sizeof(*eth) + (ip->ihl * 4);
if ((void *)udp + sizeof(*udp) > data_end)
return TC_ACT_OK;
/* Check for QUIC ports */
uint16_t dst_port = bpf_ntohs(udp->dest);
uint16_t src_port = bpf_ntohs(udp->source);
/* Common QUIC ports: 443, 80, 853 (DoQ) */
if (dst_port != 443 && dst_port != 80 && dst_port != 853 &&
src_port != 443 && src_port != 80 && src_port != 853)
return TC_ACT_OK;
/* Parse QUIC payload */
void *quic_payload = (void *)udp + sizeof(*udp);
if (quic_payload + 1 > data_end)
return TC_ACT_OK;
struct quic_header hdr = {0};
if (parse_quic_header(quic_payload, data_end, &hdr) != 0)
return TC_ACT_OK;
/* Extract connection ID */
struct quic_conn_id conn_id = {0};
if (extract_connection_id(quic_payload, data_end, &hdr, &conn_id) != 0)
return TC_ACT_OK;
/* Look up or create connection info */
struct quic_conn_info *conn = bpf_map_lookup_elem(&quic_connections, &conn_id);
if (!conn) {
struct quic_conn_info new_conn = {0};
new_conn.version = hdr.version;
new_conn.is_initial = (hdr.packet_type == 0); /* Initial packet */
new_conn.first_seen = bpf_ktime_get_ns();
new_conn.last_seen = new_conn.first_seen;
new_conn.packets_count = 1;
bpf_map_update_elem(&quic_connections, &conn_id, &new_conn, BPF_ANY);
conn = bpf_map_lookup_elem(&quic_connections, &conn_id);
}
if (conn) {
conn->last_seen = bpf_ktime_get_ns();
__sync_fetch_and_add(&conn->packets_count, 1);
__sync_fetch_and_add(&conn->bytes_count, skb->len);
/* Check for DNS over QUIC */
if (is_dns_over_quic(conn, dst_port)) {
uint32_t session_hash = ip->saddr ^ ip->daddr ^ dst_port;
struct doq_session doq = {0};
doq.conn_id = conn_id;
doq.queries_count = 1;
doq.first_seen = conn->first_seen;
bpf_map_update_elem(&doq_sessions, &session_hash, &doq, BPF_ANY);
}
}
return TC_ACT_OK;
}
/* XDP version for QUIC processing */
SEC("xdp")
int quic_filter_xdp(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
/* Parse Ethernet header */
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return XDP_PASS;
/* Only process IP packets */
if (eth->h_proto != bpf_htons(ETH_P_IP))
return XDP_PASS;
/* Parse IP header */
struct iphdr *ip = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip) > data_end)
return XDP_PASS;
/* Only process UDP packets */
if (ip->protocol != IPPROTO_UDP)
return XDP_PASS;
/* Fast path QUIC detection */
struct udphdr *udp = data + sizeof(*eth) + (ip->ihl * 4);
if ((void *)udp + sizeof(*udp) > data_end)
return XDP_PASS;
uint16_t dst_port = bpf_ntohs(udp->dest);
/* Quick QUIC port check */
if (dst_port == 443 || dst_port == 80 || dst_port == 853) {
/* Potential QUIC traffic - pass to TC layer for detailed processing */
return XDP_PASS;
}
return XDP_PASS;
}

298
ebpf/src/sni_encrypt.bpf.c

@ -0,0 +1,298 @@
/*
* SNI Encryption eBPF Program
* ECH (Encrypted ClientHello) and ESNI support for TLS privacy
*/
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include "../include/zapret_ebpf.h"
/* License required for eBPF programs */
char LICENSE[] SEC("license") = "GPL";
/* ECH configuration */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, uint32_t);
__type(value, struct ech_config);
} ech_config SEC(".maps");
/* SNI encryption keys */
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1000);
__type(key, char[MAX_HOSTNAME_LEN]);
__type(value, struct sni_key);
} sni_keys SEC(".maps");
/* Encryption statistics */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, uint32_t);
__type(value, struct sni_stats);
} sni_stats SEC(".maps");
/* Helper function to extract SNI from TLS Client Hello */
static __always_inline int extract_sni(void *data, void *data_end, char *sni_buf, int buf_len) {
if (!data || !data_end || !sni_buf || buf_len <= 0)
return -1;
uint8_t *tls_data = (uint8_t *)data;
/* Check for TLS handshake record (0x16) */
if (data + 5 > data_end || tls_data[0] != 0x16)
return -1;
/* Check for Client Hello (0x01) */
if (data + 9 > data_end || tls_data[5] != 0x01)
return -1;
/* Skip to extensions */
uint8_t *pos = tls_data + 43; /* Skip fixed part of Client Hello */
if (pos + 1 > data_end)
return -1;
/* Skip session ID */
uint8_t session_id_len = *pos++;
pos += session_id_len;
if (pos + 2 > data_end)
return -1;
/* Skip cipher suites */
uint16_t cipher_suites_len = (pos[0] << 8) | pos[1];
pos += 2 + cipher_suites_len;
if (pos + 1 > data_end)
return -1;
/* Skip compression methods */
uint8_t comp_methods_len = *pos++;
pos += comp_methods_len;
if (pos + 2 > data_end)
return -1;
/* Parse extensions */
uint16_t extensions_len = (pos[0] << 8) | pos[1];
pos += 2;
uint8_t *extensions_end = pos + extensions_len;
if (extensions_end > data_end)
extensions_end = data_end;
/* Look for SNI extension (type 0x0000) */
while (pos + 4 < extensions_end) {
uint16_t ext_type = (pos[0] << 8) | pos[1];
uint16_t ext_len = (pos[2] << 8) | pos[3];
pos += 4;
if (ext_type == 0x0000) { /* SNI extension */
if (pos + 5 < extensions_end) {
uint16_t sni_list_len = (pos[0] << 8) | pos[1];
pos += 2;
if (pos + 3 < extensions_end) {
uint8_t name_type = pos[0];
uint16_t name_len = (pos[1] << 8) | pos[2];
pos += 3;
if (name_type == 0 && pos + name_len <= extensions_end) {
/* Copy SNI hostname */
int copy_len = name_len;
if (copy_len >= buf_len)
copy_len = buf_len - 1;
for (int i = 0; i < copy_len; i++) {
sni_buf[i] = pos[i];
}
sni_buf[copy_len] = '\0';
return copy_len;
}
}
}
break;
}
pos += ext_len;
}
return -1;
}
/* Helper function to check if SNI should be encrypted */
static __always_inline int should_encrypt_sni(char *hostname, struct ech_config *config) {
if (!hostname || !config || !config->enabled)
return 0;
/* Check if hostname matches encryption patterns */
int hostname_len = 0;
for (int i = 0; i < MAX_HOSTNAME_LEN && hostname[i] != '\0'; i++) {
hostname_len++;
}
if (hostname_len == 0)
return 0;
/* Check against configured domains */
for (int i = 0; i < config->domain_count && i < MAX_ECH_DOMAINS; i++) {
int domain_len = 0;
for (int j = 0; j < MAX_HOSTNAME_LEN && config->domains[i][j] != '\0'; j++) {
domain_len++;
}
if (domain_len > 0 && hostname_len >= domain_len) {
/* Check if hostname ends with domain */
int match = 1;
for (int j = 0; j < domain_len; j++) {
if (hostname[hostname_len - domain_len + j] != config->domains[i][j]) {
match = 0;
break;
}
}
if (match)
return 1;
}
}
return 0;
}
/* Helper function to apply SNI encryption */
static __always_inline int encrypt_sni(void *data, void *data_end, char *hostname) {
if (!data || !data_end || !hostname)
return -1;
/* Look up encryption key for hostname */
struct sni_key *key = bpf_map_lookup_elem(&sni_keys, hostname);
if (!key)
return -1;
/* For actual encryption, this would require packet modification
* capabilities that are limited in eBPF. This is a placeholder
* for marking packets that need SNI encryption in user space. */
return 0;
}
/* Main SNI encryption function */
SEC("tc")
int sni_encryptor(struct __sk_buff *skb) {
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
/* Parse Ethernet header */
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return TC_ACT_OK;
/* Only process IP packets */
if (eth->h_proto != bpf_htons(ETH_P_IP))
return TC_ACT_OK;
/* Parse IP header */
struct iphdr *ip = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip) > data_end)
return TC_ACT_OK;
/* Only process TCP packets */
if (ip->protocol != IPPROTO_TCP)
return TC_ACT_OK;
/* Parse TCP header */
struct tcphdr *tcp = data + sizeof(*eth) + (ip->ihl * 4);
if ((void *)tcp + sizeof(*tcp) > data_end)
return TC_ACT_OK;
/* Check for TLS handshake on port 443 */
uint16_t dst_port = bpf_ntohs(tcp->dest);
if (dst_port != 443)
return TC_ACT_OK;
/* Get ECH configuration */
uint32_t config_key = 0;
struct ech_config *config = bpf_map_lookup_elem(&ech_config, &config_key);
if (!config)
return TC_ACT_OK;
/* Parse TLS payload */
void *tls_payload = (void *)tcp + (tcp->doff * 4);
if (tls_payload + 5 > data_end)
return TC_ACT_OK;
/* Extract SNI from TLS Client Hello */
char sni_hostname[MAX_HOSTNAME_LEN] = {0};
int sni_len = extract_sni(tls_payload, data_end, sni_hostname, MAX_HOSTNAME_LEN);
if (sni_len <= 0)
return TC_ACT_OK;
/* Check if SNI should be encrypted */
if (!should_encrypt_sni(sni_hostname, config))
return TC_ACT_OK;
/* Apply SNI encryption */
if (encrypt_sni(tls_payload, data_end, sni_hostname) == 0) {
/* Update statistics */
uint32_t stats_key = 0;
struct sni_stats *stats = bpf_map_lookup_elem(&sni_stats, &stats_key);
if (stats) {
__sync_fetch_and_add(&stats->sni_encrypted, 1);
if (config->use_ech)
__sync_fetch_and_add(&stats->ech_used, 1);
else
__sync_fetch_and_add(&stats->esni_used, 1);
}
}
return TC_ACT_OK;
}
/* XDP version for SNI detection */
SEC("xdp")
int sni_encrypt_xdp(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
/* Parse Ethernet header */
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return XDP_PASS;
/* Only process IP packets */
if (eth->h_proto != bpf_htons(ETH_P_IP))
return XDP_PASS;
/* Parse IP header */
struct iphdr *ip = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip) > data_end)
return XDP_PASS;
/* Only process TCP packets */
if (ip->protocol != IPPROTO_TCP)
return XDP_PASS;
/* Fast path TLS detection */
struct tcphdr *tcp = data + sizeof(*eth) + (ip->ihl * 4);
if ((void *)tcp + sizeof(*tcp) > data_end)
return XDP_PASS;
uint16_t dst_port = bpf_ntohs(tcp->dest);
if (dst_port == 443) {
/* Potential TLS traffic - pass to TC layer for SNI processing */
return XDP_PASS;
}
return XDP_PASS;
}

166
ebpf/src/tls_fingerprint.bpf.c

@ -0,0 +1,166 @@
/*
* TLS Fingerprint Randomization eBPF Program
* JA3/JA3S spoofing and browser fingerprint simulation
*/
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include "../include/zapret_ebpf.h"
/* License required for eBPF programs */
char LICENSE[] SEC("license") = "GPL";
/* TLS fingerprint database */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1000);
__type(key, uint32_t);
__type(value, struct tls_fingerprint);
} browser_fingerprints SEC(".maps");
/* JA3 hash to fingerprint mapping */
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10000);
__type(key, uint32_t); /* JA3 hash */
__type(value, uint32_t); /* fingerprint index */
} ja3_mapping SEC(".maps");
/* Randomization state */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, uint32_t);
__type(value, struct randomization_state);
} rand_state SEC(".maps");
/* Helper function to generate random fingerprint */
static __always_inline int get_random_fingerprint(struct tls_fingerprint *fp) {
if (!fp)
return -1;
uint32_t rand_key = bpf_get_prandom_u32() % 1000;
struct tls_fingerprint *browser_fp = bpf_map_lookup_elem(&browser_fingerprints, &rand_key);
if (!browser_fp)
return -1;
/* Copy fingerprint data */
fp->version = browser_fp->version;
fp->cipher_count = browser_fp->cipher_count;
for (int i = 0; i < MAX_CIPHER_SUITES && i < fp->cipher_count; i++) {
fp->cipher_suites[i] = browser_fp->cipher_suites[i];
}
return 0;
}
/* Main TLS fingerprint randomization function */
SEC("tc")
int tls_fingerprint_randomizer(struct __sk_buff *skb) {
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
/* Parse Ethernet header */
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return TC_ACT_OK;
/* Only process IP packets */
if (eth->h_proto != bpf_htons(ETH_P_IP))
return TC_ACT_OK;
/* Parse IP header */
struct iphdr *ip = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip) > data_end)
return TC_ACT_OK;
/* Only process TCP packets */
if (ip->protocol != IPPROTO_TCP)
return TC_ACT_OK;
/* Parse TCP header */
struct tcphdr *tcp = data + sizeof(*eth) + (ip->ihl * 4);
if ((void *)tcp + sizeof(*tcp) > data_end)
return TC_ACT_OK;
/* Check for TLS handshake on common ports */
uint16_t dst_port = bpf_ntohs(tcp->dest);
if (dst_port != 443 && dst_port != 8443)
return TC_ACT_OK;
/* Parse TLS payload */
void *tls_payload = (void *)tcp + (tcp->doff * 4);
if (tls_payload + 5 > data_end)
return TC_ACT_OK;
uint8_t *tls_data = (uint8_t *)tls_payload;
/* Check for TLS handshake record (0x16) and Client Hello (0x01) */
if (tls_data[0] != 0x16 || tls_payload + 9 > data_end)
return TC_ACT_OK;
if (tls_data[5] != 0x01) /* Not Client Hello */
return TC_ACT_OK;
/* Apply fingerprint randomization */
struct tls_fingerprint new_fp = {0};
if (get_random_fingerprint(&new_fp) == 0) {
/* Modify TLS Client Hello with new fingerprint */
/* This would require packet modification capabilities */
/* For now, just mark the packet for user-space processing */
/* Update randomization statistics */
uint32_t state_key = 0;
struct randomization_state *state = bpf_map_lookup_elem(&rand_state, &state_key);
if (state) {
__sync_fetch_and_add(&state->randomizations_applied, 1);
}
}
return TC_ACT_OK;
}
/* XDP version for processing */
SEC("xdp")
int tls_fingerprint_xdp(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
/* Parse Ethernet header */
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return XDP_PASS;
/* Only process IP packets */
if (eth->h_proto != bpf_htons(ETH_P_IP))
return XDP_PASS;
/* Parse IP header */
struct iphdr *ip = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip) > data_end)
return XDP_PASS;
/* Only process TCP packets */
if (ip->protocol != IPPROTO_TCP)
return XDP_PASS;
/* Fast path TLS detection and marking */
struct tcphdr *tcp = data + sizeof(*eth) + (ip->ihl * 4);
if ((void *)tcp + sizeof(*tcp) > data_end)
return XDP_PASS;
uint16_t dst_port = bpf_ntohs(tcp->dest);
if (dst_port == 443 || dst_port == 8443) {
/* Mark for TLS processing in TC layer */
return XDP_PASS;
}
return XDP_PASS;
}

369
ebpf/src/zapret_filter.bpf.c

@ -0,0 +1,369 @@
/*
* Zapret eBPF Main Filter Program
* Packet filtering with DPI evasion techniques
*/
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include "../include/zapret_ebpf.h"
/* License required for eBPF programs */
char LICENSE[] SEC("license") = "GPL";
/* BPF Maps */
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 65536);
__type(key, uint64_t); /* 5-tuple hash */
__type(value, struct conn_track);
} connection_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1024);
__type(key, uint32_t);
__type(value, struct filter_rule);
} filter_rules SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, uint32_t);
__type(value, struct zapret_config);
} config_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, uint32_t);
__type(value, struct perf_stats);
} stats_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, char[MAX_HOSTNAME_LEN]);
__type(value, uint32_t); /* action flags */
} hostname_rules SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 100);
__type(key, uint32_t);
__type(value, struct tls_fingerprint);
} tls_fingerprint_pool SEC(".maps");
/* Helper Functions */
static __always_inline uint64_t compute_5tuple_hash(struct iphdr *ip, void *l4_hdr) {
uint64_t hash = 0;
if (!ip || !l4_hdr)
return 0;
hash = (uint64_t)ip->saddr;
hash = hash * 31 + (uint64_t)ip->daddr;
hash = hash * 31 + (uint64_t)ip->protocol;
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = (struct tcphdr *)l4_hdr;
hash = hash * 31 + (uint64_t)tcp->source;
hash = hash * 31 + (uint64_t)tcp->dest;
} else if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp = (struct udphdr *)l4_hdr;
hash = hash * 31 + (uint64_t)udp->source;
hash = hash * 31 + (uint64_t)udp->dest;
}
return hash;
}
static __always_inline int parse_tls_client_hello(void *data, void *data_end, struct tls_fingerprint *fp) {
if (!data || !data_end || !fp)
return -1;
/* Basic TLS record parsing */
if (data + 5 > data_end)
return -1;
uint8_t *tls_data = (uint8_t *)data;
/* Check for TLS handshake record (0x16) */
if (tls_data[0] != 0x16)
return -1;
/* Extract TLS version */
fp->version = (tls_data[1] << 8) | tls_data[2];
/* Parse Client Hello message */
if (data + 9 > data_end)
return -1;
/* Skip to cipher suites */
uint8_t *pos = tls_data + 43; /* Skip fixed part of Client Hello */
if (pos + 1 > data_end)
return -1;
/* Skip session ID */
uint8_t session_id_len = *pos++;
pos += session_id_len;
if (pos + 2 > data_end)
return -1;
/* Parse cipher suites */
uint16_t cipher_suites_len = (pos[0] << 8) | pos[1];
pos += 2;
if (pos + cipher_suites_len > data_end)
return -1;
/* Store first few cipher suites for fingerprinting */
int cipher_count = cipher_suites_len / 2;
if (cipher_count > MAX_CIPHER_SUITES)
cipher_count = MAX_CIPHER_SUITES;
fp->cipher_count = cipher_count;
for (int i = 0; i < cipher_count && i < MAX_CIPHER_SUITES; i++) {
if (pos + 2 <= data_end) {
fp->cipher_suites[i] = (pos[0] << 8) | pos[1];
pos += 2;
}
}
return 0;
}
static __always_inline int parse_quic_initial(void *data, void *data_end, struct quic_conn_info *quic) {
if (!data || !data_end || !quic)
return -1;
/* Basic QUIC initial packet parsing */
if (data + 1 > data_end)
return -1;
uint8_t *quic_data = (uint8_t *)data;
/* Check for QUIC initial packet (long header with type 0) */
if ((quic_data[0] & 0x80) == 0) /* Not a long header */
return -1;
if ((quic_data[0] & 0x30) != 0x00) /* Not initial packet */
return -1;
/* Extract version */
if (data + 5 > data_end)
return -1;
quic->version = (quic_data[1] << 24) | (quic_data[2] << 16) |
(quic_data[3] << 8) | quic_data[4];
/* Mark as initial packet */
quic->is_initial = 1;
return 0;
}
static __always_inline int randomize_tls_fingerprint(void *data, void *data_end, struct tls_fingerprint *fp) {
if (!data || !data_end || !fp)
return -1;
/* Get random fingerprint from pool */
uint32_t key = bpf_get_prandom_u32() % 100;
struct tls_fingerprint *random_fp = bpf_map_lookup_elem(&tls_fingerprint_pool, &key);
if (!random_fp)
return -1;
/* Apply randomization to current fingerprint */
fp->version = random_fp->version;
fp->cipher_count = random_fp->cipher_count;
for (int i = 0; i < MAX_CIPHER_SUITES && i < fp->cipher_count; i++) {
fp->cipher_suites[i] = random_fp->cipher_suites[i];
}
return 0;
}
static __always_inline int fragment_packet(struct __sk_buff *skb, struct fragment_ctx *ctx) {
if (!skb || !ctx || !ctx->enabled)
return 0;
/* Simple fragmentation logic - split packet at configured position */
if (skb->len > ctx->fragment_size) {
/* Mark for fragmentation in user space */
ctx->needs_fragmentation = 1;
ctx->original_size = skb->len;
}
return 0;
}
static __always_inline void update_performance_stats(struct perf_stats *stats, uint64_t start_time) {
if (!stats)
return;
uint64_t end_time = bpf_ktime_get_ns();
uint64_t processing_time = end_time - start_time;
__sync_fetch_and_add(&stats->packets_processed, 1);
__sync_fetch_and_add(&stats->total_processing_time, processing_time);
if (processing_time > stats->max_processing_time)
stats->max_processing_time = processing_time;
}
/* Main packet filter function */
SEC("tc")
int zapret_packet_filter(struct __sk_buff *skb) {
uint64_t start_time = bpf_ktime_get_ns();
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
/* Parse Ethernet header */
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return TC_ACT_OK;
/* Only process IP packets */
if (eth->h_proto != bpf_htons(ETH_P_IP))
return TC_ACT_OK;
/* Parse IP header */
struct iphdr *ip = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip) > data_end)
return TC_ACT_OK;
/* Get configuration */
uint32_t config_key = 0;
struct zapret_config *config = bpf_map_lookup_elem(&config_map, &config_key);
if (!config)
return TC_ACT_OK;
/* Compute connection hash */
void *l4_hdr = data + sizeof(*eth) + (ip->ihl * 4);
if (l4_hdr + sizeof(struct tcphdr) > data_end &&
l4_hdr + sizeof(struct udphdr) > data_end)
return TC_ACT_OK;
uint64_t conn_hash = compute_5tuple_hash(ip, l4_hdr);
/* Look up or create connection tracking entry */
struct conn_track *conn = bpf_map_lookup_elem(&connection_map, &conn_hash);
if (!conn) {
struct conn_track new_conn = {0};
new_conn.src_ip = ip->saddr;
new_conn.dst_ip = ip->daddr;
new_conn.protocol = ip->protocol;
new_conn.first_seen = start_time;
new_conn.last_seen = start_time;
new_conn.packets_count = 1;
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = (struct tcphdr *)l4_hdr;
new_conn.src_port = tcp->source;
new_conn.dst_port = tcp->dest;
} else if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp = (struct udphdr *)l4_hdr;
new_conn.src_port = udp->source;
new_conn.dst_port = udp->dest;
}
bpf_map_update_elem(&connection_map, &conn_hash, &new_conn, BPF_ANY);
conn = bpf_map_lookup_elem(&connection_map, &conn_hash);
}
if (conn) {
conn->last_seen = start_time;
__sync_fetch_and_add(&conn->packets_count, 1);
__sync_fetch_and_add(&conn->bytes_count, skb->len);
}
/* Process TLS packets */
if (ip->protocol == IPPROTO_TCP && config->enable_tls_randomization) {
struct tcphdr *tcp = (struct tcphdr *)l4_hdr;
void *payload = l4_hdr + (tcp->doff * 4);
if (payload < data_end) {
struct tls_fingerprint fp = {0};
if (parse_tls_client_hello(payload, data_end, &fp) == 0) {
if (conn) {
conn->tls_fp = fp;
randomize_tls_fingerprint(payload, data_end, &conn->tls_fp);
}
}
}
}
/* Process QUIC packets */
if (ip->protocol == IPPROTO_UDP && config->enable_quic_filtering) {
struct udphdr *udp = (struct udphdr *)l4_hdr;
void *payload = l4_hdr + sizeof(*udp);
if (payload < data_end) {
struct quic_conn_info quic = {0};
if (parse_quic_initial(payload, data_end, &quic) == 0) {
if (conn) {
conn->quic_info = quic;
}
}
}
}
/* Apply packet fragmentation if enabled */
if (config->enable_packet_fragmentation && conn) {
fragment_packet(skb, &conn->frag_ctx);
}
/* Update performance statistics */
if (config->enable_performance_monitoring) {
uint32_t stats_key = 0;
struct perf_stats *stats = bpf_map_lookup_elem(&stats_map, &stats_key);
if (stats) {
update_performance_stats(stats, start_time);
}
}
return TC_ACT_OK;
}
/* XDP version for packet processing */
SEC("xdp")
int zapret_xdp_filter(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
/* Parse Ethernet header */
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return XDP_PASS;
/* Only process IP packets */
if (eth->h_proto != bpf_htons(ETH_P_IP))
return XDP_PASS;
/* Parse IP header */
struct iphdr *ip = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip) > data_end)
return XDP_PASS;
/* Fast path filtering - basic DPI evasion */
if (ip->protocol == IPPROTO_TCP) {
/* TCP-based filtering logic */
return XDP_PASS;
} else if (ip->protocol == IPPROTO_UDP) {
/* UDP/QUIC-based filtering logic */
return XDP_PASS;
}
return XDP_PASS;
}

92
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
Loading…
Cancel
Save