refactor(firewall): split firewall.rs by subsystem, collapse duplicated test fixtures

firewall.rs held two subsystems that share nothing but the nft command layer: the host
INPUT firewall, applied in the host namespace and owning inet awg2_host_firewall, and the
anti-torrent guard, applied in the namespace chosen by config and owning inet
awg2_harness. The split follows that boundary rather than an inspect/apply one:

  firewall/mod.rs          apply_firewall, namespace selection, shared table constants
  firewall/nft.rs          text normalization and the nft command wrappers
  firewall/host.rs         host INPUT firewall and its 5 tests
  firewall/antitorrent.rs  connection-count guard and its 6 tests

Production logic is unchanged. Function sets before and after are identical: 25 against
25, verified by extracting names from the old blob and the four new files and diffing.

Not a pure move in one respect, stated here so it is not a surprise in review: the test
module contained four near-identical 45-line Config literals, about 180 lines of
copy-paste, differing only in the single field each test actually cared about. Carrying
four copies into the new files would have knowingly propagated the duplication, so they
collapse into one test_config() helper in firewall/mod.rs that tests mutate. This is why
the four files total 875 lines against the old 890 despite gaining doc comments.

Doc comments now record the reasoning that was only implicit before: why nft output is
compared after normalization (rendering differs across versions - quoting, comma-separated
ct state lists, brace layout), why the host firewall batch never contains a delete (a
partially applied INPUT chain could lock out SSH), why ct state new is load-bearing in the
anti-torrent rule, why connlimit_state_present accepts two renderings, and why the
anti-torrent rule defaults to the container namespace.

IMPLEMENTATION_SPEC section 3 is updated: it described the old flat layout and carried the
split as an unfulfilled intention. Both traffic and firewall now match what it documents.

cargo test --workspace: 110 + 4 passed, unchanged count. clippy -D warnings clean.
This commit is contained in:
2026-08-06 18:51:31 +05:00
parent b42d99068b
commit dbceda5c13
6 changed files with 890 additions and 895 deletions
+15 -5
View File
@@ -82,8 +82,6 @@ awg2-harness validate-runtime
## 3. Структура модулей
Фактическая раскладка — плоская, без вложенных каталогов модулей:
```text
traffic_core/src/
├── main.rs точка входа и диспетчер подкоманд
@@ -94,8 +92,16 @@ traffic_core/src/
├── netns.rs транспорты выполнения команд
├── iface.rs обнаружение интерфейса
├── wg.rs обнаружение peers и разбор вывода awg/wg
├── traffic.rs tc/IFB: инспекция, diff, teardown, apply
├── firewall.rs nftables: host firewall и anti-torrent
├── traffic/
│ ├── mod.rs общие модели и точки входа
│ ├── parse_tc.rs чистые парсеры вывода tc/ip
│ ├── inspect.rs inspect_and_diff и контракт class id
│ └── apply.rs teardown и полная пересборка топологии
├── firewall/
│ ├── mod.rs apply_firewall, выбор namespace, общие константы
│ ├── nft.rs нормализация текста nftables и обёртки команд
│ ├── host.rs таблица inet awg2_host_firewall (host namespace)
│ └── antitorrent.rs таблица inet awg2_harness (namespace по конфигу)
├── sysctl_tuning.rs управляемый sysctl baseline
├── swap.rs управляемый swap
├── status.rs сборка и печать статуса
@@ -106,7 +112,11 @@ reboot_module/src/
└── main.rs планировщик перезагрузки
```
Разбиение `traffic` и `firewall` на подкаталоги (`model.rs`, `inspect.rs`, `apply.rs`) обсуждалось, но не выполнено. Пока эти модули остаются одиночными файлами, любые изменения должны сохранять разделение на инспекцию, diff и apply внутри файла.
`traffic` разбит по ответственности: парсеры отдельно от инспекции, инспекция отдельно от apply. Чистые парсеры собраны в `parse_tc.rs` вместе со своими тестами — это тестируемая поверхность модуля, всё остальное требует живого контейнера.
`firewall` разбит по подсистемам, а не по фазам: host firewall и anti-torrent независимы, применяются в разных namespace и владеют разными таблицами. Общий у них только слой работы с `nft`.
Приватные типы объявлены в соответствующем `mod.rs`. В Rust приватные элементы видны в модуле объявления и всех дочерних модулях, поэтому подмодули работают с ними без расширения видимости до `pub(crate)`.
## 4. Модель данных