Un server primește telemetrie pe UDP 9998, dar vechiul port UDP 9999 trebuie eliminat înainte ca traficul să ajungă în stiva de rețea. Vom compila un program XDP care lasă să treacă restul traficului, blochează exclusiv destinația UDP 9999 și păstrează contoare separate pentru pachetele acceptate și respinse.
Rezultatul rulează pe interfața dedicată enp2s0, în modul nativ xdpdrv, este pornit de systemd și verificat la fiecare minut. Un client din rețeaua de documentare 192.0.2.0/24 va primi răspuns de la UDP 9998, dar nu de la UDP 9999. Dacă programul dispare, monitorul eșuează vizibil în jurnal.
Acesta este un laborator defensiv pentru un server propriu. Nu atașa programul pe interfața de administrare și nu lucra fără acces local sau consolă out-of-band: o regulă XDP greșită poate întrerupe traficul înainte ca firewall-ul, tcpdump sau aplicațiile să îl vadă.
Fixăm filtrul la Debian 12 și la o interfață separată de management
Varianta de aici folosește Debian 12 „bookworm” pe x86_64, kernelul 6.1, Clang 14, iproute2, bpftool și antetele libbpf din distribuție. Sunt necesare minimum 1 GB RAM, aproximativ 200 MB liberi și o placă al cărei driver acceptă XDP nativ.
Scenariul are două interfețe:
enp1s0: management; nu este modificată;enp2s0: rețeaua de test, cu adresa exemplu192.0.2.10/24;- client extern:
192.0.2.20/24; - UDP 9998: serviciu permis;
- UDP 9999: port retras și blocat de XDP.
Înlocuiește enp2s0 numai dacă interfața ta dedicată are alt nume. Păstrează același nume în comenzile ulterioare.
Verifică mediul înainte de instalare:
cat /etc/os-release
uname -r
uname -m
ip -brief link
ip -brief address show dev enp2s0
ethtool -i enp2s0
systemd --version | head -n 1
df -h /opt /usr/local
free -h
Un rezultat compatibil arată astfel; versiunea minoră a kernelului și driverul vor diferi:
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
6.1.0-xx-amd64
x86_64
enp2s0 UP 192.0.2.10/24
driver: <driverul plăcii>
systemd 252
ethtool -i identifică driverul, dar nu garantează singur suportul XDP. Testul decisiv este atașarea cu xdpdrv: această opțiune interzice revenirea automată la implementarea generică. Dacă driverul nu acceptă modul nativ, comanda va eșua, iar tutorialul nu maschează problema cu xdpgeneric.
Timpul estimat este de 45–60 de minute, inclusiv testul de pe a doua mașină. Dacă dorești mai întâi o topologie complet izolată, articolul despre network namespaces și veth pairs explică separarea traficului de laborator; aici păstrăm însă o singură variantă, testată prin interfața fizică dedicată.
Instalează toate componentele într-un singur bloc:
sudo apt update
sudo apt install --yes
clang-14
llvm-14
libbpf-dev
bpftool
iproute2
ethtool
jq
python3
clang-14 --version | head -n 1
bpftool version
ip -Version
dpkg-query -W libbpf-dev
Ieșirea trebuie să confirme Clang 14 și să afișeze versiunile instalate de depozitul Debian 12:
Debian clang version 14.0.6
bpftool v7.1.0
ip utility, iproute2-6.1.0
libbpf-dev <versiunea instalată>
Numerele complete ale pachetelor pot include revizii de securitate Debian. Nu continua dacă clang-14 nu listează ținta BPF la compilare sau dacă mașina nu este x86_64, deoarece scriptul de compilare de mai jos fixează explicit arhitectura.
Cele șapte fișiere separă filtrarea, încărcarea și supravegherea
Proiectul sursă va fi creat în /opt/xdp-guard-src, iar componentele instalate vor ajunge în /usr/local/lib/xdp-guard, /usr/local/sbin și /etc/systemd/system.
/opt/xdp-guard-src/
├── src/
│ └── xdp_guard.bpf.c programul executat în hook-ul XDP
├── scripts/
│ ├── build.sh compilare reproductibilă cu Clang 14
│ ├── xdp-guard-attach validare și atașare în modul driver
│ └── xdp-guard-monitor verificarea programului și a contoarelor
└── systemd/
├── xdp-guard@.service ciclul de viață al programului XDP
├── xdp-guard-monitor@.service o verificare fără stare
└── xdp-guard-monitor@.timer verificare repetată la un minut
Obiectul compilat va fi instalat ca /usr/local/lib/xdp-guard/xdp_guard.bpf.o. Nu folosim parole, tokenuri sau fișiere de mediu, iar numele interfeței este transmis prin instanța systemd.
Pasul 1 — Clasificăm doar IPv4/UDP și lăsăm toate celelalte cadre să treacă
De ce: filtrul trebuie să aibă o regulă îngustă și verificabilă. Cadrele trunchiate, traficul non-IPv4, IPv6, TCP și orice port UDP diferit de 9999 primesc XDP_PASS. Doar pachetele IPv4/UDP cu portul destinație 9999 primesc XDP_DROP.
Creează directoarele fără să suprascrii un proiect existent:
sudo test ! -e /opt/xdp-guard-src
sudo install -d -m 0755
/opt/xdp-guard-src/src
/opt/xdp-guard-src/scripts
/opt/xdp-guard-src/systemd
Fișierul /opt/xdp-guard-src/src/xdp_guard.bpf.c:
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
#define BLOCKED_UDP_PORT 9999
enum counter_index {
COUNTER_PASS = 0,
COUNTER_DROP = 1,
};
struct counter_value {
__u64 packets;
__u64 bytes;
};
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 2);
__type(key, __u32);
__type(value, struct counter_value);
} xdp_stats SEC(".maps");
static __always_inline int account_and_return(
__u32 key,
int action,
__u64 packet_bytes)
{
struct counter_value *counter;
counter = bpf_map_lookup_elem(&xdp_stats, &key);
if (counter) {
__sync_fetch_and_add(&counter->packets, 1);
__sync_fetch_and_add(&counter->bytes, packet_bytes);
}
return action;
}
SEC("xdp")
int xdp_guard(struct xdp_md *ctx)
{
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
__u64 packet_bytes = (__u64)(data_end - data);
struct ethhdr *eth = data;
struct iphdr *ip;
struct udphdr *udp;
void *layer4;
__u32 ip_header_bytes;
if ((void *)(eth + 1) > data_end)
return account_and_return(
COUNTER_PASS, XDP_PASS, packet_bytes);
if (eth->h_proto != bpf_htons(ETH_P_IP))
return account_and_return(
COUNTER_PASS, XDP_PASS, packet_bytes);
ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end)
return account_and_return(
COUNTER_PASS, XDP_PASS, packet_bytes);
if (ip->ihl < 5)
return account_and_return(
COUNTER_PASS, XDP_PASS, packet_bytes);
ip_header_bytes = (__u32)ip->ihl * 4;
layer4 = (void *)ip + ip_header_bytes;
if (layer4 > data_end)
return account_and_return(
COUNTER_PASS, XDP_PASS, packet_bytes);
if (ip->protocol != IPPROTO_UDP)
return account_and_return(
COUNTER_PASS, XDP_PASS, packet_bytes);
udp = layer4;
if ((void *)(udp + 1) > data_end)
return account_and_return(
COUNTER_PASS, XDP_PASS, packet_bytes);
if (udp->dest == bpf_htons(BLOCKED_UDP_PORT))
return account_and_return(
COUNTER_DROP, XDP_DROP, packet_bytes);
return account_and_return(
COUNTER_PASS, XDP_PASS, packet_bytes);
}
char LICENSE[] SEC("license") = "GPL";
Ce înseamnă: data și data_end delimitează memoria pe care verificatorul eBPF permite programului să o citească. Fiecare antet este validat înainte de acces. Harta BPF_MAP_TYPE_ARRAY are cheia 0 pentru trafic permis și cheia 1 pentru trafic respins. Actualizările folosesc operații atomice deoarece mai multe cozi RX și mai multe procesoare pot executa programul simultan.
Filtrul nu interpretează VLAN și nu blochează IPv6. Aceasta este o limită intenționată: un parser incomplet nu trebuie prezentat drept politică universală.
Ce trebuie să vezi: fișierul există și nu conține marcaje incomplete:
sudo test -s /opt/xdp-guard-src/src/xdp_guard.bpf.c
sudo grep -n 'BLOCKED_UDP_PORT|SEC("xdp")|XDP_DROP'
/opt/xdp-guard-src/src/xdp_guard.bpf.c
Exemplu de rezultat:
8:#define BLOCKED_UDP_PORT 9999
41:SEC("xdp")
79: COUNTER_DROP, XDP_DROP, packet_bytes);
Punct de control: dacă liniile lipsesc, oprește-te și verifică fișierul înainte de compilare; nu atașa un obiect al cărui cod sursă nu corespunde regulii urmărite.
Pasul 2 — Compilăm obiectul BPF și îl inspectăm înainte de încărcare
De ce: un obiect ELF valid și o secțiune xdp vizibilă sunt condiții necesare, dar nu suficiente. Verificatorul kernelului va face validarea finală la încărcare.
Fișierul /opt/xdp-guard-src/scripts/build.sh:
#!/usr/bin/env bash
set -euo pipefail
readonly PROJECT_DIR="/opt/xdp-guard-src"
readonly SOURCE="${PROJECT_DIR}/src/xdp_guard.bpf.c"
readonly BUILD_DIR="${PROJECT_DIR}/build"
readonly OUTPUT="${BUILD_DIR}/xdp_guard.bpf.o"
if [[ "$(uname -m)" != "x86_64" ]]; then
printf 'Eroare: acest build este fixat la x86_64.n' >&2
exit 1
fi
if [[ ! -s "${SOURCE}" ]]; then
printf 'Eroare: lipsește sursa %s.n' "${SOURCE}" >&2
exit 1
fi
install -d -m 0755 "${BUILD_DIR}"
clang-14
-target bpf
-D__TARGET_ARCH_x86
-O2
-g
-Wall
-Wextra
-Werror
-I/usr/include/x86_64-linux-gnu
-c "${SOURCE}"
-o "${OUTPUT}"
llvm-objdump-14 -h "${OUTPUT}"
test -s "${OUTPUT}"
printf 'Obiect creat: %sn' "${OUTPUT}"
Rulează compilarea:
sudo chmod 0755 /opt/xdp-guard-src/scripts/build.sh
sudo /opt/xdp-guard-src/scripts/build.sh
sudo llvm-objdump-14 -h
/opt/xdp-guard-src/build/xdp_guard.bpf.o
Ce înseamnă: -target bpf produce bytecode eBPF, -O2 este necesar pentru eliminarea unor construcții pe care verificatorul nu le-ar putea urmări eficient, iar -g păstrează informațiile BTF folosite de instrumentele de inspecție. -Werror oprește build-ul dacă sursa introduce avertismente.
Ce trebuie să vezi: numerele și dimensiunile secțiunilor pot varia, dar trebuie să apară xdp, .maps și license.
Sections:
Idx Name Size
3 xdp <dimensiune variabilă>
5 .maps <dimensiune variabilă>
6 license <dimensiune variabilă>
Obiect creat: /opt/xdp-guard-src/build/xdp_guard.bpf.o
Punct de control: dacă apare No available targets are compatible with triple "bpf", verifică instalarea clang-14 și execută clang-14 -print-targets | grep bpf; nu schimba ținta în x86_64, deoarece rezultatul nu ar mai fi program eBPF.
Pasul 3 — Refuzăm atașarea peste un program XDP existent
De ce: suprascrierea unui filtru deja atașat poate elimina o protecție activă sau poate rupe un dispatcher folosit de altă aplicație. Scriptul se oprește când detectează prog/xdp pe interfață și solicită explicit modul nativ.
Fișierul /opt/xdp-guard-src/scripts/xdp-guard-attach:
#!/usr/bin/env bash
set -euo pipefail
readonly OBJECT="/usr/local/lib/xdp-guard/xdp_guard.bpf.o"
readonly SECTION="xdp_guard"
readonly IFACE="${1:-}"
if [[ -z "${IFACE}" ]]; then
printf 'Utilizare: %s INTERFATAn' "$0" >&2
exit 2
fi
if [[ ! -s "${OBJECT}" ]]; then
printf 'Eroare: obiectul BPF lipsește: %sn' "${OBJECT}" >&2
exit 1
fi
if ! ip link show dev "${IFACE}" >/dev/null 2>&1; then
printf 'Eroare: interfața %s nu există.n' "${IFACE}" >&2
exit 1
fi
if ip -details link show dev "${IFACE}" | grep -q 'prog/xdp'; then
printf 'Eroare: %s are deja un program XDP atașat.n'
"${IFACE}" >&2
exit 1
fi
ip link set dev "${IFACE}"
xdpdrv obj "${OBJECT}" sec "${SECTION}"
if ! ip -details link show dev "${IFACE}" | grep -q 'prog/xdp'; then
printf 'Eroare: atașarea nu apare în starea interfeței.n' >&2
exit 1
fi
printf 'XDP atașat în mod driver pe %s.n' "${IFACE}"
Fișierul /opt/xdp-guard-src/scripts/xdp-guard-monitor:
#!/usr/bin/env bash
set -euo pipefail
readonly IFACE="${1:-}"
if [[ -z "${IFACE}" ]]; then
printf 'Utilizare: %s INTERFATAn' "$0" >&2
exit 2
fi
readonly LINK_STATE="$(ip -details link show dev "${IFACE}")"
if ! grep -q 'prog/xdp' <<<"${LINK_STATE}"; then
printf 'ALERTĂ: niciun program XDP pe %s.n' "${IFACE}" >&2
exit 1
fi
readonly MAP_COUNT="$(
bpftool map show name xdp_stats 2>/dev/null |
grep -c 'name xdp_stats' || true
)"
if [[ "${MAP_COUNT}" -ne 1 ]]; then
printf 'ALERTĂ: sunt vizibile %s hărți numite xdp_stats.n'
"${MAP_COUNT}" >&2
exit 1
fi
readonly PASS_VALUE="$(
bpftool map lookup name xdp_stats key hex 00 00 00 00
)"
readonly DROP_VALUE="$(
bpftool map lookup name xdp_stats key hex 01 00 00 00
)"
printf 'Interfață: %sn' "${IFACE}"
printf 'PASS:n%sn' "${PASS_VALUE}"
printf 'DROP:n%sn' "${DROP_VALUE}"
Ce înseamnă: xdpdrv cere atașarea în driver și nu permite fallback la XDP generic. Monitorul cere exact o hartă xdp_stats; două instanțe ale programului ar crea nume ambigue pentru interogarea prin bpftool.
Instalează obiectul și scripturile:
sudo install -d -m 0755 /usr/local/lib/xdp-guard
sudo install -m 0644
/opt/xdp-guard-src/build/xdp_guard.bpf.o
/usr/local/lib/xdp-guard/xdp_guard.bpf.o
sudo install -m 0755
/opt/xdp-guard-src/scripts/xdp-guard-attach
/usr/local/sbin/xdp-guard-attach
sudo install -m 0755
/opt/xdp-guard-src/scripts/xdp-guard-monitor
/usr/local/sbin/xdp-guard-monitor
Ce trebuie să vezi:
sudo /usr/local/sbin/xdp-guard-attach
Utilizare: /usr/local/sbin/xdp-guard-attach INTERFATA
Comanda se termină cu codul 2; acesta este testul validării argumentului, nu o defecțiune a programului.
Punct de control: înainte de activare, rulează sudo ip -details link show dev enp2s0; dacă apare deja prog/xdp, identifică proprietarul prin bpftool net show dev enp2s0 și nu continua. Pentru inspecție suplimentară, folosește fluxul din ghidul bpftool pentru administratori avansați, fără a detașa programul găsit.
Pasul 4 — Legăm atașarea și controlul periodic de systemd
De ce: programul atașat manual nu reprezintă un serviciu administrabil. Unitatea păstrează starea activă, execută detașarea la oprire și pornește un control periodic ale cărui eșecuri rămân în jurnal.
Fișierul /opt/xdp-guard-src/systemd/xdp-guard@.service:
[Unit]
Description=Filtru XDP pentru interfața %I
Documentation=man:ip-link(8) man:bpftool(8)
BindsTo=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
Before=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/xdp-guard-attach %I
ExecStop=-/usr/sbin/ip link set dev %I xdpdrv off
TimeoutStartSec=20s
TimeoutStopSec=10s
CapabilityBoundingSet=CAP_BPF CAP_NET_ADMIN CAP_PERFMON
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
RestrictAddressFamilies=AF_NETLINK AF_UNIX
[Install]
WantedBy=multi-user.target
Fișierul /opt/xdp-guard-src/systemd/xdp-guard-monitor@.service:
[Unit]
Description=Controlul filtrului XDP pe %I
Requires=xdp-guard@%i.service
After=xdp-guard@%i.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/xdp-guard-monitor %I
CapabilityBoundingSet=CAP_BPF CAP_NET_ADMIN CAP_PERFMON
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
RestrictAddressFamilies=AF_NETLINK AF_UNIX
Fișierul /opt/xdp-guard-src/systemd/xdp-guard-monitor@.timer:
[Unit]
Description=Verificare periodică XDP pe %I
[Timer]
OnBootSec=2min
OnUnitActiveSec=1min
AccuracySec=10s
Persistent=true
Unit=xdp-guard-monitor@%i.service
[Install]
WantedBy=timers.target
Înainte de copiere, păstrează copii dacă oricare dintre unități există deja:
for file in
xdp-guard@.service
xdp-guard-monitor@.service
xdp-guard-monitor@.timer
do
if sudo test -e "/etc/systemd/system/${file}"; then
sudo cp -a
"/etc/systemd/system/${file}"
"/etc/systemd/system/${file}.bak"
fi
done
sudo install -m 0644
/opt/xdp-guard-src/systemd/xdp-guard@.service
/etc/systemd/system/xdp-guard@.service
sudo install -m 0644
/opt/xdp-guard-src/systemd/xdp-guard-monitor@.service
/etc/systemd/system/xdp-guard-monitor@.service
sudo install -m 0644
/opt/xdp-guard-src/systemd/xdp-guard-monitor@.timer
/etc/systemd/system/xdp-guard-monitor@.timer
sudo systemd-analyze verify
/etc/systemd/system/xdp-guard@.service
/etc/systemd/system/xdp-guard-monitor@.service
/etc/systemd/system/xdp-guard-monitor@.timer
sudo systemctl daemon-reload
Ce înseamnă: BindsTo= oprește unitatea dacă dispare dispozitivul, RemainAfterExit=yes păstrează unitatea activă după atașare, iar ExecStop elimină programul la oprire. Timerul rulează la două minute după boot și apoi la fiecare minut. Opțiunile de izolare reduc accesul proceselor user-space, fără să transforme codul eBPF într-un program neprivilegiat.
Ordinea serviciilor de rețea trebuie tratată explicit pe serverele cu topologii mai complicate; ghidul despre dependențele systemd între rețea și aplicații explică relațiile cu unitățile care configurează interfețele.
Avertisment: următoarea comandă modifică imediat calea RX a interfeței enp2s0. Confirmă încă o dată că sesiunea de administrare folosește enp1s0 sau consola locală.
ip route get 192.0.2.20
sudo systemctl enable --now xdp-guard@enp2s0.service
sudo systemctl enable --now xdp-guard-monitor@enp2s0.timer
sudo systemctl status xdp-guard@enp2s0.service --no-pager
sudo ip -details link show dev enp2s0
sudo bpftool prog show name xdp_guard
sudo bpftool map show name xdp_stats
Ce trebuie să vezi: identificatorii, tag-ul și dimensiunile pot varia.
XDP atașat în mod driver pe enp2s0.
Active: active (exited)
prog/xdp id <ID> tag <TAG>
xdp name xdp_guard
array name xdp_stats flags 0x0
Punct de control: dacă unitatea nu devine active (exited), rulează imediat journalctl -u xdp-guard@enp2s0.service -b --no-pager; nu activa timerul până când încărcarea nu este confirmată.
Pasul 5 — Demonstrăm de pe altă mașină că 9998 trece, iar 9999 dispare înaintea socketului
De ce: inspecția locală confirmă atașarea, nu și politica. Testul de capăt folosește un serviciu UDP temporar pe ambele porturi și un singur client extern.
Pe server, creează /tmp/xdp-udp-echo.py:
#!/usr/bin/env python3
import selectors
import socket
import sys
BIND_ADDRESS = "192.0.2.10"
PORTS = (9998, 9999)
selector = selectors.DefaultSelector()
sockets = []
try:
for port in PORTS:
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.bind((BIND_ADDRESS, port))
udp_socket.setblocking(False)
selector.register(udp_socket, selectors.EVENT_READ)
sockets.append(udp_socket)
print("READY 192.0.2.10:9998,9999", flush=True)
while True:
for key, _ in selector.select():
payload, peer = key.fileobj.recvfrom(2048)
key.fileobj.sendto(b"ACK:" + payload[:128], peer)
except OSError as error:
print(f"Eroare socket: {error}", file=sys.stderr)
sys.exit(1)
finally:
for udp_socket in sockets:
udp_socket.close()
Pornește-l într-un terminal separat:
sudo -u nobody python3 /tmp/xdp-udp-echo.py
Ce înseamnă: procesul ascultă pe ambele porturi, fără privilegii root. Diferența observată de client provine din regula XDP, nu din absența unui socket pe 9999.
Ce trebuie să vezi:
READY 192.0.2.10:9998,9999
Pe clientul 192.0.2.20, salvează /tmp/xdp-client-test.py:
#!/usr/bin/env python3
import socket
import sys
SERVER = "192.0.2.10"
TESTS = (
(9998, True),
(9999, False),
)
failures = 0
for port, should_reply in TESTS:
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.settimeout(2.0)
try:
payload = f"probe-{port}".encode("ascii")
client.sendto(payload, (SERVER, port))
response, _ = client.recvfrom(2048)
replied = response == b"ACK:" + payload
except socket.timeout:
replied = False
finally:
client.close()
expected = "răspuns" if should_reply else "timeout"
observed = "răspuns" if replied else "timeout"
print(f"UDP {port}: așteptat={expected}, observat={observed}")
if replied != should_reply:
failures += 1
if failures:
print(f"TEST EȘUAT: {failures} verificări incorecte", file=sys.stderr)
sys.exit(1)
print("TEST REUȘIT: 9998 permis, 9999 blocat")
Execută testul de pe client:
python3 /tmp/xdp-client-test.py
Ce trebuie să vezi:
UDP 9998: așteptat=răspuns, observat=răspuns
UDP 9999: așteptat=timeout, observat=timeout
TEST REUȘIT: 9998 permis, 9999 blocat
Înapoi pe server, citește contoarele:
sudo /usr/local/sbin/xdp-guard-monitor enp2s0
sudo systemctl start xdp-guard-monitor@enp2s0.service
sudo journalctl
-u xdp-guard-monitor@enp2s0.service
-n 30
--no-pager
Exemplul următor este orientativ; contoarele vor include tot traficul primit pe interfață:
Interfață: enp2s0
PASS:
key:
00 00 00 00
value:
<contoare nenule>
DROP:
key:
01 00 00 00
value:
<cel puțin un pachet și numărul corespunzător de octeți>
Punct de control: rezultatul este acceptat numai dacă 9998 răspunde, 9999 expiră, cheia DROP a crescut și unitatea de monitorizare se termină cu succes. Un timeout pe ambele porturi indică o problemă de rutare, adresare sau firewall, nu dovedește filtrarea corectă.
Riscurile operaționale pe care testul trebuie să le scoată la lumină
XDP rulează înaintea unei mari părți din stiva de rețea. Un XDP_DROP poate face pachetul invizibil pentru socketuri și pentru capturile obișnuite realizate mai târziu în stivă. Contorul din harta eBPF este, prin urmare, parte din dovada operațională, nu un ornament.
Alte limite concrete:
- atașarea pe interfața de management poate bloca accesul administratorului;
xdpdrveșuează dacă driverul nu implementează XDP nativ;- un program XDP existent poate aparține unui firewall, load balancer sau dispatcher;
- unitatea presupune că nimeni nu înlocuiește programul în afara
systemd; - filtrul nu decodează VLAN și nu aplică regula pentru IPv6;
- portul 9999 este compilat în obiect, deci schimbarea lui cere rebuild și retestare;
- contoarele dispar când programul este detașat;
- mai multe instanțe creează hărți cu același nume, iar monitorul refuză intenționat ambiguitatea.
Nu compara performanța folosind doar numărul de pachete observat într-o probă scurtă. Debitului îi influențează driverul, cozile RX, dimensiunea cadrelor, CPU-ul, offload-urile și generatorul de trafic.
Cinci defecțiuni care indică exact unde s-a oprit proiectul
Cannot find device "enp2s0"
Cauza este numele greșit sau interfața absentă. Identifică dispozitivul dedicat și pornește instanța cu numele real:
ip -brief link
sudo systemctl disable --now xdp-guard@enp2s0.service
sudo systemctl enable --now xdp-guard@NUMELE_REAL.service
Nu alege interfața prin care este deschisă sesiunea SSH.
RTNETLINK answers: Operation not supported
Driverul nu acceptă atașarea nativă cerută prin xdpdrv sau configurația curentă a plăcii nu permite operația. Confirmă driverul și mesajele kernelului:
ethtool -i enp2s0
sudo journalctl -k -b --no-pager | tail -n 100
sudo systemctl status xdp-guard@enp2s0.service --no-pager
Nu înlocui automat xdpdrv cu xdpgeneric: ar schimba rezultatul tehnic și caracteristicile de performanță ale proiectului.
Error fetching program/map! sau No such file or directory la bpftool map lookup
Programul nu mai este atașat, harta a dispărut odată cu el sau numele nu corespunde obiectului instalat:
sudo ip -details link show dev enp2s0
sudo bpftool map show
sudo systemctl restart xdp-guard@enp2s0.service
sudo /usr/local/sbin/xdp-guard-monitor enp2s0
Dacă restartul eșuează, citește jurnalul unității înainte de o nouă încercare.
Eroare: enp2s0 are deja un program XDP atașat
Protecția din script a găsit un program existent. Inspectează-l:
sudo ip -details link show dev enp2s0
sudo bpftool net show dev enp2s0
sudo bpftool prog show
Nu îl elimina până când nu ai identificat serviciul proprietar. Acest tutorial nu configurează un dispatcher pentru mai multe programe.
Clientul raportează timeout atât pe 9998, cât și pe 9999
Ruta, adresa serverului, serviciul UDP sau un firewall ulterior blochează și portul permis. Verifică întâi serviciul și ruta:
ip -brief address show dev enp2s0
ip route get 192.0.2.20
ss -lunp | grep -E ':9998|:9999'
sudo bpftool map lookup name xdp_stats key hex 00 00 00 00
sudo bpftool map lookup name xdp_stats key hex 01 00 00 00
Dacă DROP nu crește, pachetul de test nu ajunge la regula XDP. Dacă PASS crește, dar 9998 nu răspunde, investigația continuă după hook-ul XDP.
Detașăm filtrul fără să atingem configurația IP a interfeței
Avertisment: oprirea serviciului reactivează accesul la UDP 9999. Fă acest lucru numai când politica alternativă este pregătită sau în fereastra de test.
Pentru revenire rapidă:
sudo systemctl disable --now
xdp-guard-monitor@enp2s0.timer
sudo systemctl disable --now
xdp-guard@enp2s0.service
sudo ip -details link show dev enp2s0
Ieșirea nu mai trebuie să conțină prog/xdp. Dacă unitatea nu mai există, detașarea manuală este:
sudo ip link set dev enp2s0 xdpdrv off
Oprește serviciul UDP temporar cu Ctrl+C, apoi șterge fișierele de test:
rm -f /tmp/xdp-udp-echo.py
Pe client:
rm -f /tmp/xdp-client-test.py
Pentru eliminarea completă:
sudo rm -f
/etc/systemd/system/xdp-guard@.service
/etc/systemd/system/xdp-guard-monitor@.service
/etc/systemd/system/xdp-guard-monitor@.timer
sudo rm -f
/usr/local/sbin/xdp-guard-attach
/usr/local/sbin/xdp-guard-monitor
sudo rm -rf
/usr/local/lib/xdp-guard
/opt/xdp-guard-src
sudo systemctl daemon-reload
sudo systemctl reset-failed
Dacă au fost create copii .bak, restaurează-le numai după verificarea conținutului:
for file in
xdp-guard@.service
xdp-guard-monitor@.service
xdp-guard-monitor@.timer
do
if sudo test -e "/etc/systemd/system/${file}.bak"; then
sudo mv
"/etc/systemd/system/${file}.bak"
"/etc/systemd/system/${file}"
fi
done
sudo systemctl daemon-reload
Pachetele pot rămâne instalate pentru administrarea altor programe eBPF. Dacă serverul a fost pregătit exclusiv pentru acest laborator, eliminarea lor se face separat, după verificarea dependențelor cu apt.
Lista de acceptare pentru interfața dedicată
- ☐ Managementul folosește altă interfață decât cea cu XDP.
- ☐ Sistemul este Debian 12 pe
x86_64, cu kernel 6.1. - ☐ Obiectul conține secțiunile
xdp,.mapsșilicense. - ☐ Atașarea
xdpdrvreușește fără fallback generic. - ☐ Niciun program XDP anterior nu a fost suprascris.
- ☐ UDP 9998 răspunde clientului extern.
- ☐ UDP 9999 produce timeout, iar contorul DROP crește.
- ☐ Timerul rulează la un minut și jurnalul conține contoarele.
- ☐
systemctl stop xdp-guard@enp2s0eliminăprog/xdp. - ☐ Procedura de rollback a fost testată înaintea folosirii operaționale.
Următorul pas logic este adăugarea suportului pentru cadre VLAN, cu limite verificate după fiecare antet. Pentru mai multe politici pe aceeași interfață este necesar un dispatcher precum cel oferit de libxdp, nu înlocuirea succesivă a programelor. Înaintea unui benchmark, salvează modelul plăcii, driverul, numărul cozilor RX și configurația offload-urilor, astfel încât rezultatul să poată fi reprodus.
Întrebări frecvente
De ce nu folosim xdpgeneric dacă driverul refuză programul?
Proiectul urmărește explicit atașarea nativă în driver. xdpgeneric mută execuția pe calea generică bazată pe stiva de rețea și ar schimba atât rezultatul, cât și profilul de performanță.
Filtrul blochează și UDP 9999 prin IPv6?
Nu. Programul tratează numai cadre Ethernet cu IPv4 și lasă IPv6 să treacă prin XDP_PASS.
Pot porni două instanțe pe două interfețe?
Programul poate fi atașat de mai multe ori, dar monitorul acestui proiect cere exact o hartă numită xdp_stats și refuză ambiguitatea. Tutorialul fixează deliberat o singură interfață.
De ce tcpdump poate să nu vadă pachetele blocate?
XDP_DROP este decis foarte devreme pe calea de recepție, înainte ca pachetul să ajungă la componentele obișnuite ale stivei. De aceea, verificarea folosește și contorul din harta eBPF.

















































