1
This commit is contained in:
1 parent
feb8024506
commit
d733bcbf4d
4 files changed
+146
-36
No files matched your search
+83
-24
@@ -30,7 +30,8 @@ typedef enum {
|
||||
SECTION_CLIENT,
|
||||
SECTION_ROUTING,
|
||||
SECTION_DEBUG,
|
||||
SECTION_FIREWALL
|
||||
SECTION_FIREWALL,
|
||||
SECTION_CONTROL
|
||||
} section_type_t;
|
||||
|
||||
static char* trim(char *str) {
|
||||
@@ -258,6 +259,51 @@ static int parse_firewall_rule(const char *rule_str, struct global_config *globa
|
||||
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) {
|
||||
struct CFG_SERVER *srv = servers;
|
||||
while (srv) {
|
||||
@@ -303,29 +349,6 @@ static int parse_global(const char *key, const char *value, struct global_config
|
||||
global->keepalive_interval = atoi(value);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (strcmp(key, "addr") == 0) {
|
||||
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, "debug") == 0) return SECTION_DEBUG;
|
||||
if (strcasecmp(section, "firewall") == 0) return SECTION_FIREWALL;
|
||||
if (strcasecmp(section, "control") == 0) return SECTION_CONTROL;
|
||||
|
||||
char *colon = strchr(section, ':');
|
||||
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_rule_count = 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;
|
||||
struct CFG_SERVER *cur_server = NULL;
|
||||
@@ -583,6 +633,11 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
}
|
||||
}
|
||||
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:
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Key outside section: %s", filename, line_num, key);
|
||||
break;
|
||||
@@ -657,6 +712,8 @@ void free_config(struct utun_config *config) {
|
||||
|
||||
// Free 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);
|
||||
}
|
||||
@@ -741,6 +798,8 @@ void print_config(const struct utun_config *cfg) {
|
||||
} else {
|
||||
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) {
|
||||
|
||||
+10
-2
@@ -66,6 +66,11 @@ struct CFG_FIREWALL_RULE {
|
||||
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 {
|
||||
char name[16]; // Instance name
|
||||
char my_private_key_hex[MAX_KEY_LEN];
|
||||
@@ -76,8 +81,7 @@ struct global_config {
|
||||
int mtu;
|
||||
struct sockaddr_storage control_sock;
|
||||
char control_ip[MAX_ADDR_LEN]; // Control server IP address
|
||||
int net_debug;
|
||||
|
||||
|
||||
// Debug and logging configuration
|
||||
char log_file[256]; // Path to log file (empty = stdout)
|
||||
char debug_level[16]; // debug level: error, warn, info, debug, trace
|
||||
@@ -101,6 +105,10 @@ struct global_config {
|
||||
struct CFG_FIREWALL_RULE *firewall_rules;
|
||||
int firewall_rule_count;
|
||||
int firewall_bypass_all;
|
||||
|
||||
// Control server configuration ([control] section)
|
||||
struct CFG_CONTROL_ALLOW *control_allows;
|
||||
int control_allow_count;
|
||||
};
|
||||
|
||||
struct utun_config {
|
||||
|
||||
@@ -270,6 +270,24 @@ void control_server_shutdown(struct control_server* server) {
|
||||
* 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) {
|
||||
struct control_server* server = (struct control_server*)arg;
|
||||
|
||||
@@ -318,6 +336,28 @@ static void accept_callback(socket_t fd, void* arg) {
|
||||
fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
#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...");
|
||||
|
||||
/* Check max clients */
|
||||
|
||||
Reference in new issue
Block a user