- Fixed incorrect init_connections() call in test_etcp_simple_traffic.c (was calling server_instance instead of client_instance) - Fixed double free in timeout_heap_pop() when handling deleted elements - Enhanced NULL pointer safety in uasync_print_resources() by removing complex heap manipulation - Added debug logging to timeout_heap_pop() for better error tracking Test results: test_etcp_simple_traffic now passes without double free errors
322 lines
8.2 KiB
C
Executable File
322 lines
8.2 KiB
C
Executable File
// utun.c - Main application for utun VPN tunnel
|
|
#define _DEFAULT_SOURCE
|
|
#define _POSIX_C_SOURCE 200809L
|
|
#include "config_parser.h"
|
|
#include "etcp_connections.h"
|
|
#include "tun_if.h"
|
|
#include "secure_channel.h"
|
|
#include "routing.h"
|
|
#include "utun_instance.h"
|
|
#include "u_async.h"
|
|
#include "debug_config.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
|
|
#include <getopt.h>
|
|
#include <arpa/inet.h>
|
|
/*
|
|
|
|
Архитектура:
|
|
main -> init utun instnaces -> mainloop()
|
|
|
|
utun instances: можно stop() - он ывзывает закрытие всех etcp (etcp_close) которые вызовут закрытие подключений), вызывает закрытие всех etcp сокетов
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Global wakeup pipe write fd for signal handler
|
|
static int g_wakeup_pipe_write_fd = -1;
|
|
|
|
#define DEFAULT_CONFIG "utun.conf"
|
|
#define DEFAULT_PIDFILE "/var/run/utun.pid"
|
|
|
|
// Command line arguments
|
|
typedef struct {
|
|
char *config_file;
|
|
char *pid_file;
|
|
char *log_file;
|
|
char *debug_config;
|
|
int foreground;
|
|
int help;
|
|
} cmd_args_t;
|
|
|
|
// Global state
|
|
|
|
|
|
|
|
|
|
|
|
// Parse subnet string
|
|
static int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_length) {
|
|
if (!subnet_str || !network || !prefix_length) return -1;
|
|
char ip[64];
|
|
int prefix;
|
|
if (sscanf(subnet_str, "%[^/]/%d", ip, &prefix) != 2) return -1;
|
|
if (prefix < 0 || prefix > 32) return -1;
|
|
struct in_addr addr;
|
|
if (inet_pton(AF_INET, ip, &addr) != 1) return -1;
|
|
*network = addr.s_addr;
|
|
*prefix_length = (uint8_t)prefix;
|
|
return 0;
|
|
}
|
|
|
|
// Extract destination IPv4 address from packet
|
|
static uint32_t get_dest_ip(const uint8_t *packet, size_t len) {
|
|
if (len < 20) return 0; // Minimum IPv4 header size
|
|
// Check IP version (first nibble)
|
|
uint8_t version = (packet[0] >> 4) & 0x0F;
|
|
if (version != 4) return 0;
|
|
// Destination IP is at offset 16
|
|
uint32_t dest_ip;
|
|
memcpy(&dest_ip, packet + 16, 4);
|
|
return dest_ip;
|
|
}
|
|
|
|
|
|
// Parse command line arguments
|
|
static void parse_args(int argc, char *argv[], cmd_args_t *args) {
|
|
memset(args, 0, sizeof(*args));
|
|
args->config_file = DEFAULT_CONFIG;
|
|
args->pid_file = DEFAULT_PIDFILE;
|
|
args->log_file = NULL;
|
|
args->debug_config = NULL;
|
|
args->foreground = 0;
|
|
args->help = 0;
|
|
|
|
static struct option long_options[] = {
|
|
{"config", required_argument, 0, 'c'},
|
|
{"pidfile", required_argument, 0, 'p'},
|
|
{"log", required_argument, 0, 'l'},
|
|
{"debug", required_argument, 0, 'd'},
|
|
{"foreground", no_argument, 0, 'f'},
|
|
{"help", no_argument, 0, 'h'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
int opt;
|
|
int option_index = 0;
|
|
|
|
while ((opt = getopt_long(argc, argv, "c:p:l:d:fh",
|
|
long_options, &option_index)) != -1) {
|
|
switch (opt) {
|
|
case 'c':
|
|
args->config_file = optarg;
|
|
break;
|
|
case 'p':
|
|
args->pid_file = optarg;
|
|
break;
|
|
case 'l':
|
|
args->log_file = optarg;
|
|
break;
|
|
case 'f':
|
|
args->foreground = 1;
|
|
break;
|
|
case 'd':
|
|
args->debug_config = optarg;
|
|
break;
|
|
case 'h':
|
|
args->help = 1;
|
|
break;
|
|
default:
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Unknown option: %c", opt);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Print usage
|
|
static void print_usage(const char *progname) {
|
|
printf("Usage: %s [OPTIONS]\n", progname);
|
|
printf("Secure VPN tunnel over UDP with TUN interface\n\n");
|
|
printf("Options:\n");
|
|
printf(" -c, --config FILE Configuration file (default: %s)\n", DEFAULT_CONFIG);
|
|
printf(" -p, --pidfile FILE PID file (default: %s)\n", DEFAULT_PIDFILE);
|
|
printf(" -l, --log FILE Log file (default: stderr)\n");
|
|
printf(" -d, --debug CONFIG Debug configuration (e.g., \"etcp:debug,routing:info\")\n");
|
|
printf(" -f, --foreground Run in foreground (don't daemonize)\n");
|
|
printf(" -h, --help Show this help\n");
|
|
printf("\nExamples:\n");
|
|
printf(" %s -c myconfig.conf\n", progname);
|
|
printf(" %s --config server.conf --pidfile /var/run/utun.pid\n", progname);
|
|
}
|
|
|
|
// Write PID file
|
|
static int write_pidfile(const char *pidfile) {
|
|
if (!pidfile) return -1;
|
|
|
|
FILE *fp = fopen(pidfile, "w");
|
|
if (!fp) {
|
|
perror("fopen pidfile");
|
|
return -1;
|
|
}
|
|
|
|
fprintf(fp, "%d\n", getpid());
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
|
|
// Remove PID file
|
|
static void remove_pidfile(const char *pidfile) {
|
|
if (pidfile) {
|
|
unlink(pidfile);
|
|
}
|
|
}
|
|
|
|
// Daemonize process
|
|
static int daemonize(void) {
|
|
pid_t pid = fork();
|
|
if (pid < 0) {
|
|
perror("fork");
|
|
return -1;
|
|
}
|
|
|
|
if (pid > 0) {
|
|
// Parent exits
|
|
exit(0);
|
|
}
|
|
|
|
// Child becomes session leader
|
|
if (setsid() < 0) {
|
|
perror("setsid");
|
|
return -1;
|
|
}
|
|
|
|
// Close standard file descriptors
|
|
close(STDIN_FILENO);
|
|
close(STDOUT_FILENO);
|
|
close(STDERR_FILENO);
|
|
|
|
// Redirect to /dev/null
|
|
int fd = open("/dev/null", O_RDWR);
|
|
if (fd >= 0) {
|
|
dup2(fd, STDIN_FILENO);
|
|
dup2(fd, STDOUT_FILENO);
|
|
dup2(fd, STDERR_FILENO);
|
|
if (fd > 2) close(fd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Open log file
|
|
static FILE* open_logfile(const char *logfile) {
|
|
if (!logfile) return stderr;
|
|
|
|
FILE *fp = fopen(logfile, "a");
|
|
if (!fp) {
|
|
perror("fopen logfile");
|
|
return stderr;
|
|
}
|
|
|
|
// Set line buffering
|
|
setlinebuf(fp);
|
|
return fp;
|
|
}
|
|
|
|
|
|
// Main function
|
|
|
|
static volatile sig_atomic_t g_running = 1;
|
|
struct UASYNC* main_ua = NULL;
|
|
|
|
static void signal_handler(int sig) {
|
|
(void)sig;
|
|
g_running = 0;
|
|
if (main_ua) {
|
|
uasync_wakeup(main_ua);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
cmd_args_t args;
|
|
parse_args(argc, argv, &args);
|
|
|
|
if (args.help) {
|
|
print_usage(argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
// Initialize global debug system early if configured from command line
|
|
if (args.debug_config) {
|
|
debug_config_init();
|
|
if (debug_parse_config(args.debug_config) < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid debug configuration: %s", args.debug_config);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Create uasync instance
|
|
struct UASYNC* ua = uasync_create();
|
|
main_ua=ua;
|
|
if (!ua) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to create uasync instance");
|
|
return 1;
|
|
}
|
|
|
|
// Create and initialize instance
|
|
struct UTUN_INSTANCE *instance = utun_instance_create(ua, args.config_file, args.log_file);
|
|
if (!instance) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to create UTUN instance");
|
|
return 1;
|
|
}
|
|
|
|
// Print config for debugging
|
|
if (args.foreground) {
|
|
print_config(instance->config);
|
|
}
|
|
|
|
// Initialize all components (TUN, routing, connections)
|
|
if (utun_instance_init(instance) < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to initialize instance");
|
|
utun_instance_destroy(instance);
|
|
return 1;
|
|
}
|
|
|
|
// Register sockets with uasync
|
|
if (utun_instance_register_sockets(instance) < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to register sockets");
|
|
utun_instance_destroy(instance);
|
|
return 1;
|
|
}
|
|
|
|
// Setup signal handlers
|
|
signal(SIGINT, signal_handler);
|
|
signal(SIGTERM, signal_handler);
|
|
signal(SIGHUP, signal_handler);
|
|
|
|
// Daemonize if not in foreground
|
|
if (!args.foreground) {
|
|
if (daemonize() < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to daemonize");
|
|
utun_instance_destroy(instance);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Write PID file
|
|
if (write_pidfile(args.pid_file) < 0) {
|
|
utun_instance_destroy(instance);
|
|
return 1;
|
|
}
|
|
|
|
while (instance->running) uasync_poll(ua, 100);
|
|
|
|
// Cleanup
|
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Shutdown");
|
|
|
|
utun_instance_unregister_sockets(instance);
|
|
utun_instance_destroy(instance);
|
|
remove_pidfile(args.pid_file);
|
|
|
|
return 0;
|
|
}
|