- Add etcp_udp_send_fn_t hook typedef, send_hook/send_hook_ctx fields to ETCP_LINK - Implement etcp_udp_send() wrapper — all UDP sends go through one point - Remove loss_rate from ETCP_SOCKET, CFG_SERVER, config parser, send path - Add dummynet_filter: inline send-hook with loss/delay, no proxy socket needed - Rewrite test_bgp_route_exchange: 3-node chain A(2 links)↔B↔C, 7 phases with dummynet_filter-based link failure/recovery simulation - Clean up tests: remove skeleton test_routing_mesh, 6 debug/stale test files - Refactor test_etcp_simple_traffic and test_pkt_normalizer_etcp (remove debug spam) - Fix test_etcp_two_instances: remove private field access, fix false PASS on timeout
181 lines
7.4 KiB
C
181 lines
7.4 KiB
C
/**
|
|
* @file dummynet.h
|
|
* @brief UDP dummynet эмулятор с задержкой, bandwidth limiting и случайными потерями
|
|
*
|
|
* Эмулирует сетевые условия: задержку (fixed + random), ограничение пропускной способности
|
|
* и случайные потери пакетов. Работает как "человек посередине" - принимает пакеты на
|
|
* listen_port и пересылает между port-1 и port+1 с эмуляцией условий канала.
|
|
*
|
|
* Архитектура:
|
|
* [UDP recv] -> [delay timer] -> [check queue/drop] -> [ll_queue] -> [shaper timer] -> [UDP send]
|
|
* ^
|
|
* loss check
|
|
*
|
|
* Поток данных:
|
|
* - Слушаем на bind_ip:listen_port
|
|
* - Пакеты от listen_port-1 -> отправляем на dest_forward (forward direction)
|
|
* - Пакеты от listen_port+1 -> отправляем на dest_backward (backward direction)
|
|
* - Каждое направление имеет свою очередь и параметры эмуляции
|
|
*/
|
|
|
|
#ifndef DUMMYNET_H
|
|
#define DUMMYNET_H
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
|
|
// Forward declarations
|
|
struct UASYNC;
|
|
struct ll_queue;
|
|
struct ll_entry;
|
|
|
|
/* Направления передачи */
|
|
#define DUMMYNET_FORWARD 0 // port-1 -> port+1
|
|
#define DUMMYNET_BACKWARD 1 // port+1 -> port-1
|
|
#define DUMMYNET_DIR_COUNT 2
|
|
|
|
/* Максимальный размер UDP пакета */
|
|
#define DUMMYNET_MAX_PKT_SIZE 1500
|
|
|
|
/* Максимальное время задержки (10 секунд) */
|
|
#define DUMMYNET_MAX_DELAY_MS 10000
|
|
|
|
/* Максимальное значение loss_permille (1000 = 100%) */
|
|
#define DUMMYNET_MAX_LOSS_PERMILLE 1000
|
|
|
|
/**
|
|
* @brief Статистика направления
|
|
*/
|
|
struct dummynet_stats {
|
|
uint64_t recv; // Принято пакетов
|
|
uint64_t sent; // Отправлено пакетов
|
|
uint64_t dropped; // Дропнуто (queue full)
|
|
uint64_t lost; // Потеряно случайно (random loss)
|
|
uint32_t queue_size; // Текущий размер очереди
|
|
uint32_t queue_max; // Максимальный размер очереди (пиковый)
|
|
};
|
|
|
|
/**
|
|
* @brief Контекст dummynet
|
|
*/
|
|
struct dummynet;
|
|
|
|
/**
|
|
* @brief Создаёт dummynet контекст
|
|
*
|
|
* @param ua Экземпляр uasync (обязателен)
|
|
* @param bind_ip IP адрес для привязки (NULL или "0.0.0.0" для всех интерфейсов)
|
|
* @param listen_port Порт для прослушивания (средний порт, port-1 и port+1 - клиенты)
|
|
* @return Указатель на контекст или NULL при ошибке
|
|
*/
|
|
struct dummynet* dummynet_create(struct UASYNC* ua, const char* bind_ip, uint16_t listen_port);
|
|
|
|
/**
|
|
* @brief Уничтожает dummynet контекст
|
|
*
|
|
* Освобождает все ресурсы, закрывает сокет, отменяет таймеры.
|
|
*
|
|
* @param dn Контекст dummynet
|
|
*/
|
|
void dummynet_destroy(struct dummynet* dn);
|
|
|
|
/**
|
|
* @brief Настраивает параметры направления
|
|
*
|
|
* @param dn Контекст dummynet
|
|
* @param direction Направление: DUMMYNET_FORWARD или DUMMYNET_BACKWARD
|
|
* @param delay_fixed_ms Фиксированная задержка (мс)
|
|
* @param delay_random_ms Случайная составляющая задержки (0..random_ms, мс)
|
|
* @param bandwidth_kbps Пропускная способность (кбит/с, 0 = unlimited)
|
|
* @param max_queue_pkts Максимальный размер очереди (пакетов)
|
|
* @param loss_permille Вероятность потери пакета (0.1%, 0-1000)
|
|
* @param dest_ip IP адрес назначения
|
|
* @param dest_port Порт назначения
|
|
* @return 0 при успехе, -1 при ошибке
|
|
*/
|
|
int dummynet_set_direction(struct dummynet* dn, int direction,
|
|
uint32_t delay_fixed_ms, uint32_t delay_random_ms,
|
|
uint32_t bandwidth_kbps, uint32_t max_queue_pkts,
|
|
uint32_t loss_permille,
|
|
const char* dest_ip, uint16_t dest_port);
|
|
|
|
/**
|
|
* @brief Получает статистику направления
|
|
*
|
|
* @param dn Контекст dummynet
|
|
* @param direction Направление
|
|
* @return Указатель на статистику или NULL
|
|
*/
|
|
const struct dummynet_stats* dummynet_get_stats(struct dummynet* dn, int direction);
|
|
|
|
/**
|
|
* @brief Сбрасывает статистику направления
|
|
*
|
|
* @param dn Контекст dummynet
|
|
* @param direction Направление (-1 для всех направлений)
|
|
*/
|
|
void dummynet_reset_stats(struct dummynet* dn, int direction);
|
|
|
|
/**
|
|
* @brief Возвращает файловый дескриптор сокета (для отладки)
|
|
*
|
|
* @param dn Контекст dummynet
|
|
* @return socket_t или SOCKET_INVALID
|
|
*/
|
|
int dummynet_get_socket(struct dummynet* dn);
|
|
|
|
/**
|
|
* @brief Возвращает listen_port
|
|
*
|
|
* @param dn Контекст dummynet
|
|
* @return Порт или 0
|
|
*/
|
|
uint16_t dummynet_get_listen_port(struct dummynet *dn);
|
|
|
|
/**
|
|
* @brief Возвращает указатель на UASYNC (для запуска poll)
|
|
*
|
|
* @param dn Контекст dummynet
|
|
* @return Указатель на UASYNC или NULL
|
|
*/
|
|
struct UASYNC *dummynet_get_uasync(struct dummynet *dn);
|
|
|
|
/**
|
|
* @brief Возвращает размер очереди направления
|
|
*
|
|
* @param dn Контекст dummynet
|
|
* @param direction Направление
|
|
* @return Размер очереди или -1
|
|
*/
|
|
int dummynet_get_queue_size(struct dummynet *dn, int direction);
|
|
|
|
/**
|
|
* @brief Получает отладочные счетчики направления
|
|
*
|
|
* @param dn Контекст dummynet
|
|
* @param direction Направление
|
|
* @param delay_set Выход: число установок delay таймеров
|
|
* @param delay_fire Выход: число срабатываний delay таймеров
|
|
* @param shaper_set Выход: число установок shaper таймеров
|
|
* @param shaper_fire Выход: число срабатываний shaper таймеров
|
|
*/
|
|
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 ===
|
|
// Не создаёт свой сокет — перехватывает пакеты напрямую из пути отправки.
|
|
|
|
struct ETCP_LINK;
|
|
struct dummynet_filter;
|
|
|
|
struct dummynet_filter* dummynet_filter_create(struct UASYNC* ua);
|
|
void dummynet_filter_set_loss(struct dummynet_filter* df, uint32_t loss_permille);
|
|
void dummynet_filter_set_delay(struct dummynet_filter* df, uint32_t delay_ms, uint32_t jitter_ms);
|
|
void dummynet_filter_attach(struct dummynet_filter* df, struct ETCP_LINK* link);
|
|
void dummynet_filter_detach(struct dummynet_filter* df);
|
|
void dummynet_filter_destroy(struct dummynet_filter* df);
|
|
const struct dummynet_stats* dummynet_filter_get_stats(struct dummynet_filter* df);
|
|
|
|
#endif // DUMMYNET_H
|