dummynet: route between explicit independently bound endpoints
This commit is contained in:
1 parent
70b55e4626
commit
12d4e2433e
4 files changed
+50
-92
No files matched your search
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user