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:
parent
78fc6e00c7
commit
269daa071f
66 changed files with 4529 additions and 1003 deletions
183
xdp/protocol.h
Normal file
183
xdp/protocol.h
Normal 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 */
|
||||
Loading…
Add table
Add a link
Reference in a new issue