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
|
|
@ -1,6 +1,6 @@
|
|||
# eBPF / XDP - Фильтрация уровня ядра
|
||||
# eBPF / XDP — Rampart XDP слой
|
||||
|
||||
> Актуально: v0.4+
|
||||
> Актуально: v0.4+
|
||||
> Требует: Linux kernel 5.10+, KVM или Bare Metal (не OpenVZ/LXC)
|
||||
|
||||
---
|
||||
|
|
@ -16,250 +16,280 @@ XDP путь:
|
|||
Никаких аллокаций, никаких копий, никаких syscall
|
||||
```
|
||||
|
||||
| Метод | Задержка дропа | CPU на 5M pps | Требует |
|
||||
|---|---|---|---|
|
||||
| iptables | ~10 мкс | ~80% | - |
|
||||
| nftables | ~8 мкс | ~70% | - |
|
||||
| Rust userspace | ~5 мкс | ~50% | - |
|
||||
| **XDP (generic)** | ~2 мкс | ~30% | любой kernel |
|
||||
| **XDP (native)** | ~0.5 мкс | ~15% | поддержка в драйвере NIC |
|
||||
| **XDP (offload)** | ~0.1 мкс | ~0% | SmartNIC |
|
||||
|
||||
Для большинства VDS - native XDP (Intel i40e, Mellanox ConnectX).
|
||||
| Метод | Задержка дропа | CPU на 5M pps |
|
||||
|-------|---------------|---------------|
|
||||
| iptables | ~10 мкс | ~80% |
|
||||
| nftables | ~8 мкс | ~70% |
|
||||
| Rust userspace | ~5 мкс | ~50% |
|
||||
| **XDP (generic)** | ~2 мкс | ~30% |
|
||||
| **XDP (native)** | ~0.5 мкс | ~15% |
|
||||
| **XDP (offload)** | ~0.1 мкс | ~0% |
|
||||
|
||||
---
|
||||
|
||||
## Граница ответственности (критично)
|
||||
## TCP State Machine
|
||||
|
||||
Rampart использует stateful подход из Minecraft-XDP-eBPF, с исправлениями:
|
||||
|
||||
```
|
||||
XDP МОЖЕТ: XDP НЕ МОЖЕТ:
|
||||
IP блэклист (LPM_TRIE) HMAC-SHA256 (нет floating point < kernel 6.x)
|
||||
SYN flood rate limit GeoIP lookup (нет heap allocation)
|
||||
Invalid TCP flags drop DNS resolve
|
||||
UDP drop (MC = TCP only) Сложные строковые операции
|
||||
Port whitelist Вызов userspace функций
|
||||
Per-IP packet rate Блокировать по hostname
|
||||
BPF map read/write TLS инспекция
|
||||
┌──────────┐
|
||||
│ SYN │
|
||||
│ received │
|
||||
└────┬─────┘
|
||||
│
|
||||
┌────▼─────┐
|
||||
┌────────►│AWAIT_ACK │◄─────────┐
|
||||
│ │ (SYN-ACK │ │
|
||||
│ │ sent) │ │
|
||||
│ └────┬─────┘ │
|
||||
│ │ ACK received │
|
||||
│ ┌────▼──────────┐ │
|
||||
│ │AWAIT_MC_ │ │ retransmit
|
||||
│ │HANDSHAKE ├─────┘ (если pure ACK)
|
||||
│ └────┬──────────┘
|
||||
│ │ MC handshake
|
||||
│ ┌────▼──────────┐
|
||||
│ │AWAIT_LOGIN │
|
||||
│ └────┬──────────┘
|
||||
│ │ LoginStart
|
||||
│ ┌────▼──────────┐ ┌──────────────┐
|
||||
│ │ VERIFIED │────►│Idle bpf_timer│
|
||||
│ └────┬──────────┘ │ (60 sec) │
|
||||
│ │ └──────┬───────┘
|
||||
│ ┌────▼──────────┐ │ timeout
|
||||
│ │ PING_SENT │ │
|
||||
│ └────┬──────────┘ ┌──────▼───────┐
|
||||
│ │ │ ENTRY_DELETED│
|
||||
│ ┌────▼──────────┐ └──────────────┘
|
||||
│ │PING_COMPLETE │
|
||||
│ └────┬──────────┘
|
||||
│ │
|
||||
│ ┌────▼──────────┐
|
||||
└─────────┤ CONN_DROP │
|
||||
│ (RST/FIN) │
|
||||
└───────────────┘
|
||||
```
|
||||
|
||||
Всё L7 (handshake парсинг, HMAC, hostname проверка) - **только в Rust userspace**.
|
||||
## Исправления относительно Minecraft-XDP-eBPF
|
||||
|
||||
---
|
||||
### 1. Pure ACK deadlock (критический баг в MC-XDP-eBPF)
|
||||
|
||||
## Структура BPF Maps
|
||||
```
|
||||
Оригинал (MC-XDP-eBPF):
|
||||
AWAIT_ACK → pure ACK → DROP + переход в AWAIT_MC_HANDSHAKE
|
||||
→ сервер не видит ACK → retransmit SYN-ACK → ~1-7 сек лага
|
||||
|
||||
Наш фикс:
|
||||
AWAIT_ACK → pure ACK → PASS + переход в AWAIT_MC_HANDSHAKE
|
||||
→ сервер видит ACK → TCP handshake завершён нормально
|
||||
```
|
||||
|
||||
```c
|
||||
// maps.h
|
||||
|
||||
// Блэклист IP (LPM - Longest Prefix Match, поддерживает CIDR)
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
|
||||
__uint(max_entries, 100000);
|
||||
__type(key, struct lpm_key); // prefixlen + ip
|
||||
__type(value, __u64); // timestamp бана
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
} blacklist_map SEC(".maps");
|
||||
|
||||
// Rate limit per IP (LRU - автоматически вытесняет старые)
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_LRU_PERCPU_HASH);
|
||||
__uint(max_entries, 500000);
|
||||
__type(key, __u32); // src IP
|
||||
__type(value, struct rate_entry);
|
||||
} rate_map SEC(".maps");
|
||||
|
||||
// Whitelist доверенных IP (edge нод например)
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 1000);
|
||||
__type(key, __u32);
|
||||
__type(value, __u8); // просто флаг
|
||||
} trusted_map SEC(".maps");
|
||||
|
||||
// Статистика (для Prometheus)
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||
__uint(max_entries, 16);
|
||||
__type(key, __u32); // индекс счётчика
|
||||
__type(value, __u64);
|
||||
} stats_map SEC(".maps");
|
||||
|
||||
// Ringbuf для передачи событий в userspace (быстрее perfbuf)
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint(max_entries, 1 << 24); // 16 MB
|
||||
} events SEC(".maps");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## XDP программа (C)
|
||||
|
||||
```c
|
||||
// xdp_filter.c
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include "maps.h"
|
||||
|
||||
#define MC_PORT 25565
|
||||
#define RATE_LIMIT_PPS 20 // пакетов/сек с одного IP
|
||||
#define BAN_DURATION_NS 60000000000ULL // 60 сек
|
||||
|
||||
// Статистические индексы
|
||||
#define STAT_TOTAL 0
|
||||
#define STAT_BLOCKED 1
|
||||
#define STAT_RATELIM 2
|
||||
|
||||
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);
|
||||
// ОРИГИНАЛ (сломан):
|
||||
if (state == AWAIT_ACK) {
|
||||
initial_state->state = state = AWAIT_MC_HANDSHAKE;
|
||||
if (tcp_payload >= tcp_payload_end) {
|
||||
goto drop; // Pure ACK dropped → DEADLOCK
|
||||
}
|
||||
}
|
||||
|
||||
SEC("xdp")
|
||||
int minecraft_xdp_filter(struct xdp_md *ctx) {
|
||||
void *data = (void *)(long)ctx->data;
|
||||
void *data_end = (void *)(long)ctx->data_end;
|
||||
|
||||
inc_stat(STAT_TOTAL);
|
||||
|
||||
// ── Парсим Ethernet ──
|
||||
struct ethhdr *eth = data;
|
||||
if ((void *)(eth + 1) > data_end) return XDP_PASS;
|
||||
if (eth->h_proto != bpf_htons(ETH_P_IP)) return XDP_PASS;
|
||||
|
||||
// ── Парсим IP ──
|
||||
struct iphdr *ip = (void *)(eth + 1);
|
||||
if ((void *)(ip + 1) > data_end) return XDP_PASS;
|
||||
if (ip->protocol != IPPROTO_TCP) return XDP_PASS; // UDP → дроп неявный (MC=TCP)
|
||||
|
||||
__u32 src_ip = ip->saddr;
|
||||
|
||||
// ── Whitelist (наши edge ноды, manager) ──
|
||||
if (bpf_map_lookup_elem(&trusted_map, &src_ip)) return XDP_PASS;
|
||||
|
||||
// ── Парсим TCP ──
|
||||
struct tcphdr *tcp = (void *)ip + (ip->ihl * 4);
|
||||
if ((void *)(tcp + 1) > data_end) return XDP_PASS;
|
||||
if (tcp->dest != bpf_htons(MC_PORT)) return XDP_PASS;
|
||||
|
||||
// ── Блэклист проверка ──
|
||||
struct lpm_key key = { .prefixlen = 32, .ip = src_ip };
|
||||
__u64 *ban_ts = bpf_map_lookup_elem(&blacklist_map, &key);
|
||||
if (ban_ts) {
|
||||
__u64 now = bpf_ktime_get_ns();
|
||||
if (now - *ban_ts < BAN_DURATION_NS) {
|
||||
inc_stat(STAT_BLOCKED);
|
||||
return XDP_DROP;
|
||||
}
|
||||
bpf_map_delete_elem(&blacklist_map, &key);
|
||||
// ИСПРАВЛЕНИЕ:
|
||||
if (state == AWAIT_ACK) {
|
||||
initial_state->state = state = AWAIT_MC_HANDSHAKE;
|
||||
if (tcp_payload >= tcp_payload_end) {
|
||||
return XDP_PASS; // Pure ACK passed → handshake ok
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
// ── Invalid TCP flags ──
|
||||
// Дропаем пакеты с мусорными флагами (не SYN, не ACK, не PSH+ACK)
|
||||
__u8 flags = ((__u8 *)tcp)[13];
|
||||
if ((flags & 0x3F) == 0) { // нет флагов вообще
|
||||
inc_stat(STAT_BLOCKED);
|
||||
return XDP_DROP;
|
||||
}
|
||||
### 2. Stale conntrack на RST/FIN
|
||||
|
||||
// ── SYN rate limit ──
|
||||
if (tcp->syn && !tcp->ack) {
|
||||
struct rate_entry *entry = bpf_map_lookup_elem(&rate_map, &src_ip);
|
||||
__u64 now = bpf_ktime_get_ns();
|
||||
```c
|
||||
// В AWAIT_MC_HANDSHAKE — RST/FIN должен чистить entry:
|
||||
if ((tcp->fin || tcp->rst) && state != AWAIT_ACK) {
|
||||
bpf_map_delete_elem(&conntrack_map, &flow_key);
|
||||
return XDP_PASS;
|
||||
}
|
||||
```
|
||||
|
||||
if (entry) {
|
||||
// Простой sliding window
|
||||
if (now - entry->window_start < 1000000000ULL) { // 1 сек
|
||||
if (entry->count >= RATE_LIMIT_PPS) {
|
||||
// Баним
|
||||
__u64 ban_ts = now;
|
||||
bpf_map_update_elem(&blacklist_map, &key, &ban_ts, BPF_ANY);
|
||||
inc_stat(STAT_RATELIM);
|
||||
inc_stat(STAT_BLOCKED);
|
||||
return XDP_DROP;
|
||||
}
|
||||
__sync_fetch_and_add(&entry->count, 1);
|
||||
} else {
|
||||
// Новое окно
|
||||
entry->window_start = now;
|
||||
entry->count = 1;
|
||||
}
|
||||
} else {
|
||||
struct rate_entry new_entry = { .window_start = now, .count = 1 };
|
||||
bpf_map_update_elem(&rate_map, &src_ip, &new_entry, BPF_ANY);
|
||||
}
|
||||
}
|
||||
### 3. Player map LRU
|
||||
|
||||
```c
|
||||
// Оригинал: BPF_MAP_TYPE_HASH (plain) — при заполнении дропает новых игроков
|
||||
// Исправление:
|
||||
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");
|
||||
```
|
||||
|
||||
### 4. Idle timer на conntrack (не только на player)
|
||||
|
||||
```c
|
||||
// Добавить bpf_timer в conntrack entry:
|
||||
struct conntrack_entry {
|
||||
__u32 state;
|
||||
__u32 expected_seq;
|
||||
__u32 src_ip;
|
||||
__u16 src_port;
|
||||
__u8 fails;
|
||||
struct bpf_timer timer; // 30 sec idle timeout
|
||||
};
|
||||
```
|
||||
|
||||
### 5. IPv6 support
|
||||
|
||||
```c
|
||||
// Оригинал: return XDP_PASS for non-IP (IPv6 bypass)
|
||||
// Исправление:
|
||||
if (eth->h_proto != bpf_htons(ETH_P_IP)
|
||||
&& eth->h_proto != bpf_htons(ETH_P_IPV6)) {
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
// Отдельный flow key для IPv6:
|
||||
struct flow_key_v6 {
|
||||
struct in6_addr src_ip;
|
||||
struct in6_addr dst_ip;
|
||||
__u16 src_port;
|
||||
__u16 dst_port;
|
||||
};
|
||||
```
|
||||
|
||||
### 6. IP/CIDR whitelist (issue #42 из MC-XDP-eBPF)
|
||||
|
||||
```c
|
||||
struct whitelist_key {
|
||||
__u32 prefixlen;
|
||||
__u32 ip;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
|
||||
__uint(max_entries, 10000);
|
||||
__type(key, struct whitelist_key);
|
||||
__type(value, __u8);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
} whitelist_map SEC(".maps");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rust loader (libbpf-rs)
|
||||
## BPF Maps
|
||||
|
||||
```c
|
||||
// ── Blacklist (LPM_TRIE для CIDR) ──
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
|
||||
__uint(max_entries, 100000);
|
||||
__type(key, struct lpm_key);
|
||||
__type(value, __u64); // timestamp ban
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
} blacklist_map SEC(".maps");
|
||||
|
||||
// ── Whitelist (LPM_TRIE для CIDR) ──
|
||||
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");
|
||||
|
||||
// ── Conntrack (unverified connections, LRU) ──
|
||||
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 players (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-IP ──
|
||||
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, для Prometheus) ──
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||
__uint(max_entries, 16);
|
||||
__type(key, __u32);
|
||||
__type(value, __u64);
|
||||
} stats_map SEC(".maps");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Граница XDP / Rust
|
||||
|
||||
```
|
||||
XDP делает (L3/L4): Rust делает (L7):
|
||||
TCP state machine PoW Challenge (SHA256)
|
||||
SYN throttle per-IP MC handshake парсинг
|
||||
IP blacklist (LPM_TRIE) HMAC sign/verify
|
||||
IP whitelist (CIDR) Rate limit per-IP
|
||||
Invalid TCP flags drop Death code auto-ban
|
||||
UDP drop GeoIP/ASN reputation
|
||||
Per-connection seq tracking Blacklist (сложные правила)
|
||||
bpf_timer idle cleanup Reassembly буфер
|
||||
```
|
||||
|
||||
XDP **не может** (даже в kernel 6.x):
|
||||
- SHA256 (нет heap, нет looping на достаточное время)
|
||||
- Floating point (ограничен)
|
||||
- Сложные строковые операции
|
||||
- TLS инспекция
|
||||
- GeoIP / DNS resolve
|
||||
|
||||
---
|
||||
|
||||
## Загрузка (Rust + libbpf-rs)
|
||||
|
||||
```rust
|
||||
// xdp/loader.rs
|
||||
use libbpf_rs::{MapFlags, ObjectBuilder};
|
||||
|
||||
use libbpf_rs::{MapFlags, Object, ObjectBuilder};
|
||||
|
||||
pub struct XdpFilter {
|
||||
pub struct RampartXdp {
|
||||
obj: Object,
|
||||
interface: String,
|
||||
}
|
||||
|
||||
impl XdpFilter {
|
||||
impl RampartXdp {
|
||||
pub fn load(interface: &str) -> Result<Self> {
|
||||
let obj = ObjectBuilder::default()
|
||||
.open_file("/etc/rampart/xdp_filter.o")?
|
||||
.load()?;
|
||||
|
||||
// Аттачим XDP программу к интерфейсу
|
||||
let prog = obj.prog("minecraft_xdp_filter").unwrap();
|
||||
let prog = obj.prog("rampart_xdp_filter").unwrap();
|
||||
prog.attach_xdp(if_nametoindex(interface)?)?;
|
||||
|
||||
Ok(Self { obj, interface: interface.to_string() })
|
||||
}
|
||||
|
||||
// Добавляем IP в блэклист из Rust (обновляем BPF map)
|
||||
pub fn ban_ip(&self, ip: Ipv4Addr, duration: Duration) {
|
||||
let mut map = self.obj.map("blacklist_map").unwrap();
|
||||
let key = LpmKey::new(32, ip);
|
||||
let ts = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos() as u64;
|
||||
let ts = now_nanos() + duration.as_nanos() as u64;
|
||||
map.update(&key.to_bytes(), &ts.to_le_bytes(), MapFlags::ANY).unwrap();
|
||||
}
|
||||
|
||||
// Читаем статистику
|
||||
pub fn get_stats(&self) -> XdpStats {
|
||||
let map = self.obj.map("stats_map").unwrap();
|
||||
XdpStats {
|
||||
total: read_percpu_sum(&map, 0),
|
||||
blocked: read_percpu_sum(&map, 1),
|
||||
ratelim: read_percpu_sum(&map, 2),
|
||||
}
|
||||
}
|
||||
|
||||
// Читаем события из ringbuf (атаки, баны)
|
||||
pub async fn read_events(&self, tx: mpsc::Sender<XdpEvent>) {
|
||||
let mut ringbuf = RingBuffer::new();
|
||||
ringbuf.add(self.obj.map("events").unwrap(), move |data| {
|
||||
let event: XdpEvent = unsafe { *(data.as_ptr() as *const XdpEvent) };
|
||||
let _ = tx.try_send(event);
|
||||
0
|
||||
}).unwrap();
|
||||
|
||||
loop {
|
||||
ringbuf.poll(Duration::from_millis(10)).unwrap();
|
||||
total: self.read_percpu_sum("stats_map", 0),
|
||||
passed: self.read_percpu_sum("stats_map", 1),
|
||||
blocked: self.read_percpu_sum("stats_map", 2),
|
||||
ratelimit: self.read_percpu_sum("stats_map", 3),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -267,74 +297,38 @@ impl XdpFilter {
|
|||
|
||||
---
|
||||
|
||||
## Cargo.toml для XDP компонента
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
libbpf-rs = "0.23"
|
||||
libbpf-sys = "1.4"
|
||||
|
||||
[build-dependencies]
|
||||
libbpf-cargo = "0.23" # автокомпиляция .c → .o в build.rs
|
||||
```
|
||||
|
||||
```rust
|
||||
// build.rs
|
||||
use libbpf_cargo::SkeletonBuilder;
|
||||
|
||||
fn main() {
|
||||
SkeletonBuilder::new()
|
||||
.source("src/bpf/xdp_filter.c")
|
||||
.build_and_generate("src/bpf/xdp_filter.skel.rs")
|
||||
.unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Требования к окружению
|
||||
## Требования
|
||||
|
||||
```bash
|
||||
# Проверка что XDP поддерживается
|
||||
ethtool -i eth0 | grep driver # должен быть i40e, mlx5, или virtio
|
||||
# Проверка XDP
|
||||
ethtool -i eth0 | grep driver # i40e, mlx5, virtio
|
||||
|
||||
# Проверка типа виртуализации
|
||||
systemd-detect-virt
|
||||
# kvm → XDP работает (native или generic)
|
||||
# none → bare metal → XDP native
|
||||
# openvz / lxc → XDP НЕ работает
|
||||
# Тип виртуализации
|
||||
systemd-detect-virt # kvm/none = XDP, openvz/lxc = нет
|
||||
|
||||
# Проверка версии ядра
|
||||
uname -r
|
||||
# >= 5.10 - достаточно для нашего XDP
|
||||
# >= 6.0 - полный функционал (float в eBPF, CO-RE стабильный)
|
||||
# Версия ядра
|
||||
uname -r # >= 5.10
|
||||
|
||||
# Установка зависимостей (Ubuntu 22.04+)
|
||||
# Зависимости
|
||||
apt-get install -y libbpf-dev clang llvm linux-headers-$(uname -r)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ringbuf vs perfbuf
|
||||
## ringbuf (вместо perfbuf)
|
||||
|
||||
| | perfbuf | ringbuf (kernel 5.8+) |
|
||||
|---|---|---|
|
||||
| Тип | Per-CPU кольцевой буфер | Один разделяемый буфер |
|
||||
| Тип | Per-CPU | Один разделяемый |
|
||||
| Копирование | Одно | Одно |
|
||||
| Порядок событий | Не гарантирован | Гарантирован |
|
||||
| Потребление памяти | Per-CPU | Меньше |
|
||||
| Порядок | Не гарантирован | Гарантирован |
|
||||
| Память | Per-CPU | Меньше |
|
||||
| **Вывод** | Устаревший | **Используй ringbuf** |
|
||||
|
||||
---
|
||||
|
||||
## Известные лимиты BPF verifier
|
||||
## Лимиты BPF verifier
|
||||
|
||||
```
|
||||
Максимум инструкций: 1M (kernel 5.2+, раньше 4096)
|
||||
Максимум инструкций: 1M (kernel 5.2+)
|
||||
Максимум стека: 512 байт
|
||||
Максимум вложенности: 8 уровней (loops разрешены с 5.3+)
|
||||
Циклы: разрешены, но верификатор считает итерации
|
||||
Динамический allocation: нет (только BPF maps)
|
||||
Максимум вложенности: 8
|
||||
Циклы: разрешены с 5.3+
|
||||
Dynamic alloc: нет (только BPF maps)
|
||||
```
|
||||
|
||||
Если программа не проходит верификатор - упрости логику или разбей на несколько программ в цепочке (TC + XDP).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue