You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

191 lines
7.7 KiB

/**
* @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
#ifdef __cplusplus
extern "C" {
#endif
#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);
void dummynet_filter_block_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
void dummynet_filter_unblock_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
#ifdef __cplusplus
}
#endif
#endif // DUMMYNET_H