Skip to content
Merged
15 changes: 15 additions & 0 deletions docs/methodology/failure_emulation.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
* Проводить различные манипуляции с сетевыми пакетами, в т.ч. вызывать потерю пакетов, дублировать их, повреждать, блокировать;
* Делать порт недоступным;
* Вносить изменения в работу DNS.
* Пример создания сетевой задержки 100 мс на интерфейсе `eth0`:
```
# Создать эксперимент: задержка 100 мс, автоматическое завершение через 30 с
sudo blade create network delay --time 100 --interface eth0 --timeout 30

# Проверить статус и, при необходимости, завершить вручную
sudo blade status <experiment-id>
sudo blade destroy <experiment-id>
```
- [tc](https://man7.org/linux/man-pages/man8/tc.8.html) - утилита для управления трафиком. Для эмуляции сетевых сбоев используется `tc netem`. Как и `chaosblade` позволяет создавать задержки, потери, дубликации, неправильный порядок пакетов. Пример: добавление задержки 100 мс: ```sudo tc qdisc add dev eth0 root netem delay 100ms```

### Диски и файловые системы
Expand All @@ -32,6 +41,9 @@
+ ```# krfctl -p 100``` - установить вероятность ошибки равной 1%
+ ```# krfctl -P io``` - для указания группы системных вызовов, которые должны завершаться с ошибкой (в данном случае, все, связанные с вводом/выводом)
+ ```$ krfexec <command> [args]``` - для запуска программы через `krfexec`

Пример использования `krf` реализован в `scripts/fault-injection/krf_vm.sh`

- [epbfault](https://github.com/trailofbits/ebpfault) - утилита для внедрения ошибок в системные вызовы, работающая на основе eBPF. Удобна в использовании, так как позволяет задать в конфигурационном файле системные вызовы, в которых будет симулироваться ошибка, вощвращаемое значение и вероятность возврата этого значения. Пример конфигурационного файла:
```
{
Expand Down Expand Up @@ -68,6 +80,9 @@

Использование:
```ebpfault --config /path/to/config.json --exec /path/to/program arg1 arg2```

Пример использования `ebpfault` реализован в `scripts/fault-injection/ebpfault_vm.sh`

- Аналогично `ebpfault` можно писать свои тесты с использованием `kprobes` и `bpf`. Для этого подходит функция `bpf_override_return()`, позволяющая перезаписывать код возврата функций.

Кроме того, некоторые рассмотренные инструменты, можно использовать и для других целей. Так, chaosblade можно также использовать для эмуляции сбоев файловой подсистемы, процессора, дисков.
Expand Down
7 changes: 7 additions & 0 deletions docs/utilities/benchmarking/general/chaosblade.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,10 @@ blade status --type create --flag-filter "timeout=10"
]
}
```

6. Создать эксперимент с сетевой задержкой. Например, 100 мс на интерфейсе `eth0` с автоматическим завершением через 30 секунд:
```
blade create network delay --time 100 --interface eth0 --timeout 30
```

Задержку можно комбинировать с другими параметрами: `--offset` (jitter в мс), `--destination-ip`, `--exclude-ip`. Получение статуса и завершение эксперимента выполняются командами `blade status <experiment-id>` и `blade destroy <experiment-id>`. Реализация примера есть в скрипте `scripts/fault-injection/chaosblade_vm.sh`.
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ CONFIG_FAIL_PAGE_ALLOC=y
CONFIG_FAIL_MAKE_REQUEST=y
CONFIG_FAIL_IO_TIMEOUT=y
CONFIG_FAIL_FUTEX=y
CONFIG_BPF_KPROBE_OVERRIDE=y
67 changes: 67 additions & 0 deletions scripts/fault-injection/ebpfault_vm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/bin/bash

set -euo pipefail

DIR="${EBPFAULT_DIR:-/opt/ebpfault}"
VER="${EBPFAULT_VER:-1.1.2}"
SYSCALL="${EBPFAULT_SYSCALL:-fchmodat}"
ERRNO="${EBPFAULT_ERRNO:-ENOENT}"
PROB="${EBPFAULT_PROB:-50}"
ARGS="${EBPFAULT_ARGS:-/bin/chmod 0755 /tmp/ebpfault_marker}"

install_ebpf() {
if [[ -x "$DIR/bin/ebpfault" ]]; then
echo "[INFO] ebpfault already installed in ${DIR}"
return
fi
echo "[INFO] Installing ebpfault v${VER}..."
url="https://github.com/trailofbits/ebpfault/releases/download/v${VER}/ebpfault-${VER}-1.x86_64.tar.gz"
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
wget -q -O "$tmp/ebpfault.tar.gz" "$url" || curl -fsSL -o "$tmp/ebpfault.tar.gz" "$url"
tar -xzf "$tmp/ebpfault.tar.gz" -C "$tmp"
sudo mkdir -p "$DIR"
sudo chown "$(id -u):$(id -g)" "$DIR"
cp -r "$tmp"/ebpfault-"${VER}"-1.x86_64/. "$DIR"/
chmod +x "$DIR/bin/ebpfault"
}

kprobe_supported() {
{ zcat /proc/config.gz 2>/dev/null || cat "/boot/config-$(uname -r)" 2>/dev/null; } | grep -q '^CONFIG_BPF_KPROBE_OVERRIDE=y'
}

gen_config() {
echo "[INFO] Writing ebpfault config (${SYSCALL} -> -${ERRNO}, ${PROB}%)..."
cat > "$DIR/config.json" <<JSON
{
"fault_injectors": [
{ "syscall_name": "$SYSCALL", "error_list": [ { "exit_code": "-$ERRNO", "probability": $PROB } ] }
]
}
JSON
}

run_exp() {
echo "[INFO] Running '${ARGS}' through ebpfault..."
set +e
touch /tmp/ebpfault_marker
chmod 0644 /tmp/ebpfault_marker
"$DIR/bin/ebpfault" --config "$DIR/config.json" --exec "$ARGS"
echo "exit_code=$?"
set -e
}

main() {
install_ebpf

if ! kprobe_supported; then
echo "[WARN] CONFIG_BPF_KPROBE_OVERRIDE is not enabled in the kernel."
echo "[WARN] Enable it in a kernel config and rebuild the image."
exit 1
fi

gen_config
run_exp
echo "[INFO] Done!"
}
main "$@"
122 changes: 122 additions & 0 deletions scripts/fault-injection/krf_vm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/bin/bash

set -euo pipefail

KRF_DIR="${KRF_DIR:-/opt/krf}"
SYSCALLS="${KRF_SYSCALLS:-write}"
PROB="${KRF_PROB:-1}"
PROFILE="${KRF_PROFILE:-}"
TARGET_CMD="${KRF_TARGET:-dd if=/dev/urandom of=/dev/null bs=64K count=512}"
LOADED=0

cleanup() {
sudo "$KRF_DIR/src/krfctl/krfctl" -c >/dev/null 2>&1 || true
sudo "$KRF_DIR/src/krfctl/krfctl" -C >/dev/null 2>&1 || true
if (( LOADED )); then
sudo rmmod krfx >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT

missing() {
for tool in gcc make ruby; do
command -v "$tool" >/dev/null 2>&1 || echo "$tool"
done
ls /lib/modules/*/build >/dev/null 2>&1 || echo "kernel headers"
}

install_deps() {
sudo zypper -n install --no-recommends gcc make libelf-devel ruby git-core kernel-default-devel
}

build_krf() {
if [[ -x "$KRF_DIR/src/krfctl/krfctl" ]]; then
echo "[INFO] krf already built in ${KRF_DIR}"
return
fi
echo "[INFO] Cloning and building krf in ${KRF_DIR}..."
if [[ ! -d "$KRF_DIR" ]]; then
sudo git clone --depth 1 https://github.com/trailofbits/krf "$KRF_DIR"
sudo chown -R "$(id -u):$(id -g)" "$KRF_DIR"
fi
headers="/lib/modules/$(uname -r)/build"
if [[ ! -d "$headers" ]]; then
# shellcheck disable=SC2012
headers=$(ls -d /lib/modules/*/build 2>/dev/null | head -1)
fi
if [[ -z "$headers" || ! -d "$headers" ]]; then
echo "no kernel headers found"
exit 1
fi
kver=$(basename "$(dirname "$headers")")
echo "building against kernel headers: ${kver}"
mkdir -p /tmp/krf-fakebin
cat > /tmp/krf-fakebin/uname <<'INNER'
#!/bin/bash
if [[ "$1" == "-r" ]]; then echo "__KVER__"; else /bin/uname "$@"; fi
INNER
sed -i "s/__KVER__/${kver}/" /tmp/krf-fakebin/uname
chmod +x /tmp/krf-fakebin/uname
export PATH="/tmp/krf-fakebin:$PATH"
make -C "$KRF_DIR/src/module/linux" codegen
make -C "$headers" M="$KRF_DIR/src/module/linux" modules
make PLATFORM=linux -C "$KRF_DIR/src/krfexec"
make PLATFORM=linux -C "$KRF_DIR/src/krfctl"
make PLATFORM=linux -C "$KRF_DIR/src/krfmesg"
}

load_module() {
if lsmod | grep -q '^krfx'; then
echo "[INFO] krfx module already loaded"
return
fi
echo "[INFO] Loading krfx.ko (force; built against non-running kernel headers)..."
sudo insmod -f "$KRF_DIR/src/module/linux/krfx.ko"
LOADED=1
}

configure_krf() {
echo "[INFO] Configuring krf (syscalls='${SYSCALLS}', prob=${PROB})..."
krfctl="$KRF_DIR/src/krfctl/krfctl"
sudo "$krfctl" -F "$SYSCALLS"
sudo "$krfctl" -T personality=28
sudo "$krfctl" -p "$PROB"
if [[ -n "$PROFILE" ]]; then
sudo "$krfctl" -P "$PROFILE"
fi
echo 1 | sudo tee /proc/krf/log_faults >/dev/null
}

run_target() {
echo "[INFO] Running '${TARGET_CMD}' through krfexec..."
set +e
"$KRF_DIR/src/krfexec/krfexec" "$TARGET_CMD"
echo "exit_code=$?"
set -e
}

show_logs() {
sudo dmesg | grep -i "faulting" | tail -n 20
}

main() {
local missing_list
missing_list=$(missing)
echo "[INFO] Missing prerequisites: ${missing_list:-none}"
if [[ -n "$missing_list" ]]; then
install_deps
missing_list=$(missing)
if [[ -n "$missing_list" ]]; then
echo "[ERROR] Still missing prerequisites: $missing_list"
exit 1
fi
fi

build_krf
load_module
configure_krf
run_target
show_logs
echo "[INFO] Done!"
}
main "$@"
Loading