diff --git a/docs/methodology/failure_emulation.md b/docs/methodology/failure_emulation.md index 7bcb6d4c..f10664ab 100644 --- a/docs/methodology/failure_emulation.md +++ b/docs/methodology/failure_emulation.md @@ -11,6 +11,15 @@ * Проводить различные манипуляции с сетевыми пакетами, в т.ч. вызывать потерю пакетов, дублировать их, повреждать, блокировать; * Делать порт недоступным; * Вносить изменения в работу DNS. + * Пример создания сетевой задержки 100 мс на интерфейсе `eth0`: + ``` + # Создать эксперимент: задержка 100 мс, автоматическое завершение через 30 с + sudo blade create network delay --time 100 --interface eth0 --timeout 30 + + # Проверить статус и, при необходимости, завершить вручную + sudo blade status + sudo blade destroy + ``` - [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``` ### Диски и файловые системы @@ -32,6 +41,9 @@ + ```# krfctl -p 100``` - установить вероятность ошибки равной 1% + ```# krfctl -P io``` - для указания группы системных вызовов, которые должны завершаться с ошибкой (в данном случае, все, связанные с вводом/выводом) + ```$ krfexec [args]``` - для запуска программы через `krfexec` + + Пример использования `krf` реализован в `scripts/fault-injection/krf_vm.sh` + - [epbfault](https://github.com/trailofbits/ebpfault) - утилита для внедрения ошибок в системные вызовы, работающая на основе eBPF. Удобна в использовании, так как позволяет задать в конфигурационном файле системные вызовы, в которых будет симулироваться ошибка, вощвращаемое значение и вероятность возврата этого значения. Пример конфигурационного файла: ``` { @@ -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 можно также использовать для эмуляции сбоев файловой подсистемы, процессора, дисков. diff --git a/docs/utilities/benchmarking/general/chaosblade.md b/docs/utilities/benchmarking/general/chaosblade.md index f842a49f..c4f6045f 100644 --- a/docs/utilities/benchmarking/general/chaosblade.md +++ b/docs/utilities/benchmarking/general/chaosblade.md @@ -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 ` и `blade destroy `. Реализация примера есть в скрипте `scripts/fault-injection/chaosblade_vm.sh`. diff --git a/layers/meta-image-tests/recipes-kernel/linux/linux-yocto/fault-injection.cfg b/layers/meta-image-tests/recipes-kernel/linux/linux-yocto/fault-injection.cfg index 2c2602a3..722145fd 100644 --- a/layers/meta-image-tests/recipes-kernel/linux/linux-yocto/fault-injection.cfg +++ b/layers/meta-image-tests/recipes-kernel/linux/linux-yocto/fault-injection.cfg @@ -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 diff --git a/scripts/fault-injection/ebpfault_vm.sh b/scripts/fault-injection/ebpfault_vm.sh new file mode 100755 index 00000000..5c92e0e9 --- /dev/null +++ b/scripts/fault-injection/ebpfault_vm.sh @@ -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" </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 "$@"