dummynet: route between explicit independently bound endpoints

This commit is contained in:
evgeny committed 2026-10-10 16:46:19 +02:00
1 parent 70b55e4626
commit 12d4e2433e
4 files changed
+50 -92

No files matched your search

+18 -21
View File
@@ -110,12 +110,15 @@ static inline uint32_t random_range(uint32_t max) {
return (uint32_t)rand() % (max + 1);
}
/* Определяет направление по порту источника */
static int dummynet_get_direction(struct dummynet* dn, uint16_t src_port) {
if (src_port == dn->listen_port - 1) {
return DUMMYNET_FORWARD;
} else if (src_port == dn->listen_port + 1) {
return DUMMYNET_BACKWARD;
/* Источник одного направления — endpoint назначения обратного направления. */
static int dummynet_get_direction(struct dummynet* dn, const struct sockaddr_storage* source) {
if (source->ss_family != AF_INET) return -1;
const struct sockaddr_in* src = (const struct sockaddr_in*)source;
for (int i = 0; i < DUMMYNET_DIR_COUNT; i++) {
struct dummynet_dir* reverse = &dn->dirs[1 - i];
const struct sockaddr_in* peer = (const struct sockaddr_in*)&reverse->dest_addr;
if (reverse->configured && peer->sin_family == AF_INET &&
peer->sin_addr.s_addr == src->sin_addr.s_addr && peer->sin_port == src->sin_port) return i;
}
return -1;
}
@@ -201,17 +204,11 @@ static void dummynet_read_callback(socket_t sock, void* user_arg) {
if (n == 0) return;
uint16_t src_port = 0;
if (src_addr.ss_family == AF_INET) {
src_port = ntohs(((struct sockaddr_in*)&src_addr)->sin_port);
} else if (src_addr.ss_family == AF_INET6) {
src_port = ntohs(((struct sockaddr_in6*)&src_addr)->sin6_port);
}
int dir_idx = dummynet_get_direction(dn, src_port);
int dir_idx = dummynet_get_direction(dn, &src_addr);
if (dir_idx < 0) {
DEBUG_WARN(DEBUG_CATEGORY_DUMMYNET, "Packet from unknown port %u, expected %u or %u",
src_port, dn->listen_port - 1, dn->listen_port + 1);
DEBUG_WARN(DEBUG_CATEGORY_DUMMYNET, "Packet from unknown endpoint %s, expected %s or %s",
sockaddr_storage_to_str(&src_addr).str, sockaddr_storage_to_str(&dn->dirs[DUMMYNET_BACKWARD].dest_addr).str,
sockaddr_storage_to_str(&dn->dirs[DUMMYNET_FORWARD].dest_addr).str);
return;
}
@@ -340,7 +337,7 @@ static void dummynet_delay_callback(void* user_arg) {
/* Создаёт dummynet контекст */
struct dummynet* dummynet_create(struct UASYNC* ua, const char* bind_ip, uint16_t listen_port) {
if (!ua || listen_port == 0 || listen_port < 2 || listen_port > 65533) {
if (!ua) {
DEBUG_ERROR(DEBUG_CATEGORY_DUMMYNET, "Invalid parameters");
return NULL;
}
@@ -408,6 +405,8 @@ struct dummynet* dummynet_create(struct UASYNC* ua, const char* bind_ip, uint16_
dummynet_destroy(dn);
return NULL;
}
dn->listen_port = socket_get_bound_port(dn->sock);
if (!dn->listen_port) { dummynet_destroy(dn); return NULL; }
/* Добавляем сокет в uasync */
void* sock_id = uasync_add_socket_t(ua, dn->sock, dummynet_read_callback, NULL, NULL, "dummynet", dn);
@@ -417,10 +416,8 @@ struct dummynet* dummynet_create(struct UASYNC* ua, const char* bind_ip, uint16_
return NULL;
}
DEBUG_INFO(DEBUG_CATEGORY_DUMMYNET, "Dummynet created on %s:%u (forward: %u->%u, backward: %u->%u)",
bind_ip ? bind_ip : "0.0.0.0", listen_port,
listen_port - 1, listen_port + 1,
listen_port + 1, listen_port - 1);
DEBUG_INFO(DEBUG_CATEGORY_DUMMYNET, "Dummynet created on %s:%u; configure both peer endpoints before traffic",
bind_ip ? bind_ip : "0.0.0.0", dn->listen_port);
return dn;
}
+7 -6
View File
@@ -4,7 +4,7 @@
*
* Эмулирует сетевые условия: задержку (fixed + random), ограничение пропускной способности
* и случайные потери пакетов. Работает как "человек посередине" - принимает пакеты на
* listen_port и пересылает между port-1 и port+1 с эмуляцией условий канала.
* listen_port и пересылает между двумя явно настроенными endpoint с эмуляцией условий канала.
*
* Архитектура:
* [UDP recv] -> [delay timer] -> [check queue/drop] -> [ll_queue] -> [shaper timer] -> [UDP send]
@@ -13,8 +13,9 @@
*
* Поток данных:
* - Слушаем на bind_ip:listen_port
* - Пакеты от listen_port-1 -> отправляем на dest_forward (forward direction)
* - Пакеты от listen_port+1 -> отправляем на dest_backward (backward direction)
* - Пакеты от dest_backward -> отправляем на dest_forward (forward direction)
* - Пакеты от dest_forward -> отправляем на dest_backward (backward direction)
* - Оба направления настраиваются до начала трафика; IP и порты участников независимы
* - Каждое направление имеет свою очередь и параметры эмуляции
*/
@@ -36,8 +37,8 @@ struct ll_queue;
struct ll_entry;
/* Направления передачи */
#define DUMMYNET_FORWARD 0 // port-1 -> port+1
#define DUMMYNET_BACKWARD 1 // port+1 -> port-1
#define DUMMYNET_FORWARD 0 // client -> server
#define DUMMYNET_BACKWARD 1 // server -> client
#define DUMMYNET_DIR_COUNT 2
/* Максимальный размер UDP пакета */
@@ -71,7 +72,7 @@ struct dummynet;
*
* @param ua Экземпляр uasync (обязателен)
* @param bind_ip IP адрес для привязки (NULL или "0.0.0.0" для всех интерфейсов)
* @param listen_port Порт для прослушивания (средний порт, port-1 и port+1 - клиенты)
* @param listen_port Порт для прослушивания (0 выбирает ОС; фактический порт — get_listen_port)
* @return Указатель на контекст или NULL при ошибке
*/
struct dummynet* dummynet_create(struct UASYNC* ua, const char* bind_ip, uint16_t listen_port);