/** * @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 #include // 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