mirror of
https://github.com/bol-van/zapret.git
synced 2025-08-22 19:28:42 +00:00
.
This commit is contained in:
parent
56456c1e0c
commit
4bc2651ef2
4
Makefile
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
Normal file
175
ebpf/Makefile
Normal file
@ -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
Normal file
285
ebpf/include/zapret_ebpf.h
Normal file
@ -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
Normal file
467
ebpf/loader/ebpf_manager.c
Normal file
@ -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
Normal file
458
ebpf/loader/performance_monitor.c
Normal file
@ -0,0 +1,458 @@
|
||||
/*
|
||||
* Zapret Performance Monitor
|
||||
* Performance monitoring and optimization for eBPF packet filtering
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <math.h>
|
||||
#include "../include/zapret_ebpf.h"
|
||||
|
||||
/* Performance monitoring configuration */
|
||||
#define PERF_SAMPLE_INTERVAL 1000 /* 1 second in milliseconds */
|
||||
#define PERF_HISTORY_SIZE 3600 /* 1 hour of samples */
|
||||
#define PERF_ALERT_THRESHOLD_CPU 80.0 /* CPU usage alert threshold */
|
||||
#define PERF_ALERT_THRESHOLD_MEMORY 90.0 /* Memory usage alert threshold */
|
||||
#define PERF_ALERT_THRESHOLD_LATENCY 10000000 /* 10ms latency threshold */
|
||||
|
||||
/* Performance metrics history */
|
||||
struct perf_sample {
|
||||
time_t timestamp;
|
||||
double cpu_usage;
|
||||
double memory_usage;
|
||||
uint64_t packets_per_second;
|
||||
uint64_t bytes_per_second;
|
||||
uint64_t avg_latency_ns;
|
||||
uint64_t max_latency_ns;
|
||||
uint64_t connections_active;
|
||||
uint64_t tls_randomizations;
|
||||
uint64_t packet_fragmentations;
|
||||
uint64_t sni_encryptions;
|
||||
};
|
||||
|
||||
static struct perf_sample perf_history[PERF_HISTORY_SIZE];
|
||||
static int perf_history_index = 0;
|
||||
static int perf_history_count = 0;
|
||||
static pthread_mutex_t perf_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* Performance alerts */
|
||||
struct perf_alert {
|
||||
time_t timestamp;
|
||||
char message[256];
|
||||
int severity; /* 1=info, 2=warning, 3=critical */
|
||||
};
|
||||
|
||||
#define MAX_ALERTS 100
|
||||
static struct perf_alert alert_history[MAX_ALERTS];
|
||||
static int alert_count = 0;
|
||||
static pthread_mutex_t alert_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* External references */
|
||||
extern struct perf_stats global_stats;
|
||||
extern pthread_mutex_t stats_mutex;
|
||||
extern int get_connection_stats(int *active_connections, int *total_connections);
|
||||
|
||||
/* Forward declarations */
|
||||
void check_performance_alerts(struct perf_sample *sample);
|
||||
|
||||
/* System resource monitoring */
|
||||
static double get_cpu_usage(void) {
|
||||
static struct rusage prev_usage = {0};
|
||||
static struct timeval prev_time = {0};
|
||||
|
||||
struct rusage current_usage;
|
||||
struct timeval current_time;
|
||||
|
||||
getrusage(RUSAGE_SELF, ¤t_usage);
|
||||
gettimeofday(¤t_time, NULL);
|
||||
|
||||
if (prev_time.tv_sec == 0) {
|
||||
prev_usage = current_usage;
|
||||
prev_time = current_time;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* Calculate time differences */
|
||||
double time_diff = (current_time.tv_sec - prev_time.tv_sec) +
|
||||
(current_time.tv_usec - prev_time.tv_usec) / 1000000.0;
|
||||
|
||||
double user_time_diff = (current_usage.ru_utime.tv_sec - prev_usage.ru_utime.tv_sec) +
|
||||
(current_usage.ru_utime.tv_usec - prev_usage.ru_utime.tv_usec) / 1000000.0;
|
||||
|
||||
double sys_time_diff = (current_usage.ru_stime.tv_sec - prev_usage.ru_stime.tv_sec) +
|
||||
(current_usage.ru_stime.tv_usec - prev_usage.ru_stime.tv_usec) / 1000000.0;
|
||||
|
||||
double cpu_usage = ((user_time_diff + sys_time_diff) / time_diff) * 100.0;
|
||||
|
||||
prev_usage = current_usage;
|
||||
prev_time = current_time;
|
||||
|
||||
return cpu_usage;
|
||||
}
|
||||
|
||||
static double get_memory_usage(void) {
|
||||
FILE *fp = fopen("/proc/self/status", "r");
|
||||
if (!fp) return 0.0;
|
||||
|
||||
char line[256];
|
||||
long vm_rss = 0;
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
if (strncmp(line, "VmRSS:", 6) == 0) {
|
||||
sscanf(line, "VmRSS: %ld kB", &vm_rss);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
/* Get total system memory */
|
||||
fp = fopen("/proc/meminfo", "r");
|
||||
if (!fp) return 0.0;
|
||||
|
||||
long mem_total = 0;
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
if (strncmp(line, "MemTotal:", 9) == 0) {
|
||||
sscanf(line, "MemTotal: %ld kB", &mem_total);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (mem_total == 0) return 0.0;
|
||||
|
||||
return ((double)vm_rss / mem_total) * 100.0;
|
||||
}
|
||||
|
||||
/* Performance alert system */
|
||||
void add_performance_alert(const char *message, int severity) {
|
||||
pthread_mutex_lock(&alert_mutex);
|
||||
|
||||
if (alert_count < MAX_ALERTS) {
|
||||
alert_history[alert_count].timestamp = time(NULL);
|
||||
strncpy(alert_history[alert_count].message, message, sizeof(alert_history[alert_count].message) - 1);
|
||||
alert_history[alert_count].message[sizeof(alert_history[alert_count].message) - 1] = '\0';
|
||||
alert_history[alert_count].severity = severity;
|
||||
alert_count++;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&alert_mutex);
|
||||
|
||||
/* Print alert to console */
|
||||
const char *severity_str[] = {"", "INFO", "WARNING", "CRITICAL"};
|
||||
printf("[%s] %s\n", severity_str[severity], message);
|
||||
}
|
||||
|
||||
/* Performance sample collection */
|
||||
void collect_performance_sample(void) {
|
||||
struct perf_sample sample = {0};
|
||||
|
||||
sample.timestamp = time(NULL);
|
||||
sample.cpu_usage = get_cpu_usage();
|
||||
sample.memory_usage = get_memory_usage();
|
||||
|
||||
/* Get packet processing statistics */
|
||||
pthread_mutex_lock(&stats_mutex);
|
||||
|
||||
static uint64_t prev_packets = 0;
|
||||
static uint64_t prev_bytes = 0;
|
||||
static time_t prev_time = 0;
|
||||
|
||||
uint64_t current_packets = global_stats.packets_processed;
|
||||
uint64_t current_bytes = global_stats.bytes_processed;
|
||||
time_t current_time = sample.timestamp;
|
||||
|
||||
if (prev_time > 0) {
|
||||
time_t time_diff = current_time - prev_time;
|
||||
if (time_diff > 0) {
|
||||
sample.packets_per_second = (current_packets - prev_packets) / time_diff;
|
||||
sample.bytes_per_second = (current_bytes - prev_bytes) / time_diff;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate average latency */
|
||||
if (global_stats.packets_processed > 0) {
|
||||
sample.avg_latency_ns = global_stats.total_processing_time / global_stats.packets_processed;
|
||||
}
|
||||
|
||||
sample.max_latency_ns = global_stats.max_processing_time;
|
||||
sample.tls_randomizations = global_stats.tls_fingerprints_randomized;
|
||||
sample.packet_fragmentations = global_stats.packets_fragmented;
|
||||
sample.sni_encryptions = global_stats.sni_encrypted;
|
||||
|
||||
prev_packets = current_packets;
|
||||
prev_bytes = current_bytes;
|
||||
prev_time = current_time;
|
||||
|
||||
pthread_mutex_unlock(&stats_mutex);
|
||||
|
||||
/* Get connection statistics */
|
||||
int active_connections = 0;
|
||||
get_connection_stats(&active_connections, NULL);
|
||||
sample.connections_active = active_connections;
|
||||
|
||||
/* Store sample in history */
|
||||
pthread_mutex_lock(&perf_mutex);
|
||||
|
||||
perf_history[perf_history_index] = sample;
|
||||
perf_history_index = (perf_history_index + 1) % PERF_HISTORY_SIZE;
|
||||
|
||||
if (perf_history_count < PERF_HISTORY_SIZE) {
|
||||
perf_history_count++;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&perf_mutex);
|
||||
|
||||
/* Check for performance alerts */
|
||||
check_performance_alerts(&sample);
|
||||
}
|
||||
|
||||
/* Performance alert checking */
|
||||
void check_performance_alerts(struct perf_sample *sample) {
|
||||
char alert_msg[256];
|
||||
|
||||
/* CPU usage alert */
|
||||
if (sample->cpu_usage > PERF_ALERT_THRESHOLD_CPU) {
|
||||
snprintf(alert_msg, sizeof(alert_msg),
|
||||
"High CPU usage detected: %.1f%%", sample->cpu_usage);
|
||||
add_performance_alert(alert_msg, 2);
|
||||
}
|
||||
|
||||
/* Memory usage alert */
|
||||
if (sample->memory_usage > PERF_ALERT_THRESHOLD_MEMORY) {
|
||||
snprintf(alert_msg, sizeof(alert_msg),
|
||||
"High memory usage detected: %.1f%%", sample->memory_usage);
|
||||
add_performance_alert(alert_msg, 2);
|
||||
}
|
||||
|
||||
/* Latency alert */
|
||||
if (sample->avg_latency_ns > PERF_ALERT_THRESHOLD_LATENCY) {
|
||||
snprintf(alert_msg, sizeof(alert_msg),
|
||||
"High processing latency detected: %.2fms",
|
||||
sample->avg_latency_ns / 1000000.0);
|
||||
add_performance_alert(alert_msg, 2);
|
||||
}
|
||||
|
||||
/* Throughput monitoring */
|
||||
if (sample->packets_per_second > 100000) {
|
||||
snprintf(alert_msg, sizeof(alert_msg),
|
||||
"High packet rate: %lu pps", sample->packets_per_second);
|
||||
add_performance_alert(alert_msg, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Performance statistics calculation */
|
||||
void calculate_performance_stats(struct perf_sample *min, struct perf_sample *max, struct perf_sample *avg) {
|
||||
if (perf_history_count == 0) return;
|
||||
|
||||
pthread_mutex_lock(&perf_mutex);
|
||||
|
||||
memset(min, 0, sizeof(*min));
|
||||
memset(max, 0, sizeof(*max));
|
||||
memset(avg, 0, sizeof(*avg));
|
||||
|
||||
/* Initialize min values */
|
||||
*min = perf_history[0];
|
||||
|
||||
double sum_cpu = 0, sum_memory = 0;
|
||||
uint64_t sum_pps = 0, sum_bps = 0, sum_latency = 0, sum_connections = 0;
|
||||
|
||||
for (int i = 0; i < perf_history_count; i++) {
|
||||
struct perf_sample *sample = &perf_history[i];
|
||||
|
||||
/* Update minimums */
|
||||
if (sample->cpu_usage < min->cpu_usage) min->cpu_usage = sample->cpu_usage;
|
||||
if (sample->memory_usage < min->memory_usage) min->memory_usage = sample->memory_usage;
|
||||
if (sample->avg_latency_ns < min->avg_latency_ns) min->avg_latency_ns = sample->avg_latency_ns;
|
||||
|
||||
/* Update maximums */
|
||||
if (sample->cpu_usage > max->cpu_usage) max->cpu_usage = sample->cpu_usage;
|
||||
if (sample->memory_usage > max->memory_usage) max->memory_usage = sample->memory_usage;
|
||||
if (sample->packets_per_second > max->packets_per_second) max->packets_per_second = sample->packets_per_second;
|
||||
if (sample->bytes_per_second > max->bytes_per_second) max->bytes_per_second = sample->bytes_per_second;
|
||||
if (sample->avg_latency_ns > max->avg_latency_ns) max->avg_latency_ns = sample->avg_latency_ns;
|
||||
if (sample->max_latency_ns > max->max_latency_ns) max->max_latency_ns = sample->max_latency_ns;
|
||||
if (sample->connections_active > max->connections_active) max->connections_active = sample->connections_active;
|
||||
|
||||
/* Accumulate for averages */
|
||||
sum_cpu += sample->cpu_usage;
|
||||
sum_memory += sample->memory_usage;
|
||||
sum_pps += sample->packets_per_second;
|
||||
sum_bps += sample->bytes_per_second;
|
||||
sum_latency += sample->avg_latency_ns;
|
||||
sum_connections += sample->connections_active;
|
||||
}
|
||||
|
||||
/* Calculate averages */
|
||||
avg->cpu_usage = sum_cpu / perf_history_count;
|
||||
avg->memory_usage = sum_memory / perf_history_count;
|
||||
avg->packets_per_second = sum_pps / perf_history_count;
|
||||
avg->bytes_per_second = sum_bps / perf_history_count;
|
||||
avg->avg_latency_ns = sum_latency / perf_history_count;
|
||||
avg->connections_active = sum_connections / perf_history_count;
|
||||
|
||||
pthread_mutex_unlock(&perf_mutex);
|
||||
}
|
||||
|
||||
/* Performance report generation */
|
||||
void print_performance_report(void) {
|
||||
struct perf_sample min, max, avg;
|
||||
calculate_performance_stats(&min, &max, &avg);
|
||||
|
||||
printf("\n=== Zapret Performance Report ===\n");
|
||||
printf("Monitoring period: %d samples (%d minutes)\n",
|
||||
perf_history_count, perf_history_count / 60);
|
||||
|
||||
printf("\nCPU Usage:\n");
|
||||
printf(" Current: %.1f%%\n", perf_history_count > 0 ? perf_history[(perf_history_index - 1 + PERF_HISTORY_SIZE) % PERF_HISTORY_SIZE].cpu_usage : 0.0);
|
||||
printf(" Average: %.1f%%\n", avg.cpu_usage);
|
||||
printf(" Min/Max: %.1f%% / %.1f%%\n", min.cpu_usage, max.cpu_usage);
|
||||
|
||||
printf("\nMemory Usage:\n");
|
||||
printf(" Current: %.1f%%\n", perf_history_count > 0 ? perf_history[(perf_history_index - 1 + PERF_HISTORY_SIZE) % PERF_HISTORY_SIZE].memory_usage : 0.0);
|
||||
printf(" Average: %.1f%%\n", avg.memory_usage);
|
||||
printf(" Min/Max: %.1f%% / %.1f%%\n", min.memory_usage, max.memory_usage);
|
||||
|
||||
printf("\nThroughput:\n");
|
||||
printf(" Average PPS: %lu packets/sec\n", avg.packets_per_second);
|
||||
printf(" Peak PPS: %lu packets/sec\n", max.packets_per_second);
|
||||
printf(" Average BPS: %.2f MB/sec\n", avg.bytes_per_second / (1024.0 * 1024.0));
|
||||
printf(" Peak BPS: %.2f MB/sec\n", max.bytes_per_second / (1024.0 * 1024.0));
|
||||
|
||||
printf("\nLatency:\n");
|
||||
printf(" Average: %.2f ms\n", avg.avg_latency_ns / 1000000.0);
|
||||
printf(" Peak: %.2f ms\n", max.max_latency_ns / 1000000.0);
|
||||
|
||||
printf("\nConnections:\n");
|
||||
printf(" Average active: %lu\n", avg.connections_active);
|
||||
printf(" Peak active: %lu\n", max.connections_active);
|
||||
|
||||
printf("\nDPI Evasion Activity:\n");
|
||||
printf(" TLS fingerprints randomized: %lu\n", max.tls_randomizations);
|
||||
printf(" Packets fragmented: %lu\n", max.packet_fragmentations);
|
||||
printf(" SNI encryptions: %lu\n", max.sni_encryptions);
|
||||
|
||||
/* Performance optimization recommendations */
|
||||
printf("\n=== Performance Recommendations ===\n");
|
||||
|
||||
if (avg.cpu_usage > 70.0) {
|
||||
printf("⚠️ High CPU usage detected. Consider:\n");
|
||||
printf(" - Reducing TLS randomization frequency\n");
|
||||
printf(" - Optimizing filter rules\n");
|
||||
printf(" - Using hardware acceleration if available\n");
|
||||
}
|
||||
|
||||
if (avg.memory_usage > 80.0) {
|
||||
printf("⚠️ High memory usage detected. Consider:\n");
|
||||
printf(" - Reducing connection timeout\n");
|
||||
printf(" - Implementing connection pooling\n");
|
||||
printf(" - Cleaning up old connections more frequently\n");
|
||||
}
|
||||
|
||||
if (avg.avg_latency_ns > 5000000) { /* 5ms */
|
||||
printf("⚠️ High processing latency detected. Consider:\n");
|
||||
printf(" - Optimizing packet parsing logic\n");
|
||||
printf(" - Using eBPF for kernel-level filtering\n");
|
||||
printf(" - Reducing complexity of DPI evasion techniques\n");
|
||||
}
|
||||
|
||||
if (max.packets_per_second > 50000) {
|
||||
printf("✅ High throughput achieved. System performing well.\n");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/* Performance monitoring thread */
|
||||
void* performance_monitor_thread(void *arg) {
|
||||
printf("Performance monitor started\n");
|
||||
|
||||
while (1) {
|
||||
collect_performance_sample();
|
||||
usleep(PERF_SAMPLE_INTERVAL * 1000); /* Convert to microseconds */
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Export performance data to CSV */
|
||||
int export_performance_data(const char *filename) {
|
||||
FILE *fp = fopen(filename, "w");
|
||||
if (!fp) {
|
||||
printf("Error: Could not create performance export file %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write CSV header */
|
||||
fprintf(fp, "timestamp,cpu_usage,memory_usage,packets_per_second,bytes_per_second,"
|
||||
"avg_latency_ns,max_latency_ns,connections_active,tls_randomizations,"
|
||||
"packet_fragmentations,sni_encryptions\n");
|
||||
|
||||
pthread_mutex_lock(&perf_mutex);
|
||||
|
||||
for (int i = 0; i < perf_history_count; i++) {
|
||||
struct perf_sample *sample = &perf_history[i];
|
||||
|
||||
fprintf(fp, "%ld,%.2f,%.2f,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu\n",
|
||||
sample->timestamp,
|
||||
sample->cpu_usage,
|
||||
sample->memory_usage,
|
||||
sample->packets_per_second,
|
||||
sample->bytes_per_second,
|
||||
sample->avg_latency_ns,
|
||||
sample->max_latency_ns,
|
||||
sample->connections_active,
|
||||
sample->tls_randomizations,
|
||||
sample->packet_fragmentations,
|
||||
sample->sni_encryptions);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&perf_mutex);
|
||||
|
||||
fclose(fp);
|
||||
printf("Performance data exported to %s\n", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize performance monitoring */
|
||||
int init_performance_monitor(void) {
|
||||
/* Reset performance history */
|
||||
memset(perf_history, 0, sizeof(perf_history));
|
||||
perf_history_index = 0;
|
||||
perf_history_count = 0;
|
||||
|
||||
/* Reset alert history */
|
||||
memset(alert_history, 0, sizeof(alert_history));
|
||||
alert_count = 0;
|
||||
|
||||
printf("Performance monitor initialized\n");
|
||||
printf(" Sample interval: %d ms\n", PERF_SAMPLE_INTERVAL);
|
||||
printf(" History size: %d samples\n", PERF_HISTORY_SIZE);
|
||||
printf(" Alert thresholds: CPU %.1f%%, Memory %.1f%%, Latency %.1fms\n",
|
||||
PERF_ALERT_THRESHOLD_CPU, PERF_ALERT_THRESHOLD_MEMORY,
|
||||
PERF_ALERT_THRESHOLD_LATENCY / 1000000.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get current performance metrics */
|
||||
int get_current_performance_metrics(struct perf_sample *current) {
|
||||
if (!current || perf_history_count == 0) return -1;
|
||||
|
||||
pthread_mutex_lock(&perf_mutex);
|
||||
|
||||
int latest_index = (perf_history_index - 1 + PERF_HISTORY_SIZE) % PERF_HISTORY_SIZE;
|
||||
*current = perf_history[latest_index];
|
||||
|
||||
pthread_mutex_unlock(&perf_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
378
ebpf/loader/zapret_loader.c
Normal file
378
ebpf/loader/zapret_loader.c
Normal file
@ -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
Normal file
271
ebpf/src/packet_fragment.bpf.c
Normal file
@ -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
Normal file
265
ebpf/src/quic_filter.bpf.c
Normal file
@ -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
Normal file
298
ebpf/src/sni_encrypt.bpf.c
Normal file
@ -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
Normal file
166
ebpf/src/tls_fingerprint.bpf.c
Normal file
@ -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
Normal file
369
ebpf/src/zapret_filter.bpf.c
Normal file
@ -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
Normal file
92
zapret_config.conf
Normal file
@ -0,0 +1,92 @@
|
||||
# Zapret eBPF Configuration
|
||||
# DPI evasion and packet filtering settings
|
||||
|
||||
[general]
|
||||
# Enable/disable core features
|
||||
enable_tls_randomization=1
|
||||
enable_quic_filtering=1
|
||||
enable_packet_fragmentation=1
|
||||
enable_sni_encryption=1
|
||||
enable_performance_monitoring=1
|
||||
|
||||
# eBPF specific settings
|
||||
ebpf_enabled=1
|
||||
ebpf_xdp_mode=1
|
||||
ebpf_tc_mode=1
|
||||
|
||||
[performance]
|
||||
# Connection and performance limits
|
||||
max_connections=10000
|
||||
connection_timeout=300
|
||||
fragment_threshold=1200
|
||||
processing_threads=4
|
||||
|
||||
# Memory and resource limits
|
||||
max_memory_mb=512
|
||||
map_size_connections=65536
|
||||
map_size_rules=1024
|
||||
|
||||
[tls]
|
||||
# TLS fingerprint randomization
|
||||
randomize_cipher_order=1
|
||||
use_browser_fingerprints=1
|
||||
min_cipher_suites=8
|
||||
max_cipher_suites=16
|
||||
|
||||
# JA3 fingerprint settings
|
||||
ja3_randomization=1
|
||||
ja3_database_size=1000
|
||||
|
||||
[quic]
|
||||
# QUIC/HTTP3 filtering
|
||||
randomize_connection_id=1
|
||||
fake_retry_packets=1
|
||||
support_0rtt=1
|
||||
|
||||
# DNS over QUIC settings
|
||||
doq_filtering=1
|
||||
doq_port=853
|
||||
|
||||
[fragmentation]
|
||||
# Packet fragmentation strategies
|
||||
min_fragment_size=64
|
||||
max_fragment_size=1200
|
||||
random_fragment_order=1
|
||||
fragment_strategy=tcp_seg
|
||||
|
||||
# TCP segmentation
|
||||
tcp_segment_size=536
|
||||
tcp_random_window=1
|
||||
|
||||
[sni_encryption]
|
||||
# SNI encryption (ECH/ESNI)
|
||||
ech_enabled=1
|
||||
esni_fallback=1
|
||||
encryption_key_rotation=3600
|
||||
|
||||
[monitoring]
|
||||
# Performance monitoring
|
||||
stats_interval=1
|
||||
history_size=3600
|
||||
export_csv=1
|
||||
|
||||
# Alert thresholds
|
||||
cpu_alert_threshold=80
|
||||
memory_alert_threshold=90
|
||||
latency_alert_threshold=10
|
||||
|
||||
[logging]
|
||||
# Debug and logging
|
||||
log_level=info
|
||||
debug_mode=0
|
||||
log_file=/var/log/zapret_ebpf.log
|
||||
|
||||
[network]
|
||||
# Network interface settings
|
||||
default_interface=eth0
|
||||
monitor_interfaces=eth0,wlan0
|
||||
|
||||
# Protocol ports
|
||||
https_ports=443,8443
|
||||
quic_ports=443,80
|
||||
dns_ports=53,853
|
Loading…
Reference in New Issue
Block a user