Browse Source

linux routing + debug output

nodeinfo-routing-update
Evgeny 1 month ago
parent
commit
1aec884c2d
  1. 4
      lib/debug_config.c
  2. 12
      src/config_parser.c
  3. 2
      src/routing.c
  4. 119
      src/tun_route.c
  5. 9
      src/tun_route.h
  6. 39
      src/utun_instance.c
  7. BIN
      tests/bench_timeout_heap
  8. BIN
      tests/bench_uasync_timeouts

4
lib/debug_config.c

@ -75,6 +75,10 @@ static debug_category_t get_category_by_name(const char* name) {
{"tun", DEBUG_CATEGORY_TUN},
{"routing", DEBUG_CATEGORY_ROUTING},
{"timers", DEBUG_CATEGORY_TIMERS},
{"normalizer", DEBUG_CATEGORY_NORMALIZER},
{"bgp", DEBUG_CATEGORY_BGP},
{"socket", DEBUG_CATEGORY_SOCKET},
{"control", DEBUG_CATEGORY_CONTROL},
{"dump", DEBUG_CATEGORY_DUMP},
{"all", DEBUG_CATEGORY_ALL},
{NULL, 0}

12
src/config_parser.c

@ -532,6 +532,18 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
add_route_entry(&cfg->my_subnets, value);
}
break;
case SECTION_DEBUG:
// Format: category=level (e.g., etcp=debug, crypto=info)
if (cfg->global.debug_levels.count < 16) {
strncpy(cfg->global.debug_levels.category[cfg->global.debug_levels.count],
key, 15);
cfg->global.debug_levels.category[cfg->global.debug_levels.count][15] = '\0';
strncpy(cfg->global.debug_levels.level[cfg->global.debug_levels.count],
value, 15);
cfg->global.debug_levels.level[cfg->global.debug_levels.count][15] = '\0';
cfg->global.debug_levels.count++;
}
break;
default:
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Key outside section: %s", filename, line_num, key);
break;

2
src/routing.c

@ -177,7 +177,7 @@ static void routing_pkt_from_tun_cb(struct ll_queue* q, void* arg) {
struct ROUTE_ARRAY* routes = route_table_lookup(instance->rt, dst_ip);
if (!routes || routes->routes == 0) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "No route to %s, dropping packet", ip_to_str(&addr, AF_INET).str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "No route to %s, dropping packet", ip_to_str(&addr, AF_INET).str);
free(pkt->ll.dgram);
memory_pool_free(instance->tun->pool, pkt);
} else {

119
src/tun_route.c

@ -46,18 +46,37 @@ struct nl_req {
};
static int netlink_route(int ifindex, uint32_t network, uint8_t prefix_len, int cmd, int flags) {
int fd = socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE);
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create netlink socket: %s", strerror(errno));
return -1;
}
// Bind to receive responses
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = 0;
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to bind netlink socket: %s", strerror(errno));
close(fd);
return -1;
}
// Prepare destination address (kernel)
struct sockaddr_nl dest;
memset(&dest, 0, sizeof(dest));
dest.nl_family = AF_NETLINK;
dest.nl_pid = 0; // kernel
dest.nl_groups = 0;
struct nl_req req;
memset(&req, 0, sizeof(req));
req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.nl.nlmsg_type = cmd; // RTM_NEWROUTE or RTM_DELROUTE
req.nl.nlmsg_flags = flags | NLM_F_REQUEST;
req.nl.nlmsg_flags = flags | NLM_F_REQUEST | NLM_F_ACK; // Request ACK from kernel
req.nl.nlmsg_seq = 1;
req.nl.nlmsg_pid = getpid();
@ -82,12 +101,15 @@ static int netlink_route(int ifindex, uint32_t network, uint8_t prefix_len, int
memcpy(RTA_DATA(rta), &ifindex, 4);
req.nl.nlmsg_len = NLMSG_ALIGN(req.nl.nlmsg_len) + RTA_ALIGN(rta->rta_len);
if (send(fd, &req, req.nl.nlmsg_len, 0) < 0) {
if (sendto(fd, &req, req.nl.nlmsg_len, 0, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to send netlink message: %s", strerror(errno));
close(fd);
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Netlink message sent: seq=%u pid=%u len=%d",
req.nl.nlmsg_seq, req.nl.nlmsg_pid, req.nl.nlmsg_len);
// Wait for response with timeout using poll
struct pollfd pfd = { .fd = fd, .events = POLLIN };
int poll_ret = poll(&pfd, 1, 1000); // 1 second timeout
@ -96,11 +118,13 @@ static int netlink_route(int ifindex, uint32_t network, uint8_t prefix_len, int
close(fd);
return -1;
} else if (poll_ret == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Timeout waiting for netlink response");
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Timeout waiting for netlink response (sent %d bytes)", req.nl.nlmsg_len);
close(fd);
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Poll returned: revents=%d", pfd.revents);
// Receive response
char reply[4096];
ssize_t len = recv(fd, reply, sizeof(reply), 0);
@ -110,7 +134,12 @@ static int netlink_route(int ifindex, uint32_t network, uint8_t prefix_len, int
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Received netlink response: len=%d", (int)len);
struct nlmsghdr *nl_hdr = (struct nlmsghdr *)reply;
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Netlink msg: type=%d seq=%d pid=%d",
nl_hdr->nlmsg_type, nl_hdr->nlmsg_seq, nl_hdr->nlmsg_pid);
if (nl_hdr->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nl_hdr);
if (err->error < 0) {
@ -138,44 +167,42 @@ static int ip_route_cmd(const char *ifname, uint32_t network, uint8_t prefix_len
return (ret == 0) ? 0 : -1;
}
int tun_route_add(uint32_t ifindex, uint32_t network, uint8_t prefix_len) {
if (ifindex == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Interface index is 0");
int tun_route_add(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
if (ifindex == 0 && (!ifname || !ifname[0])) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Interface index and name are both invalid");
return -1;
}
network = htonl(network);
// Try netlink first
if (netlink_route(ifindex, network, prefix_len, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL) == 0) {
if (ifindex != 0 && netlink_route(ifindex, network, prefix_len, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL) == 0) {
return 0;
}
// Fallback to ip route command
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Netlink failed, trying ip route command for ifindex %u", ifindex);
char ifname[16];
snprintf(ifname, sizeof(ifname), "%u", ifindex);
return ip_route_cmd(ifname, ntohl(network), prefix_len, "add");
const char *dev = ifname ? ifname : "";
return ip_route_cmd(dev, ntohl(network), prefix_len, "add");
}
int tun_route_del(uint32_t ifindex, uint32_t network, uint8_t prefix_len) {
if (ifindex == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_del: ifindex is 0");
int tun_route_del(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
if (ifindex == 0 && (!ifname || !ifname[0])) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_del: ifindex and ifname are both invalid");
return -1;
}
network = htonl(network);
// Try netlink first
if (netlink_route(ifindex, network, prefix_len, RTM_DELROUTE, 0) == 0) {
if (ifindex != 0 && netlink_route(ifindex, network, prefix_len, RTM_DELROUTE, 0) == 0) {
return 0;
}
// Fallback to ip route command
char ifname[16];
snprintf(ifname, sizeof(ifname), "%u", ifindex);
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Netlink failed, trying ip route command for ifindex %u", ifindex);
return ip_route_cmd(ifname, ntohl(network), prefix_len, "del");
const char *dev = ifname ? ifname : "";
return ip_route_cmd(dev, ntohl(network), prefix_len, "del");
}
int tun_route_flush(const char *ifname) {
@ -196,11 +223,30 @@ int tun_route_flush(const char *ifname) {
return (ret == 0) ? 0 : -1;
}
int tun_route_del_all(uint32_t ifindex, const char *ifname, struct CFG_ROUTE_ENTRY *routes) {
int count = 0;
struct CFG_ROUTE_ENTRY *entry = routes;
while (entry) {
uint32_t network = ntohl(entry->ip.addr.v4.s_addr);
if (entry->ip.family == AF_INET) {
if (tun_route_del(ifindex, ifname, network, entry->netmask) == 0) {
count++;
}
}
entry = entry->next;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Deleted %d routes via ifindex=%lu", count, (unsigned long)ifindex);
return count;
}
#elif defined(_WIN32)
// Windows IP Helper API implementation
int tun_route_add(uint32_t ifindex, uint32_t network, uint8_t prefix_len) {
int tun_route_add(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
(void)ifname; // Not used on Windows
if (ifindex == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_add: ifindex is 0");
return -1;
@ -248,7 +294,8 @@ int tun_route_add(uint32_t ifindex, uint32_t network, uint8_t prefix_len) {
return 0;
}
int tun_route_del(uint32_t ifindex, uint32_t network, uint8_t prefix_len) {
int tun_route_del(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
(void)ifname; // Not used on Windows
if (ifindex == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_del: ifindex is 0");
return -1;
@ -318,14 +365,14 @@ int tun_route_flush(const char *ifname) {
return 0;
}
int tun_route_del_all(uint32_t ifindex, struct CFG_ROUTE_ENTRY *routes) {
int tun_route_del_all(uint32_t ifindex, const char *ifname, struct CFG_ROUTE_ENTRY *routes) {
int count = 0;
struct CFG_ROUTE_ENTRY *entry = routes;
while (entry) {
uint32_t network = ntohl(entry->ip.addr.v4.s_addr);
if (entry->ip.family == AF_INET) {
if (tun_route_del(ifindex, network, entry->netmask) == 0) {
if (tun_route_del(ifindex, ifname, network, entry->netmask) == 0) {
count++;
}
}
@ -415,40 +462,38 @@ static int route_cmd(const char *ifname, uint32_t network, uint8_t prefix_len, c
return (ret == 0) ? 0 : -1;
}
int tun_route_add(uint32_t ifindex, uint32_t network, uint8_t prefix_len) {
if (ifindex == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Interface index is 0");
int tun_route_add(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
if (ifindex == 0 && (!ifname || !ifname[0])) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Interface index and name are both invalid");
return -1;
}
// Try routing socket first
if (routing_socket_cmd(ifindex, network, prefix_len, RTM_ADD) == 0) {
if (ifindex != 0 && routing_socket_cmd(ifindex, network, prefix_len, RTM_ADD) == 0) {
return 0;
}
// Fallback to route command
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Routing socket failed, trying route command for ifindex %u", ifindex);
char ifname[16];
snprintf(ifname, sizeof(ifname), "%u", ifindex);
return route_cmd(ifname, network, prefix_len, "add");
const char *dev = ifname ? ifname : "";
return route_cmd(dev, network, prefix_len, "add");
}
int tun_route_del(uint32_t ifindex, uint32_t network, uint8_t prefix_len) {
if (ifindex == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_del: ifindex is 0");
int tun_route_del(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
if (ifindex == 0 && (!ifname || !ifname[0])) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_route_del: ifindex and ifname are both invalid");
return -1;
}
// Try routing socket first
if (routing_socket_cmd(ifindex, network, prefix_len, RTM_DELETE) == 0) {
if (ifindex != 0 && routing_socket_cmd(ifindex, network, prefix_len, RTM_DELETE) == 0) {
return 0;
}
// Fallback to route command
char ifname[16];
snprintf(ifname, sizeof(ifname), "%u", ifindex);
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Routing socket failed, trying route command for ifindex %u", ifindex);
return route_cmd(ifname, network, prefix_len, "delete");
const char *dev = ifname ? ifname : "";
return route_cmd(dev, network, prefix_len, "delete");
}
int tun_route_flush(const char *ifname) {
@ -474,7 +519,7 @@ int tun_route_add_all(uint32_t ifindex, const char *ifname, struct CFG_ROUTE_ENT
while (entry) {
uint32_t network = ntohl(entry->ip.addr.v4.s_addr);
if (entry->ip.family == AF_INET) {
if (tun_route_add(ifindex, network, entry->netmask) == 0) {
if (tun_route_add(ifindex, ifname, network, entry->netmask) == 0) {
struct in_addr addr;
addr.s_addr = entry->ip.addr.v4.s_addr;
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Added route %s/%d via %s", ip_to_str(&addr, AF_INET).str, entry->netmask, ifname);

9
src/tun_route.h

@ -14,28 +14,31 @@ struct CFG_ROUTE_ENTRY;
/**
* @brief Add system route for a subnet via TUN interface
* @param ifindex Interface index
* @param ifname Interface name (e.g., "tun0")
* @param network Network address in host byte order
* @param prefix_len Prefix length (1-32)
* @return 0 on success, -1 on error
*/
int tun_route_add(uint32_t ifindex, uint32_t network, uint8_t prefix_len);
int tun_route_add(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len);
/**
* @brief Delete system route for a subnet via TUN interface
* @param ifindex Interface index
* @param ifname Interface name (e.g., "tun0")
* @param network Network address in host byte order
* @param prefix_len Prefix length (1-32)
* @return 0 on success, -1 on error
*/
int tun_route_del(uint32_t ifindex, uint32_t network, uint8_t prefix_len);
int tun_route_del(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len);
/**
* @brief Delete all system routes from config entries
* @param ifindex Interface index
* @param ifname Interface name (e.g., "tun0")
* @param routes Linked list of route entries to delete
* @return Number of routes deleted
*/
int tun_route_del_all(uint32_t ifindex, struct CFG_ROUTE_ENTRY *routes);
int tun_route_del_all(uint32_t ifindex, const char *ifname, struct CFG_ROUTE_ENTRY *routes);
/**
* @brief Delete all system routes via specified interface

39
src/utun_instance.c

@ -159,6 +159,43 @@ struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char *config
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Applied debug_level from config: %s", config->global.debug_level);
}
// Apply per-category debug levels from [debug] section
for (int i = 0; i < config->global.debug_levels.count; i++) {
const char* cat_name = config->global.debug_levels.category[i];
const char* lvl_str = config->global.debug_levels.level[i];
debug_category_t cat = 0;
// Map category name to bitmask
if (strcmp(cat_name, "uasync") == 0) cat = DEBUG_CATEGORY_UASYNC;
else if (strcmp(cat_name, "ll_queue") == 0) cat = DEBUG_CATEGORY_LL_QUEUE;
else if (strcmp(cat_name, "connection") == 0) cat = DEBUG_CATEGORY_CONNECTION;
else if (strcmp(cat_name, "etcp") == 0) cat = DEBUG_CATEGORY_ETCP;
else if (strcmp(cat_name, "crypto") == 0) cat = DEBUG_CATEGORY_CRYPTO;
else if (strcmp(cat_name, "memory") == 0) cat = DEBUG_CATEGORY_MEMORY;
else if (strcmp(cat_name, "timing") == 0) cat = DEBUG_CATEGORY_TIMING;
else if (strcmp(cat_name, "config") == 0) cat = DEBUG_CATEGORY_CONFIG;
else if (strcmp(cat_name, "tun") == 0) cat = DEBUG_CATEGORY_TUN;
else if (strcmp(cat_name, "routing") == 0) cat = DEBUG_CATEGORY_ROUTING;
else if (strcmp(cat_name, "timers") == 0) cat = DEBUG_CATEGORY_TIMERS;
else if (strcmp(cat_name, "normalizer") == 0) cat = DEBUG_CATEGORY_NORMALIZER;
else if (strcmp(cat_name, "bgp") == 0) cat = DEBUG_CATEGORY_BGP;
else if (strcmp(cat_name, "socket") == 0) cat = DEBUG_CATEGORY_SOCKET;
else if (strcmp(cat_name, "control") == 0) cat = DEBUG_CATEGORY_CONTROL;
else if (strcmp(cat_name, "dump") == 0) cat = DEBUG_CATEGORY_DUMP;
if (cat) {
debug_level_t lvl = DEBUG_LEVEL_NONE;
if (strcmp(lvl_str, "error") == 0) lvl = DEBUG_LEVEL_ERROR;
else if (strcmp(lvl_str, "warn") == 0) lvl = DEBUG_LEVEL_WARN;
else if (strcmp(lvl_str, "info") == 0) lvl = DEBUG_LEVEL_INFO;
else if (strcmp(lvl_str, "debug") == 0) lvl = DEBUG_LEVEL_DEBUG;
else if (strcmp(lvl_str, "trace") == 0) lvl = DEBUG_LEVEL_TRACE;
debug_set_category_level(cat, lvl);
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Applied per-category debug: %s=%s", cat_name, lvl_str);
}
}
// Allocate instance
struct UTUN_INSTANCE *instance = calloc(1, sizeof(struct UTUN_INSTANCE));
if (!instance) {
@ -252,7 +289,7 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
// Delete system routes added at startup
if (instance->tun->ifindex && instance->route_subnets) {
int deleted = tun_route_del_all(instance->tun->ifindex, instance->route_subnets);
int deleted = tun_route_del_all(instance->tun->ifindex, instance->tun->ifname, instance->route_subnets);
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Deleted %d system routes for TUN interface", deleted);
}

BIN
tests/bench_timeout_heap

Binary file not shown.

BIN
tests/bench_uasync_timeouts

Binary file not shown.
Loading…
Cancel
Save