// 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 #include #include #include #include #include #include #include #include #include /* Архитектура: 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; }