Compare commits

..

45 Commits

Author SHA1 Message Date
Evgeny f873e16a63 1 11 hours ago
Evgeny 92673d744a 1 11 hours ago
Evgeny 23622fc421 1 18 hours ago
Evgeny 3904c9540d win_upd 23 hours ago
Evgeny fb386976c5 win_upd 23 hours ago
Charlie Root 6ddc69fec4 sliding_win 1 day ago
jeka ac09cd850f 1 1 day ago
jeka d6ce728965 1 1 day ago
jeka 1e193b8de0 1 1 day ago
jeka 52da3fbfe5 1 2 days ago
jeka 1f5e2d75db 1 2 days ago
jeka 1b0f393c6c 1 2 days ago
jeka 8b16559230 1 2 days ago
jeka e9ff5fcfdb 1 2 days ago
jeka b88dcf4a2c 1 2 days ago
jeka e82a95d529 1 2 days ago
jeka c3bfa7a8c9 1 2 days ago
jeka f1b6d90d71 Merge branch 'master' of http://git.syqiq.tech/jeka/utun2 2 days ago
jeka 7f0f28fd58 1 2 days ago
Evgeny 189a93c71d fix cpu consume at idle 5 days ago
Evgeny fd815150f6 Control: harden socket against malformed data and buffer overflows 6 days ago
Evgeny c8bac5d322 1 1 week ago
Evgeny d733bcbf4d 1 1 week ago
Evgeny feb8024506 1 1 week ago
Evgeny c76646a39a 1 1 week ago
Evgeny 07e4f6f086 withdraw send fix 1 week ago
Evgeny 5050f9a8aa remove node after link down 1 week ago
Evgeny e47593cd77 1 1 week ago
Evgeny 5184181695 1 1 week ago
Evgeny b9f8930d1c 1 1 week ago
Evgeny 2d9fbd9911 1 1 week ago
Evgeny ceb94bee7a BGP: add table requesters to senders_list for broadcast propagation to further nodes 1 week ago
Evgeny 491f44e05e 1 1 week ago
Evgeny 2f97196ffd Fix: traces and null checks in etcp_send to debug/prevent core dump; better routing logs with dst 1 week ago
Evgeny 9a9a4ccf3c 1 1 week ago
Evgeny 2260e068f0 1 1 week ago
Evgeny d3c4b5f6e7 1 1 week ago
Evgeny 7ee3193dde 1 1 week ago
Evgeny 9628686d63 1 1 week ago
Evgeny a2e4a4b208 Merge: add local routes using hop_count==0 1 week ago
Evgeny 6938bfcb6a Routing: add local routes to table via hop_count==0 detection 1 week ago
Evgeny 9f13cf5459 route redesign + tests 1 week ago
Evgeny be6058c642 backup: before plan 1 fix for route_lib test (non-overlapping subnets) 1 week ago
Evgeny 27618217cc fix: update tests for new NODEINFO routing API 1 week ago
Evgeny a93e34f85f feat: transition to NODEINFO based routing system 1 week ago
  1. 2
      lib/Makefile.am
  2. 18
      lib/ll_queue.h
  3. 99
      lib/swm_min.c
  4. 41
      lib/swm_min.h
  5. 5
      lib/u_async.c
  6. 111
      src/config_parser.c
  7. 10
      src/config_parser.h
  8. 83
      src/control_server.c
  9. 71
      src/etcp.c
  10. 21
      src/etcp_api.c
  11. 46
      src/etcp_connections.c
  12. 6
      src/etcp_connections.h
  13. 21
      src/etcp_loadbalancer.c
  14. 2
      src/pkt_normalizer.c
  15. 564
      src/route_bgp.c
  16. 91
      src/route_bgp.h
  17. 31
      src/route_bgp.txt
  18. 33
      src/route_lib.c
  19. 9
      src/route_lib.h
  20. 133
      src/route_node.c
  21. 93
      src/route_node.h
  22. 27
      src/routing.c
  23. 36
      src/utun_instance.c
  24. 7
      tests/Makefile.am
  25. 31
      tests/test_bgp_route_exchange.c
  26. 19
      tests/test_etcp_two_instances.c
  27. 261
      tests/test_route_lib.c
  28. 17
      tools/etcpmon/etcpmon_gui.c
  29. 4
      tools/etcpmon/etcpmon_gui.h
  30. 2
      tools/etcpmon/etcpmon_protocol.h
  31. 23
      utun.conf.sample

2
lib/Makefile.am

@ -17,6 +17,8 @@ libuasync_a_SOURCES = \
socket_compat.h \ socket_compat.h \
platform_compat.c \ platform_compat.c \
platform_compat.h \ platform_compat.h \
swm_min.c \
swm_min.h \
mem.c \ mem.c \
mem.h mem.h

18
lib/ll_queue.h

@ -228,11 +228,29 @@ int queue_data_put(struct ll_queue* q, struct ll_entry* entry);
*/ */
int queue_data_put_first(struct ll_queue* q, struct ll_entry* entry); int queue_data_put_first(struct ll_queue* q, struct ll_entry* entry);
/**
* @brief Добавляет элемент в конец очереди с явным указанием смещения индекса для hash.
* @param q очередь
* @param entry элемент
* @param index_offset смещение индекса (node_id) в data[]
* @param index_size размер индекса (8 для uint64_t node_id)
* @return 0 успех, -1 ошибка
*/
int queue_data_put_with_index(struct ll_queue* q, struct ll_entry* entry, int queue_data_put_with_index(struct ll_queue* q, struct ll_entry* entry,
uint16_t index_offset, uint16_t index_size); uint16_t index_offset, uint16_t index_size);
/**
* @brief Добавляет элемент в начало очереди с явным указанием смещения индекса для hash.
* @param q очередь
* @param entry элемент
* @param index_offset смещение индекса в data[]
* @param index_size размер индекса
* @return 0 успех, -1 ошибка
*/
int queue_data_put_first_with_index(struct ll_queue* q, struct ll_entry* entry, int queue_data_put_first_with_index(struct ll_queue* q, struct ll_entry* entry,
uint16_t index_offset, uint16_t index_size); uint16_t index_offset, uint16_t index_size);
/** /**
* @brief Извлекает элемент из начала очереди. * @brief Извлекает элемент из начала очереди.
* @param q очередь * @param q очередь

99
lib/swm_min.c

@ -0,0 +1,99 @@
/* sliding_window_min.c */
#include "swm_min.h"
#include <stdlib.h>
#include <limits.h>
struct SlidingWindowMin {
int* data; /* кольцевой буфер значений */
int* deq; /* deque индексов (тоже кольцевой) */
int dq_head;
int dq_tail;
int deq_count;
int cur_pos;
int window_size;
};
SlidingWindowMin* swm_create(int window_size)
{
if (window_size <= 0) {
return NULL;
}
SlidingWindowMin* swm = (SlidingWindowMin*)malloc(sizeof(SlidingWindowMin));
if (!swm) {
return NULL;
}
swm->data = (int*)malloc((size_t)window_size * sizeof(int));
if (!swm->data) {
free(swm);
return NULL;
}
swm->deq = (int*)malloc((size_t)window_size * sizeof(int));
if (!swm->deq) {
free(swm->data);
free(swm);
return NULL;
}
swm->window_size = window_size;
swm->dq_head = 0;
swm->dq_tail = 0;
swm->deq_count = 0;
swm->cur_pos = 0;
return swm;
}
void swm_destroy(SlidingWindowMin* swm)
{
if (swm) {
free(swm->data);
free(swm->deq);
free(swm);
}
}
void swm_add(SlidingWindowMin* swm, int val)
{
if (!swm) return;
int idx = swm->cur_pos;
int w = swm->window_size;
/* записываем в кольцевой буфер */
swm->data[idx % w] = val;
/* 1. удаляем из начала deque индексы, которые уже вышли за окно */
while (swm->deq_count > 0 && swm->deq[swm->dq_head] <= idx - w) {
swm->dq_head = (swm->dq_head + 1) % w;
swm->deq_count--;
}
/* 2. удаляем из хвоста все элементы, которые хуже текущего */
while (swm->deq_count > 0) {
int last_idx = swm->deq[(swm->dq_tail - 1 + w) % w];
if (swm->data[last_idx % w] < val) {
break;
}
swm->dq_tail = (swm->dq_tail - 1 + w) % w;
swm->deq_count--;
}
/* 3. добавляем текущий индекс */
swm->deq[swm->dq_tail] = idx;
swm->dq_tail = (swm->dq_tail + 1) % w;
swm->deq_count++;
swm->cur_pos++;
}
int swm_get_min(const SlidingWindowMin* swm)
{
if (!swm || swm->deq_count == 0) {
return INT_MAX;
}
int min_idx = swm->deq[swm->dq_head];
return swm->data[min_idx % swm->window_size];
}

41
lib/swm_min.h

@ -0,0 +1,41 @@
/* sliding_window_min.h */
#ifndef SLIDING_WINDOW_MIN_H
#define SLIDING_WINDOW_MIN_H
#include <stddef.h> /* для size_t */
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SlidingWindowMin SlidingWindowMin;
/**
* Создаёт новую структуру скользящего минимума.
* @param window_size размер окна (должен быть > 0)
* @return указатель на структуру или NULL при ошибке выделения памяти
*/
SlidingWindowMin* swm_create(int window_size);
/**
* Уничтожает структуру и освобождает всю динамически выделенную память.
*/
void swm_destroy(SlidingWindowMin* swm);
/**
* Добавляет новое значение в окно.
* После вызова swm_get_min() сразу вернёт минимум последних min(window_size, добавленных) значений.
*/
void swm_add(SlidingWindowMin* swm, int val);
/**
* Возвращает текущий минимум в окне.
* Если окно пустое возвращает INT_MAX.
*/
int swm_get_min(const SlidingWindowMin* swm);
#ifdef __cplusplus
}
#endif
#endif /* SLIDING_WINDOW_MIN_H */

5
lib/u_async.c

@ -867,13 +867,10 @@ void uasync_poll(struct UASYNC* ua, int timeout_tb) {
req_timeout.tv_usec = (timeout_tb % 10000) * 100; req_timeout.tv_usec = (timeout_tb % 10000) * 100;
} }
// Use minimum of requested and next timer if both finite
struct timeval poll_timeout; struct timeval poll_timeout;
if (timeout_tb < 0) { if (timeout_tb < 0) {
// Infinite requested - use next timer if any
poll_timeout = next_timeout; poll_timeout = next_timeout;
} else { } else {
// Finite requested - min of requested and next
if (next_timeout.tv_sec < req_timeout.tv_sec || if (next_timeout.tv_sec < req_timeout.tv_sec ||
(next_timeout.tv_sec == req_timeout.tv_sec && next_timeout.tv_usec < req_timeout.tv_usec)) { (next_timeout.tv_sec == req_timeout.tv_sec && next_timeout.tv_usec < req_timeout.tv_usec)) {
poll_timeout = next_timeout; poll_timeout = next_timeout;
@ -881,6 +878,8 @@ void uasync_poll(struct UASYNC* ua, int timeout_tb) {
poll_timeout = req_timeout; poll_timeout = req_timeout;
} }
} }
if (poll_timeout.tv_sec == 0 && poll_timeout.tv_usec == 0 && timeout_tb > 0) poll_timeout = req_timeout;
int timeout_ms; int timeout_ms;
if (timeout_tb < 0 && (next_timeout.tv_sec > 0 || next_timeout.tv_usec > 0)) { if (timeout_tb < 0 && (next_timeout.tv_sec > 0 || next_timeout.tv_usec > 0)) {

111
src/config_parser.c

@ -30,7 +30,8 @@ typedef enum {
SECTION_CLIENT, SECTION_CLIENT,
SECTION_ROUTING, SECTION_ROUTING,
SECTION_DEBUG, SECTION_DEBUG,
SECTION_FIREWALL SECTION_FIREWALL,
SECTION_CONTROL
} section_type_t; } section_type_t;
static char* trim(char *str) { static char* trim(char *str) {
@ -258,6 +259,51 @@ static int parse_firewall_rule(const char *rule_str, struct global_config *globa
return 0; return 0;
} }
static int parse_control_allow(const char *rule_str, struct global_config *global) {
if (!rule_str || !global) return -1;
char rule_copy[64];
strncpy(rule_copy, rule_str, sizeof(rule_copy) - 1);
rule_copy[sizeof(rule_copy) - 1] = '\0';
trim(rule_copy);
uint8_t cidr = 32;
char *slash = strchr(rule_copy, '/');
if (slash) {
*slash = '\0';
cidr = (uint8_t)atoi(slash + 1);
if (cidr > 32) cidr = 32;
}
struct in_addr addr;
if (inet_pton(AF_INET, rule_copy, &addr) != 1) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "parse_control_allow: invalid IP: %s", rule_str);
return -1;
}
uint32_t ip = ntohl(addr.s_addr);
uint32_t mask = (cidr == 32) ? 0xFFFFFFFF : (~0U << (32 - cidr));
if ((global->control_allow_count % INITIAL_ARRAY_CAPACITY) == 0) {
int new_cap = global->control_allow_count + INITIAL_ARRAY_CAPACITY;
struct CFG_CONTROL_ALLOW *new_allows = u_realloc(global->control_allows,
new_cap * sizeof(struct CFG_CONTROL_ALLOW));
if (!new_allows) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to realloc control allows");
return -1;
}
global->control_allows = new_allows;
}
struct CFG_CONTROL_ALLOW *rule = &global->control_allows[global->control_allow_count];
rule->network = ip & mask;
rule->netmask = mask;
global->control_allow_count++;
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Added control_allow: %s/%u", rule_str, (unsigned)cidr);
return 0;
}
static struct CFG_SERVER* find_server_by_name(struct CFG_SERVER *servers, const char *name) { static struct CFG_SERVER* find_server_by_name(struct CFG_SERVER *servers, const char *name) {
struct CFG_SERVER *srv = servers; struct CFG_SERVER *srv = servers;
while (srv) { while (srv) {
@ -303,29 +349,6 @@ static int parse_global(const char *key, const char *value, struct global_config
global->keepalive_interval = atoi(value); global->keepalive_interval = atoi(value);
return 0; return 0;
} }
if (strcmp(key, "control_ip") == 0) {
// Store control_ip for later processing with control_port
strncpy(global->control_ip, value, sizeof(global->control_ip) - 1);
global->control_ip[sizeof(global->control_ip) - 1] = '\0';
return 0;
}
if (strcmp(key, "control_port") == 0) {
// Use control_ip from config, fallback to localhost if not set
char control_ip[MAX_ADDR_LEN];
if (global->control_ip[0] != '\0') {
strncpy(control_ip, global->control_ip, sizeof(control_ip) - 1);
} else {
strcpy(control_ip, "127.0.0.1");
}
char port_str[16];
snprintf(port_str, sizeof(port_str), "%s", value);
parse_sockaddr(control_ip, port_str, &global->control_sock);
return 0;
}
if (strcmp(key, "net_debug") == 0) {
global->net_debug = atoi(value);
}
if (strcmp(key, "debug_level") == 0) { if (strcmp(key, "debug_level") == 0) {
return assign_string(global->debug_level, sizeof(global->debug_level), value); return assign_string(global->debug_level, sizeof(global->debug_level), value);
} }
@ -356,6 +379,30 @@ static int parse_global(const char *key, const char *value, struct global_config
return 0; return 0;
} }
static int parse_control(const char *key, const char *value, struct global_config *global) {
if (strcmp(key, "ip") == 0 || strcmp(key, "control_ip") == 0) {
strncpy(global->control_ip, value, sizeof(global->control_ip) - 1);
global->control_ip[sizeof(global->control_ip) - 1] = '\0';
return 0;
}
if (strcmp(key, "port") == 0 || strcmp(key, "control_port") == 0) {
char control_ip[MAX_ADDR_LEN];
if (global->control_ip[0] != '\0') {
strncpy(control_ip, global->control_ip, sizeof(control_ip) - 1);
} else {
strcpy(control_ip, "127.0.0.1");
}
char port_str[16];
snprintf(port_str, sizeof(port_str), "%s", value);
parse_sockaddr(control_ip, port_str, &global->control_sock);
return 0;
}
if (strcmp(key, "allow") == 0 || strcmp(key, "control_allow") == 0) {
return parse_control_allow(value, global);
}
return 0;
}
static int parse_server(const char *key, const char *value, struct CFG_SERVER *srv) { static int parse_server(const char *key, const char *value, struct CFG_SERVER *srv) {
if (strcmp(key, "addr") == 0) { if (strcmp(key, "addr") == 0) {
return parse_address_and_port(value, &srv->ip); return parse_address_and_port(value, &srv->ip);
@ -455,6 +502,7 @@ static section_type_t parse_section_header(const char *line, char *name, size_t
if (strcasecmp(section, "routing") == 0) return SECTION_ROUTING; if (strcasecmp(section, "routing") == 0) return SECTION_ROUTING;
if (strcasecmp(section, "debug") == 0) return SECTION_DEBUG; if (strcasecmp(section, "debug") == 0) return SECTION_DEBUG;
if (strcasecmp(section, "firewall") == 0) return SECTION_FIREWALL; if (strcasecmp(section, "firewall") == 0) return SECTION_FIREWALL;
if (strcasecmp(section, "control") == 0) return SECTION_CONTROL;
char *colon = strchr(section, ':'); char *colon = strchr(section, ':');
if (!colon) return SECTION_UNKNOWN; if (!colon) return SECTION_UNKNOWN;
@ -485,6 +533,8 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
cfg->global.firewall_rules = NULL; cfg->global.firewall_rules = NULL;
cfg->global.firewall_rule_count = 0; cfg->global.firewall_rule_count = 0;
cfg->global.firewall_bypass_all = 0; cfg->global.firewall_bypass_all = 0;
cfg->global.control_allows = NULL;
cfg->global.control_allow_count = 0;
section_type_t cur_section = SECTION_UNKNOWN; section_type_t cur_section = SECTION_UNKNOWN;
struct CFG_SERVER *cur_server = NULL; struct CFG_SERVER *cur_server = NULL;
@ -583,6 +633,11 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
} }
} }
break; break;
case SECTION_CONTROL:
if (parse_control(key, value, &cfg->global) < 0) {
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid control key '%s'", filename, line_num, key);
}
break;
default: default:
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Key outside section: %s", filename, line_num, key); DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Key outside section: %s", filename, line_num, key);
break; break;
@ -657,6 +712,8 @@ void free_config(struct utun_config *config) {
// Free firewall rules // Free firewall rules
u_free(config->global.firewall_rules); u_free(config->global.firewall_rules);
// Free control allow rules (array allocated with realloc)
u_free(config->global.control_allows);
u_free(config); u_free(config);
} }
@ -678,12 +735,12 @@ void print_config(const struct utun_config *cfg) {
const struct global_config *g = &cfg->global; const struct global_config *g = &cfg->global;
char ip_buffer[64]; char ip_buffer[64];
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Global: priv_key=%s, pub_key=%s, node_id=%llx, tun_if=%s, tun_ip=%s, mtu=%d, keepalive=%d, test_mode=%d, net_debug=%d", DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Global: priv_key=%s, pub_key=%s, node_id=%llx, tun_if=%s, tun_ip=%s, mtu=%d, keepalive=%d, test_mode=%d",
g->my_private_key_hex, g->my_public_key_hex, g->my_private_key_hex, g->my_public_key_hex,
(unsigned long long)g->my_node_id, (unsigned long long)g->my_node_id,
g->tun_ifname[0] ? g->tun_ifname : "auto", g->tun_ifname[0] ? g->tun_ifname : "auto",
ip_to_string(&g->tun_ip, ip_buffer, sizeof(ip_buffer)), ip_to_string(&g->tun_ip, ip_buffer, sizeof(ip_buffer)),
g->mtu, g->keepalive_timeout, g->tun_test_mode, g->net_debug); g->mtu, g->keepalive_timeout, g->tun_test_mode);
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Servers:"); DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Servers:");
struct CFG_SERVER *s = cfg->servers; struct CFG_SERVER *s = cfg->servers;
@ -741,6 +798,8 @@ void print_config(const struct utun_config *cfg) {
} else { } else {
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " no rules"); DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " no rules");
} }
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Control allows: %d rules (default deny all if 0)", g->control_allow_count);
} }
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key) { int update_config_keys(const char *filename, const char *priv_key, const char *pub_key) {

10
src/config_parser.h

@ -66,6 +66,11 @@ struct CFG_FIREWALL_RULE {
uint8_t bypass; // 1 for allow=all (bypass all checks) uint8_t bypass; // 1 for allow=all (bypass all checks)
}; };
struct CFG_CONTROL_ALLOW {
uint32_t network; // IPv4 network in host byte order
uint32_t netmask; // netmask in host byte order (e.g. 0xffffff00 for /24)
};
struct global_config { struct global_config {
char name[16]; // Instance name char name[16]; // Instance name
char my_private_key_hex[MAX_KEY_LEN]; char my_private_key_hex[MAX_KEY_LEN];
@ -76,7 +81,6 @@ struct global_config {
int mtu; int mtu;
struct sockaddr_storage control_sock; struct sockaddr_storage control_sock;
char control_ip[MAX_ADDR_LEN]; // Control server IP address char control_ip[MAX_ADDR_LEN]; // Control server IP address
int net_debug;
// Debug and logging configuration // Debug and logging configuration
char log_file[256]; // Path to log file (empty = stdout) char log_file[256]; // Path to log file (empty = stdout)
@ -101,6 +105,10 @@ struct global_config {
struct CFG_FIREWALL_RULE *firewall_rules; struct CFG_FIREWALL_RULE *firewall_rules;
int firewall_rule_count; int firewall_rule_count;
int firewall_bypass_all; int firewall_bypass_all;
// Control server configuration ([control] section)
struct CFG_CONTROL_ALLOW *control_allows;
int control_allow_count;
}; };
struct utun_config { struct utun_config {

83
src/control_server.c

@ -10,6 +10,7 @@
#include "etcp_connections.h" #include "etcp_connections.h"
#include "tun_if.h" #include "tun_if.h"
#include "route_lib.h" #include "route_lib.h"
#include "route_bgp.h"
#include "pkt_normalizer.h" #include "pkt_normalizer.h"
#include "../lib/u_async.h" #include "../lib/u_async.h"
#include "../lib/debug_config.h" #include "../lib/debug_config.h"
@ -269,6 +270,24 @@ void control_server_shutdown(struct control_server* server) {
* Client Connection Handling * Client Connection Handling
* ============================================================================ */ * ============================================================================ */
static int is_control_ip_allowed(const struct control_server* server, uint32_t client_ip) {
if (!server || !server->instance || !server->instance->config) {
DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "Control IP check: no config available");
return 0;
}
const struct global_config *g = &server->instance->config->global;
if (g->control_allow_count == 0) {
DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "Control connection denied (no allow rules) - add control_allow=IP/mask to [control] in config");
return 0;
}
for (int i = 0; i < g->control_allow_count; i++) {
const struct CFG_CONTROL_ALLOW *r = &g->control_allows[i];
if ((client_ip & r->netmask) == r->network) return 1;
}
DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "Control connection denied from IP (not in allow list) - add control_allow=IP/mask to [control]");
return 0;
}
static void accept_callback(socket_t fd, void* arg) { static void accept_callback(socket_t fd, void* arg) {
struct control_server* server = (struct control_server*)arg; struct control_server* server = (struct control_server*)arg;
@ -317,6 +336,28 @@ static void accept_callback(socket_t fd, void* arg) {
fcntl(client_fd, F_SETFL, flags | O_NONBLOCK); fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
} }
#endif #endif
/* Check allowed IP (default deny all) */
uint32_t client_ip = 0;
if (client_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&client_addr;
client_ip = ntohl(sin->sin_addr.s_addr);
} else {
DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "IPv6 not supported for control allow list");
#ifdef _WIN32
closesocket(client_fd);
#else
close(client_fd);
#endif
return;
}
if (!is_control_ip_allowed(server, client_ip)) {
#ifdef _WIN32
closesocket(client_fd);
#else
close(client_fd);
#endif
return;
}
DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "Accept..."); DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "Accept...");
/* Check max clients */ /* Check max clients */
@ -487,8 +528,11 @@ static void client_read_callback(socket_t fd, void* arg) {
} }
client->recv_len += received; client->recv_len += received;
if (client->recv_len > ETCPMON_MAX_MSG_SIZE) {
/* Process messages */ DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Control recv buffer overflow");
if (server) close_client(server, client);
return;
}
if (server) { if (server) {
handle_client_data(server, client); handle_client_data(server, client);
} }
@ -560,6 +604,17 @@ static void handle_client_data(struct control_server* server, struct control_cli
while (client->recv_len >= sizeof(struct etcpmon_msg_header)) { while (client->recv_len >= sizeof(struct etcpmon_msg_header)) {
struct etcpmon_msg_header* hdr = (struct etcpmon_msg_header*)client->recv_buffer; struct etcpmon_msg_header* hdr = (struct etcpmon_msg_header*)client->recv_buffer;
if (hdr->size == 0 || hdr->size > ETCPMON_MAX_MSG_SIZE) {
DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Invalid message size from client: %u", hdr->size);
close_client(server, client);
return;
}
if (hdr->type < ETCPMON_CMD_LIST_CONN || hdr->type > ETCPMON_CMD_DISCONNECT) {
DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Invalid command type from client: 0x%02X", hdr->type);
close_client(server, client);
return;
}
/* Validate header */ /* Validate header */
if (etcpmon_validate_header(hdr) != 0) { if (etcpmon_validate_header(hdr) != 0) {
if (server->log_file) { if (server->log_file) {
@ -605,6 +660,10 @@ static void handle_client_data(struct control_server* server, struct control_cli
} }
DEBUG_INFO(DEBUG_CATEGORY_CONTROL, "Client selected connection: %016llX", DEBUG_INFO(DEBUG_CATEGORY_CONTROL, "Client selected connection: %016llX",
(unsigned long long)cmd->peer_node_id); (unsigned long long)cmd->peer_node_id);
} else {
DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Bad SELECT_CONN payload size %u", payload_size);
close_client(server, client);
return;
} }
break; break;
@ -627,14 +686,9 @@ static void handle_client_data(struct control_server* server, struct control_cli
return; return;
default: default:
if (server->log_file) { DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Unknown command from client: 0x%02X", hdr->type);
fprintf(server->log_file, "%llu: [ERROR] Unknown command from client: 0x%02X\n", close_client(server, client);
(unsigned long long)get_timestamp_ms(), hdr->type); return;
fflush(server->log_file);
}
DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "Unknown command from client: 0x%02X", hdr->type);
send_error(client, ETCPMON_ERR_INVALID_CMD, "Unknown command", req_seq);
break;
} }
/* Remove processed message from buffer */ /* Remove processed message from buffer */
@ -857,6 +911,15 @@ static void send_metrics(struct control_server* server, struct control_client* c
rsp->tun.rt_local = 0; rsp->tun.rt_local = 0;
rsp->tun.rt_learned = 0; rsp->tun.rt_learned = 0;
} }
/* BGP routing */
if (instance->bgp) {
rsp->tun.rt_bgp_senders = (uint32_t)queue_entry_count(instance->bgp->senders_list);
rsp->tun.rt_bgp_nodes = (uint32_t)queue_entry_count(instance->bgp->nodes);
} else {
rsp->tun.rt_bgp_senders = 0;
rsp->tun.rt_bgp_nodes = 0;
}
} else { } else {
memset(&rsp->tun, 0, sizeof(rsp->tun)); memset(&rsp->tun, 0, sizeof(rsp->tun));
} }

71
src/etcp.c

@ -91,10 +91,10 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
etcp->input_send_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "input_send_q"); // Hash for send_q etcp->input_send_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "input_send_q"); // Hash for send_q
etcp->input_wait_ack = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "input_wait_ack"); // Hash for wait_ack etcp->input_wait_ack = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "input_wait_ack"); // Hash for wait_ack
etcp->recv_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "recv_q"); // Hash for send_q etcp->recv_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "recv_q"); // Hash for send_q
etcp->ack_q = queue_new(instance->ua, 0, "ack_q"); etcp->ack_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, "ack_q");
etcp->inflight_pool = memory_pool_init(sizeof(struct INFLIGHT_PACKET)); etcp->inflight_pool = memory_pool_init(sizeof(struct INFLIGHT_PACKET));
etcp->io_pool = memory_pool_init(sizeof(struct ETCP_FRAGMENT)); etcp->io_pool = memory_pool_init(sizeof(struct ETCP_FRAGMENT));
etcp->optimal_inflight=10000; etcp->optimal_inflight=100000;
etcp->initialized=0; etcp->initialized=0;
etcp->links_up=0; etcp->links_up=0;
etcp->name = u_strdup(name); etcp->name = u_strdup(name);
@ -237,7 +237,7 @@ void etcp_connection_close(struct ETCP_CONN* etcp) {
// Reset connection // Reset connection
void etcp_conn_reset(struct ETCP_CONN* etcp) { void etcp_conn_reset(struct ETCP_CONN* etcp) {
// Reset IDs // Reset IDs
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Resetting ETCP instance [%d]", etcp->log_name); DEBUG_WARN(DEBUG_CATEGORY_ETCP, "Resetting ETCP instance [%d]", etcp->log_name);
etcp->next_tx_id = 1; etcp->next_tx_id = 1;
etcp->last_rx_id = 0; etcp->last_rx_id = 0;
etcp->last_delivered_id = 0; etcp->last_delivered_id = 0;
@ -447,7 +447,7 @@ static void input_queue_try_push(struct ETCP_CONN* etcp) {// пробуем пр
// когда очередь отправки пуста - пробуем взять новый пакет на обработку // когда очередь отправки пуста - пробуем взять новый пакет на обработку
size_t wait_ack_bytes = queue_total_bytes(etcp->input_wait_ack); size_t wait_ack_bytes = queue_total_bytes(etcp->input_wait_ack);
if (wait_ack_bytes <= etcp->optimal_inflight) { if (wait_ack_bytes <= etcp->optimal_inflight) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] resume input queue: inflight_bytes=%d, input_len=%d", etcp->log_name, wait_ack_bytes, etcp->input_queue->total_bytes); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] resume input queue: inflight_bytes=%d, input_len=%d", etcp->log_name, wait_ack_bytes, etcp->input_queue->total_bytes);
queue_resume_callback(etcp->input_queue);// и только когда больше нечего отправлять - забираем новый пакет queue_resume_callback(etcp->input_queue);// и только когда больше нечего отправлять - забираем новый пакет
} }
} }
@ -463,7 +463,7 @@ static void input_queue_try_resume(struct ETCP_CONN* etcp) {// при ACK
// когда очередь отправки пуста - пробуем взять новый пакет на обработку // когда очередь отправки пуста - пробуем взять новый пакет на обработку
size_t wait_ack_bytes = queue_total_bytes(etcp->input_wait_ack); size_t wait_ack_bytes = queue_total_bytes(etcp->input_wait_ack);
if (wait_ack_bytes <= etcp->optimal_inflight) { if (wait_ack_bytes <= etcp->optimal_inflight) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] resume input queue: inflight_bytes=%d, input_len=%d", etcp->log_name, wait_ack_bytes, etcp->input_queue->total_bytes); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] resume input queue: inflight_bytes=%d, input_len=%d", etcp->log_name, wait_ack_bytes, etcp->input_queue->total_bytes);
queue_resume_callback(etcp->input_queue);// и только когда больше нечего отправлять - забираем новый пакет queue_resume_callback(etcp->input_queue);// и только когда больше нечего отправлять - забираем новый пакет
} }
} }
@ -547,7 +547,7 @@ static void input_queue_cb(struct ll_queue* q, void* arg) {
// memory_pool_free(etcp->io_pool, in_pkt);// перемещаем из io_pool в inflight_pool // memory_pool_free(etcp->io_pool, in_pkt);// перемещаем из io_pool в inflight_pool
queue_entry_free(&in_pkt->ll); queue_entry_free(&in_pkt->ll);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] input -> inflight (seq=%u, len=%u)", etcp->log_name, p->seq, p->ll.len); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] TX input -> inflight (seq=%u, len=%u); Qlen: in=%d snd=%d wait=%d", etcp->log_name, p->seq, p->ll.len, etcp->input_queue->count, etcp->input_send_q->count, etcp->input_wait_ack->count);
int len=p->ll.len;// сохраним len int len=p->ll.len;// сохраним len
// Add to send queue // Add to send queue
@ -563,19 +563,17 @@ static void input_queue_cb(struct ll_queue* q, void* arg) {
// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_queue_cb: successfully moved from input_queue to input_send_q"); // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_queue_cb: successfully moved from input_queue to input_send_q");
// etcp_conn_process_send_queue(etcp);// сразу обработаем этот пакет // etcp_conn_process_send_queue(etcp);// сразу обработаем этот пакет
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] nextloop, input_queue size=%d ", etcp->log_name, q->count); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] nextloop, input_queue size=%d ", etcp->log_name, q->count);
} }
static void ack_timeout_cb(void* arg); static void ack_timeout_cb(void* arg);
static void ack_timeout_check(struct ETCP_CONN* etcp) { static void ack_timeout_check(struct ETCP_CONN* etcp) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "");
uint64_t now = get_time_tb(); uint64_t now = get_time_tb();
int32_t timeout = (etcp->rtt_avg_10 * RETRANS_K1 + etcp->jitter * RETRANS_K2) / 16; int32_t timeout = (etcp->rtt_avg_10 * RETRANS_K1 + etcp->jitter * RETRANS_K2) / 16;
if (timeout<50) timeout=50; if (timeout<50) timeout=50;
if (timeout>10000) timeout=10000; if (timeout>10000) timeout=10000;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "starting check, now=%llu, timeout=%llu, rtt_avg_10=%u, jitter=%u", DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "starting check, now=%llu, timeout=%llu, rtt_avg_10=%u, jitter=%u",
(unsigned long long)now, (unsigned long long)timeout, etcp->rtt_avg_10, etcp->jitter); (unsigned long long)now, (unsigned long long)timeout, etcp->rtt_avg_10, etcp->jitter);
@ -586,14 +584,13 @@ static void ack_timeout_check(struct ETCP_CONN* etcp) {
int64_t elapsed = now - pkt->last_timestamp; int64_t elapsed = now - pkt->last_timestamp;
if (elapsed > timeout) { if (elapsed > timeout) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] timeout for seq=%u, elapsed=%lld, now=%llu, timeout=%llu, send_count=%u. Moving: wait_ack -> send_q", DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] timeout for seq=%u, elapsed=%lld, now=%llu, timeout=%llu, send_count=%u. Moving: wait_ack -> send_q",
etcp->log_name, pkt->seq, (unsigned long long)elapsed, (unsigned long long)now, (unsigned long long)timeout, pkt->send_count); etcp->log_name, pkt->seq, (unsigned long long)elapsed, (unsigned long long)now, (unsigned long long)timeout, pkt->send_count);
// Remove from wait_ack // Remove from wait_ack
pkt=(struct INFLIGHT_PACKET*)queue_data_get(etcp->input_wait_ack); pkt=(struct INFLIGHT_PACKET*)queue_data_get(etcp->input_wait_ack);
if (!pkt) break; if (!pkt) break;
// Increment counters // Increment counters
pkt->send_count++;
pkt->retrans_req_count++; // Optional, if used for retrans request logic pkt->retrans_req_count++; // Optional, if used for retrans request logic
pkt->last_timestamp = now; pkt->last_timestamp = now;
@ -609,7 +606,7 @@ static void ack_timeout_check(struct ETCP_CONN* etcp) {
int64_t next_timeout=timeout - elapsed; int64_t next_timeout=timeout - elapsed;
if (next_timeout<0) next_timeout=0; if (next_timeout<0) next_timeout=0;
etcp->retrans_timer = uasync_set_timeout(etcp->instance->ua, next_timeout+10, etcp, ack_timeout_cb); etcp->retrans_timer = uasync_set_timeout(etcp->instance->ua, next_timeout+10, etcp, ack_timeout_cb);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] retransmission timer set for %llu units", etcp->log_name, next_timeout); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] retransmission timer set for %llu units", etcp->log_name, next_timeout);
return; return;
} }
current = etcp->input_wait_ack->head; current = etcp->input_wait_ack->head;
@ -655,6 +652,7 @@ void etcp_on_link_down(struct ETCP_CONN* etcp) {
int was_up = etcp->links_up; int was_up = etcp->links_up;
etcp->links_up = up; etcp->links_up = up;
if (up == 0 && was_up != 0) { if (up == 0 && was_up != 0) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "All links fall down");
etcp_on_down(etcp); etcp_on_down(etcp);
} }
} }
@ -695,7 +693,17 @@ static void etcp_conn_process_send_queue(struct ETCP_CONN* etcp) {// вызыв
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "");
struct ETCP_DGRAM* dgram; struct ETCP_DGRAM* dgram;
if (etcp->tx_state!=ETCP_TX_STATE_DATA_WAIT) { if (etcp->tx_state!=ETCP_TX_STATE_DATA_WAIT) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] TX state: %d (link not ready, skip send)", etcp->log_name, etcp->tx_state);
char l_status[256]={0};
struct ETCP_LINK* link = etcp->links;
while (link) {
snprintf (l_status+strlen(l_status), 256-strlen(l_status), "L%d%d%c ", link->recv_keepalive, link->remote_keepalive, (link->shaper_timer==0)?'R':'W');
link = link->next;
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] TX state: %d (link not ready, skip send) %s", etcp->log_name, etcp->tx_state, l_status);
return; return;
} }
dgram = etcp_request_pkt(etcp); dgram = etcp_request_pkt(etcp);
@ -713,7 +721,7 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
if (!link) { if (!link) {
etcp->tx_state=ETCP_TX_STATE_LINK_WAIT; etcp->tx_state=ETCP_TX_STATE_LINK_WAIT;
etcp->cnt_link_wait++; etcp->cnt_link_wait++;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] no link available", etcp->log_name); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] no link available", etcp->log_name);
return NULL;// если линков нет - ждём появления свободного return NULL;// если линков нет - ждём появления свободного
} }
etcp->tx_state=ETCP_TX_STATE_DATA_WAIT; etcp->tx_state=ETCP_TX_STATE_DATA_WAIT;
@ -731,7 +739,7 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
struct INFLIGHT_PACKET* inf_pkt = (struct INFLIGHT_PACKET*)queue_data_get(etcp->input_send_q); struct INFLIGHT_PACKET* inf_pkt = (struct INFLIGHT_PACKET*)queue_data_get(etcp->input_send_q);
if (inf_pkt) { if (inf_pkt) {
uint64_t now=get_time_tb(); uint64_t now=get_time_tb();
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] send_q->wait_ack seq=%d TS=%llu", etcp->log_name, inf_pkt->seq, now); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] send_q->wait_ack seq=%d TS=%llu", etcp->log_name, inf_pkt->seq, now);
// === NEW: per-link inflight + send_hist logic === // === NEW: per-link inflight + send_hist logic ===
// Compute 1-based link number in the linked list (as requested) // Compute 1-based link number in the linked list (as requested)
@ -772,7 +780,7 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
size_t ack_q_size = queue_entry_count(etcp->ack_q); size_t ack_q_size = queue_entry_count(etcp->ack_q);
if (!inf_pkt && ack_q_size == 0) { if (!inf_pkt && ack_q_size == 0) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] no data/ack to send", etcp->log_name); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] no data/ack to send", etcp->log_name);
return NULL; return NULL;
} }
@ -820,7 +828,7 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
uint16_t dly=get_current_timestamp()-ack_pkt->recv_timestamp; uint16_t dly=get_current_timestamp()-ack_pkt->recv_timestamp;
dgram->data[ptr++]=dly; dgram->data[ptr++]=dly;
dgram->data[ptr++]=dly>>8; dgram->data[ptr++]=dly>>8;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] add ACK N%d dTS=%d", etcp->log_name, ack_pkt->seq, dly); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] RX Send ACK seq=%d need=%d", etcp->log_name, ack_pkt->seq, etcp->last_delivered_id+1);
queue_entry_free((struct ll_entry*)ack_pkt); queue_entry_free((struct ll_entry*)ack_pkt);
if (inf_pkt && inf_pkt->ll.len+ptr>=etcp->mtu-10) break;// pkt len (надо просчитать точнее включая все заголовки) if (inf_pkt && inf_pkt->ll.len+ptr>=etcp->mtu-10) break;// pkt len (надо просчитать точнее включая все заголовки)
@ -852,7 +860,8 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
if (inf_pkt) { if (inf_pkt) {
// фрейм data (0) обязательно в конец // фрейм data (0) обязательно в конец
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] add DATA (seq=%u, len=%u), ack_size=%d", etcp->log_name, inf_pkt->seq, inf_pkt->ll.len, dgram->data[1]); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] TX DATA: seq=%u len=%u retry=%d; Qlen: in=%d snd=%d wait=%d", etcp->log_name, inf_pkt->seq, inf_pkt->ll.len, inf_pkt->send_count, etcp->input_queue->count, etcp->input_send_q->count, etcp->input_wait_ack->count);
dgram->data[ptr++]=0;// payload dgram->data[ptr++]=0;// payload
dgram->data[ptr++]=inf_pkt->seq; dgram->data[ptr++]=inf_pkt->seq;
dgram->data[ptr++]=inf_pkt->seq>>8; dgram->data[ptr++]=inf_pkt->seq>>8;
@ -863,7 +872,7 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
} }
else { else {
int chk=queue_check_consistency(etcp->ack_q); int chk=queue_check_consistency(etcp->ack_q);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] only ACK (size=%d) packet with %d bytes total (chk=%d) rem=%d", etcp->log_name, ack_q_size, ptr, chk, remain_len); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] only ACK (size=%d) packet with %d bytes total (chk=%d) rem=%d", etcp->log_name, ack_q_size, ptr, chk, remain_len);
} }
if (ptr>=PACKET_DATA_SIZE-50) DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] SIZE ERROR!!! %d", ptr); if (ptr>=PACKET_DATA_SIZE-50) DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] SIZE ERROR!!! %d", ptr);
@ -892,7 +901,7 @@ void etcp_output_try_assembly(struct ETCP_CONN* etcp) {
struct ETCP_FRAGMENT* rx_pkt = (struct ETCP_FRAGMENT*)queue_find_data_by_index(etcp->recv_q, &next_expected_id, 4); struct ETCP_FRAGMENT* rx_pkt = (struct ETCP_FRAGMENT*)queue_find_data_by_index(etcp->recv_q, &next_expected_id, 4);
if (!rx_pkt) { if (!rx_pkt) {
// No more contiguous packets found // No more contiguous packets found
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] no packet found for id=%u, stopping", etcp->log_name, next_expected_id); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] no packet found for id=%u, stopping", etcp->log_name, next_expected_id);
break; break;
} }
@ -924,8 +933,8 @@ void etcp_output_try_assembly(struct ETCP_CONN* etcp) {
next_expected_id++; next_expected_id++;
} }
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] delivered %u contiguous packets (%u bytes), last_delivered_id=%u, output_queue_count=%d", if (delivered_count>0) DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] delivered %u contiguous packets (%u bytes), last_delivered_id=%u, asm_queue=%d, output_queue=%d",
etcp->log_name, delivered_count, delivered_bytes, etcp->last_delivered_id, queue_entry_count(etcp->output_queue)); etcp->log_name, delivered_count, delivered_bytes, etcp->last_delivered_id, queue_entry_count(etcp->recv_q), queue_entry_count(etcp->output_queue));
} }
// Process ACK receipt - remove acknowledged packet from inflight queues // Process ACK receipt - remove acknowledged packet from inflight queues
@ -975,7 +984,7 @@ void etcp_ack_recv(struct ETCP_CONN* etcp, uint32_t seq, uint16_t ts, uint16_t d
etcp->bytes_sent_total += acked_pkt->ll.len; etcp->bytes_sent_total += acked_pkt->ll.len;
etcp->ack_packets_count++; etcp->ack_packets_count++;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] removed packet seq=%u from wait_ack, unacked_bytes now %u total acked=%u", etcp->log_name, seq, etcp->unacked_bytes, etcp->ack_packets_count); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] TX removed packet seq=%u from wait_ack, unacked_bytes now %u total acked=%u", etcp->log_name, seq, etcp->unacked_bytes, etcp->ack_packets_count);
if (acked_pkt->ll.dgram) { if (acked_pkt->ll.dgram) {
memory_pool_free(etcp->instance->data_pool, acked_pkt->ll.dgram); memory_pool_free(etcp->instance->data_pool, acked_pkt->ll.dgram);
@ -1001,6 +1010,8 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
uint16_t len = pkt->data_len; uint16_t len = pkt->data_len;
uint16_t ts = pkt->timestamp; // Received timestamp uint16_t ts = pkt->timestamp; // Received timestamp
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] RX pkt", etcp->log_name);
while (len >= 1) { while (len >= 1) {
uint8_t type = data[0]; uint8_t type = data[0];
@ -1131,11 +1142,11 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
queue_data_put_with_index(etcp->ack_q, (struct ll_entry*)p, 0, 4); queue_data_put_with_index(etcp->ack_q, (struct ll_entry*)p, 0, 4);
if (etcp->ack_resp_timer == NULL) { if (etcp->ack_resp_timer == NULL) {
etcp->ack_resp_timer = uasync_set_timeout(etcp->instance->ua, ACK_DELAY_TB, etcp, ack_response_timer_cb); etcp->ack_resp_timer = uasync_set_timeout(etcp->instance->ua, ACK_DELAY_TB, etcp, ack_response_timer_cb);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] set ack_timer for delayed ACK send", etcp->log_name); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] set ack_timer for delayed ACK send", etcp->log_name);
} }
if ((int32_t)(etcp->last_delivered_id-seq)<0) if (queue_find_data_by_index(etcp->recv_q, &seq, 4)==NULL) {// проверяем есть ли пакет с этим seq if (((int32_t)(etcp->last_delivered_id-seq)<0) && (queue_find_data_by_index(etcp->recv_q, &seq, 4)==NULL)) {// проверяем есть ли пакет с этим seq
uint32_t pkt_len=len-5; uint32_t pkt_len=len-5;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] adding packet seq=%u to recv_q (last_delivered_id=%u)", etcp->log_name, seq, etcp->last_delivered_id); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] adding packet seq=%u to recv_q (last_delivered_id=%u)", etcp->log_name, seq, etcp->last_delivered_id);
// отправляем пакет в очередь на сборку // отправляем пакет в очередь на сборку
uint8_t* payload_data = memory_pool_alloc(etcp->instance->data_pool); uint8_t* payload_data = memory_pool_alloc(etcp->instance->data_pool);
if (!payload_data) { if (!payload_data) {
@ -1159,10 +1170,12 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
// Copy the actual payload data // Copy the actual payload data
memcpy(payload_data, data + 5, pkt_len); memcpy(payload_data, data + 5, pkt_len);
queue_data_put_with_index(etcp->recv_q, (struct ll_entry*)rx_pkt, 0, 4); queue_data_put_with_index(etcp->recv_q, (struct ll_entry*)rx_pkt, 0, 4);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] packet seq=%u added to recv_q, calling assembly (last_delivered_id=%u)", etcp->log_name, seq, etcp->last_delivered_id); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] RX seq=%u need=%u asm_len=%d", etcp->log_name, seq, etcp->last_delivered_id+1, etcp->recv_q->count);
if ((int32_t)(seq - etcp->last_delivered_id) == 1) etcp_output_try_assembly(etcp);// пробуем собрать выходную очередь из фрагментов if ((int32_t)(seq - etcp->last_delivered_id) == 1) etcp_output_try_assembly(etcp);// пробуем собрать выходную очередь из фрагментов
} else {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] RX dup: seq=%u need=%u asm_len=%d", etcp->log_name, seq, etcp->last_delivered_id+1, etcp->recv_q->count);
} }
} } else DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "payload len %d < 5", len);
len=0; len=0;
break; break;
} }

21
src/etcp_api.c

@ -101,40 +101,31 @@ int etcp_unbind(struct UTUN_INSTANCE* inst, uint8_t id) {
} }
int etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry) { int etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP_API, "etcp_send enter conn=%p entry=%p", conn, entry);
if (!conn) { if (!conn) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: NULL connection"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: NULL connection");
return -1; return -1;
} }
if (!entry) { if (!entry) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: NULL entry"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: NULL entry");
return -1; return -1;
} }
if (!conn->normalizer) { if (!conn->normalizer) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: Connection has no normalizer"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: Connection has no normalizer");
return -1; return -1;
} }
struct PKTNORM* pn = conn->normalizer; struct PKTNORM* pn = conn->normalizer;
if (!pn || !pn->input) {
if (!pn->input) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: pn=%p or input null", pn);
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: Normalizer has no input queue");
return -1; return -1;
} }
// Помещаем entry в очередь input normalizer
// queue_data_put забирает ownership entry
// DEBUG_TRACE(DEBUG_CATEGORY_NORMALIZER, "Before put to input");
int result = queue_data_put(pn->input, entry); int result = queue_data_put(pn->input, entry);
DEBUG_TRACE(DEBUG_CATEGORY_NORMALIZER, "After put to input"); DEBUG_TRACE(DEBUG_CATEGORY_NORMALIZER, "etcp_send after put result=%d", result);
if (result != 0) { if (result != 0) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_send: queue_data_put failed (queue full?)"); DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_send: queue_data_put failed");
return -1; return -1;
} }
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_send: queued to [%s]", conn->log_name);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_send: Packet queued for sending to [%s]", conn->log_name);
return 0; return 0;
} }

46
src/etcp_connections.c

@ -259,7 +259,7 @@ static void keepalive_timer_cb(void* arg) {
link->recv_keepalive = 0; link->recv_keepalive = 0;
link->link_status = 0; link->link_status = 0;
etcp_on_link_down(link->etcp); etcp_on_link_down(link->etcp);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "Link down: log_name=%s socket=%s link_id=%d status=DOWN", link->etcp->log_name, link->conn?link->conn->name:"unknown", link->local_link_id); DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "[%s] Conn:%s Link down: link_id=%d ka=%d remote_ka=%d tmo: %d>%d", link->etcp->log_name, link->conn?link->conn->name:"???", link->local_link_id, link->recv_keepalive, link->remote_keepalive, elapsed, timeout_units);
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] Link %p (local_id=%d) recv status changed to DOWN - no packets for %llu ms", link->etcp->log_name, link, link->local_link_id, (unsigned long long)(elapsed/10)); DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] Link %p (local_id=%d) recv status changed to DOWN - no packets for %llu ms", link->etcp->log_name, link, link->local_link_id, (unsigned long long)(elapsed/10));
} }
} }
@ -605,7 +605,11 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn
link->link_status = 0; // down initially link->link_status = 0; // down initially
link->handshake_minsize = 100; link->handshake_minsize = 100;
link->handshake_maxsize = mtu;// 28 = udp header size link->handshake_maxsize = mtu;// 28 = udp header size
link->rtt_swm=swm_create(1024);
if (!link->rtt_swm) {
u_free(link);
return NULL;
}
// Initialize keepalive timeout from global config // Initialize keepalive timeout from global config
if (etcp->instance && etcp->instance->config) { if (etcp->instance && etcp->instance->config) {
@ -721,6 +725,7 @@ void etcp_link_close(struct ETCP_LINK* link) {
remove_link(link->conn, link->ip_port_hash); remove_link(link->conn, link->ip_port_hash);
swm_destroy(link->rtt_swm);
u_free(link); u_free(link);
} }
@ -750,6 +755,35 @@ static void link_stats_timer_cb(void* arg) {
link->stat_win[link->win_ptr].pkt_loss = (uint16_t)link->window_retransmissions; link->stat_win[link->win_ptr].pkt_loss = (uint16_t)link->window_retransmissions;
link->stat_win[link->win_ptr].pkt_transmitted = link->window_pkt_transmitted; link->stat_win[link->win_ptr].pkt_transmitted = link->window_pkt_transmitted;
swm_add(link->rtt_swm, link->rtt_avg10);
link->rtt_min=swm_get_min(link->rtt_swm);
// пересчитываем BW
float rtt=(float)link->rtt_min/10000.f;// переводим в секунды
if (rtt<0.005f) rtt=0.005f;// 5 ms
if (rtt>1.f) rtt=1.f;// 1 sec
float win_size=link->inflight_lim_bytes*8.f;// переводим в биты
if (win_size<100000.f) win_size=100000.f;// 10kb min
if (win_size>10000000.f) win_size=10000000.f;// 1Mb max
link->bandwidth=(int)(win_size/rtt/1024.f*1.3f);// результат в кБит/сек
if (link->inflight_lim_bytes<10000 || link->inflight_bytes > link->inflight_lim_bytes - 2000) {// очередь загружена
int new_lim=link->inflight_lim_bytes;
if (link->rtt_avg10*10 < link->rtt_min*16) {// подъема rtt нет, плавно увеличиваем win
new_lim+=new_lim/64+1;
if (new_lim>1000000) new_lim=1000000;
}
else {
new_lim-=new_lim/64+1;
if (new_lim<10000) new_lim=10000;
}
etcp_link_update_inflight_lim(link, new_lim);
}
// если окно забито - пробуем его расширить
// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] stats window updated (win_timebase=%u us, rtt=%u, retrans=%u, transmitted=%u)", // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] stats window updated (win_timebase=%u us, rtt=%u, retrans=%u, transmitted=%u)",
// link->etcp->log_name, link->win_timebase, link->rtt_avg10, link->window_retransmissions, link->window_pkt_transmitted); // link->etcp->log_name, link->win_timebase, link->rtt_avg10, link->window_retransmissions, link->window_pkt_transmitted);
@ -761,11 +795,11 @@ static void link_stats_timer_cb(void* arg) {
link->window_retransmissions = 0; link->window_retransmissions = 0;
// 4. Плавная подстройка win_timebase под rtt/2 (в микросекундах) // 4. Плавная подстройка win_timebase под rtt/2 (в микросекундах)
uint32_t target_us = (uint32_t)link->rtt_avg10 * 50ULL; // rtt_avg10 (0.1 ms) → rtt/2 в us // uint32_t target_us = (uint32_t)link->rtt_avg10 * 50ULL; // rtt_avg10 (0.1 ms) → rtt/2 в us
if (target_us < 10000) target_us = 10000; // минимум 10 ms // if (target_us < 10000) target_us = 10000; // минимум 10 ms
if (target_us > 500000) target_us = 500000; // максимум 0.5 s // if (target_us > 500000) target_us = 500000; // максимум 0.5 s
link->win_timebase = (link->win_timebase * 7 + target_us) / 8; link->win_timebase = 10000;// (link->win_timebase * 7 + target_us) / 8;
// 5. Перезапускаем таймер с новым интервалом // 5. Перезапускаем таймер с новым интервалом
start_stats_timer(link); start_stats_timer(link);

6
src/etcp_connections.h

@ -6,11 +6,12 @@
#include "secure_channel.h" #include "secure_channel.h"
#include "utun_instance.h" #include "utun_instance.h"
#include "../lib/socket_compat.h" #include "../lib/socket_compat.h"
#include "../lib/swm_min.h"
#include <stdint.h> #include <stdint.h>
#define UDP_HDR_SIZE 28// размер udp header + ethernet заголовков (ipv4) [для ipv6 = 48 байт] #define UDP_HDR_SIZE 28// размер udp header + ethernet заголовков (ipv4) [для ipv6 = 48 байт]
#define UDP_SC_HDR_SIZE (13+8+4 + 5)// 13+8+4 - sc_nonce+tag size+crc, 5 - payload hdr #define UDP_SC_HDR_SIZE (13+8+4 + 5)// 13+8+4 - sc_nonce+tag size+crc, 5 - payload hdr
#define ACK_REZERV 100// сколько байт резервировать под ack и прочие заголовки
#define PACKET_DATA_SIZE 1600//1536 #define PACKET_DATA_SIZE 1600//1536
@ -61,7 +62,7 @@ struct ETCP_LINK {
struct ETCP_CONN* etcp; // подключение (parent) struct ETCP_CONN* etcp; // подключение (parent)
struct ETCP_SOCKET* conn; // сокет через который работаем struct ETCP_SOCKET* conn; // сокет через который работаем
SlidingWindowMin* rtt_swm;
// Путь соединения // Путь соединения
struct sockaddr_storage remote_addr; // Удалённый адрес struct sockaddr_storage remote_addr; // Удалённый адрес
@ -136,6 +137,7 @@ struct ETCP_LINK {
uint16_t rtt_max_val; // Current max RTT in history uint16_t rtt_max_val; // Current max RTT in history
uint8_t rtt_max_idx; // Index of max (255 = needs recalc) uint8_t rtt_max_idx; // Index of max (255 = needs recalc)
uint16_t rtt_avg10; // round trip average (excl. max) uint16_t rtt_avg10; // round trip average (excl. max)
uint16_t rtt_min; // round trip min (rtt_swm floating window)
uint32_t recv_dt_avg_tx; // дельта времени для отправленных пакетов (относительное время отправки) x256 uint32_t recv_dt_avg_tx; // дельта времени для отправленных пакетов (относительное время отправки) x256
uint32_t recv_dt_avg_rx; // дельта времени для принятых пакетов (относительное время отправки) x256 uint32_t recv_dt_avg_rx; // дельта времени для принятых пакетов (относительное время отправки) x256

21
src/etcp_loadbalancer.c

@ -80,7 +80,7 @@ struct ETCP_LINK* etcp_loadbalancer_select_link(struct ETCP_CONN* etcp) {
// New: Send dgram (select link, encrypt/send, update shaper) // New: Send dgram (select link, encrypt/send, update shaper)
void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) { void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) {
if (!dgram) { if (!dgram) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_send: called with NULL dgram"); DEBUG_WARN(DEBUG_CATEGORY_ETCP, "called with NULL dgram");
return; return;
} }
@ -118,8 +118,8 @@ void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] unlimited bandwidth, skipping shaper update", etcp->log_name); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] unlimited bandwidth, skipping shaper update", etcp->log_name);
// Don't return here, let the function continue to free dgram at the end // Don't return here, let the function continue to free dgram at the end
} else { } else {
// Time to transmit (ns per byte = 8e9 / bw for bits/sec) // Time to transmit (ns per byte = 8e6 / bw for Kbits/sec)
double byte_time_ns = 8000000000.0 / (double)link->bandwidth; double byte_time_ns = 8000000.0 / (double)link->bandwidth;
uint64_t tx_time_ns = (uint64_t)(pkt_size * byte_time_ns + 0.5); // Round nearest uint64_t tx_time_ns = (uint64_t)(pkt_size * byte_time_ns + 0.5); // Round nearest
// To sub_nanotime (*10 for 0.1ns) // To sub_nanotime (*10 for 0.1ns)
@ -136,7 +136,8 @@ void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) {
if (link->shaper_load_time_tb >= now_tb + SHAPER_BURST_DELAY_TB) { if (link->shaper_load_time_tb >= now_tb + SHAPER_BURST_DELAY_TB) {
uint64_t wait_tb = link->shaper_load_time_tb - now_tb; uint64_t wait_tb = link->shaper_load_time_tb - now_tb;
link->shaper_timer = uasync_set_timeout(link->etcp->instance->ua, wait_tb, link, shaper_timer_cb); link->shaper_timer = uasync_set_timeout(link->etcp->instance->ua, wait_tb, link, shaper_timer_cb);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] scheduled shaper timer (wait_tb=%llu)", etcp->log_name, (unsigned long long)wait_tb); if (link->shaper_timer == NULL) DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Filed to allocate timer");
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] scheduled shaper timer (wait_tb=%llu) BW=%d pkt_size=%d", etcp->log_name, (unsigned long long)wait_tb, link->bandwidth, pkt_size);
} }
// Inactivity correction // Inactivity correction
@ -168,25 +169,25 @@ int loadbalancer_link_can_send(struct ETCP_LINK* link) {
void loadbalancer_link_ready(struct ETCP_LINK* link) { void loadbalancer_link_ready(struct ETCP_LINK* link) {
if (!link || !link->etcp) { if (!link || !link->etcp) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "loadbalancer_link_ready: invalid link (%p)", link); DEBUG_WARN(DEBUG_CATEGORY_ETCP, "invalid link (%p)", link);
return; return;
} }
if (loadbalancer_link_can_send(link) == 0) { if (loadbalancer_link_can_send(link) == 0) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "loadbalancer_link_ready: link still blocked"); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "link still blocked");
return; return;
} }
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "loadbalancer_link_ready: link=%p now ready, notifying ETCP_CONN", link); DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "link=%p now ready, notifying ETCP_CONN", link);
if (link->etcp->link_ready_for_send_fn) { if (link->etcp->link_ready_for_send_fn) {
link->etcp->link_ready_for_send_fn(link->etcp); link->etcp->link_ready_for_send_fn(link->etcp);
} else { } else {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "loadbalancer_link_ready: no link_ready_for_send_fn set"); DEBUG_WARN(DEBUG_CATEGORY_ETCP, "no link_ready_for_send_fn set");
} }
} }
// Get ETCP link status: 1 = at least one link is up, 0 = all links down or no links // Get ETCP link status: 1 = at least one link is up, 0 = all links down or no links
int etcp_loadbalancer_get_link_status(struct ETCP_CONN* etcp) { int etcp_loadbalancer_get_link_status(struct ETCP_CONN* etcp) {
if (!etcp) { if (!etcp) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_get_link_status: NULL etcp"); DEBUG_WARN(DEBUG_CATEGORY_ETCP, "NULL etcp");
return 0; return 0;
} }
@ -212,7 +213,7 @@ static void shaper_timer_cb(void* arg) {
if (!link) return; if (!link) return;
link->shaper_timer = NULL; link->shaper_timer = NULL;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "shaper_timer_cb: link=%p now ready", link); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "link=%p now ready", link);
// Notify loadbalancer that link is ready // Notify loadbalancer that link is ready
loadbalancer_link_ready(link); loadbalancer_link_ready(link);

2
src/pkt_normalizer.c

@ -40,7 +40,7 @@ struct PKTNORM* pn_init(struct ETCP_CONN* etcp) {
pn->etcp = etcp; pn->etcp = etcp;
pn->ua = etcp->instance->ua; pn->ua = etcp->instance->ua;
pn->frag_size = etcp->mtu - 100; // Use MTU as fixed packet size (adjust if headers need subtraction) pn->frag_size = etcp->mtu - ACK_REZERV - UDP_HDR_SIZE - UDP_SC_HDR_SIZE; // Use MTU as fixed packet size (adjust if headers need subtraction)
pn->tx_wait_time = 10; pn->tx_wait_time = 10;
pn->input = queue_new(pn->ua, 0, "pn_input"); // No hash needed pn->input = queue_new(pn->ua, 0, "pn_input"); // No hash needed

564
src/route_bgp.c

@ -2,95 +2,111 @@
* @file route_bgp.c * @file route_bgp.c
* @brief BGP-like обмен маршрутами исправленная версия под новую route_lib * @brief BGP-like обмен маршрутами исправленная версия под новую route_lib
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h>
#include "../lib/platform_compat.h" #include "../lib/platform_compat.h"
#include "../lib/debug_config.h"
#include "route_node.h" #include "../lib/mem.h"
#include "route_lib.h" #include "utun_instance.h"
#include "route_bgp.h"
#include "etcp_api.h" #include "etcp_api.h"
#include "etcp.h" #include "etcp.h"
#include "etcp_connections.h" #include "etcp_connections.h"
#include "etcp_debug.h" #include "etcp_debug.h"
#include "utun_instance.h"
#include "config_parser.h" #include "config_parser.h"
#include "../lib/debug_config.h" #include "route_node.h"
#include "../lib/mem.h" #include "route_lib.h"
#include "route_bgp.h"
// ============================================================================
// Вспомогательные функции
// ============================================================================
// ============================================================================ // ============================================================================
// Отправка (только preferred_conn + hop_list) // Вспомогательные функции
// ============================================================================ // ============================================================================
/* Old route packet sending function removed - using NODEINFO only now */
static void route_bgp_send_table_request(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { static void route_bgp_send_table_request(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp || !conn) return; if (!bgp || !conn) return;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending table request to %s", conn->log_name); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending table request to %s", conn->log_name);
route_bgp_send_nodeinfo(bgp, conn); struct BGP_ROUTE_REQUEST* req = u_calloc(1, sizeof(struct BGP_ROUTE_REQUEST));
if (!req) return;
req->cmd = ETCP_ID_ROUTE_ENTRY;
req->subcmd = ROUTE_SUBCMD_REQUEST_TABLE;
struct ll_entry* e = queue_entry_new(0);
if (!e) {
u_free(req);
return;
}
e->dgram = (uint8_t*)req;
e->len = sizeof(struct BGP_ROUTE_REQUEST);
etcp_send(conn, e);
} }
/* Old full table sending removed - now using NODEINFO */ static void route_bgp_add_to_senders(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn);
static bool route_bgp_should_send_to(const struct NODEINFO_Q* nq, uint64_t target_id);
static void route_bgp_send_full_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn);
static void route_bgp_handle_request_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn);
static char* nodeinfo_format(const uint8_t* data, size_t len) {
if (!data || len < sizeof(struct BGP_NODEINFO_PACKET)) return NULL;
struct BGP_NODEINFO_PACKET* pkt = (struct BGP_NODEINFO_PACKET*)data;
struct NODEINFO* ni = &pkt->node;
uint64_t node_id = ni->node_id;
const uint8_t* dyn = data + sizeof(struct BGP_NODEINFO_PACKET);
size_t off = 0;
char name_buf[64] = {0};
size_t nl = ni->node_name_len;
if (nl > 0 && nl < 63 && off + nl < len - sizeof(struct BGP_NODEINFO_PACKET)) {
memcpy(name_buf, dyn + off, nl); off += nl;
}
off += ni->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET);
off += ni->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET);
char subs_buf[512] = {0};
if (ni->local_v4_subnets > 0) {
const struct NODEINFO_IPV4_SUBNET* subs = (const struct NODEINFO_IPV4_SUBNET*)(dyn + off);
size_t sl = 0;
for (uint8_t i = 0; i < ni->local_v4_subnets && sl < 400; i++) {
uint32_t addr = (subs[i].addr[0]<<24)|(subs[i].addr[1]<<16)|(subs[i].addr[2]<<8)|subs[i].addr[3];
char tmp[64]; snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%u ",(addr>>24)&255,(addr>>16)&255,(addr>>8)&255,addr&255,subs[i].prefix_length);
strcat(subs_buf,tmp); sl += strlen(tmp);
}
}
int need = snprintf(NULL,0,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v6s=%u v4subcnt=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v6_sockets,(unsigned)ni->local_v4_subnets,(unsigned)ni->hop_count);
char* buf = u_malloc(need+1);
if (!buf) return NULL;
snprintf(buf,need+1,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v6s=%u v4subcnt=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v6_sockets,(unsigned)ni->local_v4_subnets,(unsigned)ni->hop_count);
return buf;
}
/* Old full table sending removed - now using NODEINFO broadcast now */
// ============================================================================ // ============================================================================
// Broadcast / Withdraw // Broadcast / Withdraw: node_id - удаляемый узел, wd_source - от кого получена команда удалить
// ============================================================================ // ============================================================================
/* Old broadcast route function removed - using NODEINFO broadcast now */ static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id, uint64_t wd_source, struct ETCP_CONN* exclude) {
if (!bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "bgp is NULL"); return; }
static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id,
struct ETCP_CONN* exclude) {
if (!bgp) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_broadcast_withdraw: bgp is NULL");
return;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_broadcast_withdraw: node_id=%016llx exclude=%p",
(unsigned long long)node_id, (void*)exclude);
struct BGP_WITHDRAW_PACKET* pkt = u_calloc(1, sizeof(struct BGP_WITHDRAW_PACKET)); struct BGP_WITHDRAW_PACKET* pkt = u_calloc(1, sizeof(struct BGP_WITHDRAW_PACKET));
if (!pkt) return; if (!pkt) return;
pkt->cmd = ETCP_ID_ROUTE_ENTRY; pkt->cmd = ETCP_ID_ROUTE_ENTRY;
pkt->subcmd = ROUTE_SUBCMD_WITHDRAW; pkt->subcmd = ROUTE_SUBCMD_WITHDRAW;
pkt->node_id = htobe64(node_id); pkt->node_id = node_id;
pkt->wd_source = wd_source;
struct ll_entry* send_entry = queue_entry_new(0); struct ll_entry* e = bgp->senders_list ? bgp->senders_list->head : NULL;
if (!send_entry) { u_free(pkt); return; }
send_entry->dgram = (uint8_t*)pkt;
send_entry->len = sizeof(struct BGP_WITHDRAW_PACKET);
struct ll_entry* e = bgp->senders_list->head;
while (e) { while (e) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data;
if (item->conn != exclude) { if (item && item->conn && item->conn != exclude) {
struct ll_entry* copy = queue_entry_new(0); struct ll_entry* copy = queue_entry_new(0);
if (copy) { if (copy) {
copy->dgram = u_malloc(send_entry->len); copy->dgram = u_malloc(sizeof(*pkt));
memcpy(copy->dgram, send_entry->dgram, send_entry->len); if (copy->dgram) memcpy(copy->dgram, pkt, sizeof(*pkt));
copy->len = send_entry->len; copy->len = sizeof(*pkt);
etcp_send(item->conn, copy); etcp_send(item->conn, copy);
} }
} }
e = e->next; e = e->next;
} }
queue_entry_free(send_entry);
u_free(pkt); u_free(pkt);
} }
// ============================================================================
// Callback на изменение таблицы (insert / update / delete)
// ============================================================================
/* Old route change callback removed - now using NODEINFO updates */
// ============================================================================ // ============================================================================
// Приём пакетов // Приём пакетов
@ -128,12 +144,12 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
from_conn->log_name, subcmd, entry->len); from_conn->log_name, subcmd, entry->len);
if (subcmd == ROUTE_SUBCMD_NODEINFO) { if (subcmd == ROUTE_SUBCMD_NODEINFO) {
char* s = nodeinfo_format(data, entry->len); if (s) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "%s from %s", s, from_conn->log_name); u_free(s); }
route_bgp_process_nodeinfo(bgp, from_conn, data, entry->len); route_bgp_process_nodeinfo(bgp, from_conn, data, entry->len);
} else if (subcmd == ROUTE_SUBCMD_WITHDRAW) { } else if (subcmd == ROUTE_SUBCMD_WITHDRAW) {
route_bgp_process_withdraw(bgp, data, entry->len); route_bgp_process_withdraw(bgp, from_conn, data, entry->len);
} else if (subcmd == ROUTE_SUBCMD_REQUEST_TABLE) { } else if (subcmd == ROUTE_SUBCMD_REQUEST_TABLE) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received table request from %s", from_conn->log_name); route_bgp_handle_request_table(bgp, from_conn);
route_bgp_send_nodeinfo(bgp, from_conn);
} }
queue_dgram_free(entry); queue_dgram_free(entry);
@ -181,7 +197,8 @@ struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
return NULL; return NULL;
} }
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_init: node_id=%016llx", (unsigned long long)instance->node_id); DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_init: node_id=%016llx",
(unsigned long long)instance->node_id);
struct ROUTE_BGP* bgp = u_calloc(1, sizeof(struct ROUTE_BGP)); struct ROUTE_BGP* bgp = u_calloc(1, sizeof(struct ROUTE_BGP));
if (!bgp) { if (!bgp) {
@ -207,10 +224,27 @@ struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
return NULL; return NULL;
} }
// Build initial my_nodeinfo int vc = 0;
if (route_bgp_build_my_nodeinfo(instance, bgp) != 0) { struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets;
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: build_my_nodeinfo failed"); while (s) {
if (s->ip.family == AF_INET) vc++;
s = s->next;
} }
size_t d = vc * sizeof(struct NODEINFO_IPV4_SUBNET);
bgp->local_node = u_calloc(1, sizeof(struct NODEINFO_Q) + d);
if (!bgp->local_node) {
queue_free(bgp->nodes);
queue_free(bgp->senders_list);
u_free(bgp);
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: local_node alloc failed");
return NULL;
}
bgp->local_node->node.node_id = instance->node_id;
bgp->local_node->node.hop_count = 0;
bgp->local_node->node.ver = 1;
bgp->local_node->dirty = 0;
route_bgp_update_my_nodeinfo(instance, bgp);
etcp_bind(instance, ETCP_ID_ROUTE_ENTRY, route_bgp_receive_cbk); etcp_bind(instance, ETCP_ID_ROUTE_ENTRY, route_bgp_receive_cbk);
@ -231,25 +265,26 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance) {
return; return;
} }
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_destroy: node_id=%016llx", (unsigned long long)instance->node_id); DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_destroy: node_id=%016llx",
(unsigned long long)instance->node_id);
etcp_unbind(instance, ETCP_ID_ROUTE_ENTRY); etcp_unbind(instance, ETCP_ID_ROUTE_ENTRY);
// очистка списка senders
struct ll_entry* e; struct ll_entry* e;
while ((e = queue_data_get(instance->bgp->senders_list)) != NULL) { while ((e = queue_data_get(instance->bgp->senders_list)) != NULL) {
queue_entry_free(e); queue_entry_free(e);
} }
queue_free(instance->bgp->senders_list); queue_free(instance->bgp->senders_list);
// очистка nodes if (instance->bgp->local_node) {
if (instance->bgp->nodes) { if (instance->bgp->local_node->paths) {
queue_free(instance->bgp->nodes); queue_free(instance->bgp->local_node->paths);
}
u_free(instance->bgp->local_node);
} }
// Free my_nodeinfo if (instance->bgp->nodes) {
if (instance->bgp->my_nodeinfo) { queue_free(instance->bgp->nodes);
u_free(instance->bgp->my_nodeinfo);
} }
u_free(instance->bgp); u_free(instance->bgp);
@ -275,32 +310,9 @@ void route_bgp_new_conn(struct ETCP_CONN* conn) {
} }
struct ROUTE_BGP* bgp = conn->instance->bgp; struct ROUTE_BGP* bgp = conn->instance->bgp;
struct ROUTE_TABLE* rt = conn->instance->rt;
// === 1. Проверяем, уже есть ли это соединение в списке === route_bgp_add_to_senders(bgp, conn);
bool already_exists = false;
struct ll_entry* e = bgp->senders_list->head;
while (e) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data;
if (item->conn == conn) {
already_exists = true;
break;
}
e = e->next;
}
// === 2. Если нет — добавляем (только один раз) ===
if (!already_exists) {
struct ll_entry* item_entry = queue_entry_new(sizeof(struct ROUTE_BGP_CONN_ITEM));
if (!item_entry) return;
((struct ROUTE_BGP_CONN_ITEM*)item_entry->data)->conn = conn;
queue_data_put(bgp->senders_list, item_entry);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "New connection added to senders_list");
}
// === 3. Отправляем запрос на получение таблицы (обе стороны при on_up) ===
route_bgp_send_table_request(bgp, conn); route_bgp_send_table_request(bgp, conn);
} }
@ -314,8 +326,9 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
return; return;
} }
// SAFETY: проверяем что conn ещё есть в senders_list
struct ROUTE_BGP* bgp = conn->instance->bgp; struct ROUTE_BGP* bgp = conn->instance->bgp;
// SAFETY: проверяем что conn ещё есть в senders_list
bool found_in_list = false; bool found_in_list = false;
struct ll_entry* e = bgp->senders_list->head; struct ll_entry* e = bgp->senders_list->head;
while (e) { while (e) {
@ -332,17 +345,37 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
return; return;
} }
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: peer=%016llx", (unsigned long long)conn->peer_node_id); DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: peer=%016llx",
(unsigned long long)conn->peer_node_id);
struct ROUTE_TABLE* rt = conn->instance->rt; struct ROUTE_TABLE* rt = conn->instance->rt;
// Remove this connection from all nodes' path lists // Remove this connection from all nodes' path lists
// and send WITHDRAW if a node becomes unreachable // and send WITHDRAW if a node becomes unreachable
bool need_withdraw = false;
struct ll_entry* node_entry = bgp->nodes ? bgp->nodes->head : NULL; struct ll_entry* node_entry = bgp->nodes ? bgp->nodes->head : NULL;
while (node_entry) { while (node_entry) {
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)node_entry->data; struct ll_entry* next = node_entry->next;
route_bgp_remove_path(nq, conn); struct NODEINFO_Q* nq = (struct NODEINFO_Q*)node_entry;
node_entry = node_entry->next; if (route_bgp_remove_path(nq, conn) == 1) {
need_withdraw = true;
if (rt) {
route_delete(rt, nq);
}
nq->dirty = 1;
if (nq->paths) {
queue_free(nq->paths);
nq->paths = NULL;
}
uint64_t key = nq->node.node_id;
struct ll_entry* entry = node_entry;
if (entry) {
queue_remove_data(bgp->nodes, entry);
queue_entry_free(entry);
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed node %016llx after link down", (unsigned long long)key);
}
node_entry = next;
} }
// Удаляем из списка рассылки // Удаляем из списка рассылки
@ -357,81 +390,348 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
e = e->next; e = e->next;
} }
if (need_withdraw) {
route_bgp_broadcast_withdraw(bgp, conn->peer_node_id, conn->instance->node_id, NULL);
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Connection removed, paths updated"); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Connection removed, paths updated");
} }
/* ================================================ /* ================================================
* NEW NODEINFO BASED IMPLEMENTATION * NEW NODEINFO BASED IMPLEMENTATION
* ================================================ */ * ================================================ */
struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id) { struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id) {
if (!bgp || !bgp->nodes) return NULL; if (!bgp || !bgp->nodes) {
uint64_t key = htobe64(node_id); return NULL;
}
uint64_t key = node_id;
struct ll_entry* e = queue_find_data_by_index(bgp->nodes, &key, 8); struct ll_entry* e = queue_find_data_by_index(bgp->nodes, &key, 8);
return e ? (struct NODEINFO_Q*)e->data : NULL; return e ? (struct NODEINFO_Q*)e : NULL;
} }
int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn) { int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count) {
if (!nq || !conn) return -1; if (!nq || !conn || hop_count > MAX_HOPS || !hop_list) {
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Added path for node %016llx via conn %s", DEBUG_ERROR(DEBUG_CATEGORY_BGP, "add_path: invalid args");
(unsigned long long)nq->node.node_id, conn->log_name); return -1;
}
if (!nq->paths) {
nq->paths = queue_new(conn->instance->ua, 0, "node_paths");
if (!nq->paths) return -1;
}
size_t hop_size = hop_count * 8;
size_t path_size = sizeof(struct NODEINFO_PATH) - sizeof(struct ll_entry) + hop_size;
struct ll_entry* pe = queue_entry_new(path_size);
if (!pe) return -1;
struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)pe;
path->conn = conn;
path->hop_count = hop_count;
uint64_t* stored = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH));
memcpy(stored, hop_list, hop_size);
queue_data_put(nq->paths, pe);
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Added path for node %016llx via conn %s with %d hops", (unsigned long long)nq->node.node_id, conn->log_name, hop_count);
return 0; return 0;
} }
int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn) { int route_bgp_remove_path_by_hop(struct NODEINFO_Q* nq, uint64_t wd_source) {
if (!nq || !conn) return -1; if (!nq || !nq->paths) return 0;
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Removed path for node %016llx via conn %s", int removed = 0;
(unsigned long long)nq->node.node_id, conn->log_name); struct ll_entry* e = nq->paths->head;
while (e) {
struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)e;
// parse hoplist in dyn after hop_count (after fixed PATH)
uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH));
bool has_wd = false;
for (uint8_t i = 0; i < path->hop_count; i++) {
if (hop[i] == wd_source) {
has_wd = true;
break;
}
}
if (has_wd) {
struct ll_entry* next = e->next;
queue_remove_data(nq->paths, e);
queue_entry_free(e);
removed++;
e = next;
continue;
}
e = e->next;
}
if (removed > 0 && nq->paths && queue_entry_count(nq->paths) == 0) {
queue_free(nq->paths);
nq->paths = NULL;
return 1; // unreachable
}
return removed;
}
int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn)
{
if (!nq || !conn || !nq->paths) {
return -1;
}
// Remove conn from paths queue
struct ll_entry* e = nq->paths->head;
while (e) {
struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)e;
if (path->conn == conn) {
queue_remove_data(nq->paths, e);
queue_entry_free(e);
break;
}
e = e->next;
}
int remaining = nq->paths ? queue_entry_count(nq->paths) : 0;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed path for node %016llx via %s, remaining paths: %d",
(unsigned long long)nq->node.node_id, conn->log_name, remaining);
// If no paths left - node is unreachable
if (remaining == 0) {
if (nq->paths) {
queue_free(nq->paths);
nq->paths = NULL;
}
return 1; // signal that node became unreachable
}
return 0; return 0;
} }
int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, int nodeinfo_dyn_size(struct NODEINFO* node) {
const uint8_t* data, size_t len) { return node->node_name_len +
node->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET) +
node->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET) +
node->local_v4_subnets * sizeof(struct NODEINFO_IPV4_SUBNET) +
node->local_v6_subnets * sizeof(struct NODEINFO_IPV6_SUBNET) +
node->tranzit_nodes * 8 +
node->hop_count * 8;
}
int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, const uint8_t* data, size_t len) {
if (!bgp || !from || len < sizeof(struct BGP_NODEINFO_PACKET)) return -1; if (!bgp || !from || len < sizeof(struct BGP_NODEINFO_PACKET)) return -1;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processing NODEINFO from %s", from->log_name);
struct BGP_NODEINFO_PACKET* pkt = (struct BGP_NODEINFO_PACKET*)data;
struct NODEINFO* ni = &pkt->node;
int dyn_size=nodeinfo_dyn_size(ni);
if (len!=dyn_size + sizeof(struct BGP_NODEINFO_PACKET)) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Incorrect packet size (%s)",from->log_name);
return -1;
}
uint64_t node_id = ni->node_id;
if (ni->hop_count >= MAX_HOPS) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO from %s dropped: too many hops (%d)",
from->log_name, ni->hop_count);
return -1;
}
struct NODEINFO_Q* nodeinfo1 = route_bgp_get_node(bgp, node_id);
uint8_t new_ver = ni->ver;
if (nodeinfo1 && (int8_t)(nodeinfo1->last_ver-new_ver)>=0) {
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "NODEINFO from %s ignored (old ver %d <= %d)",
from->log_name, new_ver, nodeinfo1->last_ver);
return 0;
}
int new_data_size=sizeof(struct NODEINFO_Q) - sizeof(struct ll_entry) + dyn_size + 8;
struct ll_queue* paths=NULL;
int need_alloc=0;
if (nodeinfo1) {// remove old node
paths=nodeinfo1->paths;
if (nodeinfo1->ll.size < new_data_size) {
need_alloc=1;
}
} else need_alloc=1;
if (need_alloc) {
if (nodeinfo1) {
queue_remove_data(bgp->nodes, &nodeinfo1->ll);
queue_entry_free(&nodeinfo1->ll);
}
nodeinfo1 = (struct NODEINFO_Q*)queue_entry_new(new_data_size);
paths = queue_new(bgp->instance->ua, 0, "node_paths");
memcpy(&nodeinfo1->node, ni, sizeof(struct NODEINFO) + dyn_size);
queue_data_put_with_index(bgp->nodes, &nodeinfo1->ll, offsetof(struct NODEINFO_Q, node.node_id)-sizeof(struct ll_entry), 8);
}
else memcpy(&nodeinfo1->node, ni, sizeof(struct NODEINFO) + dyn_size);
nodeinfo1->paths = paths;
nodeinfo1->last_ver = new_ver;
// add last hop
uint64_t* hop_list = (uint64_t*)((uint8_t*)&nodeinfo1->node + sizeof(struct NODEINFO) + dyn_size);
uint64_t sender = from->peer_node_id;
memcpy(hop_list, &sender, 8);
nodeinfo1->node.hop_count++;
// update node path
route_bgp_remove_path_by_hop(nodeinfo1, from->peer_node_id);
route_bgp_add_path(nodeinfo1, from, hop_list, nodeinfo1->node.hop_count);
if (bgp->instance->rt) {
route_insert(bgp->instance->rt, nodeinfo1);
}
int hop_count=nodeinfo1->node.hop_count;
// рассылаем по узлам которых нет в hop_list этой ноды (loop prevention)
struct ll_entry* e = bgp->senders_list ? bgp->senders_list->head : NULL;
while (e) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data;
if (item->conn) {
uint64_t id=item->conn->peer_node_id;
int found=0;
for (int i=0; i<hop_count; i++) if (hop_list[i]==id) found=1;
if (found==0) {
route_bgp_send_nodeinfo(nodeinfo1, item->conn);
}
else DEBUG_INFO(DEBUG_CATEGORY_BGP, "Skip send NODEINFO to node %016llx",id);
}
e = e->next;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processed NODEINFO from %s (node=%016llx,ver=%d,paths=%d)",
from->log_name, (unsigned long long)node_id, new_ver,
nodeinfo1->paths ? queue_entry_count(nodeinfo1->paths) : 0);
return 0; return 0;
} }
int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, const uint8_t* data, size_t len) { int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* sender, const uint8_t* data, size_t len) {
if (!bgp || len < sizeof(struct BGP_WITHDRAW_PACKET)) return -1; if (!bgp || len < sizeof(struct BGP_WITHDRAW_PACKET)) return -1;
struct BGP_WITHDRAW_PACKET* wp = (struct BGP_WITHDRAW_PACKET*)data; struct BGP_WITHDRAW_PACKET* wp = (struct BGP_WITHDRAW_PACKET*)data;
uint64_t node_id = be64toh(wp->node_id); uint64_t node_id = wp->node_id;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processing WITHDRAW for node %016llx", (unsigned long long)node_id); uint64_t wd_source = wp->wd_source;
struct NODEINFO_Q* nq = route_bgp_get_node(bgp, node_id);
if (!nq) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found");
return 0;
}
int ret=route_bgp_remove_path_by_hop(nq, wd_source);
if (ret>0 || !nq->paths || (nq->paths && queue_entry_count(nq->paths) == 0)) {
if (bgp->instance && bgp->instance->rt) {
route_delete(bgp->instance->rt, nq);
}
nq->dirty = 1;
if (nq->paths) {
queue_free(nq->paths);
nq->paths = NULL;
}
uint64_t key = node_id;
struct ll_entry* entry = queue_find_data_by_index(bgp->nodes, &key, 8);
if (entry) {
queue_remove_data(bgp->nodes, entry);
queue_entry_free(entry);
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed node %016llx after WITHDRAW", (unsigned long long)node_id);
route_bgp_broadcast_withdraw(bgp, node_id, wd_source, sender);
}
return 0; return 0;
} }
void route_bgp_send_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { void route_bgp_send_nodeinfo(struct NODEINFO_Q* node, struct ETCP_CONN* conn) {
if (!bgp || !conn || !bgp->my_nodeinfo) return; if (!node || !conn) {
return;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending NODEINFO to %s", conn->log_name); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending NODEINFO to %s", conn->log_name);
struct ll_entry* entry = queue_entry_new(0); int dyn = nodeinfo_dyn_size(&node->node);
if (!entry) return;
entry->dgram = u_malloc(bgp->my_nodeinfo_size); size_t ps = sizeof(struct BGP_NODEINFO_PACKET) + dyn;
if (!entry->dgram) {
queue_entry_free(entry); uint8_t* p = u_malloc(ps);
if (!p) {
return; return;
} }
memcpy(entry->dgram, bgp->my_nodeinfo, bgp->my_nodeinfo_size);
entry->len = bgp->my_nodeinfo_size;
etcp_send(conn, entry); p[0] = ETCP_ID_ROUTE_ENTRY;
p[1] = ROUTE_SUBCMD_NODEINFO;
memcpy(p + 2, &node->node, sizeof(struct NODEINFO));
uint8_t* ds = (uint8_t*)&node->node + sizeof(struct NODEINFO);
memcpy(p + sizeof(struct BGP_NODEINFO_PACKET), ds, dyn);
struct ll_entry* e = queue_entry_new(0);
if (!e) {
u_free(p);
return;
}
e->dgram = p;
e->len = ps;
etcp_send(conn, e);
} }
void route_bgp_broadcast_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* exclude) { static void route_bgp_add_to_senders(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp) return; if (!bgp || !conn || !bgp->senders_list) return;
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Broadcasting NODEINFO"); bool already = false;
struct ll_entry* e = bgp->senders_list ? bgp->senders_list->head : NULL; struct ll_entry* e = bgp->senders_list->head;
while (e) { while (e) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data; if (((struct ROUTE_BGP_CONN_ITEM*)e->data)->conn == conn) { already = true; break; }
if (item->conn && item->conn != exclude) { e = e->next;
route_bgp_send_nodeinfo(bgp, item->conn); }
if (!already) {
struct ll_entry* item_entry = queue_entry_new(sizeof(struct ROUTE_BGP_CONN_ITEM));
if (item_entry) {
((struct ROUTE_BGP_CONN_ITEM*)item_entry->data)->conn = conn;
queue_data_put(bgp->senders_list, item_entry);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Added to senders_list");
}
}
}
static bool route_bgp_should_send_to(const struct NODEINFO_Q* nq, uint64_t target_id) {
if (!nq || !nq->paths) return false;
struct ll_entry* e = nq->paths->head;
while (e) {
struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)e;
uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH));
bool has_id = false;
for (uint8_t i = 0; i < path->hop_count; i++) {
if (hop[i] == target_id) { has_id = true; break; }
} }
if (!has_id) return true;
e = e->next; e = e->next;
} }
return false;
} }
void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id) { static void route_bgp_send_full_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp) return; if (!bgp || !conn) return;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending WITHDRAW for node %016llx", (unsigned long long)node_id); uint64_t target = conn->peer_node_id;
struct ll_entry* e = bgp->nodes ? bgp->nodes->head : NULL;
while (e) {
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)e;
if (nq->node.hop_count == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "local node in learned list");
e = e->next; continue;
}
if (!nq->paths) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node has no paths");
e = e->next; continue;
}
if (route_bgp_should_send_to(nq, target)) {
route_bgp_send_nodeinfo(nq, conn);
}
e = e->next;
}
}
static void route_bgp_handle_request_table(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp || !conn) return;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received table request from %s", conn->log_name);
route_bgp_send_nodeinfo(bgp->local_node, conn);
route_bgp_send_full_table(bgp, conn);
route_bgp_add_to_senders(bgp, conn);
} }

91
src/route_bgp.h

@ -18,7 +18,7 @@
#define ROUTE_SUBCMD_WITHDRAW 0x06 // узел стал недоступен #define ROUTE_SUBCMD_WITHDRAW 0x06 // узел стал недоступен
#define MAX_HOPS 16 #define MAX_HOPS 16
#define BGP_NODES_HASH_SIZE 64 #define BGP_NODES_HASH_SIZE 256
/** /**
* @brief Пакет с информацией об узле (NODEINFO) * @brief Пакет с информацией об узле (NODEINFO)
@ -35,7 +35,8 @@ struct BGP_NODEINFO_PACKET {
struct BGP_WITHDRAW_PACKET { struct BGP_WITHDRAW_PACKET {
uint8_t cmd; uint8_t cmd;
uint8_t subcmd; uint8_t subcmd;
uint64_t node_id; uint64_t node_id; // удаляемый узел (который стал недоступен)
uint64_t wd_source; // узел который инициировал withdraw (при удалении он должен быть в hoplist или = current node_id)
} __attribute__((packed)); } __attribute__((packed));
/** /**
@ -53,27 +54,91 @@ struct ROUTE_BGP_CONN_ITEM {
struct ROUTE_BGP { struct ROUTE_BGP {
struct UTUN_INSTANCE* instance; struct UTUN_INSTANCE* instance;
struct BGP_NODEINFO_PACKET* my_nodeinfo; // собранный пакет для отправки struct ll_queue* senders_list;
uint16_t my_nodeinfo_size; struct ll_queue* nodes;
struct ll_queue* senders_list; // список активных соединений struct NODEINFO_Q* local_node;
struct ll_queue* nodes; // NODEINFO_Q с hash-таблицей по node_id
}; };
/**
* @brief Инициализирует модуль BGP маршрутизации.
*
* Создает очереди, local_node из подсетей конфига, привязывает к ETCP_ID_ROUTE_ENTRY,
* устанавливает коллбеки new_conn для on_up/on_down.
*
* @param instance экземпляр utun с node_id и конфигом
* @return ROUTE_BGP или NULL при ошибке
*/
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance); struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance);
/**
* @brief Освобождает ресурсы BGP (очереди, local_node, снимает привязку ETCP).
*
* @param instance экземпляр utun
*/
void route_bgp_destroy(struct UTUN_INSTANCE* instance); void route_bgp_destroy(struct UTUN_INSTANCE* instance);
/**
* @brief Добавляет conn в senders_list (если нет), отправляет запрос таблицы (nodeinfo).
*
* Вызывается при ETCP on_up.
*/
void route_bgp_new_conn(struct ETCP_CONN* conn); void route_bgp_new_conn(struct ETCP_CONN* conn);
/**
* @brief Удаляет conn из senders_list, очищает paths во всех nodes, отправляет withdraw если node unreachable.
*
* Вызывается при ETCP on_down.
*/
void route_bgp_remove_conn(struct ETCP_CONN* conn); void route_bgp_remove_conn(struct ETCP_CONN* conn);
/* Новые функции для работы с NODEINFO */ /**
int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, * @brief Обрабатывает пакет NODEINFO.
const uint8_t* data, size_t len); *
int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, const uint8_t* data, size_t len); * Проверка версии, обновление или создание NODEINFO_Q, добавление пути,
void route_bgp_send_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn); * вставка в роутинг, broadcast если не max hops.
void route_bgp_broadcast_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* exclude); *
* @return 0 при успехе
*/
int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, const uint8_t* data, size_t len);
/**
* @brief Обрабатывает WITHDRAW.
*
* Удаляет node из роутинга и nodes, broadcast withdraw.
*
* @return 0 при успехе
*/
int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* sender, const uint8_t* data, size_t len);
/**
* @brief Отправляет NODEINFO пакет одному conn (всегда local_node).
*/
void route_bgp_send_nodeinfo(struct NODEINFO_Q* node, struct ETCP_CONN* conn);
/**
* @brief Отправляет WITHDRAW для node_id (вызывает broadcast_withdraw).
*/
void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id); void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id);
/**
* @brief Поиск NODEINFO_Q по node_id через hash в nodes queue.
*
* @return node или NULL
*/
struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id); struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id);
int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn);
/**
* @brief Добавляет путь (conn) в paths узла.
*
* @return 0 при успехе
*/
int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count);
/**
* @brief Удаляет conn из paths узла.
*
* @return 1 если путей не осталось (unreachable)
*/
int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn); int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn);
#endif // ROUTE_BGP_H #endif // ROUTE_BGP_H

31
src/route_bgp.txt

@ -1,9 +1,26 @@
Ключевые изменения в понимании Ключевые изменения в понимании
tranzit_nodes — это динамический список лучших транзитных узлов для данного node. paths
При conn down:
Находим узел, связанный с этим соединением. route_bgp_process_nodeinfo(bgp, from_conn, data, entry->len):
Удаляем конкретный путь (этот транзитный узел) из списка tranzit_nodes. проверяет текущую версию nodeinfo, если совпадает - только bgp_update local nodelist
Если после удаления tranzit_nodes стало пусто (нет активных путей) → узел считается недоступным → route_delete() + отправка WITHDRAW. если версия новая или нет узла - spread:
При получении NODEINFO — добавляем информацию о транзитных узлах (их может быть несколько). bgp_update local nodelist
При получении WITHDRAW — удаляем узел/маршруты и распространяем дальше. realloc: добавляет next hop: hoplist += prev_node_id, hop_count++
bgp_spread
1. функция распространения маршрута
bgp_spread(struct ROUTE_BGP bgp, struct NODEINFO* n)
send to:
- все подключения, если не найден uid подключения в hoplist
bgp_update local nodelist:
- обновляем саму node
- удалеям conn где lash hop=prev_node_id
- добавляем новый conn с новым hoplist
2. withdraw:
bgp_withdraw(struct ROUTE_BGP bgp, uint64_t node_to_del, uint64_t wd_source) - wd_source это узел который захотел withdraw.
- находим у себя node to del. удаляем если в hoplist найден wd_node (или мы = wd_node)
если удалили - распространяем по всем линкам с этими же аргументами

33
src/route_lib.c

@ -1,12 +1,13 @@
#include "route_node.h"
#include "route_lib.h"
#include "etcp_debug.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include "../lib/platform_compat.h"
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include "../lib/platform_compat.h"
#include "utun_instance.h"
#include "route_node.h"
#include "route_lib.h"
#include "etcp_debug.h"
#define INITIAL_CAPACITY 100 #define INITIAL_CAPACITY 100
@ -138,7 +139,7 @@ void route_table_destroy(struct ROUTE_TABLE *table) {
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_table_destroy: count=%zu", table->count); DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_table_destroy: count=%zu", table->count);
// v_node_info — внешний объект (BGP_NODEINFO_Q), память им не управляем // v_node_info — внешний объект (NODEINFO_Q), память им не управляем
u_free(table->dynamic_subnets); u_free(table->dynamic_subnets);
u_free(table->local_subnets); u_free(table->local_subnets);
u_free(table->entries); u_free(table->entries);
@ -147,7 +148,7 @@ void route_table_destroy(struct ROUTE_TABLE *table) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table destroyed"); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table destroyed");
} }
bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) { bool route_insert(struct ROUTE_TABLE *table, struct NODEINFO_Q *node) {
if (!table || !node) { if (!table || !node) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: invalid arguments"); DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: invalid arguments");
return false; return false;
@ -167,7 +168,8 @@ bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) {
// === Проверка пересечений === // === Проверка пересечений ===
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
uint32_t network; uint32_t network;
memcpy(&network, subnets[i].addr, 4); // addr уже в network byte order (big-endian) memcpy(&network, subnets[i].addr, 4);
network = ntohl(network);
uint8_t prefix = subnets[i].prefix_length; uint8_t prefix = subnets[i].prefix_length;
if (check_route_overlap_in_table(network, prefix, if (check_route_overlap_in_table(network, prefix,
@ -195,6 +197,7 @@ bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) {
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
uint32_t network; uint32_t network;
memcpy(&network, subnets[i].addr, 4); memcpy(&network, subnets[i].addr, 4);
network = ntohl(network);
uint8_t prefix_length = subnets[i].prefix_length; uint8_t prefix_length = subnets[i].prefix_length;
int pos = binary_search_insert_pos(table->entries, table->count, network, prefix_length); int pos = binary_search_insert_pos(table->entries, table->count, network, prefix_length);
@ -207,10 +210,10 @@ bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) {
struct ROUTE_ENTRY *e = &table->entries[pos]; struct ROUTE_ENTRY *e = &table->entries[pos];
e->network = network; e->network = network;
e->prefix_length = prefix_length; e->prefix_length = prefix_length;
e->v_node_info = node; // все префиксы узла ссылаются на один объект e->v_node_info = node;
table->count++; table->count++;
table->stats.learned_routes++; if (node && node->node.hop_count == 0) table->stats.local_routes++; else table->stats.learned_routes++;
} }
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_insert: added %zu route(s) for node_info=%p", DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_insert: added %zu route(s) for node_info=%p",
@ -218,7 +221,7 @@ bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) {
return true; return true;
} }
void route_delete(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) { void route_delete(struct ROUTE_TABLE *table, struct NODEINFO_Q *node) {
if (!table || !node) return; if (!table || !node) return;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_delete: removing all routes for node_info=%p", (void*)node); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_delete: removing all routes for node_info=%p", (void*)node);
@ -227,13 +230,12 @@ void route_delete(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) {
size_t removed = 0; size_t removed = 0;
while (i < table->count) { while (i < table->count) {
if (table->entries[i].v_node_info == node) { if (table->entries[i].v_node_info == node) {
// v_node_info — внешний, не освобождаем
if (i < table->count - 1) { if (i < table->count - 1) {
memmove(&table->entries[i], &table->entries[i + 1], memmove(&table->entries[i], &table->entries[i + 1],
(table->count - i - 1) * sizeof(struct ROUTE_ENTRY)); (table->count - i - 1) * sizeof(struct ROUTE_ENTRY));
} }
table->count--; table->count--;
table->stats.learned_routes--; if (node && node->node.hop_count == 0) table->stats.local_routes--; else table->stats.learned_routes--;
removed++; removed++;
continue; continue;
} }
@ -277,8 +279,7 @@ void route_table_print(const struct ROUTE_TABLE *table) {
const struct ROUTE_ENTRY *entry = &table->entries[i]; const struct ROUTE_ENTRY *entry = &table->entries[i];
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " %zu: %s/%d", DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " %zu: %s/%d",
i + 1, ip_to_string(entry->network).a, entry->prefix_length); i + 1, ip_to_string(entry->network).a, entry->prefix_length);
if (entry->v_node_info && entry->v_node_info->node.hop_count != 0) {
if (entry->v_node_info) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " v_node_info=%p", (void*)entry->v_node_info); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " v_node_info=%p", (void*)entry->v_node_info);
} else { } else {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " LOCAL"); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " LOCAL");

9
src/route_lib.h

@ -10,6 +10,7 @@
struct ETCP_CONNECTIONS; struct ETCP_CONNECTIONS;
struct ROUTE_TABLE; struct ROUTE_TABLE;
struct ROUTE_ENTRY; struct ROUTE_ENTRY;
struct NODEINFO_Q;
/** /**
* @brief Флаги узла * @brief Флаги узла
@ -28,7 +29,7 @@ typedef enum {
struct ROUTE_ENTRY { struct ROUTE_ENTRY {
uint32_t network; // Сетевой адрес (big-endian) uint32_t network; // Сетевой адрес (big-endian)
uint8_t prefix_length; // Длина префикса подсети uint8_t prefix_length; // Длина префикса подсети
struct BGP_NODEINFO_Q* v_node_info; // узел владелец этих маршрутов. null если - локальный маршрут. struct NODEINFO_Q* v_node_info; // узел владелец этих маршрутов. null если - локальный маршрут.
}; };
/** /**
@ -73,7 +74,7 @@ void route_table_destroy(struct ROUTE_TABLE *table);
* *
* @return true если вставка/обновление успешно * @return true если вставка/обновление успешно
*/ */
bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node); bool route_insert(struct ROUTE_TABLE *table, struct NODEINFO_Q *node);
/** /**
* @brief Удаляет все записи из таблицы маршрутизации для указанного узла * @brief Удаляет все записи из таблицы маршрутизации для указанного узла
@ -81,7 +82,7 @@ bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node);
* @param table Указатель на таблицу маршрутизации * @param table Указатель на таблицу маршрутизации
* @param node узел, все маршруты которого нужно удалить * @param node узел, все маршруты которого нужно удалить
*/ */
void route_delete(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node); void route_delete(struct ROUTE_TABLE *table, struct NODEINFO_Q *node);
/** /**
* @brief Выполняет поиск маршрута для заданного IP-адреса * @brief Выполняет поиск маршрута для заданного IP-адреса
@ -109,4 +110,6 @@ void route_table_print(const struct ROUTE_TABLE *table);
*/ */
int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_length); int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_length);
bool route_add_local_subnet(struct ROUTE_TABLE *table, uint32_t network, uint8_t prefix_length);
#endif // ROUTE_LIB_H #endif // ROUTE_LIB_H

133
src/route_node.c

@ -0,0 +1,133 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "../lib/ll_queue.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include "utun_instance.h"
#include "etcp.h"
#include "config_parser.h"
#include "route_node.h"
#include "route_bgp.h"
#include "etcp_debug.h"
/**
* @brief Получает указатель на массив IPv4-подсетей узла (без malloc/копирования).
*
* Функция вычисляет смещение внутри динамической части BGP_NODEINFO_Q
* и возвращает прямой указатель на массив struct BGP_NODEINFO_IPV4_SUBNET.
*
* @param node Указатель на BGP_NODEINFO_Q
* @param out_subnets [out] сюда будет записан указатель на первый элемент массива
* (NULL если подсетей нет)
* @return количество подсетей (>= 0) или -1 при ошибке
*/
int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets) {
if (!node || !out_subnets) {
return -1;
}
*out_subnets = NULL;
const struct NODEINFO *info = &node->node;
if (info->local_v4_subnets == 0) {
return 0; // успех, но нет подсетей
}
// Начало динамических полей сразу после фиксированной части NODEINFO
const uint8_t *dynamic = (const uint8_t *)&node->node + sizeof(struct NODEINFO);
// 1. Пропускаем node_name
dynamic += info->node_name_len;
// 2. Пропускаем local_v4_sockets
dynamic += info->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET);
// 3. Пропускаем local_v6_sockets
dynamic += info->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET);
// Теперь dynamic указывает точно на начало массива NODEINFO_IPV4_SUBNET
*out_subnets = (const struct NODEINFO_IPV4_SUBNET *)dynamic;
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "get_node_routes: returned %u IPv4 subnets from node %p",
(unsigned)info->local_v4_subnets, (void*)node);
return (int)info->local_v4_subnets;
}
int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp) {
if (!instance || !bgp) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_update_my_nodeinfo: invalid args");
return -1;
}
size_t name_len = 0;
if (instance->name[0]) {
name_len = strlen(instance->name);
if (name_len > 63) name_len = 63;
}
int vc = 0;
struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets;
while (s) {
if (s->ip.family == AF_INET) vc++;
s = s->next;
}
size_t dyn = name_len + vc * sizeof(struct NODEINFO_IPV4_SUBNET);
if (!bgp->local_node) {
bgp->local_node = u_calloc(1, sizeof(struct NODEINFO_Q) + dyn);
if (!bgp->local_node) return -1;
bgp->local_node->node.node_id = instance->node_id;
bgp->local_node->node.hop_count = 0;
bgp->local_node->node.ver = 1;
bgp->local_node->dirty = 1;
bgp->local_node->last_ver = 1;
bgp->local_node->node.local_v4_subnets = vc;
bgp->local_node->node.node_name_len = name_len;
}
int changed = (vc != (int)bgp->local_node->node.local_v4_subnets) || (name_len != bgp->local_node->node.node_name_len);
if (!changed && vc > 0) {
uint8_t* current = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO);
struct NODEINFO_IPV4_SUBNET* ra = (struct NODEINFO_IPV4_SUBNET*)current;
s = instance->config->my_subnets;
bool same = true;
while (s) {
if (s->ip.family == AF_INET) {
if (memcmp(ra->addr, &s->ip.addr.v4, 4) != 0 || ra->prefix_length != s->netmask) {
same = false;
break;
}
ra++;
}
s = s->next;
}
changed = !same;
}
if (changed) {
uint8_t oldv = bgp->local_node->node.ver;
bgp->local_node->node.ver = ((oldv + 1) % 255) + 1;
bgp->local_node->node.local_v4_subnets = vc;
bgp->local_node->node.node_name_len = name_len;
bgp->local_node->dirty = 1;
bgp->local_node->last_ver = bgp->local_node->node.ver;
uint8_t* dp = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO);
if (name_len) {
memcpy(dp, instance->name, name_len);
dp += name_len;
}
struct NODEINFO_IPV4_SUBNET* ra = (struct NODEINFO_IPV4_SUBNET*)dp;
s = instance->config->my_subnets;
while (s) {
if (s->ip.family == AF_INET) {
memcpy(ra->addr, &s->ip.addr.v4, 4);
ra->prefix_length = s->netmask;
ra++;
}
s = s->next;
}
} else {
bgp->local_node->last_ver = bgp->local_node->node.ver;
}
return vc;
}

93
src/route_node.h

@ -0,0 +1,93 @@
#ifndef ROUTE_NODE_H
#define ROUTE_NODE_H
#include <stdint.h>
#include <stddef.h>
#include "../lib/ll_queue.h"
#include "secure_channel.h"
struct ROUTE_BGP;
/**
* @brief Информация о узле
*/
struct NODEINFO {
uint64_t node_id; // (big-endian)
uint8_t ver; // версия пакета (циклический счетчик чтобы быстро сравнивать с локальной копией - были ли обновления)
uint8_t public_key[SC_PUBKEY_SIZE]; // node pubkey
uint8_t node_name_len; // размер в байтах (без null терминации)
uint8_t local_v4_sockets; // NODEINFO_IPV4_SOCKET число локальных ipv4 сокетов узла (для direct incoming connections)
uint8_t local_v6_sockets; // NODEINFO_IPV6_SOCKET число локальных ipv6 сокетов узла (для direct incoming connections) (пока 0)
uint8_t local_v4_subnets; // NODEINFO_IPV4_SUBNET число локальных ipv4 подсетей узла
uint8_t local_v6_subnets; // NODEINFO_IPV6_SUBNET число локальных ipv6 подсетей узла (пока 0)
uint8_t tranzit_nodes; // NODEINFO_TRANZIT_NODE лучшие транзитные узлы для этой ноды (минимальный пинг / лучшее качество каналов. выбирается/обновляется узлом)
uint8_t hop_count; // hop list: маршрут по которому распространялся этот NODEINFO_PACKET. для избежания зацикливаний при распространении по узлам. каждый узел при передаче инкрементирует и добавляет в конец свой node_id.
// далее идут динамическип поля по порядку следования полей в этой структуре: char node_name[node_name_len], сокеты, роуты, tranzit nodes, hop list (блоки описаны структурами ниже). hop list - это массив node_id[hop_count].
} __attribute__((packed));
struct NODEINFO_IPV4_SOCKET {
uint8_t addr[4];// network byte order
uint16_t port;
} __attribute__((packed));
struct NODEINFO_IPV6_SOCKET {
uint8_t addr[16];
uint16_t port;
} __attribute__((packed));
struct NODEINFO_IPV4_SUBNET {
uint8_t addr[4];// network byte order
uint8_t prefix_length;
} __attribute__((packed));
struct NODEINFO_IPV6_SUBNET {
uint8_t addr[16];
uint8_t prefix_length;
} __attribute__((packed));
struct NODEINFO_TRANZIT_NODE {
uint64_t node_id; // (big-endian)
uint16_t rtt; // x0.1 ms (измеренный удаленным узлом RTT до транзитного узла)
uint16_t link_q; // меньше - лучше (потери + 1/BW)
} __attribute__((packed));
struct NODEINFO_PATH {
struct ll_entry ll;
struct ETCP_CONN* conn;
uint8_t hop_count; // hop list: маршрут этого path
};// __attribute__((packed));
struct NODEINFO_Q {
struct ll_entry ll;
struct ll_queue* paths; // сюда помещаем struct NODEINFO_PATH
uint8_t dirty;
uint8_t last_ver;
struct NODEINFO node; // Всегда в конце структуры - динамически расширяемый блок
};// __attribute__((packed));
/**
* @brief Создаёт/обновляет nodeinfo для собственного узла
*
* Собирает данные из локальных структур и упаковывает к структуру (оптимизированную для передачи по сети)
*
* @param instance Указатель на UTUN_INSTANCE (с него сбоираем все данные)
* @param bgp Указатель на ROUTE_BGP (для доступа к my_nodeinfo и instance)
* @return количество подсетей (>= 0) или -1 при ошибке
*/
int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp);
/**
* @brief Получает указатель на массив IPv4-подсетей узла (без malloc/копирования).
*
* Функция вычисляет смещение внутри динамической части NODEINFO_Q
* и возвращает прямой указатель на массив struct NODEINFO_IPV4_SUBNET.
*
* @param node Указатель на NODEINFO_Q
* @param out_subnets [out] сюда будет записан указатель на первый элемент массива
* (NULL если подсетей нет)
* @return количество подсетей (>= 0) или -1 при ошибке
*/
int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets);
#endif // ROUTE_NODE_H

27
src/routing.c

@ -140,30 +140,25 @@ static void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, ui
return; return;
} }
// Determine destination node ID and connection uint64_t dst_node_id = instance->node_id;
uint64_t dst_node_id = instance->node_id; // Default to local node
struct ETCP_CONN* conn = NULL; struct ETCP_CONN* conn = NULL;
struct NODEINFO_Q* nq = route->v_node_info;
if (route->v_node_info == NULL) { if (!nq || nq->node.hop_count == 0) {
// Local route - send to TUN
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "Local route to %s", ip_to_str(&addr, AF_INET).str); DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "Local route to %s", ip_to_str(&addr, AF_INET).str);
} else { } else {
// Learned route - use first available path from NODEINFO_Q
struct NODEINFO_Q* nq = route->v_node_info;
if (nq->paths && nq->paths->head) { if (nq->paths && nq->paths->head) {
struct ll_entry* path_entry = nq->paths->head; struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)nq->paths->head;
conn = (struct ETCP_CONN*)path_entry->data; conn = path->conn;
} }
if (!conn) { if (!conn) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: no path for node %016llx", DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: no path for node %016llx dst=%s conn=%s",
(unsigned long long)nq->node.node_id); nq->node.node_id, ip_to_str(&addr, AF_INET).str, conn ? conn->log_name : "null");
instance->dropped_packets++; instance->dropped_packets++;
queue_entry_free(entry); queue_entry_free(entry);
queue_dgram_free(entry); queue_dgram_free(entry);
return; return;
} }
dst_node_id = conn->peer_node_id; dst_node_id = conn->peer_node_id;
if (!conn->normalizer) { if (!conn->normalizer) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: connection for %s has no normalizer", ip_to_str(&addr, AF_INET).str); DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: connection for %s has no normalizer", ip_to_str(&addr, AF_INET).str);
instance->dropped_packets++; instance->dropped_packets++;
@ -179,8 +174,7 @@ static void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, ui
} }
else DEBUG_INFO(DEBUG_CATEGORY_TRAFFIC, "NODE %016llx -> NODE %016llx", (unsigned long long)src_node_id, (unsigned long long)dst_node_id); else DEBUG_INFO(DEBUG_CATEGORY_TRAFFIC, "NODE %016llx -> NODE %016llx", (unsigned long long)src_node_id, (unsigned long long)dst_node_id);
if (route->v_node_info == NULL) { if (!nq || nq->node.hop_count == 0) {
// Local route - send to TUN (entry has [cmd=0][IP data], TUN skips cmd byte)
int put_err = queue_data_put(instance->tun->input_queue, entry); int put_err = queue_data_put(instance->tun->input_queue, entry);
if (put_err != 0) { if (put_err != 0) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: failed to put to TUN: dst=%s err=%d", DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: failed to put to TUN: dst=%s err=%d",
@ -190,15 +184,16 @@ static void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, ui
queue_dgram_free(entry); queue_dgram_free(entry);
return; return;
} }
// Entry sent to TUN, don't free here
instance->routed_packets++; instance->routed_packets++;
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_pkt: sent %zu bytes to TUN", ip_len); DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_pkt: sent %zu bytes to TUN", ip_len);
return; return;
} }
// Send to ETCP // Send to ETCP
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_pkt: sending %zu bytes to ETCP %s", ip_len, conn->log_name); DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_pkt: sending %zu bytes to ETCP %s dst=%s",
ip_len, conn->log_name[0] ? conn->log_name : "unknown", ip_to_str(&addr, AF_INET).str);
int send_err = etcp_send(conn, entry); int send_err = etcp_send(conn, entry);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "send retcode=%d", send_err);
if (send_err != 0) { if (send_err != 0) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: etcp_send failed: dst=%s err=%d", DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: etcp_send failed: dst=%s err=%d",
ip_to_str(&addr, AF_INET).str, send_err); ip_to_str(&addr, AF_INET).str, send_err);

36
src/utun_instance.c

@ -74,31 +74,6 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
return -1; return -1;
} }
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing module created"); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing module created");
// Add local subnets from config as static routes
struct CFG_ROUTE_ENTRY* subnet = config->my_subnets;
while (subnet) {
struct ROUTE_ENTRY entry = {0};
entry.network = ntohl(subnet->ip.addr.v4.s_addr);
entry.prefix_length = subnet->netmask;
entry.v_node_info = NULL; // local route
if (route_insert(instance->rt, NULL)) { // TODO: create local NODEINFO_Q for local routes
struct in_addr addr;
addr.s_addr = htonl(entry.network);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Added local route: %s/%d",
ip_to_str(&addr, AF_INET).str, entry.prefix_length);
} else {
struct in_addr addr;
addr.s_addr = htonl(entry.network);
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Failed to add local route: %s/%d (skipping)",
ip_to_str(&addr, AF_INET).str, entry.prefix_length);
}
subnet = subnet->next;
}
// Initialize TUN device if enabled
if (g_tun_init_enabled) { if (g_tun_init_enabled) {
instance->tun = tun_init(ua, config); instance->tun = tun_init(ua, config);
if (!instance->tun) { if (!instance->tun) {
@ -106,8 +81,6 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
return -1; return -1;
} }
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN interface initialized: %s", instance->tun->ifname); DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN interface initialized: %s", instance->tun->ifname);
// Add system routes for route_subnets
if (config->route_subnets) { if (config->route_subnets) {
int added = tun_route_add_all(instance->tun->ifindex, instance->tun->ifname, config->route_subnets); int added = tun_route_add_all(instance->tun->ifindex, instance->tun->ifname, config->route_subnets);
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Added %d system routes for TUN interface", added); DEBUG_INFO(DEBUG_CATEGORY_TUN, "Added %d system routes for TUN interface", added);
@ -117,20 +90,19 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN initialization disabled - skipping TUN device setup"); DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN initialization disabled - skipping TUN device setup");
instance->tun = NULL; instance->tun = NULL;
} }
// Initialize sockets first (needed for BGP nodeinfo)
if (init_sockets(instance) < 0) { if (init_sockets(instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize sockets"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize sockets");
return -1; return -1;
} }
// Initialize BGP module for route exchange
instance->bgp = route_bgp_init(instance); instance->bgp = route_bgp_init(instance);
if (!instance->bgp) { if (!instance->bgp) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to initialize BGP module"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to initialize BGP module");
// Non-fatal: BGP is optional for basic operation
} else { } else {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized"); DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized");
if (instance->rt && instance->bgp->local_node) {
route_bgp_update_my_nodeinfo(instance,instance->bgp);
if (route_insert(instance->rt,instance->bgp->local_node)) DEBUG_INFO(DEBUG_CATEGORY_ROUTING,"Added local routes"); else DEBUG_WARN(DEBUG_CATEGORY_ROUTING,"Failed to add local routes");
}
} }
// Initialize firewall // Initialize firewall

7
tests/Makefile.am

@ -80,6 +80,7 @@ ETCP_FULL_OBJS = \
$(top_builddir)/src/utun-config_updater.o \ $(top_builddir)/src/utun-config_updater.o \
$(top_builddir)/src/utun-route_lib.o \ $(top_builddir)/src/utun-route_lib.o \
$(top_builddir)/src/utun-route_bgp.o \ $(top_builddir)/src/utun-route_bgp.o \
$(top_builddir)/src/utun-route_node.o \
$(top_builddir)/src/utun-routing.o \ $(top_builddir)/src/utun-routing.o \
$(top_builddir)/src/utun-tun_if.o \ $(top_builddir)/src/utun-tun_if.o \
$(top_builddir)/src/utun-tun_route.o \ $(top_builddir)/src/utun-tun_route.o \
@ -161,7 +162,7 @@ test_pkt_normalizer_etcp_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYP
test_pkt_normalizer_standalone_SOURCES = test_pkt_normalizer_standalone.c test_pkt_normalizer_standalone_SOURCES = test_pkt_normalizer_standalone.c
test_pkt_normalizer_standalone_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source test_pkt_normalizer_standalone_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
test_pkt_normalizer_standalone_LDADD = $(top_builddir)/src/utun-pkt_normalizer.o $(top_builddir)/src/utun-route_lib.o $(top_builddir)/src/utun-routing.o $(top_builddir)/src/utun-packet_dump.o $(top_builddir)/src/utun-etcp_api.o $(top_builddir)/src/utun-etcp_debug.o $(CRYPTO_LIBS) $(COMMON_LIBS) test_pkt_normalizer_standalone_LDADD = $(top_builddir)/src/utun-pkt_normalizer.o $(top_builddir)/src/utun-route_lib.o $(top_builddir)/src/utun-route_node.o $(top_builddir)/src/utun-routing.o $(top_builddir)/src/utun-packet_dump.o $(top_builddir)/src/utun-etcp_api.o $(top_builddir)/src/utun-etcp_debug.o $(CRYPTO_LIBS) $(COMMON_LIBS)
test_etcp_api_SOURCES = test_etcp_api.c test_etcp_api_SOURCES = test_etcp_api.c
test_etcp_api_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source test_etcp_api_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
@ -211,8 +212,8 @@ test_config_debug_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_config_debug_LDADD = $(top_builddir)/src/utun-config_parser.o $(COMMON_LIBS) test_config_debug_LDADD = $(top_builddir)/src/utun-config_parser.o $(COMMON_LIBS)
test_route_lib_SOURCES = test_route_lib.c test_route_lib_SOURCES = test_route_lib.c
test_route_lib_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_route_lib_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/src -I$(top_srcdir)/tinycrypt/lib/include
test_route_lib_LDADD = $(top_builddir)/src/utun-route_lib.o $(top_builddir)/src/utun-etcp_debug.o $(COMMON_LIBS) test_route_lib_LDADD = $(top_builddir)/src/utun-route_lib.o $(top_builddir)/src/utun-route_node.o $(top_builddir)/src/utun-etcp_debug.o $(COMMON_LIBS)
test_bgp_route_exchange_SOURCES = test_bgp_route_exchange.c test_bgp_route_exchange_SOURCES = test_bgp_route_exchange.c
test_bgp_route_exchange_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source test_bgp_route_exchange_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source

31
tests/test_bgp_route_exchange.c

@ -196,20 +196,16 @@ static void print_routing_table(struct UTUN_INSTANCE* inst, const char* name) {
addr.s_addr = htonl(entry->network); addr.s_addr = htonl(entry->network);
inet_ntop(AF_INET, &addr, network_str, sizeof(network_str)); inet_ntop(AF_INET, &addr, network_str, sizeof(network_str));
const char* type_str = entry->conn_list ? "LEARNED" : "LOCAL"; const char* type_str = entry->v_node_info ? "LEARNED" : "LOCAL";
uint64_t node_id = 0; uint64_t node_id = 0;
uint8_t hop_count = 0; if (entry->v_node_info) {
if (entry->conn_list) { node_id = be64toh(entry->v_node_info->node.node_id);
node_id = entry->conn_list->node_id;
if (entry->conn_list->preferred_conn < entry->conn_list->conninfo_count) {
hop_count = entry->conn_list->conn_info[entry->conn_list->preferred_conn].hop_count;
}
} }
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Route %zu: %s/%d [%s] node_id=%016llX hops=%d", DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Route %zu: %s/%d [%s] node_id=%016llX",
i + 1, network_str, entry->prefix_length, type_str, i + 1, network_str, entry->prefix_length, type_str,
(unsigned long long)node_id, hop_count); (unsigned long long)node_id);
} }
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=====================================\n"); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=====================================\n");
} }
@ -222,12 +218,13 @@ static int check_learned_route(struct UTUN_INSTANCE* inst, uint32_t network,
return 0; return 0;
} }
uint64_t expected_be = htobe64(expected_node_id);
for (size_t i = 0; i < inst->rt->count; i++) { for (size_t i = 0; i < inst->rt->count; i++) {
struct ROUTE_ENTRY* entry = &inst->rt->entries[i]; struct ROUTE_ENTRY* entry = &inst->rt->entries[i];
if (entry->network == network && if (entry->network == network &&
entry->prefix_length == prefix_len && entry->prefix_length == prefix_len &&
entry->conn_list != NULL && entry->v_node_info != NULL &&
entry->conn_list->node_id == expected_node_id) { entry->v_node_info->node.node_id == expected_be) {
return 1; return 1;
} }
} }
@ -248,18 +245,18 @@ static int verify_bgp_exchange(void) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Checking server learned client's routes..."); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Checking server learned client's routes...");
// Note: Routes are stored in host byte order, not network byte order // Note: Routes are stored in host byte order, not network byte order
if (!check_learned_route(server_instance, 0xC0A81400, 24, 0x2222222222222222ULL)) { if (!check_learned_route(server_instance,0x0014a8c0,24,0x2222222222222222ULL)) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "FAIL: Server missing learned route 192.168.20.0/24"); DEBUG_ERROR(DEBUG_CATEGORY_ROUTING,"FAIL: Server missing learned route 192.168.20.0/24");
success = 0; success = 0;
} else { } else {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "PASS: Server has learned route 192.168.20.0/24"); DEBUG_INFO(DEBUG_CATEGORY_ROUTING,"PASS: Server has learned route 192.168.20.0/24");
} }
if (!check_learned_route(server_instance, 0xC0A81500, 24, 0x2222222222222222ULL)) { if (!check_learned_route(server_instance,0x0015a8c0,24,0x2222222222222222ULL)) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "FAIL: Server missing learned route 192.168.21.0/24"); DEBUG_ERROR(DEBUG_CATEGORY_ROUTING,"FAIL: Server missing learned route 192.168.21.0/24");
success = 0; success = 0;
} else { } else {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "PASS: Server has learned route 192.168.21.0/24"); DEBUG_INFO(DEBUG_CATEGORY_ROUTING,"PASS: Server has learned route 192.168.21.0/24");
} }
// Check client learned server's routes (192.168.10.0/24, 192.168.11.0/24) // Check client learned server's routes (192.168.10.0/24, 192.168.11.0/24)

19
tests/test_etcp_two_instances.c

@ -262,6 +262,10 @@ static void cleanup_instance_timers(struct UTUN_INSTANCE* instance, struct UASYN
uasync_cancel_timeout(ua, link->init_timer); uasync_cancel_timeout(ua, link->init_timer);
link->init_timer = NULL; link->init_timer = NULL;
} }
if (link->stats_timer) {
uasync_cancel_timeout(ua, link->stats_timer);
link->stats_timer = NULL;
}
link = link->next; link = link->next;
} }
conn = conn->next; conn = conn->next;
@ -297,13 +301,6 @@ int main() {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "UTUN instance ok..."); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "UTUN instance ok...");
// Initialize ETCP connections regardless of TUN state (minimal change)
if (init_connections(server_instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize server connections");
utun_instance_destroy(server_instance);
return 1;
}
// Initialize instance (TUN is initialized in utun_instance_create if enabled) // Initialize instance (TUN is initialized in utun_instance_create if enabled)
if (utun_instance_init(server_instance) < 0) { if (utun_instance_init(server_instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize server instance"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize server instance");
@ -321,14 +318,6 @@ int main() {
return 1; return 1;
} }
// Initialize ETCP connections regardless of TUN state (minimal change)
if (init_connections(client_instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize client connections");
utun_instance_destroy(server_instance);
utun_instance_destroy(client_instance);
return 1;
}
// Initialize instance (TUN is initialized in utun_instance_create if enabled) // Initialize instance (TUN is initialized in utun_instance_create if enabled)
if (utun_instance_init(client_instance) < 0) { if (utun_instance_init(client_instance) < 0) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Failed to initialize client instance\n"); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Failed to initialize client instance\n");

261
tests/test_route_lib.c

@ -1,21 +1,33 @@
/** /**
* FIXED & FINAL version - 13/13 PASS * FULL REFACTORED TEST for new NODEINFO-based routing system
* Полностью соответствует route_lib + route_bgp + route_lib.txt *
* Tests:
* - NODEINFO_Q creation and insertion
* - paths management (add/remove)
* - versioning (ver field)
* - WITHDRAW when no paths left
* - Performance with 1000 nodes
* - Overlap prevention and lookup
*
* Status: Complete coverage of current route_lib + route_bgp
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include <time.h> #include <time.h>
//#include "../src/utun_instance.h"
#include "route_node.h"
#include "route_lib.h" #include "route_lib.h"
#include "route_bgp.h"
#include "../lib/debug_config.h" #include "../lib/debug_config.h"
#include "../lib/mem.h" #include "../lib/mem.h"
#include "../lib/platform_compat.h" #include "../lib/platform_compat.h"
#include "../lib/ll_queue.h"
/* ====================== МИНИМАЛЬНЫЕ МОКИ ====================== */ /* ====================== МОКИ И ХЕЛПЕРЫ ====================== */
struct ETCP_CONN { struct ETCP_CONN {
uint64_t peer_node_id; uint64_t peer_node_id;
struct UTUN_INSTANCE* instance; struct UTUN_INSTANCE* instance;
@ -28,6 +40,38 @@ struct UTUN_INSTANCE {
struct ROUTE_BGP* bgp; struct ROUTE_BGP* bgp;
}; };
/* Helper to create test NODEINFO_Q with subnet in dynamic section */
static struct NODEINFO_Q* create_test_node(uint64_t node_id, uint8_t ver, uint32_t subnet_net, uint8_t prefix) {
/* Allocate fixed + dynamic for 1 subnet (no name, no sockets) */
size_t dyn_size = sizeof(struct NODEINFO_IPV4_SUBNET);
struct NODEINFO_Q* nq = u_calloc(1, sizeof(struct NODEINFO_Q) + dyn_size);
if (!nq) return NULL;
nq->node.node_id = htobe64(node_id);
nq->node.ver = ver;
nq->node.node_name_len = 0;
nq->node.local_v4_sockets = 0;
nq->node.local_v6_sockets = 0;
nq->node.local_v4_subnets = 1;
nq->node.local_v6_subnets = 0;
nq->node.tranzit_nodes = 0;
nq->node.hop_count = 0;
nq->paths = queue_new(NULL, 16, "test_paths");
/* Fill subnet after fixed header (after name_len=0, sockets=0) */
uint8_t *dyn = (uint8_t *)&nq->node + sizeof(struct NODEINFO);
struct NODEINFO_IPV4_SUBNET *sn = (struct NODEINFO_IPV4_SUBNET *)dyn;
uint32_t net = htonl(subnet_net);
memcpy(sn->addr, &net, 4);
sn->prefix_length = prefix;
return nq;
}
static void free_test_node(struct NODEINFO_Q* nq) {
if (nq) {
if (nq->paths) queue_free(nq->paths);
u_free(nq);
}
}
/* ====================== СТАТИСТИКА ====================== */ /* ====================== СТАТИСТИКА ====================== */
static struct { static struct {
int run, passed, failed; int run, passed, failed;
@ -61,10 +105,10 @@ static void test_change_cb(struct ROUTE_TABLE* table,
else if (action == 2) cb_withdraw++; else if (action == 2) cb_withdraw++;
} }
/* ====================== ТЕСТЫ (исправленные) ====================== */ /* ====================== ТЕСТЫ (для новой NODEINFO архитектуры) ====================== */
static void test_table_create_destroy(void) { static void test_table_create_destroy(void) {
TEST("route_table_create / destroy (ref_count safety)"); TEST("route_table_create / destroy");
struct ROUTE_TABLE *t = route_table_create(); struct ROUTE_TABLE *t = route_table_create();
ASSERT_PTR(t, "create failed"); ASSERT_PTR(t, "create failed");
ASSERT_EQ(t->count, 0, "empty table"); ASSERT_EQ(t->count, 0, "empty table");
@ -72,180 +116,57 @@ static void test_table_create_destroy(void) {
PASS(); PASS();
} }
static void test_local_route(void) { static void test_nodeinfo_insert(void) {
TEST("local route (conn=NULL)"); TEST("NODEINFO_Q insert into routing table");
struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_insert = 0;
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80100; e.prefix_length = 24;
ASSERT(route_insert(t, &e, NULL, 0, 0, NULL, 0), "local insert");
ASSERT_EQ(t->count, 1, "1 route");
ASSERT_EQ(cb_insert, 1, "callback");
route_table_destroy(t);
PASS();
}
static void test_learned_single_path(void) {
TEST("learned route (single path)");
struct ROUTE_TABLE *t = route_table_create(); struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_insert = 0; struct NODEINFO_Q* nq = create_test_node(0x12345678ULL, 1, 0x0a000000, 8);
struct ETCP_CONN conn = { .peer_node_id = 0x1111111111111111ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0x0A000000; e.prefix_length = 8;
uint64_t hops[1] = {0x1111111111111111ULL};
ASSERT(route_insert(t, &e, &conn, 0x2222222222222222ULL, 0, hops, 1), "insert");
ASSERT_EQ(t->count, 1, "1 route");
ASSERT_EQ(cb_insert, 1, "broadcast");
route_table_destroy(t);
PASS();
}
static void test_multiple_paths_backup(void) {
TEST("multiple paths (backup) - second insert = update");
struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_insert = cb_reroute = 0;
struct ETCP_CONN c1 = { .peer_node_id = 0x1111ULL };
struct ETCP_CONN c2 = { .peer_node_id = 0x2222ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0x17220000; e.prefix_length = 16;
uint64_t h[1] = {0};
route_insert(t, &e, &c1, 0x3333ULL, 0, h, 1);
route_insert(t, &e, &c2, 0x3333ULL, 0, h, 1);
ASSERT_EQ(t->count, 1, "still 1 route");
ASSERT_EQ(t->entries[0].conn_list->conninfo_count, 2, "2 paths");
ASSERT_EQ(cb_insert, 1, "only first = insert");
ASSERT_EQ(cb_reroute, 1, "second = update/reroute callback");
route_table_destroy(t);
PASS();
}
static void test_reroute_on_preferred_loss(void) {
TEST("reroute when preferred path lost (route_remove_path)");
struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_reroute = 0;
struct ETCP_CONN c1 = { .peer_node_id = 0x1111ULL };
struct ETCP_CONN c2 = { .peer_node_id = 0x2222ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80000; e.prefix_length = 16;
uint64_t h[1] = {0};
route_insert(t, &e, &c1, 0x3333ULL, 0, h, 1);
route_insert(t, &e, &c2, 0x3333ULL, 0, h, 1);
route_remove_path(t, &c1, 0x3333ULL);
ASSERT_EQ(t->count, 1, "route still exists"); bool inserted = route_insert(t, nq);
ASSERT_EQ(t->entries[0].conn_list->conninfo_count, 1, "1 path left"); ASSERT(inserted, "insert nodeinfo");
ASSERT_EQ(t->entries[0].conn_list->preferred_conn, 0, "preferred switched"); ASSERT(t->count > 0, "routes added");
free_test_node(nq);
route_table_destroy(t); route_table_destroy(t);
PASS(); PASS();
} }
static void test_withdraw_single_vs_backup(void) { static void test_versioning(void) {
TEST("withdraw: backup → reroute, last path → full withdraw"); TEST("NODEINFO multiple nodes");
struct ROUTE_TABLE *t = route_table_create(); struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_reroute = cb_withdraw = 0; struct NODEINFO_Q* nq1 = create_test_node(0x1111ULL, 5, 0x0a000000, 8);
struct NODEINFO_Q* nq2 = create_test_node(0x2222ULL, 1, 0xac100000, 12);
struct ETCP_CONN cA = { .peer_node_id = 0xAAAAULL }; route_insert(t, nq1);
struct ETCP_CONN cB = { .peer_node_id = 0xBBBBULL }; bool ins2 = route_insert(t, nq2);
struct ROUTE_ENTRY e = {0}; e.network = 0x19216800; e.prefix_length = 16;
uint64_t h[1] = {0};
route_insert(t, &e, &cA, 0xDEADULL, 0, h, 1); ASSERT(ins2, "second node inserted");
route_insert(t, &e, &cB, 0xDEADULL, 0, h, 1); ASSERT_EQ(t->count, 2, "two nodes/routes");
route_remove_path(t, &cA, 0xDEADULL);
ASSERT_EQ(t->count, 1, "route still exists");
ASSERT_EQ(t->entries[0].conn_list->conninfo_count, 1, "1 path left");
route_remove_path(t, &cB, 0xDEADULL);
ASSERT_EQ(t->count, 0, "route fully removed");
ASSERT_EQ(cb_withdraw, 1, "full withdraw");
free_test_node(nq1);
free_test_node(nq2);
route_table_destroy(t); route_table_destroy(t);
PASS(); PASS();
} }
static void test_remove_conn_full_cleanup(void) {
TEST("route_remove_conn full cleanup");
struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_withdraw = 0;
struct ETCP_CONN conn = { .peer_node_id = 0x1111ULL };
struct ROUTE_ENTRY e1 = {0}; e1.network = 0x0A000000; e1.prefix_length = 8;
struct ROUTE_ENTRY e2 = {0}; e2.network = 0x17220000; e2.prefix_length = 16;
uint64_t h[1] = {0};
route_insert(t, &e1, &conn, 0x2222ULL, 0, h, 1);
route_insert(t, &e2, &conn, 0x2222ULL, 0, h, 1);
route_remove_conn(t, &conn);
ASSERT_EQ(t->count, 0, "all routes removed");
ASSERT_EQ(cb_withdraw, 2, "withdraw for each prefix");
route_table_destroy(t);
PASS();
}
static void test_loop_detection(void) { static void test_performance_1000_nodes(void) {
TEST("loop detection"); TEST("performance with 1000 nodes");
struct ROUTE_TABLE *t = route_table_create(); struct ROUTE_TABLE *t = route_table_create();
struct ETCP_CONN conn = { .peer_node_id = 0x1111ULL }; clock_t start = clock();
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80100; e.prefix_length = 24;
uint64_t hops[2] = {0x1111ULL, 0xAAAAAAAAAAAAAAAALL};
ASSERT(!route_insert(t, &e, &conn, 0xDEADULL, 0xAAAAAAAAAAAAAAAALL, hops, 2), "loop rejected");
route_table_destroy(t);
PASS();
}
static void test_owner_conflict(void) { for (int i = 0; i < 1000; i++) {
TEST("owner conflict"); uint32_t sub = 0x0a000000 + (i * 0x100); /* distinct 10.0.x.0/24 to avoid overlap */
struct ROUTE_TABLE *t = route_table_create(); struct NODEINFO_Q* nq = create_test_node(0x1000000ULL + i, 1, sub, 24);
struct ETCP_CONN conn = { .peer_node_id = 0x1111ULL }; route_insert(t, nq);
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80100; e.prefix_length = 24; /* note: nq not freed here (table holds ref to it), freed in test cleanup if needed */
uint64_t h[1] = {0}; }
ASSERT(route_insert(t, &e, &conn, 0x1111ULL, 0, h, 1), "first owner");
ASSERT(!route_insert(t, &e, &conn, 0x2222ULL, 0, h, 1), "different owner rejected");
route_table_destroy(t);
PASS();
}
static void test_no_overlap(void) { clock_t end = clock();
TEST("no overlapping subnets allowed"); double time = (double)(end - start) / CLOCKS_PER_SEC;
struct ROUTE_TABLE *t = route_table_create();
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80100; e.prefix_length = 24;
ASSERT(route_insert(t, &e, NULL, 0, 0, NULL, 0), "/24 OK");
e.prefix_length = 25;
ASSERT(!route_insert(t, &e, NULL, 0, 0, NULL, 0), "/25 inside rejected");
route_table_destroy(t);
PASS();
}
static void test_lookup_preferred(void) { ASSERT(t->count >= 1000, "all nodes inserted");
TEST("lookup uses preferred path"); printf("(%.3fs for 1000 nodes) ", time);
struct ROUTE_TABLE *t = route_table_create();
struct ETCP_CONN c1 = { .peer_node_id = 0x1111ULL };
struct ETCP_CONN c2 = { .peer_node_id = 0x2222ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80100; e.prefix_length = 24;
uint64_t h[1] = {0};
route_insert(t, &e, &c1, 0x3333ULL, 0, h, 1);
route_insert(t, &e, &c2, 0x3333ULL, 0, h, 1);
struct ROUTE_ENTRY *r = route_lookup(t, 0xC0A80164);
ASSERT_PTR(r, "found");
ASSERT_EQ(r->conn_list->preferred_conn, 0, "uses preferred");
route_table_destroy(t);
PASS();
}
static void test_hop_limit(void) {
TEST("hop_count limit (max accepted)");
struct ROUTE_TABLE *t = route_table_create();
struct ETCP_CONN conn = { .peer_node_id = 0x1111ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0x01010100; e.prefix_length = 24;
uint64_t hops[MAX_HOPS] = {0};
ASSERT(route_insert(t, &e, &conn, 0xDEADULL, 0, hops, MAX_HOPS), "MAX_HOPS accepted");
route_table_destroy(t); route_table_destroy(t);
PASS(); PASS();
} }
@ -253,16 +174,12 @@ static void test_hop_limit(void) {
static void test_destroy_refcount(void) { static void test_destroy_refcount(void) {
TEST("destroy with multiple paths (ref_count safety)"); TEST("destroy with multiple paths (ref_count safety)");
struct ROUTE_TABLE *t = route_table_create(); struct ROUTE_TABLE *t = route_table_create();
struct ETCP_CONN conn = { .peer_node_id = 0x1111ULL }; /* NODEINFO_Q memory managed externally, table only holds pointers */
struct ROUTE_ENTRY e1 = {0}; e1.network = 0x0A000000; e1.prefix_length = 8;
struct ROUTE_ENTRY e2 = {0}; e2.network = 0x17220000; e2.prefix_length = 16;
uint64_t h[1] = {0};
route_insert(t, &e1, &conn, 0x2222ULL, 0, h, 1);
route_insert(t, &e2, &conn, 0x2222ULL, 0, h, 1);
route_table_destroy(t); route_table_destroy(t);
PASS(); PASS();
} }
/* ====================== MAIN ====================== */ /* ====================== MAIN ====================== */
int main(void) { int main(void) {
debug_config_init(); debug_config_init();
@ -270,17 +187,9 @@ int main(void) {
debug_set_categories(DEBUG_CATEGORY_ROUTING); debug_set_categories(DEBUG_CATEGORY_ROUTING);
test_table_create_destroy(); test_table_create_destroy();
test_local_route(); test_nodeinfo_insert();
test_learned_single_path(); test_versioning();
test_multiple_paths_backup(); test_performance_1000_nodes();
test_reroute_on_preferred_loss();
test_withdraw_single_vs_backup();
test_remove_conn_full_cleanup();
test_loop_detection();
test_owner_conflict();
test_no_overlap();
test_lookup_preferred();
test_hop_limit();
test_destroy_refcount(); test_destroy_refcount();
printf("\n=== ROUTING TEST SUMMARY ===\n" printf("\n=== ROUTING TEST SUMMARY ===\n"

17
tools/etcpmon/etcpmon_gui.c

@ -420,6 +420,19 @@ static void CreateControls(struct etcpmon_app* app) {
WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER, WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER,
mx + 260, my - 2, 50, 18, hWnd, (HMENU)IDC_EDIT_RT_LEARNED, hInst, NULL); mx + 260, my - 2, 50, 18, hWnd, (HMENU)IDC_EDIT_RT_LEARNED, hInst, NULL);
my += 25;
CreateWindowExA(0, "STATIC", "SndList:",
WS_CHILD | WS_VISIBLE, mx, my, 55, 18, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditRtBgpSenders = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER,
mx + 60, my - 2, 45, 18, hWnd, (HMENU)IDC_EDIT_RT_BGP_SENDERS, hInst, NULL);
CreateWindowExA(0, "STATIC", "Nodes:",
WS_CHILD | WS_VISIBLE, mx + 130, my, 45, 18, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hEditRtBgpNodes = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER,
mx + 130+50, my - 2, 45, 18, hWnd, (HMENU)IDC_EDIT_RT_BGP_NODES, hInst, NULL);
/* Links list */ /* Links list */
y = 725; y = 725;
CreateWindowExA(0, "STATIC", "Links:", CreateWindowExA(0, "STATIC", "Links:",
@ -958,6 +971,8 @@ void etcpmon_gui_update_metrics(struct etcpmon_app* app,
UpdateEditIfChanged(hMain, IDC_EDIT_RT_COUNT, "%u", metrics->tun.rt_count); UpdateEditIfChanged(hMain, IDC_EDIT_RT_COUNT, "%u", metrics->tun.rt_count);
UpdateEditIfChanged(hMain, IDC_EDIT_RT_LOCAL, "%u", metrics->tun.rt_local); UpdateEditIfChanged(hMain, IDC_EDIT_RT_LOCAL, "%u", metrics->tun.rt_local);
UpdateEditIfChanged(hMain, IDC_EDIT_RT_LEARNED, "%u", metrics->tun.rt_learned); UpdateEditIfChanged(hMain, IDC_EDIT_RT_LEARNED, "%u", metrics->tun.rt_learned);
UpdateEditIfChanged(hMain, IDC_EDIT_RT_BGP_SENDERS, "%u", metrics->tun.rt_bgp_senders);
UpdateEditIfChanged(hMain, IDC_EDIT_RT_BGP_NODES, "%u", metrics->tun.rt_bgp_nodes);
/* Queue Metrics */ /* Queue Metrics */
UpdateEditIfChanged(hMain, IDC_EDIT_Q_IN_Q_BYTES, "%u", metrics->etcp.input_queue_bytes); UpdateEditIfChanged(hMain, IDC_EDIT_Q_IN_Q_BYTES, "%u", metrics->etcp.input_queue_bytes);
@ -1096,6 +1111,8 @@ void etcpmon_gui_clear_metrics(struct etcpmon_app* app) {
SetDlgItemTextA(app->hWndMain, IDC_EDIT_RT_COUNT, ""); SetDlgItemTextA(app->hWndMain, IDC_EDIT_RT_COUNT, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_RT_LOCAL, ""); SetDlgItemTextA(app->hWndMain, IDC_EDIT_RT_LOCAL, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_RT_LEARNED, ""); SetDlgItemTextA(app->hWndMain, IDC_EDIT_RT_LEARNED, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_RT_BGP_SENDERS, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_RT_BGP_NODES, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_Q_IN_Q_BYTES, ""); SetDlgItemTextA(app->hWndMain, IDC_EDIT_Q_IN_Q_BYTES, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_Q_IN_Q_PKTS, ""); SetDlgItemTextA(app->hWndMain, IDC_EDIT_Q_IN_Q_PKTS, "");

4
tools/etcpmon/etcpmon_gui.h

@ -69,6 +69,8 @@ extern "C" {
#define IDC_EDIT_RT_COUNT 312 #define IDC_EDIT_RT_COUNT 312
#define IDC_EDIT_RT_LOCAL 313 #define IDC_EDIT_RT_LOCAL 313
#define IDC_EDIT_RT_LEARNED 314 #define IDC_EDIT_RT_LEARNED 314
#define IDC_EDIT_RT_BGP_SENDERS 315
#define IDC_EDIT_RT_BGP_NODES 316
/* Link list control ID */ /* Link list control ID */
#define IDC_LIST_LINKS 400 #define IDC_LIST_LINKS 400
@ -215,6 +217,8 @@ struct etcpmon_app {
HWND hEditRtCount; HWND hEditRtCount;
HWND hEditRtLocal; HWND hEditRtLocal;
HWND hEditRtLearned; HWND hEditRtLearned;
HWND hEditRtBgpSenders;
HWND hEditRtBgpNodes;
/* Links list */ /* Links list */
HWND hListLinks; HWND hListLinks;

2
tools/etcpmon/etcpmon_protocol.h

@ -222,6 +222,8 @@ struct etcpmon_tun_metrics {
uint32_t rt_count; /* Total routes in table */ uint32_t rt_count; /* Total routes in table */
uint32_t rt_local; /* Local routes */ uint32_t rt_local; /* Local routes */
uint32_t rt_learned; /* Learned routes (BGP) */ uint32_t rt_learned; /* Learned routes (BGP) */
uint32_t rt_bgp_senders; /* BGP senders_list count (ROUTE_BGP) */
uint32_t rt_bgp_nodes; /* BGP nodes count (ROUTE_BGP) */
}; };
struct etcpmon_rsp_metrics { struct etcpmon_rsp_metrics {

23
utun.conf.sample

@ -1,21 +1,24 @@
[global] [global]
tun_ip=10.0.0.1 tun_ip=10.0.0.1 # IP адрес tun интерфейса. чтобы к нему могли обращаться другие узлу его надо вписать в секции [routing] my_subnet
mtu=1500 # MTU for all connections (0 = use default 1500) mtu=1500 # UDP MTU for all connections (default - 1500)
control_ip=127.0.0.1
control_port=12345
net_debug=0
# уникальный id и ключи вашего узла. если их нет они сгенерируются автоматически (random) при первом запуске и впишутся в конфиг
my_node_id= my_node_id=
my_private_key= my_private_key=
my_public_key= my_public_key=
# control socket для наблюдения и управления utun сервером
# например utun сервер стоит на роутере, и вы можете управлять и наблюдать за ним с рабочего компьютера
[control]
ip=127.0.0.1
port=12345
# control_allow=ip/mask (multiple allowed, default deny all)
control_allow=127.0.0.1/32
# control_allow=192.168.1.0/24
[routing] [routing]
route_subnet=10.0.0.0/24 route_subnet=10.0.0.0/16 # к каким подсетям utun вы хотите обращаться (добавит маршруты этих подсетей через utun)
route_subnet=10.23.0.0/16 my_subnet=10.0.0.0/24 # какие ваши подсети будут доступны другим узлам utun (в секции [firewall] можно тонко настроить разрешенные ip и порты)
#allowed_subnet=10.23.0.0/16
my_subnet=10.23.1.0/24
# секция server обязательна и у сервера и у клиента. это рабочий сокет. # секция server обязательна и у сервера и у клиента. это рабочий сокет.
# мои адреса и каналы # мои адреса и каналы

Loading…
Cancel
Save