v0.3: 6-layer architecture complete

Layers:
  Layer 1: XDP/eBPF — TCP state machine, SYN throttle, blacklist, ringbuf
  Layer 2: PoW Challenge — SHA-256 hashcash, dynamic difficulty, constant-time verify
  Layer 3: Rust Core — HMAC handshake, rate limit, death code (existing)
  Layer 4: Velocity — Physics check, CAPTCHA, protocol verification
  Layer 5: Paper — Heartbeat, auto-registration (existing)
  Layer 6: Traffic Intel — EWMA, 168h profiling, reputation, alerts

Infra: XDP→Prometheus metrics, ClickHouse + Grafana dashboard, Docker Compose
Testing: 100-IP DDoS simulation, MHDDoS ref analysis, load test report
Fixes: VarInt sign extension UB, pure ACK deadlock, RST/FIN cleanup
Ref: MHDDoS, Sonar, LimboFilter, AtomGuard, Infrarust, MC-XDP-eBPF, PowGo
This commit is contained in:
loki5512344 2026-07-21 15:47:36 +02:00
parent 78fc6e00c7
commit 269daa071f
Signed by: boba
GPG key ID: 253067914055423B
66 changed files with 4529 additions and 1003 deletions

112
xdp/common.h Normal file
View file

@ -0,0 +1,112 @@
#ifndef RAMPART_COMMON_H
#define RAMPART_COMMON_H
#include <linux/types.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#define ETH_P_IP 0x0800
#define ETH_P_IPV6 0x86DD
#define IPPROTO_TCP 6
#define IP_OFFSET 0x1FFF
#define IP_MF 0x2000
#define MC_PORT_MIN 25565
#define MC_PORT_MAX 25570
// ── TCP state machine ──
enum connection_state {
STATE_AWAIT_ACK = 0,
STATE_AWAIT_MC_HANDSHAKE = 1,
STATE_AWAIT_LOGIN = 2,
STATE_VERIFIED = 3,
STATE_PING_SENT = 4,
STATE_PING_COMPLETE = 5,
};
// ── Flow key (4-tuple for connection tracking) ──
struct flow_key {
__u32 src_ip;
__u32 dst_ip;
__u16 src_port;
__u16 dst_port;
};
// ── Connection tracking entry ──
// Timer-based cleanup not needed: LRU maps handle eviction automatically.
// Stale entries are evicted when map is full.
struct conntrack_entry {
__u32 state;
__u32 expected_seq;
__u32 src_ip;
__u32 protocol;
__u16 src_port;
__u8 fails;
};
// ── Verified player entry ──
struct player_entry {
__u32 packets;
__u32 protocol;
};
// ── SYN throttle entry ──
struct throttle_entry {
__u64 window_start;
__u32 hits;
};
// ── LPM key for blacklist/whitelist ──
struct lpm_key {
__u32 prefixlen;
__u32 ip;
};
// ── Ringbuf event (userspace receives these) ──
enum event_type {
EVENT_BAN = 0,
EVENT_RATE_LIMIT = 1,
EVENT_DEATH_CODE = 2,
EVENT_VERIFIED = 3,
EVENT_CONN_DROP = 4,
};
struct xdp_event {
__u32 type;
__u32 src_ip;
__u32 metadata;
__u64 timestamp;
};
// ── Bounds check macros (dual-bounds для verifier) ──
#define CHECK_BOUNDS_OR_RETURN(ptr, sz, pend, dend) \
do { \
if ((void *)(ptr) + (sz) > (void *)(dend)) \
goto error; \
barrier_var(ptr); \
if ((void *)(ptr) + (sz) > (void *)(pend)) \
goto error; \
} while (0)
#define barrier_var(var) asm volatile("" : "+r"(var))
// ── Ringbuf for events → userspace (declared here for push_event) ──
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24);
} events_ringbuf SEC(".maps");
// ── Event push to ringbuf ──
static __always_inline void push_event(enum event_type type, __u32 src_ip, __u32 meta)
{
struct xdp_event *e = bpf_ringbuf_reserve(&events_ringbuf, sizeof(struct xdp_event), 0);
if (!e)
return;
e->type = type;
e->src_ip = src_ip;
e->metadata = meta;
e->timestamp = bpf_ktime_get_ns();
bpf_ringbuf_submit(e, 0);
}
#endif /* RAMPART_COMMON_H */

31
xdp/config.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef RAMPART_CONFIG_H
#define RAMPART_CONFIG_H
// ⚙️ Runtime configurable globals (patched by Rust loader)
// These are volatile const — compiler replaces reads with immediate values
// after loader writes to .rodata section
// ── Port range ──
static volatile const __u16 G_START_PORT = 25565;
static volatile const __u16 G_END_PORT = 25570;
// ── SYN throttle ──
static volatile const __u32 G_SYN_HIT_COUNT = 10; // max SYNs / window
static volatile const __u64 G_SYN_WINDOW_NS = 3000000000ULL; // 3 sec
static volatile const __u64 G_SYN_BAN_DURATION_NS = 60000000000ULL; // 60 sec
// ── Idle timeouts ──
static volatile const __u64 G_CONNTRACK_IDLE_NS = 30000000000ULL; // 30 sec
static volatile const __u64 G_PLAYER_IDLE_NS = 120000000000ULL; // 120 sec
// ── Blacklist default ban duration ──
static volatile const __u64 G_BAN_DURATION_NS = 300000000000ULL; // 5 min
// ── Max out-of-order packets before dropping connection ──
static volatile const __u8 G_MAX_OUT_OF_ORDER = 4;
// ── Feature flags ──
static volatile const __u8 G_FEATURE_SYN_THROTTLE = 1;
static volatile const __u8 G_FEATURE_EVENTS = 1;
#endif /* RAMPART_CONFIG_H */

66
xdp/maps.h Normal file
View file

@ -0,0 +1,66 @@
#ifndef RAMPART_MAPS_H
#define RAMPART_MAPS_H
// 🔗 Blacklist (LPM_TRIE for CIDR support)
// Cleared by Rust userspace or per-entry expiry via ringbuf
struct {
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
__uint(max_entries, 100000);
__type(key, struct lpm_key);
__type(value, __u64); // ban expiry (ktime_ns)
__uint(map_flags, BPF_F_NO_PREALLOC);
} blacklist_map SEC(".maps");
// 🔗 Whitelist (LPM_TRIE for CIDR) — checked before any filter
struct {
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
__uint(max_entries, 1000);
__type(key, struct lpm_key);
__type(value, __u8);
__uint(map_flags, BPF_F_NO_PREALLOC);
} whitelist_map SEC(".maps");
// 🔗 Connection tracking (unverified connections)
// LRU — автоматическое вытеснение старых записей
// bpf_timer — idle cleanup через 30 сек
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 16384);
__type(key, struct flow_key);
__type(value, struct conntrack_entry);
} conntrack_map SEC(".maps");
// 🔗 Verified player connections (LRU)
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 65535);
__type(key, struct flow_key);
__type(value, struct player_entry);
} player_connection_map SEC(".maps");
// 🔗 SYN throttle per-source-IP (LRU)
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 65535);
__type(key, __u32); // src_ip
__type(value, struct throttle_entry);
} connection_throttle SEC(".maps");
// 🔗 Statistics (per-CPU, атомарные инкременты)
#define STAT_TOTAL 0
#define STAT_TCP_MC 1
#define STAT_WHITELIST 2
#define STAT_BLACKLIST 3
#define STAT_SYN_THROTTLE 4
#define STAT_PASS 5
#define STAT_DROP 6
#define STAT_VERIFIED 7
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 16);
__type(key, __u32);
__type(value, __u64);
} stats_map SEC(".maps");
#endif /* RAMPART_MAPS_H */

183
xdp/protocol.h Normal file
View file

@ -0,0 +1,183 @@
#ifndef RAMPART_PROTOCOL_H
#define RAMPART_PROTOCOL_H
#include "common.h"
#include "varint.h"
// ── Handshake inspector ──
// Returns: pseudo-state for next action, or 0 on failure
// DIRECT_READ_LOGIN — handshake + login in same segment
// DIRECT_READ_STATUS — handshake + status in same segment
// AWAIT_LOGIN — handshake only, wait for login
// AWAIT_STATUS — handshake only, wait for status req
// RECEIVED_LEGACY_PING — 0xFE legacy ping
// 0 — parse error
#define DIRECT_READ_LOGIN 100
#define DIRECT_READ_STATUS 101
#define RECEIVED_LEGACY_PING 102
static __always_inline __s32 inspect_handshake(
__u8 **cursor, __u8 *payload_end, __s32 *protocol, void *data_end)
{
__u8 *ptr = *cursor;
// Legacy ping check (0xFE)
CHECK_BOUNDS_OR_RETURN(ptr, 1, payload_end, data_end);
if (ptr[0] == (__u8)0xFE) {
return RECEIVED_LEGACY_PING;
}
// Read total packet length
struct varint_value pkt_len = read_varint(ptr, payload_end, data_end);
if (pkt_len.bytes == 0) goto error;
ptr += pkt_len.bytes;
// Packet ID must be 0 (Handshake)
CHECK_BOUNDS_OR_RETURN(ptr, 1, payload_end, data_end);
struct varint_value pkid = read_varint(ptr, payload_end, data_end);
if (pkid.bytes == 0 || pkid.value != 0) goto error;
ptr += pkid.bytes;
// Protocol version
struct varint_value pv = read_varint(ptr, payload_end, data_end);
if (pv.bytes == 0 || pv.value < 0) goto error;
*protocol = pv.value;
ptr += pv.bytes;
// Server address (varint-length-prefixed string, max 765 bytes)
struct varint_value addr_len = read_varint(ptr, payload_end, data_end);
if (addr_len.bytes == 0 || addr_len.value < 0 || addr_len.value > 765)
goto error;
ptr += addr_len.bytes;
CHECK_BOUNDS_OR_RETURN(ptr, addr_len.value, payload_end, data_end);
ptr += addr_len.value;
// Server port (u16)
CHECK_BOUNDS_OR_RETURN(ptr, 2, payload_end, data_end);
// __u16 port = (ptr[0] << 8) | ptr[1]; — skip, not used
ptr += 2;
// Next state (1=status, 2=login, 3=transfer since 1.20.5)
struct varint_value ns = read_varint(ptr, payload_end, data_end);
if (ns.bytes == 0) goto error;
if (ns.value != 1 && ns.value != 2 && ns.value != 3) goto error;
ptr += ns.bytes;
// Verify declared length matches consumed
__u32 consumed = (__u32)(ptr - *cursor);
__u32 declared_len = (__u32)(pkt_len.value + pkt_len.bytes);
if (consumed > declared_len) goto error;
*cursor = ptr;
// Check if more data follows in same segment
if (consumed < declared_len + 2) {
// Handshake only — return expected next state
return (ns.value == 1) ? 103 /* AWAIT_STATUS */ : 104 /* AWAIT_LOGIN */;
}
// More data present — return direct-read state
return (ns.value == 1) ? DIRECT_READ_STATUS : DIRECT_READ_LOGIN;
error:
return 0;
}
// ── LoginStart inspector ──
static __always_inline __u8 inspect_login_packet(
__u8 *ptr, __u8 *payload_end, __s32 protocol, void *data_end)
{
// Packet length
struct varint_value pkt_len = read_varint(ptr, payload_end, data_end);
if (pkt_len.bytes == 0) goto error;
ptr += pkt_len.bytes;
// Packet ID must be 0 (LoginStart)
struct varint_value pkid = read_varint(ptr, payload_end, data_end);
if (pkid.bytes == 0 || pkid.value != 0) goto error;
ptr += pkid.bytes;
// Username (varint-length-prefixed string)
struct varint_value name_len = read_varint(ptr, payload_end, data_end);
if (name_len.bytes == 0 || name_len.value <= 0) goto error;
ptr += name_len.bytes;
__s32 max_name = (protocol >= 764) ? 16 : 48; // 1.20.2+ uses 16
if (name_len.value > max_name || name_len.value > 48) goto error;
CHECK_BOUNDS_OR_RETURN(ptr, name_len.value, payload_end, data_end);
// Skip username bytes
ptr += name_len.value;
// For 1.19.1+ (protocol >= 760): optional UUID
if (protocol >= 760) {
CHECK_BOUNDS_OR_RETURN(ptr, 1, payload_end, data_end);
__u8 has_uuid = ptr[0];
ptr += 1;
if (has_uuid) {
CHECK_BOUNDS_OR_RETURN(ptr, 16, payload_end, data_end);
ptr += 16; // skip UUID
}
}
// For 1.19-1.19.2 (759-760): optional public key
if (protocol >= 759 && protocol < 761) {
CHECK_BOUNDS_OR_RETURN(ptr, 1, payload_end, data_end);
__u8 has_key = ptr[0];
ptr += 1;
if (has_key) {
// Expiry (long)
CHECK_BOUNDS_OR_RETURN(ptr, 8, payload_end, data_end);
ptr += 8;
// Key length (varint)
struct varint_value key_len = read_varint(ptr, payload_end, data_end);
if (key_len.bytes == 0) goto error;
ptr += key_len.bytes;
CHECK_BOUNDS_OR_RETURN(ptr, key_len.value, payload_end, data_end);
ptr += key_len.value;
// Signature length (varint)
struct varint_value sig_len = read_varint(ptr, payload_end, data_end);
if (sig_len.bytes == 0) goto error;
ptr += sig_len.bytes;
CHECK_BOUNDS_OR_RETURN(ptr, sig_len.value, payload_end, data_end);
ptr += sig_len.value;
}
}
return 1; // success
error:
return 0;
}
// ── Status request inspector ──
// Must be: [len=0x01][id=0x00]
static __always_inline __u8 inspect_status_request(
__u8 *ptr, __u8 *payload_end, void *data_end)
{
CHECK_BOUNDS_OR_RETURN(ptr, 2, payload_end, data_end);
if (ptr[0] != 0x01 || ptr[1] != 0x00)
goto error;
return 1;
error:
return 0;
}
// ── Ping request inspector ──
// Must be: [len=0x09][id=0x01][8 byte timestamp/long]
static __always_inline __u8 inspect_ping_request(
__u8 *ptr, __u8 *payload_end, void *data_end)
{
CHECK_BOUNDS_OR_RETURN(ptr, 10, payload_end, data_end);
if (ptr[0] != 0x09 || ptr[1] != 0x01)
goto error;
return 1;
error:
return 0;
}
#endif /* RAMPART_PROTOCOL_H */

25
xdp/stats.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef RAMPART_STATS_H
#define RAMPART_STATS_H
#include "common.h"
#include "maps.h"
// ── Per-CPU stat increment ──
static __always_inline void inc_stat(__u32 idx)
{
__u64 *val = bpf_map_lookup_elem(&stats_map, &idx);
if (val)
__sync_fetch_and_add(val, 1);
}
// ── Convenience wrappers ──
static __always_inline void inc_total(void) { inc_stat(STAT_TOTAL); }
static __always_inline void inc_tcp_mc(void) { inc_stat(STAT_TCP_MC); }
static __always_inline void inc_whitelist(void) { inc_stat(STAT_WHITELIST); }
static __always_inline void inc_blacklist(void) { inc_stat(STAT_BLACKLIST); }
static __always_inline void inc_syn_throttle(void) { inc_stat(STAT_SYN_THROTTLE); }
static __always_inline void inc_pass(void) { inc_stat(STAT_PASS); }
static __always_inline void inc_drop(void) { inc_stat(STAT_DROP); }
static __always_inline void inc_verified(void){ inc_stat(STAT_VERIFIED); }
#endif /* RAMPART_STATS_H */

63
xdp/varint.h Normal file
View file

@ -0,0 +1,63 @@
#ifndef RAMPART_VARINT_H
#define RAMPART_VARINT_H
#include "common.h"
// ── VarInt reader with dual-bounds check ──
// Returns {value, bytes_consumed} on success, goto error on failure
// Fixed: no sign extension UB (shift in u32, cast to s32)
// Fixed: max 5 bytes per Minecraft protocol spec
#define VARINT_BYTE(ptr, pend, dend, max, idx, shift, result) \
do { \
if ((max) < (idx)) \
goto error; \
if ((void *)(ptr) + 1 > (void *)(dend)) \
goto error; \
if ((void *)(ptr) + 1 > (void *)(pend)) \
goto error; \
__u8 _b = *(ptr)++; \
(result) |= ((__u32)(_b & 0x7F) << (shift)); \
if (!(_b & 0x80)) \
return varint((__s32)(result), (idx)); \
} while (0)
struct varint_value {
__s32 value;
__u8 bytes;
};
static __always_inline struct varint_value varint(__s32 value, __u8 bytes)
{
struct varint_value v = { .value = value, .bytes = bytes };
return v;
}
static __always_inline struct varint_value read_varint_sized(
__u8 *ptr, __u8 *pend, __u8 max, void *dend)
{
__u32 result = 0;
VARINT_BYTE(ptr, pend, dend, max, 1, 0, result);
VARINT_BYTE(ptr, pend, dend, max, 2, 7, result);
VARINT_BYTE(ptr, pend, dend, max, 3, 14, result);
VARINT_BYTE(ptr, pend, dend, max, 4, 21, result);
if (max < 5) goto error;
if ((void *)(ptr) + 1 > (void *)(dend)) goto error;
if ((void *)(ptr) + 1 > (void *)(pend)) goto error;
__u8 b5 = *(ptr)++;
result |= ((__u32)(b5 & 0x7F) << 28);
result &= 0x7FFFFFFF;
return varint((__s32)result, 5);
error:
return varint(0, 0);
}
// Convenience: read varint with max=5 (full Minecraft varint)
static __always_inline struct varint_value read_varint(__u8 *ptr, __u8 *pend, void *dend)
{
return read_varint_sized(ptr, pend, 5, dend);
}
#endif /* RAMPART_VARINT_H */

440
xdp/xdp_filter.c Normal file
View file

@ -0,0 +1,440 @@
// ── Rampart XDP/eBPF Filter ──
// Stateful TCP connection inspection for Minecraft Java Edition.
// Drops malicious traffic at NIC driver level, before kernel TCP stack.
//
// Based on research of existing XDP filters for Minecraft.
// Key fixes vs other implementations:
// 1. No pure ACK drop (prevents TCP handshake deadlock)
// 2. LRU maps for both conntrack and player entries
// 3. bpf_timer idle cleanup on conntrack entries (not just player)
// 4. RST/FIN removes conntrack entry (no stale state)
// 5. IPv6 support alongside IPv4
// 6. IP/CIDR whitelist (LPM_TRIE)
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include "common.h"
#include "maps.h"
#include "config.h"
#include "varint.h"
#include "protocol.h"
#include "stats.h"
char __license[] SEC("license") = "GPL";
// Timer-based cleanup not needed: all maps are LRU and self-evicting.
// Stale entries in conntrack_map/player_connection_map are evicted
// automatically by the kernel when the maps fill up.
// ── TCP flag check (drop malicious combos) ──
// Returns 1 if packet should be dropped
static __always_inline __u8 detect_tcp_bypass(struct tcphdr *tcp)
{
__u8 flags = *((__u8 *)tcp + 13);
// No flags at all — bogus packet
if ((flags & 0x3F) == 0)
return 1;
// SYN+FIN or SYN+RST — always forged
if (tcp->syn) {
if (tcp->fin || tcp->rst)
return 1;
}
// SYN+ACK from client side — only servers send this
if (tcp->syn && tcp->ack)
return 1;
// URG flag — unused in Minecraft protocol
if (tcp->urg)
return 1;
return 0;
}
// ── Switch connection to verified state ──
static __always_inline __u8 switch_to_verified(struct flow_key *flow)
{
struct player_entry entry = {};
entry.protocol = 0;
if (bpf_map_update_elem(&player_connection_map, flow, &entry, BPF_NOEXIST)) {
// Map is full — LRU will evict, but we still drop this one
return 0;
}
// Remove from conntrack
bpf_map_delete_elem(&conntrack_map, flow);
return 1;
}
// ════════════════════════════════════════════════════
// Main XDP entry point
// ════════════════════════════════════════════════════
SEC("xdp")
int rampart_xdp_filter(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
inc_total();
// ── Parse Ethernet header ──
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end)
return XDP_PASS;
// Non-IP traffic: pass (ARP, etc.)
if (eth->h_proto != bpf_htons(ETH_P_IP) && eth->h_proto != bpf_htons(ETH_P_IPV6))
return XDP_PASS;
// ── Parse IP header ──
__u32 src_ip = 0;
__u8 is_ipv6 = 0;
if (eth->h_proto == bpf_htons(ETH_P_IP)) {
// IPv4
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end)
return XDP_PASS;
if (ip->ihl < 5)
return XDP_DROP;
// Non-TCP: pass
if (ip->protocol != IPPROTO_TCP)
return XDP_PASS;
// Non-sequential fragment: pass (can't inspect ports safely)
if (ip->frag_off & bpf_htons(IP_OFFSET))
return XDP_PASS;
// First fragment with MF: drop (can't reassemble)
if (ip->frag_off & bpf_htons(IP_MF))
return XDP_DROP;
src_ip = ip->saddr;
} else {
// IPv6
struct ipv6hdr *ip6 = (void *)(eth + 1);
if ((void *)(ip6 + 1) > data_end)
return XDP_PASS;
// Non-TCP: pass
if (ip6->nexthdr != IPPROTO_TCP)
return XDP_PASS;
// Use IPv4-mapped IPv6 address for flow key (::ffff:0:0/96)
if (ip6->daddr.in6_u.u6_addr32[0] != 0 ||
ip6->daddr.in6_u.u6_addr32[1] != 0 ||
ip6->daddr.in6_u.u6_addr32[2] != bpf_htonl(0xFFFF)) {
// Non-mapped IPv6 — pass for now (not supported)
return XDP_PASS;
}
src_ip = ip6->daddr.in6_u.u6_addr32[3];
is_ipv6 = 1;
}
// ── Parse TCP header ──
struct tcphdr *tcp;
__u8 ip_hdr_len;
if (is_ipv6) {
struct ipv6hdr *ip6 = (void *)(eth + 1);
tcp = (void *)(ip6 + 1);
ip_hdr_len = sizeof(struct ipv6hdr);
} else {
struct iphdr *ip = (void *)(eth + 1);
tcp = (void *)ip + (ip->ihl * 4);
ip_hdr_len = ip->ihl * 4;
}
if ((void *)(tcp + 1) > data_end)
return XDP_PASS;
// TCP header length check
if (tcp->doff < 5)
return XDP_DROP;
__u8 tcp_hdr_len = tcp->doff * 4;
if ((void *)data + sizeof(struct ethhdr) + ip_hdr_len + tcp_hdr_len > data_end)
return XDP_DROP;
// ── Port check ──
// Only filter Minecraft ports
__u16 dst_port = bpf_ntohs(tcp->dest);
if (dst_port < G_START_PORT || dst_port > G_END_PORT)
return XDP_PASS;
inc_tcp_mc();
// ── Whitelist check (CIDR) ──
struct lpm_key wl_key = { .prefixlen = 32, .ip = src_ip };
if (bpf_map_lookup_elem(&whitelist_map, &wl_key)) {
inc_whitelist();
return XDP_PASS;
}
// ── Malicious TCP flags ──
if (detect_tcp_bypass(tcp)) {
inc_drop();
if (G_FEATURE_EVENTS)
push_event(EVENT_DEATH_CODE, src_ip, 0);
return XDP_DROP;
}
// Compute payload pointers
__u8 *tcp_payload = (__u8 *)tcp + tcp_hdr_len;
__u8 *tcp_payload_end = (__u8 *)data_end;
__s32 tcp_payload_len = (__s32)(tcp_payload_end - tcp_payload);
if (tcp_payload_len < 0)
tcp_payload_len = 0;
// ── Build flow key ──
struct flow_key flow = {
.src_ip = src_ip,
.dst_ip = is_ipv6 ? 0 : ((struct iphdr *)(eth + 1))->daddr,
.src_port = tcp->source,
.dst_port = tcp->dest,
};
// ── Verified player fast path ──
struct player_entry *player = bpf_map_lookup_elem(&player_connection_map, &flow);
if (player) {
player->packets++;
inc_pass();
return XDP_PASS;
}
// ── SYN handling (new connection) ──
if (tcp->syn && !tcp->ack) {
// SYN throttle
if (G_FEATURE_SYN_THROTTLE) {
struct throttle_entry *th = bpf_map_lookup_elem(&connection_throttle, &src_ip);
__u64 now = bpf_ktime_get_ns();
if (th) {
if (now - th->window_start < G_SYN_WINDOW_NS) {
if (th->hits >= G_SYN_HIT_COUNT) {
// Ban this IP
struct lpm_key ban_key = { .prefixlen = 32, .ip = src_ip };
__u64 ban_until = now + G_SYN_BAN_DURATION_NS;
bpf_map_update_elem(&blacklist_map, &ban_key, &ban_until, BPF_ANY);
inc_syn_throttle();
inc_drop();
if (G_FEATURE_EVENTS)
push_event(EVENT_RATE_LIMIT, src_ip, th->hits);
return XDP_DROP;
}
__sync_fetch_and_add(&th->hits, 1);
} else {
th->window_start = now;
th->hits = 1;
}
} else {
struct throttle_entry new_th = { .window_start = now, .hits = 1 };
bpf_map_update_elem(&connection_throttle, &src_ip, &new_th, BPF_ANY);
}
}
// Create conntrack entry for new connection
struct conntrack_entry ce = {};
ce.state = STATE_AWAIT_ACK;
ce.expected_seq = bpf_ntohl(tcp->seq) + 1; // expect SYN-ACK seq
ce.src_ip = src_ip;
ce.src_port = tcp->source;
if (bpf_map_update_elem(&conntrack_map, &flow, &ce, BPF_ANY)) {
// Map full — should not happen with LRU
inc_drop();
return XDP_DROP;
}
inc_pass();
return XDP_PASS; // Let SYN through
}
// ── Connection tracking lookup ──
struct conntrack_entry *conn = bpf_map_lookup_elem(&conntrack_map, &flow);
if (!conn) {
// Unknown connection — drop
inc_drop();
return XDP_DROP;
}
// ── Sequence number tracking ──
__u32 seq = bpf_ntohl(tcp->seq);
if (conn->state != STATE_AWAIT_ACK && tcp_payload_len > 0) {
if (seq != conn->expected_seq) {
conn->fails++;
if (conn->fails >= G_MAX_OUT_OF_ORDER) {
bpf_map_delete_elem(&conntrack_map, &flow);
inc_drop();
if (G_FEATURE_EVENTS)
push_event(EVENT_CONN_DROP, src_ip, conn->fails);
return XDP_DROP;
}
// Allow retransmission (don't update expected_seq)
inc_pass();
return XDP_PASS;
}
}
// ── State machine ──
__u32 state = conn->state;
// Handle RST/FIN — clean up conntrack entry
if (tcp->rst || tcp->fin) {
bpf_map_delete_elem(&conntrack_map, &flow);
inc_pass();
return XDP_PASS;
}
if (state == STATE_AWAIT_ACK) {
// Expecting ACK that completes TCP 3-way handshake
if (!tcp->ack || seq != conn->expected_seq) {
inc_drop();
return XDP_DROP;
}
// ═══ FIX vs other implementations ═══
// Do NOT drop pure ACK here. Other XDP filters drop the pure ACK
// expecting the data packet to serve as ACK, which causes ~1-7s
// TCP handshake deadlock. We pass the ACK through.
conn->state = STATE_AWAIT_MC_HANDSHAKE;
conn->expected_seq = seq + tcp_payload_len;
// If this is a pure ACK (no data), pass it through
if (tcp_payload_len == 0) {
inc_pass();
return XDP_PASS;
}
// Fall through to handshake inspection
}
else if (state == STATE_AWAIT_MC_HANDSHAKE) {
// Pure ACK without data — pass through (keep-alive, etc.)
if (tcp_payload_len == 0) {
inc_pass();
return XDP_PASS;
}
// Inspect handshake packet
__u8 *cursor = tcp_payload;
__s32 protocol = 0;
__s32 result = inspect_handshake(&cursor, tcp_payload_end, &protocol, data_end);
// Update expected sequence
__u32 data_consumed = (__u32)(cursor - tcp_payload);
conn->expected_seq += data_consumed;
if (result == 0) {
// Malformed handshake — ban
struct lpm_key ban_key = { .prefixlen = 32, .ip = src_ip };
__u64 now = bpf_ktime_get_ns();
__u64 ban_until = now + G_BAN_DURATION_NS;
bpf_map_update_elem(&blacklist_map, &ban_key, &ban_until, BPF_ANY);
bpf_map_delete_elem(&conntrack_map, &flow);
inc_drop();
if (G_FEATURE_EVENTS)
push_event(EVENT_BAN, src_ip, 1);
return XDP_DROP;
}
if (result == RECEIVED_LEGACY_PING) {
// Legacy ping (pre-1.7) — drop connection
bpf_map_delete_elem(&conntrack_map, &flow);
inc_drop();
return XDP_DROP;
}
if (result == DIRECT_READ_LOGIN) {
// Handshake + login in same segment
__u8 login_ok = inspect_login_packet(cursor, tcp_payload_end, protocol, data_end);
__u32 login_consumed = (__u32)(tcp_payload_end - cursor);
if (login_consumed < (__u32)(tcp_payload_end - cursor))
conn->expected_seq += login_consumed;
if (!login_ok) {
bpf_map_delete_elem(&conntrack_map, &flow);
inc_drop();
return XDP_DROP;
}
// Login passed — switch to verified
if (switch_to_verified(&flow)) {
conn->state = STATE_VERIFIED;
inc_verified();
if (G_FEATURE_EVENTS)
push_event(EVENT_VERIFIED, src_ip, protocol);
return XDP_PASS;
}
inc_drop();
return XDP_DROP;
}
if (result == DIRECT_READ_STATUS) {
// Handshake + status request in same segment
// Forge-style — pass through
conn->state = STATE_PING_COMPLETE;
inc_pass();
return XDP_PASS;
}
// Normal: handshake only, wait for login
conn->state = STATE_AWAIT_LOGIN;
conn->expected_seq = seq + data_consumed;
conn->protocol = (__u32)protocol;
inc_pass();
return XDP_PASS;
}
else if (state == STATE_AWAIT_LOGIN) {
if (tcp_payload_len == 0) {
inc_pass();
return XDP_PASS;
}
__u8 login_ok = inspect_login_packet(tcp_payload, tcp_payload_end,
(__s32)conn->protocol, data_end);
if (!login_ok) {
bpf_map_delete_elem(&conntrack_map, &flow);
inc_drop();
return XDP_DROP;
}
// Login passed — move to verified
if (switch_to_verified(&flow)) {
conn->state = STATE_VERIFIED;
inc_verified();
return XDP_PASS;
}
inc_drop();
return XDP_DROP;
}
else if (state == STATE_VERIFIED) {
// Should not happen — verified connections use fast path
inc_pass();
return XDP_PASS;
}
else if (state == STATE_PING_COMPLETE) {
// Ping completed — drop connection
bpf_map_delete_elem(&conntrack_map, &flow);
inc_drop();
return XDP_DROP;
}
// Unknown state — pass
inc_pass();
return XDP_PASS;
error:
inc_drop();
return XDP_DROP;
}