smoke: add xdp netns test script; fix XDP filter attach/globals
Some checks are pending
CI / Rust — check & clippy (push) Waiting to run
CI / Rust — test (push) Waiting to run
CI / Repo — module size & default secrets (push) Waiting to run
CI / Rust — cargo-deny (push) Waiting to run
CI / Docker — build edge image (push) Blocked by required conditions
Some checks are pending
CI / Rust — check & clippy (push) Waiting to run
CI / Rust — test (push) Waiting to run
CI / Repo — module size & default secrets (push) Waiting to run
CI / Rust — cargo-deny (push) Waiting to run
CI / Docker — build edge image (push) Blocked by required conditions
This commit is contained in:
parent
bd40a44980
commit
a6a6fc7348
4 changed files with 276 additions and 12 deletions
188
scripts/xdp_netns_smoke.sh
Executable file
188
scripts/xdp_netns_smoke.sh
Executable file
|
|
@ -0,0 +1,188 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# netns+veth smoke-тест XDP-пути на РЕАЛЬНОМ ядре (вывод №7 конкурентного анализа).
|
||||||
|
#
|
||||||
|
# Проверяет то, что невозможно проверить в контейнере CI без CAP_BPF:
|
||||||
|
# 1. verifier принимает universal_filter.o (загрузка через libbpf);
|
||||||
|
# 2. attach на живом интерфейсе в netns, native-режим;
|
||||||
|
# 3. легитимный HTTP проксируется;
|
||||||
|
# 4. SYN-флуд порождает ringbuf-события -> opt-in auto-ban работает;
|
||||||
|
# 5. по завершении бана трафик восстанавливается (expiry);
|
||||||
|
# 6. после SIGTERM XDP-ПРОГРАММА РЕАЛЬНО ОТВЯЗАНА (регрессия на bug fd=-1).
|
||||||
|
#
|
||||||
|
# Запуск: на хосте с root/CAP_BPF (VDS или VM-раннер CI), не в контейнере:
|
||||||
|
# BIN=./target/release/rampart bash scripts/xdp_netns_smoke.sh
|
||||||
|
#
|
||||||
|
# Требования: iproute2, bpftool, hping3, python3 (http.server), curl.
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
NS=rampart_smoke
|
||||||
|
SUBNET=10.244.251
|
||||||
|
HOST_IP=$SUBNET.1
|
||||||
|
EDGE_IP=$SUBNET.2
|
||||||
|
EDGE_PORT=9000
|
||||||
|
ORIGIN_PORT=9100
|
||||||
|
METRICS_PORT=9595
|
||||||
|
BIN=${BIN:-./target/release/rampart}
|
||||||
|
WORK=$(mktemp -d)
|
||||||
|
RAMPID=""
|
||||||
|
SRVPID=""
|
||||||
|
PASS=1
|
||||||
|
|
||||||
|
fail() { echo "FAIL: $1"; PASS=0; }
|
||||||
|
ok() { echo "ok: $1"; }
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
[ -n "$RAMPID" ] && kill -9 "$RAMPID" 2>/dev/null
|
||||||
|
[ -n "$SRVPID" ] && kill -9 "$SRVPID" 2>/dev/null
|
||||||
|
ip netns del "$NS" 2>/dev/null
|
||||||
|
rm -rf "$WORK"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
[ "$(id -u)" = 0 ] || { echo "нужен root (CAP_BPF+CAP_NET_ADMIN)"; exit 2; }
|
||||||
|
[ -x "$BIN" ] || { echo "бинарь не найден: $BIN"; exit 2; }
|
||||||
|
|
||||||
|
# --- 1. netns + veth: весь тест изолирован, прод-трафик не затрагивается ---
|
||||||
|
ip netns del "$NS" 2>/dev/null
|
||||||
|
ip netns add "$NS" || { echo "не удалось создать netns"; exit 2; }
|
||||||
|
ip link add rth0 type veth peer name rtap
|
||||||
|
ip link set rtap netns "$NS"
|
||||||
|
ip addr add "$HOST_IP/24" dev rth0; ip link set rth0 up
|
||||||
|
ip -n "$NS" addr add "$EDGE_IP/24" dev rtap
|
||||||
|
ip -n "$NS" link set rtap up
|
||||||
|
ip -n "$NS" link set lo up
|
||||||
|
ok "netns $NS + veth подняты"
|
||||||
|
|
||||||
|
# --- 2. конфиг edge: узкий защищённый диапазон, автобан включён (иначе нечего тестить) ---
|
||||||
|
cat > "$WORK/edge.toml" <<EOF
|
||||||
|
[bind]
|
||||||
|
address = "$EDGE_IP"
|
||||||
|
port = $EDGE_PORT
|
||||||
|
|
||||||
|
[backend]
|
||||||
|
upstreams = ["$HOST_IP:$ORIGIN_PORT"]
|
||||||
|
|
||||||
|
[limits]
|
||||||
|
handshake_timeout_secs = 5
|
||||||
|
|
||||||
|
[xdp]
|
||||||
|
enabled = true
|
||||||
|
interface = "rtap"
|
||||||
|
protected_port_start = $EDGE_PORT
|
||||||
|
protected_port_end = $EDGE_PORT
|
||||||
|
events_enabled = true
|
||||||
|
throttle_enabled = true
|
||||||
|
auto_ban_enabled = true
|
||||||
|
auto_ban_duration_secs = 5
|
||||||
|
auto_ban_conn_drop_fails = 3
|
||||||
|
|
||||||
|
[store]
|
||||||
|
redis_url = ""
|
||||||
|
blacklist_cache_ttl_secs = 300
|
||||||
|
clickhouse_url = ""
|
||||||
|
|
||||||
|
[metrics]
|
||||||
|
enabled = true
|
||||||
|
port = $METRICS_PORT
|
||||||
|
|
||||||
|
[logging]
|
||||||
|
level = "info"
|
||||||
|
format = "text"
|
||||||
|
|
||||||
|
[pow]
|
||||||
|
enabled = false
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# --- 3. origin на host-половине veth ---
|
||||||
|
echo "<html><body>rampart-smoke-origin</body></html>" > "$WORK/index.html"
|
||||||
|
python3 -m http.server "$ORIGIN_PORT" --bind "$HOST_IP" -d "$WORK" >/dev/null 2>&1 &
|
||||||
|
SRVPID=$!
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
|
# --- 4. старт edge внутри netns, ожидание attach ---
|
||||||
|
RUST_LOG=rampart=debug RAMPART_CONFIG="$WORK/edge.toml" \
|
||||||
|
ip netns exec "$NS" "$BIN" > "$WORK/rampart.log" 2>&1 &
|
||||||
|
RAMPID=$!
|
||||||
|
ATTACHED=no
|
||||||
|
for _ in $(seq 60); do
|
||||||
|
grep -q "XDP filter attached" "$WORK/rampart.log" && { ATTACHED=yes; break; }
|
||||||
|
kill -0 "$RAMPID" 2>/dev/null || break
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
if [ "$ATTACHED" = yes ]; then
|
||||||
|
ok "XDP attached (verifier принят ядром $(uname -r))"
|
||||||
|
else
|
||||||
|
fail "XDP не аттачнулся; лог:"; sed -n '1,30p' "$WORK/rampart.log"; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Режим attach: native ожидаем на veth, но fallback на generic — штатный
|
||||||
|
# путь лестницы в load(); сверяем с честным логом режима от rampart.
|
||||||
|
if ip -n "$NS" link show rtap | grep -qE "xdp( |$)"; then
|
||||||
|
ok "attach native (ip link: xdp)"
|
||||||
|
elif ip -n "$NS" link show rtap | grep -q "xdpgeneric"; then
|
||||||
|
echo "warn: attach в generic (skb) — veth обычно тянет native, проверь лог"
|
||||||
|
else
|
||||||
|
fail "на rtap нет XDP-программы после 'attached'"
|
||||||
|
fi
|
||||||
|
grep -oE "mode: [a-z ()]+" "$WORK/rampart.log" | tail -1
|
||||||
|
|
||||||
|
# --- 5. легитимный HTTP проходит прокси ---
|
||||||
|
BODY=$(curl -s --max-time 5 "http://$EDGE_IP:$EDGE_PORT/" || true)
|
||||||
|
echo "$BODY" | grep -q "rampart-smoke-origin" \
|
||||||
|
&& ok "легитимный HTTP проксируется" \
|
||||||
|
|| fail "легитимный HTTP не дошёл до origin"
|
||||||
|
|
||||||
|
# --- 6. SYN-флуд -> throttle -> ringbuf events -> auto-ban ---
|
||||||
|
hping3 -S -p "$EDGE_PORT" -c 4000 -i u200 "$EDGE_IP" >/dev/null 2>&1 || true
|
||||||
|
sleep 3
|
||||||
|
grep -q "xdp event" "$WORK/rampart.log" \
|
||||||
|
&& ok "ringbuf-события долетают до userspace" \
|
||||||
|
|| fail "ни одного 'xdp event' в логе"
|
||||||
|
if grep -q "xdp auto-ban issued" "$WORK/rampart.log"; then
|
||||||
|
ok "auto-ban выдан по событию"
|
||||||
|
else
|
||||||
|
fail "auto-ban не сработал"
|
||||||
|
fi
|
||||||
|
BANS=$(ip netns exec "$NS" curl -s --max-time 2 "http://127.0.0.1:$METRICS_PORT/metrics" \
|
||||||
|
| awk '/^rampart_xdp_autobans_total /{print $2}')
|
||||||
|
[ "${BANS:-0}" != "0" ] && ok "метрика rampart_xdp_autobans_total=$BANS" \
|
||||||
|
|| fail "метрика autobans пуста"
|
||||||
|
|
||||||
|
# забаненный источник отклоняется до истечения 5с
|
||||||
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 2 "http://$EDGE_IP:$EDGE_PORT/" || echo "err")
|
||||||
|
[ "$HTTP_CODE" = "err" ] || [ "$HTTP_CODE" = "000" ] \
|
||||||
|
&& ok "забаненный клиент отклонён (http=$HTTP_CODE)" \
|
||||||
|
|| fail "забаненный клиент получил ответ ($HTTP_CODE)"
|
||||||
|
|
||||||
|
# --- 7. expiry: через 7с (> ban 5s) трафик возвращается ---
|
||||||
|
sleep 7
|
||||||
|
BODY=$(curl -s --max-time 5 "http://$EDGE_IP:$EDGE_PORT/" || true)
|
||||||
|
echo "$BODY" | grep -q "rampart-smoke-origin" \
|
||||||
|
&& ok "бан истёк, легитимный трафик восстановлен" \
|
||||||
|
|| fail "после expiry трафик не восстановился"
|
||||||
|
|
||||||
|
# --- 8. ГРЕЙСФУЛ СТОП -> ПРОГРАММА ОТВЯЗАНА (регрессия на borrow_raw(-1)) ---
|
||||||
|
kill -TERM "$RAMPID"
|
||||||
|
wait "$RAMPID"; RC=$?
|
||||||
|
RAMPID=""
|
||||||
|
[ "$RC" = 0 ] && ok "rampart завершился с кодом 0" || fail "код выхода rampart: $RC"
|
||||||
|
grep -q "XDP filter detached" "$WORK/rampart.log" \
|
||||||
|
&& ok "лог: detach выполнен" || fail "в логе нет строки detach"
|
||||||
|
sleep 1
|
||||||
|
if ip -n "$NS" link show rtap | grep -q "xdp"; then
|
||||||
|
fail "XDP ПРОГРАММА ОСТАЛАСЬ НА ИНТЕРФЕЙСЕ ПОСЛЕ ВЫХОДА ПРОЦЕССА (detach сломан)"
|
||||||
|
else
|
||||||
|
ok "XDP-программа реально отвязана после SIGTERM"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- итог ---
|
||||||
|
echo
|
||||||
|
if [ "$PASS" = 1 ]; then
|
||||||
|
echo "PASS — xdp netns smoke ($(uname -r))"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "FAIL — см. вывод выше; полный лог edge:"
|
||||||
|
tail -40 "$WORK/rampart.log"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result, bail};
|
||||||
use libbpf_rs::{MapCore, Object, OpenObject, RingBuffer, RingBufferBuilder};
|
use libbpf_rs::{MapCore, Object, OpenObject, RingBuffer, RingBufferBuilder};
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
@ -18,17 +18,45 @@ pub(super) fn interface_cstr(name: &str) -> Result<CString> {
|
||||||
CString::new(name).with_context(|| format!("interface name {name:?} contains an interior NUL byte"))
|
CString::new(name).with_context(|| format!("interface name {name:?} contains an interior NUL byte"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Патчит карту `rampart_.rodata` (volatile const глобалы) ДО загрузки объекта.
|
/// Внутренний data-map `.rodata` ищется по суффиксу: libbpf именует его
|
||||||
|
/// `<obj_name>.rodata`, а имя объекта при `open_memory` без явного `.name()`
|
||||||
|
/// вообще `memory` — точное сравнение ломалось на живом ядре (smoke-тест).
|
||||||
|
pub(super) fn is_rodata_map(name: &str) -> bool {
|
||||||
|
name.ends_with(".rodata")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Патчит карту `*.rodata` (volatile const глобалы) ДО загрузки объекта.
|
||||||
pub(super) fn patch_rodata(open_obj: &mut OpenObject, globals: &XdpGlobals) -> Result<()> {
|
pub(super) fn patch_rodata(open_obj: &mut OpenObject, globals: &XdpGlobals) -> Result<()> {
|
||||||
let image = globals.build_rodata_image();
|
let image = globals.build_rodata_image();
|
||||||
|
if !open_obj
|
||||||
|
.maps_mut()
|
||||||
|
.any(|m| m.name().to_str().is_some_and(is_rodata_map))
|
||||||
|
{
|
||||||
|
let names: Vec<String> = open_obj
|
||||||
|
.maps()
|
||||||
|
.map(|m| m.name().to_string_lossy().into_owned())
|
||||||
|
.collect();
|
||||||
|
bail!("no .rodata map ('{RODATA_MAP_NAME}') in XDP object, maps: {names:?}");
|
||||||
|
}
|
||||||
let mut map = open_obj
|
let mut map = open_obj
|
||||||
.maps_mut()
|
.maps_mut()
|
||||||
.find(|m| m.name() == RODATA_MAP_NAME)
|
.find(|m| m.name().to_str().is_some_and(is_rodata_map))
|
||||||
.with_context(|| format!("map '{RODATA_MAP_NAME}' not found in XDP object"))?;
|
.context("rodata map vanished between passes")?;
|
||||||
map.set_initial_value(&image)
|
map.set_initial_value(&image)
|
||||||
.with_context(|| format!("failed to set initial value of '{RODATA_MAP_NAME}'"))
|
.with_context(|| format!("failed to set initial value of '{RODATA_MAP_NAME}'"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Честное имя режима attach по `attach_mode` из `bpf_xdp_query_opts`
|
||||||
|
/// (kernel `enum xdp_attached_mode`: NONE=0, DRV=1, OFFLOAD=2, SKB=3).
|
||||||
|
pub(super) fn xdp_attach_mode_name(mode: u8) -> &'static str {
|
||||||
|
match mode {
|
||||||
|
1 => "native (drv)",
|
||||||
|
2 => "hardware offload",
|
||||||
|
3 => "generic (skb)",
|
||||||
|
_ => "none",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Собирает ringbuf-колбэк для `events_map`.
|
/// Собирает ringbuf-колбэк для `events_map`.
|
||||||
///
|
///
|
||||||
/// Колбэк обязан быть `'static`, поэтому всё разделяемое состояние (`Arc<Blacklist>`,
|
/// Колбэк обязан быть `'static`, поэтому всё разделяемое состояние (`Arc<Blacklist>`,
|
||||||
|
|
@ -103,4 +131,23 @@ mod tests {
|
||||||
fn interface_cstr_rejects_interior_nul() {
|
fn interface_cstr_rejects_interior_nul() {
|
||||||
assert!(interface_cstr("et\0h0").is_err());
|
assert!(interface_cstr("et\0h0").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rodata_map_matches_libbpf_naming() {
|
||||||
|
// libbpf строит имя `<obj><section>` без замены точки (libbpf.c ~1911),
|
||||||
|
// при open_memory без name объект называется "memory":
|
||||||
|
assert!(is_rodata_map("rampart.rodata"));
|
||||||
|
assert!(is_rodata_map("memory.rodata"));
|
||||||
|
assert!(is_rodata_map(".rodata"));
|
||||||
|
assert!(!is_rodata_map("rampart.bss"));
|
||||||
|
assert!(!is_rodata_map("stats_map"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn attach_mode_names_are_honest() {
|
||||||
|
assert_eq!(xdp_attach_mode_name(1), "native (drv)");
|
||||||
|
assert_eq!(xdp_attach_mode_name(3), "generic (skb)");
|
||||||
|
assert_eq!(xdp_attach_mode_name(2), "hardware offload");
|
||||||
|
assert_eq!(xdp_attach_mode_name(0), "none");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use crate::config::XdpConfig;
|
||||||
use crate::filter::blacklist::Blacklist;
|
use crate::filter::blacklist::Blacklist;
|
||||||
use crate::traffic::prefix::{PrefixKey, PrefixStatsVal};
|
use crate::traffic::prefix::{PrefixKey, PrefixStatsVal};
|
||||||
|
|
||||||
use attach::{build_ringbuf, interface_cstr, patch_rodata};
|
use attach::{build_ringbuf, interface_cstr, patch_rodata, xdp_attach_mode_name};
|
||||||
use events::AutoBanConfig;
|
use events::AutoBanConfig;
|
||||||
use maps::{blacklist_expiry_ns, blacklist_key, unix_ns};
|
use maps::{blacklist_expiry_ns, blacklist_key, unix_ns};
|
||||||
|
|
||||||
|
|
@ -62,7 +62,11 @@ impl XdpFilter {
|
||||||
super::probe::preflight(&self.interface)?;
|
super::probe::preflight(&self.interface)?;
|
||||||
|
|
||||||
let bpf_obj = include_bytes!(concat!(env!("OUT_DIR"), "/universal_filter.o"));
|
let bpf_obj = include_bytes!(concat!(env!("OUT_DIR"), "/universal_filter.o"));
|
||||||
|
// Имя объекта задаётся ЯВНО: при open_memory libbpf не знает имени файла,
|
||||||
|
// а по нему именуются внутренние maps ("rampart_.rodata" в patch_rodata).
|
||||||
let mut open_obj = ObjectBuilder::default()
|
let mut open_obj = ObjectBuilder::default()
|
||||||
|
.name("rampart")
|
||||||
|
.context("failed to set BPF object name")?
|
||||||
.open_memory(bpf_obj)
|
.open_memory(bpf_obj)
|
||||||
.context("Failed to open XDP object")?;
|
.context("Failed to open XDP object")?;
|
||||||
patch_rodata(&mut open_obj, &self.globals)?;
|
patch_rodata(&mut open_obj, &self.globals)?;
|
||||||
|
|
@ -83,27 +87,48 @@ impl XdpFilter {
|
||||||
.progs()
|
.progs()
|
||||||
.find(|p| p.name() == "rampart_universal_filter")
|
.find(|p| p.name() == "rampart_universal_filter")
|
||||||
.context("XDP program 'rampart_universal_filter' not found")?;
|
.context("XDP program 'rampart_universal_filter' not found")?;
|
||||||
Xdp::new(prog.as_fd()).attach(ifindex as i32, XdpFlags::NONE)?;
|
let xdp = Xdp::new(prog.as_fd());
|
||||||
|
// Лестница режимов: пробуем native (drv); если драйвер не тянет —
|
||||||
|
// честный fallback в generic (skb) с предупреждением (вывод №1 из
|
||||||
|
// конкурентного анализа: печатать режим как есть, без вранья).
|
||||||
|
let ifindex = ifindex as i32;
|
||||||
|
if let Err(e) = xdp.attach(ifindex, XdpFlags::NONE) {
|
||||||
|
tracing::warn!(
|
||||||
|
interface = %self.interface,
|
||||||
|
error = %e,
|
||||||
|
"native XDP attach failed, falling back to generic (skb) mode"
|
||||||
|
);
|
||||||
|
xdp.attach(ifindex, XdpFlags::SKB_MODE)
|
||||||
|
.context("XDP attach failed in both native and generic modes")?;
|
||||||
|
}
|
||||||
|
let mode = xdp
|
||||||
|
.query(ifindex, XdpFlags::NONE)
|
||||||
|
.map(|q| xdp_attach_mode_name(q.attach_mode))
|
||||||
|
.unwrap_or("unknown");
|
||||||
|
|
||||||
super::metrics::init();
|
super::metrics::init();
|
||||||
let rbuf = build_ringbuf(&obj, self.blacklist.clone(), self.autoban)?;
|
let rbuf = build_ringbuf(&obj, self.blacklist.clone(), self.autoban)?;
|
||||||
|
|
||||||
self.obj = Some(obj);
|
self.obj = Some(obj);
|
||||||
self.ringbuf = Some(rbuf);
|
self.ringbuf = Some(rbuf);
|
||||||
self.ifindex = ifindex as i32;
|
self.ifindex = ifindex;
|
||||||
tracing::info!("XDP filter attached to {}", self.interface);
|
tracing::info!("XDP filter attached to {} (mode: {mode})", self.interface);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unload(&mut self) -> Result<()> {
|
pub fn unload(&mut self) -> Result<()> {
|
||||||
|
let was_attached = self.obj.is_some();
|
||||||
|
let mut result = Ok(());
|
||||||
if let Some(obj) = self.obj.as_ref() {
|
if let Some(obj) = self.obj.as_ref() {
|
||||||
self.detach_program(obj)?;
|
result = self.detach_program(obj);
|
||||||
}
|
}
|
||||||
self.ringbuf = None;
|
self.ringbuf = None;
|
||||||
self.obj = None;
|
self.obj = None;
|
||||||
self.ifindex = 0;
|
self.ifindex = 0;
|
||||||
tracing::info!("XDP filter detached from {}", self.interface);
|
if was_attached {
|
||||||
Ok(())
|
tracing::info!("XDP filter detached from {}", self.interface);
|
||||||
|
}
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detaches the XDP program using the real program fd owned by the loaded
|
/// Detaches the XDP program using the real program fd owned by the loaded
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,11 @@ use anyhow::{Context, Result, bail};
|
||||||
use crate::config::XdpConfig;
|
use crate::config::XdpConfig;
|
||||||
|
|
||||||
/// Имя карты с образом `.rodata` загруженного BPF-объекта.
|
/// Имя карты с образом `.rodata` загруженного BPF-объекта.
|
||||||
pub(crate) const RODATA_MAP_NAME: &str = "rampart_.rodata";
|
/// Имя внутренней `.rodata`-карты при `ObjectBuilder::name("rampart")`:
|
||||||
|
/// libbpf конструирует `<obj_name><section>` без замены точки (sanitizer
|
||||||
|
/// пропускаёт `.`; libbpf.c, map name logic) → `rampart.rodata`.
|
||||||
|
/// Поиск в `patch_rodata` всё равно по суффиксу — имя здесь для диагностики.
|
||||||
|
pub(crate) const RODATA_MAP_NAME: &str = "rampart.rodata";
|
||||||
|
|
||||||
/// Размер образа `.rodata` в байтах (см. `build_rodata_image`).
|
/// Размер образа `.rodata` в байтах (см. `build_rodata_image`).
|
||||||
pub const RODATA_SIZE: usize = 96;
|
pub const RODATA_SIZE: usize = 96;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue