guard/xdp/hooks/hook_api.h
loki5512344 15f474486a
feat!: universal redesign — drop Minecraft stack, single-crate architecture
- remove Java plugins (velocity/paper), dashboard, all MC-specific code
  (handshake, death_code, varint, hostname-HMAC); available in history pre-v0.2
- merge crates/* into one package with src/bin/{rampart,rampart-manager,rampart-cli}
- ProtocolHandler trait + registry (no implementations yet), universal PoW kept
- XDP: universal L3/L4 filter (xdp/core/) + pluggable hook API (xdp/hooks/),
  fix IPv6 saddr bug; clang build verified
- docs: bilingual knowledge base (docs/kb/: attacks x4, defense-levels,
  practice x3), rewrite README/architecture for universal concept
- TODO.md v4.0: <=300-line module limit, competitor benchmark section (ref/)
- deploy/CI/docs cleanup: no MC references, new binary names

cargo build/clippy(-D warnings)/test green (55 tests)
2026-08-24 01:50:22 +02:00

121 lines
4.6 KiB
C

#ifndef RAMPART_HOOK_API_H
#define RAMPART_HOOK_API_H
/*
* ── Rampart protocol-hook contract ──
*
* The universal XDP filter is protocol-agnostic: it validates L2/L3/L4,
* tracks the TCP handshake (SYN_RECEIVED → ESTABLISHED), applies
* blacklist/whitelist/throttle policy — and then delegates deep payload
* inspection to an optional protocol hook.
*
* ── WHAT THE HOOK SEES ──
*
* proto_hook() is called ONLY when:
* - packet is TCP, destination port is inside the protected range;
* - connection passed SYN throttle and completed the 3-way handshake
* (state == STATE_ESTABLISHED);
* - the segment carries a non-zero payload.
*
* Pure SYN / SYN-ACK / ACK (handshake segments) NEVER reach the hook:
* the hook physically cannot block a clean TCP handshake.
* Empty ACKs also never reach the hook (keep-alives pass untouched).
*
* struct cursor — bounds-checked window over the TCP payload:
* pos = first payload byte, end = one past the last
* byte of the packet (validated against data_end).
* struct pkt_meta — read-only metadata: 4-tuple (ports in HOST byte
* order), payload length, full TCP flags byte,
* L3 family.
*
* ── VERDICTS ──
*
* XDP_PASS — accept the segment; filter advances expected_seq by
* the consumed payload length and keeps conntrack entry.
* XDP_DROP — drop the segment AND tear down the conntrack entry.
* Anything else (XDP_ABORTED etc.) is treated as XDP_PASS; do not
* return it.
*
* ── RULES FOR HOOK IMPLEMENTATIONS ──
*
* 1. Never block handshake traffic — guaranteed by design (see above),
* do not try to work around it either.
* 2. Bounded loops only: every loop must have a compile-time-constant
* trip count (or be provably bounded for the verifier), keep total
* complexity low — the program must still load with unrolled loops.
* 3. Every cursor advance MUST be bounds-checked against c->end
* (use cursor_take()); reading past c->end is forbidden even if it
* seems in-bounds in the packet.
* 4. Do not update conntrack/throttle/blacklist maps from the hook;
* emit events via push_event() if needed (ringbuf, may fail silently).
* 5. Keep the hook stateless per-call: all per-connection state lives
* in conntrack_map, which belongs to the core filter.
*
* ── HOW TO ATTACH A HOOK ──
*
* 1. Create hooks/<proto>_hook.h containing ONLY your own includes
* (common.h etc.) and a definition of proto_hook(). Do NOT include
* hook_api.h from the hook header — the core includes it for you:
*
* // hooks/myproto_hook.h
* #include "../core/common.h"
* static __always_inline enum xdp_action
* proto_hook(struct cursor *c, struct pkt_meta *m)
* {
* ... parse m, advance c ...
* return XDP_PASS; // or XDP_DROP
* }
*
* 2. Build the object with the macro pointing at your header:
*
* clang -O2 -g -target bpf \
* -DRAMPART_PROTO_HOOK='"../hooks/myproto_hook.h"' \
* -c xdp/core/universal_filter.c -o universal_filter.o
*
* Without RAMPART_PROTO_HOOK the default no-op stub below is used
* and every established connection passes.
*/
#include <linux/bpf.h>
#include "../core/common.h"
// ── Bounds-checked cursor over the payload window ──
struct cursor {
__u8 *pos; // next unparsed byte
__u8 *end; // one past last valid byte (== data_end)
};
// Take n bytes from the cursor; returns pointer or NULL on overrun.
static __always_inline __u8 *cursor_take(struct cursor *c, __u32 n)
{
if (c->pos + n > c->end)
return NULL;
barrier_var(c->pos);
__u8 *p = c->pos;
c->pos += n;
return p;
}
// ── Read-only packet metadata handed to the hook ──
struct pkt_meta {
__u32 src_ip; // IPv4 addr, or low 32 bits of IPv4-mapped IPv6
__u32 dst_ip; // same convention as src_ip
__u16 src_port; // host byte order
__u16 dst_port; // host byte order
__u16 payload_len; // TCP payload bytes in this segment
__u8 tcp_flags; // full 13th-byte flags field
__u8 is_ipv6; // 1 if L3 is IPv4-mapped IPv6
};
#ifdef RAMPART_PROTO_HOOK
#include RAMPART_PROTO_HOOK
#else
/* Default stub: no deep inspection, everything established passes. */
static __always_inline enum xdp_action
proto_hook(struct cursor *c, struct pkt_meta *m)
{
return XDP_PASS;
}
#endif /* RAMPART_PROTO_HOOK */
#endif /* RAMPART_HOOK_API_H */