From bbc901d14a65bbb75a6d90bd505bac1624bc797c Mon Sep 17 00:00:00 2001 From: Koda YeenBean Date: Tue, 14 Jul 2026 18:30:24 +0000 Subject: [PATCH] implement auto gateway Ip auto detection --- README.md | 6 +++- files/openuf.conf | 5 +-- src/config.c | 28 +++++++++++---- src/config.h | 5 +-- src/main.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 119 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 335edef..edc9d7e 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ vi /etc/openuf/openuf.conf Example Configuration ```ini -controller_ip = 192.168.1.1 # UniFi controller IP +controller_ip = # Empty: use IPv4 default gateway lan_if = br-lan # Default Lan interface Name ufmodel = u6-inwall # emulated model inform_interval = 10 # inform interval @@ -96,6 +96,10 @@ protocol_debug_level = 0 # 0=off, 1=safe response details, ``` +Set `controller_ip` to a controller IPv4 address or hostname to override +autodetection. The automatic mode uses the IPv4 default-route gateway for +initial adoption; after adoption, the controller-provided inform URL is kept. + Daemon settings live in `/etc/openuf/openuf.conf`. Adoption and controller state are persisted separately in `/etc/openuf/state.json`; do not copy that file between devices because it contains the device authentication key. diff --git a/files/openuf.conf b/files/openuf.conf index 3665479..8930262 100644 --- a/files/openuf.conf +++ b/files/openuf.conf @@ -1,6 +1,7 @@ # openuf — configuration # -# controller_ip: UniFi controller IP address or hostname +# controller_ip: UniFi controller IP address or hostname; when empty, +# autodetect from the IPv4 default-route gateway # lan_if: primary LAN interface (used for the AP MAC and IP) # ufmodel: emulated model (u6-inwall recommended) # inform_interval: seconds between inform requests (minimum 5) @@ -10,7 +11,7 @@ # protocol_debug_level: 0=off, 1=safe response structure, # 2=full decrypted response; may expose credentials -controller_ip = 10.10.10.1 +controller_ip = lan_if = br-lan ufmodel = uapg2-ac-lr inform_interval = 10 diff --git a/src/config.c b/src/config.c index 42c0ab9..0a7d9b3 100644 --- a/src/config.c +++ b/src/config.c @@ -1,11 +1,13 @@ #include #include #include +#include #include "config.h" void config_load(openuf_config_t *cfg) { /* Defaults */ + memset(cfg, 0, sizeof(*cfg)); strncpy(cfg->controller_ip, DEFAULT_CONTROLLER_IP, sizeof(cfg->controller_ip) - 1); strncpy(cfg->lan_if, DEFAULT_LAN_IF, sizeof(cfg->lan_if) - 1); strncpy(cfg->ufmodel, DEFAULT_UFMODEL, sizeof(cfg->ufmodel) - 1); @@ -21,15 +23,29 @@ void config_load(openuf_config_t *cfg) char line[256]; while (fgets(line, sizeof(line), f)) { - /* Strip newline */ + char *key, *val, *eq, *end; + line[strcspn(line, "\r\n")] = '\0'; - /* Skip comments / empty */ - if (line[0] == '#' || line[0] == '\0') continue; + key = line; + while (isspace((unsigned char)*key)) key++; + if (*key == '#' || *key == '\0') continue; - char key[64] = {0}, val[192] = {0}; - if (sscanf(line, " %63[^= ] = %191s", key, val) != 2) continue; + eq = strchr(key, '='); + if (!eq) continue; + *eq = '\0'; + end = eq; + while (end > key && isspace((unsigned char)end[-1])) *--end = '\0'; - if (!strcmp(key, "controller_ip")) strncpy(cfg->controller_ip, val, sizeof(cfg->controller_ip) - 1); + val = eq + 1; + while (isspace((unsigned char)*val)) val++; + end = val; + while (*end && !isspace((unsigned char)*end) && *end != '#') end++; + *end = '\0'; + + if (!strcmp(key, "controller_ip")) { + strncpy(cfg->controller_ip, val, sizeof(cfg->controller_ip) - 1); + cfg->controller_ip[sizeof(cfg->controller_ip) - 1] = '\0'; + } else if (!strcmp(key, "lan_if")) strncpy(cfg->lan_if, val, sizeof(cfg->lan_if) - 1); else if (!strcmp(key, "ufmodel")) strncpy(cfg->ufmodel, val, sizeof(cfg->ufmodel) - 1); else if (!strcmp(key, "inform_interval")) cfg->inform_interval = atoi(val); diff --git a/src/config.h b/src/config.h index f474fd4..6824b8d 100644 --- a/src/config.h +++ b/src/config.h @@ -9,7 +9,7 @@ #ifndef ENABLE_LOGGING #define ENABLE_LOGGING 1 #endif -#define DEFAULT_CONTROLLER_IP "10.10.10.1" +#define DEFAULT_CONTROLLER_IP "" #define DEFAULT_LAN_IF "br-lan" #define DEFAULT_UFMODEL "uapg2-ac-lr" #define DEFAULT_INFORM_INTERVAL 10 @@ -39,7 +39,8 @@ typedef struct { } openuf_config_t; /* Parse /etc/openuf/openuf.conf (simple key=value). - * Fills *cfg with defaults first, then overrides from file. */ + * Fills *cfg with defaults first, then overrides from file. + * An empty controller_ip selects default-gateway autodetection. */ void config_load(openuf_config_t *cfg); #endif /* OPENUF_CONFIG_H */ diff --git a/src/main.c b/src/main.c index afaa020..0089cb0 100644 --- a/src/main.c +++ b/src/main.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,54 @@ static int get_ip(const char *iface, char *out, size_t sz) return ret; } +/* Find the IPv4 next hop used by the kernel's default route. Prefer the + * configured LAN interface when more than one default route exists. */ +static int get_default_gateway(const char *preferred_iface, + char *out, size_t sz) +{ + FILE *f = fopen("/proc/net/route", "r"); + if (!f) return -1; + + char line[256]; + char iface[IFNAMSIZ]; + unsigned long destination, gateway; + unsigned int flags, metric; + unsigned int best_metric = ~0U; + unsigned long best_gateway = 0; + int best_preferred = 0; + + /* Skip the column headings. */ + if (!fgets(line, sizeof(line), f)) { + fclose(f); + return -1; + } + + while (fgets(line, sizeof(line), f)) { + if (sscanf(line, "%15s %lx %lx %x %*u %*u %u %*lx", + iface, &destination, &gateway, &flags, + &metric) != 5) + continue; + if (destination != 0 || gateway == 0 || + !(flags & RTF_UP) || !(flags & RTF_GATEWAY)) + continue; + + int preferred = preferred_iface && preferred_iface[0] && + !strcmp(iface, preferred_iface); + if (!best_gateway || preferred > best_preferred || + (preferred == best_preferred && metric < best_metric)) { + best_gateway = gateway; + best_metric = metric; + best_preferred = preferred; + } + } + fclose(f); + + if (!best_gateway) return -1; + + struct in_addr addr = { .s_addr = (in_addr_t)best_gateway }; + return inet_ntop(AF_INET, &addr, out, sz) ? 0 : -1; +} + int main(int argc, char *argv[]) { for (int i = 1; i < argc-1; i++) @@ -105,9 +154,31 @@ int main(int argc, char *argv[]) LOG("Initial device state: adopted=%d, authkey=%.8s...", state.adopted, state.authkey[0] ? state.authkey : "DEFAULT"); - if (!state.adopted || !state.inform_url[0]) + /* + * A configured controller is an explicit override, including after + * adoption. With an empty setting, discover the initial controller from + * the default-route gateway and then preserve controller-issued state. + */ + if (cfg.controller_ip[0]) { snprintf(state.inform_url, sizeof(state.inform_url), "http://%s:%d%s", cfg.controller_ip, INFORM_PORT, INFORM_PATH); + LOG("Using configured controller override: %s", cfg.controller_ip); + } else if (!state.adopted || !state.inform_url[0]) { + char controller_ip[INET_ADDRSTRLEN] = {0}; + if (get_default_gateway(cfg.lan_if, controller_ip, + sizeof(controller_ip)) == 0) { + snprintf(state.inform_url, sizeof(state.inform_url), + "http://%s:%d%s", controller_ip, + INFORM_PORT, INFORM_PATH); + LOG("Autodetected controller from default gateway: %s", + controller_ip); + } else { + state.inform_url[0] = '\0'; + fprintf(stderr, + "[openuf] No controller configured and no IPv4 default " + "gateway found\n"); + } + } state_save(&state); printf("[openuf] Starting model=%-8s MAC=%s IP=%s\n", @@ -180,6 +251,20 @@ int main(int argc, char *argv[]) last_inform = now; LOG("Sending inform"); + /* The default route may appear after the service starts. */ + if (!cfg.controller_ip[0] && !state.inform_url[0]) { + char controller_ip[INET_ADDRSTRLEN] = {0}; + if (get_default_gateway(cfg.lan_if, controller_ip, + sizeof(controller_ip)) == 0) { + snprintf(state.inform_url, sizeof(state.inform_url), + "http://%s:%d%s", controller_ip, + INFORM_PORT, INFORM_PATH); + LOG("Autodetected controller after startup: %s", + controller_ip); + state_save(&state); + } + } + /* Update IP each cycle */ char new_ip[64] = {0}; if (get_ip(cfg.lan_if, new_ip, sizeof(new_ip)) == 0 || -- 2.54.0