Specify dummynet datagram timing reconfiguration and filter ownership contracts
This commit is contained in:
1 parent
8d035a065a
commit
e996e09f6e
2 files changed
+80
-25
No files matched your search
@@ -1,22 +1,25 @@
|
||||
/**
|
||||
* @file dummynet.h
|
||||
* @brief UDP dummynet эмулятор с задержкой, bandwidth limiting и случайными потерями
|
||||
* @brief Двунаправленный IPv4 UDP-эмулятор и inline-фильтр потерь для ETCP.
|
||||
*
|
||||
* Эмулирует сетевые условия: задержку (fixed + random), ограничение пропускной способности
|
||||
* и случайные потери пакетов. Работает как "человек посередине" - принимает пакеты на
|
||||
* listen_port и пересылает между двумя явно настроенными endpoint с эмуляцией условий канала.
|
||||
* FULL: create(ua, bind_ip, port), set_direction для обоих различных IP:порт, затем poll(ua).
|
||||
* Эмулирует задержку fixed+random, пропускную способность и случайные потери.
|
||||
* Принимает датаграммы 0..1500 байт; более длинные отбрасывает целиком. Только IPv4-литералы.
|
||||
* Неизвестные источники не пересылаются и не входят в статистику направлений.
|
||||
*
|
||||
* Архитектура:
|
||||
* [UDP recv] -> [check queue/drop] -> [delay timer] -> [ll_queue] -> [shaper timer] -> [UDP send]
|
||||
* ^
|
||||
* loss check
|
||||
* UDP recv -> size/DROP/loss/queue limit -> delay timer -> очередь -> shaper timer -> UDP send
|
||||
*
|
||||
* Поток данных:
|
||||
* - Слушаем на bind_ip:listen_port
|
||||
* - Пакеты от dest_backward -> отправляем на dest_forward (forward direction)
|
||||
* - Пакеты от dest_forward -> отправляем на dest_backward (backward direction)
|
||||
* - Оба направления настраиваются до начала трафика; IP и порты участников независимы
|
||||
* - Каждое направление имеет свою очередь и параметры эмуляции
|
||||
* - Каждое направление имеет свою очередь и параметры эмуляции; random delay может менять порядок пакетов.
|
||||
* INLINE: filter_create/attach вставляет фильтр потерь и блокировок в один свободный UDP send_hook.
|
||||
* Он не создаёт сокета, не добавляет задержки/BWlim; закрытие линка автоматически отвязывает фильтр.
|
||||
* Все вызовы — в потоке ua. ua должен жить до destroy; статистика заимствована до destroy.
|
||||
* Потери используют rand(): seed задаёт вызывающий. Подробнее — dummynet_doc.md.
|
||||
*/
|
||||
|
||||
#ifndef DUMMYNET_H
|
||||
@@ -60,13 +63,13 @@ enum dummynet_mode {
|
||||
* @brief Статистика направления
|
||||
*/
|
||||
struct dummynet_stats {
|
||||
uint64_t recv; // Принято пакетов
|
||||
uint64_t recv; // FULL: пакеты распознанного направления, включая слишком большие; INLINE: вызовы hook
|
||||
uint64_t sent; // Отправлено пакетов
|
||||
uint64_t dropped; // Переполнение очереди, отказ выделения/таймера или ошибка отправки
|
||||
uint64_t dropped; // Слишком большой пакет, переполнение очереди, отказ выделения/таймера или ошибка отправки
|
||||
uint64_t lost; // Случайные потери и DROP
|
||||
uint64_t recv_bytes; // Принято UDP-байт, без IP/UDP заголовков
|
||||
uint64_t sent_bytes; // Передано UDP-байт, включая протокольные накладные расходы ETCP
|
||||
uint64_t blocked; // Пакетов отброшено режимом DROP (включены в lost)
|
||||
uint64_t sent_bytes; // Передано UDP-байт, без IP/UDP заголовков; пустой пакет увеличивает sent, но не sent_bytes
|
||||
uint64_t blocked; // FULL: режим DROP; INLINE: блокировка IP:порта; включены в lost
|
||||
uint64_t max_queue_delay_us; // максимум времени до отправки минус заданная propagation delay; включает сериализацию/HOLD
|
||||
uint32_t queue_size; // Все ожидающие пакеты, включая delay и HOLD
|
||||
uint32_t queue_max; // Максимальный размер очереди (пиковый)
|
||||
@@ -90,7 +93,8 @@ struct dummynet* dummynet_create(struct UASYNC* ua, const char* bind_ip, uint16_
|
||||
/**
|
||||
* @brief Уничтожает dummynet контекст
|
||||
*
|
||||
* Освобождает все ресурсы, закрывает сокет, отменяет таймеры.
|
||||
* Освобождает пакеты, очереди и пул, закрывает сокет, отменяет все delay/shaper-таймеры.
|
||||
* ua не освобождается. NULL допустим; после вызова контекст и указатели на статистику недействительны.
|
||||
*
|
||||
* @param dn Контекст dummynet
|
||||
*/
|
||||
@@ -99,6 +103,12 @@ void dummynet_destroy(struct dummynet* dn);
|
||||
/**
|
||||
* @brief Настраивает параметры направления
|
||||
*
|
||||
* Ошибка сохраняет прежнюю конфигурацию целиком. Сумма fixed+random <= 10000 мс;
|
||||
* max_queue_pkts <= INT_MAX. loss > 1000 ограничивается до 1000 с WARN.
|
||||
* Одинаковые endpoints и dest_ip=0.0.0.0 запрещены. Смена endpoint требует пустых обоих направлений.
|
||||
* Остальные параметры можно менять с очередью: delay/loss применяются к новым входящим пакетам,
|
||||
* BW — к следующей сериализации (уже начатая завершается по прежнему сроку), лимит — к новым поступлениям.
|
||||
*
|
||||
* @param dn Контекст dummynet
|
||||
* @param direction Направление: DUMMYNET_FORWARD или DUMMYNET_BACKWARD
|
||||
* @param delay_fixed_ms Фиксированная задержка (мс)
|
||||
@@ -134,7 +144,8 @@ const struct dummynet_stats* dummynet_get_stats(struct dummynet* dn, int directi
|
||||
* @brief Сбрасывает статистику направления
|
||||
*
|
||||
* @param dn Контекст dummynet
|
||||
* @param direction Направление (-1 для всех направлений)
|
||||
* @param direction 0/1 или -1 для обоих. Текущий queue_size сохраняется, queue_max становится равным ему;
|
||||
* отладочные счётчики таймеров не сбрасываются. Остальные значения/NULL логируются без изменения состояния.
|
||||
*/
|
||||
void dummynet_reset_stats(struct dummynet* dn, int direction);
|
||||
|
||||
@@ -167,7 +178,7 @@ struct UASYNC *dummynet_get_uasync(struct dummynet *dn);
|
||||
*
|
||||
* @param dn Контекст dummynet
|
||||
* @param direction Направление
|
||||
* @return Размер очереди или -1
|
||||
* @return Число всех ожидающих пакетов (delay + очередь + сериализация/HOLD) или -1
|
||||
*/
|
||||
int dummynet_get_queue_size(struct dummynet *dn, int direction);
|
||||
|
||||
@@ -176,7 +187,8 @@ int dummynet_get_queue_size(struct dummynet *dn, int direction);
|
||||
*
|
||||
* @param dn Контекст dummynet
|
||||
* @param direction Направление
|
||||
* @param delay_set Выход: число установок delay таймеров
|
||||
* Выходы необязательны (NULL пропускается); неверный контекст/направление даёт нули с WARN.
|
||||
* @param delay_set Выход: число успешных установок delay таймеров
|
||||
* @param delay_fire Выход: число срабатываний delay таймеров
|
||||
* @param shaper_set Выход: число установок shaper таймеров
|
||||
* @param shaper_fire Выход: число срабатываний shaper таймеров
|
||||
@@ -185,18 +197,31 @@ void dummynet_get_debug_counters(struct dummynet *dn, int direction,
|
||||
uint64_t *delay_set, uint64_t *delay_fire,
|
||||
uint64_t *shaper_set, uint64_t *shaper_fire);
|
||||
|
||||
// === Inline filter: встраивается в etcp_udp_send через link->send_hook ===
|
||||
// Не создаёт свой сокет — перехватывает пакеты напрямую из пути отправки.
|
||||
/* INLINE: перехват native UDP ETCP_LINK. SOCKS5 и TCP не поддерживаются.
|
||||
* Потеря возвращает len как успешную отправку; фактически socket_sendto не вызывается.
|
||||
* Реальная ошибка отправки возвращается вызывающему и учитывается в dropped.
|
||||
* Размер UDP здесь задаёт ETCP; лимит 1500 относится только к FULL. */
|
||||
|
||||
struct ETCP_LINK;
|
||||
struct dummynet_filter;
|
||||
|
||||
/* Создать фильтр для ua (обязателен); NULL при ошибке. ua заимствован, сокетов/таймеров у фильтра нет. */
|
||||
struct dummynet_filter* dummynet_filter_create(struct UASYNC* ua);
|
||||
/* Установить вероятность 0..1000; превышение ограничивается до 1000 с WARN. */
|
||||
void dummynet_filter_set_loss(struct dummynet_filter* df, uint32_t loss_permille);
|
||||
/* Присоединить к живому native UDP link того же ua; 0/-1. Занятый hook не заменяется.
|
||||
* Повтор к своему link идемпотентен. При переходе к другому прежний отвязывается после проверки нового.
|
||||
* Фильтр не владеет линком; link close отвязывает фильтр, сохраняя сам фильтр/статистику для повторного attach. */
|
||||
int dummynet_filter_attach(struct dummynet_filter* df, struct ETCP_LINK* link);
|
||||
/* Снять свой hook; NULL/уже отвязанный допустимы. При внешней подмене сохранить чужие fn/ctx и выдать WARN. */
|
||||
void dummynet_filter_detach(struct dummynet_filter* df);
|
||||
/* Отвязать и освободить фильтр; NULL допустим. Линк/ядро могли быть уже уничтожены. */
|
||||
void dummynet_filter_destroy(struct dummynet_filter* df);
|
||||
/* Заимствованная статистика или NULL. INLINE: queue_size/queue_max/max_queue_delay_us всегда 0. */
|
||||
const struct dummynet_stats* dummynet_filter_get_stats(struct dummynet_filter* df);
|
||||
/* Добавить/снять блокировку IPv4 IP:порта в network byte order, оба ненулевые.
|
||||
* 0 — выполнено/повтор, -1 — неверный аргумент или заполнены 4 различных слота (только block).
|
||||
* Block проверяется до случайной потери. Отсутствующий unblock идемпотентен. */
|
||||
int dummynet_filter_block_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
|
||||
int dummynet_filter_unblock_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
|
||||
|
||||
|
||||
@@ -10,6 +10,18 @@
|
||||
1. `dummynet_create(ua, bind_ip, listen_port)` — создаёт контекст, открывает UDP-сокет на `listen_port`.
|
||||
2. `dummynet_set_direction(dn, DUMMYNET_FORWARD, ...)` и `DUMMYNET_BACKWARD` — настраивает параметры для каждого направления.
|
||||
3. Источник FORWARD — endpoint, заданный как назначение BACKWARD, и наоборот. Порты независимы; `listen_port=0` выбирает ОС.
|
||||
4. Вызовы выполняются в потоке `ua`; `ua` должен существовать до `dummynet_destroy`. Destroy освобождает только собственные ресурсы.
|
||||
|
||||
FULL поддерживает IPv4-литералы и датаграммы **0..1500 байт**. Пустой UDP-пакет передаётся как пакет;
|
||||
более длинный отбрасывается целиком с WARN, без пересылки усечённого содержимого.
|
||||
Неизвестный source IP:порт не пересылается и не входит в статистику направлений.
|
||||
|
||||
Настройка атомарна: ошибка сохраняет прежние параметры и адрес. Endpoints должны различаться,
|
||||
`dest_ip=0.0.0.0` и нулевой порт запрещены. Сумма fixed+random ограничена 10000 мс, очередь — `INT_MAX` пакетов;
|
||||
превышение даёт -1. Loss выше 1000 ограничивается до 1000 с WARN.
|
||||
Смена endpoint разрешена при пустых обоих направлениях. При сохранении endpoint параметры можно менять с трафиком:
|
||||
delay/loss применяются к новым поступлениям, BW — к следующей сериализации, лимит — к приёму новых пакетов.
|
||||
Уже запущенный delay и начатая сериализация сохраняют свои сроки; уменьшение лимита не удаляет накопленное.
|
||||
|
||||
```
|
||||
[dest_backward] → [dummynet: loss/queue limit→delay→serializer] → [dest_forward]
|
||||
@@ -19,9 +31,18 @@
|
||||
### Режим INLINE (send_hook в ETCP_LINK)
|
||||
|
||||
1. `dummynet_filter_create(ua)` — создаёт фильтр (нет своего сокета).
|
||||
2. `dummynet_filter_set_loss(df, permille)` — настройка потерь. `set_delay` пока только сохраняет параметры.
|
||||
3. `dummynet_filter_attach(df, link)` — встраивается в `link->send_hook`, перехватывая все вызовы `socket_sendto`.
|
||||
4. Дополнительно: `dummynet_filter_block_addr/unblock_addr` — блокировка отправки на конкретные адреса.
|
||||
2. `dummynet_filter_set_loss(df, permille)` — настройка потерь; 0..1000, превышение ограничивается с WARN.
|
||||
3. `dummynet_filter_attach(df, link)` — 0/-1, присоединяет к одному живому native UDP-линку того же `ua`.
|
||||
TCP/SOCKS5 отклоняются; занятый hook не заменяется. Повтор к своему линку идемпотентен.
|
||||
4. `dummynet_filter_block_addr/unblock_addr` — 0/-1, IP и порт в **network byte order**, до 4 различных пар.
|
||||
Повторное добавление и удаление отсутствующей пары дают 0. Переполнение списка логируется и даёт -1.
|
||||
5. `dummynet_filter_detach/destroy` снимает только свой hook. Закрытие ETCP_LINK само отвязывает фильтр
|
||||
через `send_hook_release`, очищая все поля hook до callback. Фильтр можно уничтожить после линка/ядра
|
||||
или присоединить к новому линку. При ручной внешней замене hook сначала следует выполнить detach.
|
||||
|
||||
INLINE не создаёт сокетов/таймеров и не эмулирует задержку/BW. Не работавший `filter_set_delay` удалён.
|
||||
Фильтр не владеет `ua`/линком; заимствованная статистика действительна до destroy фильтра.
|
||||
SOCKS5 имеет приоритет перед send_hook в общем пути UDP, поэтому attach к нему запрещён.
|
||||
|
||||
### Механизмы эмуляции
|
||||
|
||||
@@ -39,6 +60,11 @@
|
||||
`dropped` — переполнение/ошибка ресурсов или отправки, `lost` — случайные потери и DROP, `blocked` — только DROP.
|
||||
`queue_size`/`queue_max` учитывают все ожидающие пакеты, включая delay/HOLD. Сброс статистики сохраняет текущий размер очереди.
|
||||
Дополнительные отладочные счётчики: `delay_timer_set/fire`, `shaper_timer_set/fire`.
|
||||
FULL учитывает в recv/recv_bytes также целиком прочитанные слишком большие датаграммы и относит их к dropped.
|
||||
INLINE учитывает все вызовы hook и их байты; блок адреса входит в lost и blocked, ошибка socket_sendto — в dropped.
|
||||
Пустой пакет увеличивает recv/sent, не увеличивая счётчики байтов. Queue/delay-поля у INLINE всегда нулевые.
|
||||
Указатели на статистику заимствованы. Необязательные выходы get_debug_counters могут быть NULL;
|
||||
для неверного контекста/направления запрошенные выходы обнуляются с WARN.
|
||||
|
||||
### Ключевые нюансы
|
||||
- Оба направления нужно настроить до начала трафика; источник проверяется по IP и порту.
|
||||
@@ -46,7 +72,12 @@
|
||||
- Таймеры uasync имеют миллисекундную точность: несколько готовых пакетов могут выйти вместе; средняя скорость сохраняется.
|
||||
- При достижении `max_queue_pkts` новый пакет отбрасывается. Лимит включает ещё не сработавшие delay-таймеры.
|
||||
- Все операции — в потоке ua. Destroy отменяет также delay-таймеры, освобождает их аргументы и пакеты.
|
||||
- INLINE-фильтр в режиме блокировки адреса не дропает пакет, а возвращает `len` (пакет «успешно отправлен», но фактически никуда не ушёл).
|
||||
- Ошибка delay-таймера теряет текущий пакет; ошибка shaper-таймера теряет голову очереди и продолжает обработку остальных.
|
||||
Отказы памяти/таймеров/отправки логируются и не завершают процесс. Частичный create откатывает ресурсы.
|
||||
- INLINE при случайной потере/блокировке возвращает `len`: транспорт считает отправку успешной, фактической отправки нет.
|
||||
- Потери и jitter используют `rand()`; `srand()` задаёт вызывающий. Случайная задержка может переставить пакеты.
|
||||
- Категория диагностики — `etcp`. INFO — создание/настройка/режим/уничтожение, DEBUG — пакеты/потери/очередь,
|
||||
WARN/ERROR — неверные параметры, переполненные блокировки и ошибки ресурсов/отправки.
|
||||
|
||||
## 3. API
|
||||
|
||||
@@ -72,8 +103,7 @@
|
||||
|---------|------------|
|
||||
| `dummynet_filter_create(ua)` | Создать фильтр |
|
||||
| `dummynet_filter_set_loss(df, permille)` | Установить вероятность потерь |
|
||||
| `dummynet_filter_set_delay(df, delay_ms, jitter_ms)` | Установить задержку (NOTE: в текущей реализации delay не применяется в хуке) |
|
||||
| `dummynet_filter_attach(df, link)` | Прикрепить хук к ETCP_LINK |
|
||||
| `dummynet_filter_attach(df, link)` | Прикрепить хук к свободному native UDP ETCP_LINK, 0/-1 |
|
||||
| `dummynet_filter_detach(df)` | Открепить хук |
|
||||
| `dummynet_filter_destroy(df)` | Уничтожить фильтр |
|
||||
| `dummynet_filter_get_stats(df)` | Получить статистику |
|
||||
@@ -87,7 +117,7 @@
|
||||
| `dummynet_stats` | Счётчики пакетов/байтов, потерь и размера очереди; точные поля — в dummynet.h |
|
||||
| `dummynet_dir` | Внутренняя: параметры направления, очередь, таймеры шейпера, отладочные счётчики |
|
||||
| `dummynet` | Контекст FULL-режима: ua, сокет, два `dummynet_dir`, пул пакетов |
|
||||
| `dummynet_filter` | Контекст INLINE-режима: ua, loss_permille, delay, link, статистика, список блокировок |
|
||||
| `dummynet_filter` | Контекст INLINE-режима: ua, loss_permille, link, статистика, список блокировок |
|
||||
|
||||
### Зависимости
|
||||
`u_async.h`, `ll_queue.h`, `memory_pool.h`, `socket_compat.h`, `etcp_connections.h` (для ETCP_LINK), `platform_compat.h`
|
||||
Reference in new issue
Block a user