implement auto gateway Ip auto detection #25

Merged
Koda merged 1 commits from gatewayip into main 2026-07-14 19:32:02 +01:00
5 changed files with 119 additions and 12 deletions
Showing only changes of commit bbc901d14a - Show all commits
+5 -1
View File
@@ -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.
+3 -2
View File
@@ -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
+22 -6
View File
@@ -1,11 +1,13 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#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);
+3 -2
View File
@@ -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 */
+86 -1
View File
@@ -15,6 +15,7 @@
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/route.h>
#include <arpa/inet.h>
#include <netinet/in.h>
@@ -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 ||