Translated all files to ENG
This commit is contained in:
+4
-4
@@ -1,11 +1,11 @@
|
||||
# openuf — Makefile para compilar directamente en el dispositivo
|
||||
#
|
||||
# Requisitos:
|
||||
# opkg install gcc make \
|
||||
# Requirements:
|
||||
# apk add gcc make \
|
||||
# libmbedtls-dev libuci-dev libjson-c-dev \
|
||||
# lldpd (opcional, para leer vecinos LLDP)
|
||||
# lldpd (optional for LLDP neighbor discovery, UniFi tree view)
|
||||
#
|
||||
# Uso:
|
||||
# Use:
|
||||
# make -f Makefile.standalone
|
||||
# make -f Makefile.standalone install
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ The process is automatic:
|
||||
2. Click on "Adopt" → the controller sends a new key.
|
||||
3. The AP applies the key and becomes "Connected".
|
||||
4. The controller pushes the WiFi configuration (SSIDs, channels, etc.).
|
||||
5. Make sure your WiFi Name doesn't have characters like " - " cause of bad JSON! Grrr
|
||||
|
||||
To reset: `rm /etc/openuf/state.json && reboot`
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
TNBU - TNBU is the magic string/identifier at the start of the binary packet format used in this custom Inform protocol implementation.
|
||||
CCQ Client Connection Quality
|
||||
+19
-19
@@ -1,26 +1,26 @@
|
||||
/*
|
||||
* openuf - announce.c
|
||||
*
|
||||
* Implementa el protocolo de descubrimiento UDP de UniFi (puerto 10001).
|
||||
* Implements the UniFi UDP discovery protocol (port 10001).
|
||||
*
|
||||
* ── Destinos ─────────────────────────────────────────────────────────
|
||||
* El protocolo especifica que los paquetes de anuncio se envían a DOS destinos:
|
||||
* ── Discovery Targets ────────────────────────────────────────────────
|
||||
* The protocol specifies that discovery packets are sent to TWO targets:
|
||||
* 1. Broadcast: 255.255.255.255:10001
|
||||
* 2. Multicast: 233.89.188.1:10001 ← requerido para redes con multicast
|
||||
* 2. Multicast: 233.89.188.1:10001 ← required on multicast-enabled networks
|
||||
*
|
||||
* El controlador UniFi escucha en ambas direcciones.
|
||||
* Usar sólo broadcast puede fallar en redes donde el broadcast está filtrado.
|
||||
* The UniFi controller listens on both addresses.
|
||||
* Using broadcast alone may fail on networks where broadcast traffic is filtered.
|
||||
*
|
||||
* ── Formato del paquete ──────────────────────────────────────────────
|
||||
* Header: [0x02][0x06][0x00][total_payload_len] (4 bytes fijos)
|
||||
* ── Packet Format ────────────────────────────────────────────────────
|
||||
* Header: [0x02][0x06][0x00][total_payload_len] (fixed 4-byte header)
|
||||
* TLVs: [type:1][len_hi:1][len_lo:1][value:len]
|
||||
*
|
||||
* ── Modelo U6 InWall ─────────────────────────────────────────────────
|
||||
* Se emula este modelo específicamente porque:
|
||||
* - Tiene 5 puertos GbE (eth0-eth4): cubre la mayoría de routers OpenWrt
|
||||
* - Soporta WiFi 6 (802.11ax) en 2.4 GHz y 5 GHz
|
||||
* - Tiene PoE passthrough (útil para redes de campus)
|
||||
* - Es un modelo actual y bien soportado por el controlador
|
||||
* ── U6 In-Wall Model ─────────────────────────────────────────────────
|
||||
* This model is emulated because:
|
||||
* - It provides 5 Gigabit Ethernet ports (eth0-eth4), covering most OpenWrt routers
|
||||
* - Supports WiFi 6 (802.11ax) on both 2.4 GHz and 5 GHz bands
|
||||
* - Includes PoE passthrough, useful for campus and enterprise deployments
|
||||
* - Is a current model with excellent UniFi Controller compatibility
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -212,9 +212,9 @@ int announce_init(announce_ctx_t *ctx,
|
||||
};
|
||||
bind(ctx->sockfd, (struct sockaddr *)&bind_addr, sizeof(bind_addr));
|
||||
|
||||
/* ── Socket para multicast 233.89.188.1 ────────────────────── */
|
||||
/* El controlador UniFi también escucha en este grupo multicast.
|
||||
* Esto es necesario cuando broadcast está filtrado en la red. */
|
||||
/* ── Multicast Socket (233.89.188.1) ────────────────────────── */
|
||||
/* The UniFi Controller also listens on this multicast group,
|
||||
* allowing discovery even when broadcast traffic is filtered. */
|
||||
ctx->sockfd_mcast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (ctx->sockfd_mcast >= 0) {
|
||||
int ttl = 1; /* TTL=1: no cruzar router */
|
||||
@@ -241,7 +241,7 @@ int announce_send(announce_ctx_t *ctx)
|
||||
|
||||
int ret = 0;
|
||||
|
||||
/* ── Envío 1: Broadcast 255.255.255.255:10001 ─────────────── */
|
||||
/* ── Sending 1: Broadcast 255.255.255.255:10001 ─────────────── */
|
||||
struct sockaddr_in dest_bcast = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = htons(ANNOUNCE_PORT),
|
||||
@@ -253,7 +253,7 @@ int announce_send(announce_ctx_t *ctx)
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
/* ── Envío 2: Multicast 233.89.188.1:10001 ────────────────── */
|
||||
/* ── Sending 2: Multicast 233.89.188.1:10001 ────────────────── */
|
||||
if (ctx->sockfd_mcast >= 0) {
|
||||
struct sockaddr_in dest_mcast = {
|
||||
.sin_family = AF_INET,
|
||||
|
||||
+25
-22
@@ -1,11 +1,13 @@
|
||||
/*
|
||||
* openuf - clients.c
|
||||
*
|
||||
* Enumera clientes para el payload inform → sta_table.
|
||||
*
|
||||
* Enumerates clients for the inform payload → sta_table.
|
||||
*
|
||||
* ── Parseo de iw dev station dump ───────────────────────────────────
|
||||
* ── Parsing `iw dev station dump` Output ────────────────────────────
|
||||
*
|
||||
* The output is organized into one block per client:
|
||||
*
|
||||
* La salida tiene bloques por cliente:
|
||||
*
|
||||
* Station aa:bb:cc:dd:ee:ff (on wlan0)
|
||||
* inactive time: 120 ms
|
||||
@@ -18,16 +20,16 @@
|
||||
* rx bitrate: 108.0 MBit/s
|
||||
* connected time: 1800 seconds
|
||||
*
|
||||
* Detectamos el inicio de cada cliente con "Station XX:XX:..." y
|
||||
* rellenamos los campos hasta encontrar el siguiente cliente.
|
||||
* We detect the start of each client by looking for "Station XX:XX:..."
|
||||
* and populate its fields until the next client entry is encountered.
|
||||
*
|
||||
* ── ARP: /proc/net/arp ─────────────────────────────────────────────
|
||||
*
|
||||
* IP HW type Flags HW addr Mask Device
|
||||
* 192.168.1.x 0x1 0x2 aa:bb:cc:dd:ee:ff * br-lan
|
||||
*
|
||||
* Flags 0x2 = entrada completa (reachable).
|
||||
* Flags 0x0 = incompleta (no responde ARP), ignorar.
|
||||
* Flags 0x2 = complete entry (reachable).
|
||||
* Flags 0x0 = incomplete (no ARP responce), ignore.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
@@ -39,7 +41,7 @@
|
||||
|
||||
#include "clients.h"
|
||||
|
||||
/* ─── Normalizar MAC a minúsculas ─────────────────────────────────── */
|
||||
/* ─── Convert MAC address to lowercase ─────────────────────────────────── */
|
||||
static void mac_lower(const char *src, char *dst, size_t sz)
|
||||
{
|
||||
for (size_t i = 0; src[i] && i < sz-1; i++)
|
||||
@@ -114,7 +116,7 @@ int clients_mac_to_hostname(const char *mac, char *out, size_t sz)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ─── Parsear tasa de bits "144.4 MBit/s ..." → kbps ───────────── */
|
||||
/* ─── Parse bitrate "144.4 MBit/s ..." → kbps ───────────── */
|
||||
static long parse_rate_kbps(const char *s)
|
||||
{
|
||||
float r = 0;
|
||||
@@ -144,7 +146,7 @@ int clients_read_wifi(const char *wlan_iface,
|
||||
while (fgets(line, sizeof(line), p)) {
|
||||
line[strcspn(line, "\r\n")] = '\0';
|
||||
|
||||
/* ── Nueva estación ──────────────────────────────────────── */
|
||||
/* ── New station ──────────────────────────────────────── */
|
||||
char mac[32], on_iface[32];
|
||||
if (sscanf(line, "Station %31s (on %31[^)])", mac, on_iface) == 2) {
|
||||
if (count >= max_out) break;
|
||||
@@ -159,14 +161,14 @@ int clients_read_wifi(const char *wlan_iface,
|
||||
}
|
||||
if (!cur) continue;
|
||||
|
||||
/* ── Contadores ──────────────────────────────────────────── */
|
||||
/* ── Counters ──────────────────────────────────────────── */
|
||||
long long llv;
|
||||
if (sscanf(line, " rx bytes: %lld", &llv) == 1) { cur->rx_bytes = llv; continue; }
|
||||
if (sscanf(line, " tx bytes: %lld", &llv) == 1) { cur->tx_bytes = llv; continue; }
|
||||
if (sscanf(line, " rx packets: %lld", &llv) == 1) { cur->rx_packets = llv; continue; }
|
||||
if (sscanf(line, " tx packets: %lld", &llv) == 1) { cur->tx_packets = llv; continue; }
|
||||
|
||||
/* ── Señal ───────────────────────────────────────────────── */
|
||||
/* ── Signal ───────────────────────────────────────────────── */
|
||||
int sig;
|
||||
if (sscanf(line, " signal: %d", &sig) == 1) { cur->signal = sig; continue; }
|
||||
|
||||
@@ -179,7 +181,7 @@ int clients_read_wifi(const char *wlan_iface,
|
||||
cur->rx_rate = parse_rate_kbps(rest); continue;
|
||||
}
|
||||
|
||||
/* ── Tiempo conectado ────────────────────────────────────── */
|
||||
/* ── Connection time ────────────────────────────────────── */
|
||||
int upt;
|
||||
if (sscanf(line, " connected time: %d seconds", &upt) == 1) {
|
||||
cur->uptime = upt; continue;
|
||||
@@ -187,7 +189,7 @@ int clients_read_wifi(const char *wlan_iface,
|
||||
}
|
||||
pclose(p);
|
||||
|
||||
/* ── Enriquecer: IP, hostname, rssi, CCQ ─────────────────────── */
|
||||
/* ── Enrich with IP address, hostname, RSSI, and CCQ.─────────────────────── */
|
||||
for (int i = 0; i < count; i++) {
|
||||
sta_info_t *s = &out[i];
|
||||
clients_mac_to_ip(s->mac, s->ip, sizeof(s->ip));
|
||||
@@ -195,14 +197,15 @@ int clients_read_wifi(const char *wlan_iface,
|
||||
if (!s->hostname[0])
|
||||
strncpy(s->hostname, s->mac, sizeof(s->hostname)-1);
|
||||
|
||||
/* RSN = SNR estimado (signal - noise) */
|
||||
/* Estimated SNR (signal - noise). */
|
||||
s->rssi = s->signal - s->noise;
|
||||
if (s->rssi < 0) s->rssi = 0;
|
||||
|
||||
/* CCQ: métrica 0-1000
|
||||
* -50 dBm → 1000 (excelente)
|
||||
* -90 dBm → 0 (muy malo)
|
||||
* fórmula lineal: (signal + 90) * 25, limitado 0-1000 */
|
||||
/* CCQ: 0-1000 quality metric
|
||||
* -50 dBm → 1000 (excellent)
|
||||
* -90 dBm → 0 (very poor)
|
||||
* Linear mapping: (signal + 90) * 25, clamped to the range 0-1000.
|
||||
*/
|
||||
int ccq = (s->signal + 90) * 25;
|
||||
s->ccq = (ccq < 0) ? 0 : (ccq > 1000) ? 1000 : ccq;
|
||||
}
|
||||
@@ -210,11 +213,11 @@ int clients_read_wifi(const char *wlan_iface,
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
Construir JSON sta_table para un VAP
|
||||
Build the sta_table JSON array for a VAP.
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
El JSON array resultante se anida dentro de vap_table[i].sta_table
|
||||
en el payload inform. Ejemplo de entrada:
|
||||
The resulting JSON array is embedded in vap_table[i].sta_table
|
||||
within the inform payload. Example input:
|
||||
{
|
||||
"mac": "aa:bb:cc:dd:ee:ff",
|
||||
"ip": "192.168.1.100",
|
||||
|
||||
+25
-21
@@ -4,31 +4,35 @@
|
||||
/*
|
||||
* openuf - clients.h
|
||||
*
|
||||
* Enumera clientes conectados (WiFi y ethernet) para el sta_table
|
||||
* del payload inform.
|
||||
* Enumerates connected clients (Wi-Fi and Ethernet) for the
|
||||
* sta_table in the inform payload.
|
||||
*
|
||||
* ── WiFi: iw dev <iface> station dump ────────────────────────────
|
||||
* ── Wi-Fi: iw dev <iface> station dump ───────────────────────────
|
||||
*
|
||||
* Por cada cliente asociado devuelve:
|
||||
* MAC, señal (dBm), tx/rx bitrate (MBit/s), tx/rx bytes,
|
||||
* tx/rx packets, connected time (segundos)
|
||||
* Returns the following information for each associated client:
|
||||
* MAC address, signal strength (dBm), TX/RX bitrate (Mbit/s),
|
||||
* TX/RX bytes, TX/RX packets, and connected time (seconds).
|
||||
*
|
||||
* ── IP del cliente: /proc/net/arp ───────────────────────────────
|
||||
* ── Client IP Address: /proc/net/arp ─────────────────────────────
|
||||
*
|
||||
* Cruce MAC → IP. Solo entradas completas (flags=0x2).
|
||||
* Maps MAC addresses to IP addresses. Only complete entries
|
||||
* (flags = 0x2) are used.
|
||||
*
|
||||
* ── Hostname: /tmp/dhcp.leases (dnsmasq) ────────────────────────
|
||||
* ── Hostname: /tmp/dhcp.leases (dnsmasq) ─────────────────────────
|
||||
*
|
||||
* Formato: timestamp MAC IP hostname client-id
|
||||
* Format: timestamp MAC IP hostname client-id
|
||||
*
|
||||
* ── Ethernet: bridge fdb show ───────────────────────────────────
|
||||
*
|
||||
* MACs dinámicas (no permanent, no multicast) en el bridge.
|
||||
* Discovers dynamic MAC addresses (excluding permanent and
|
||||
* multicast entries) in the bridge forwarding database.
|
||||
*
|
||||
* ── CCQ (Client Connection Quality) ─────────────────────────────
|
||||
* ── CCQ (Client Connection Quality) ──────────────────────────────
|
||||
*
|
||||
* Estimated 0–1000 quality metric derived from RSSI. The
|
||||
* UniFi Controller displays it as the client's signal quality
|
||||
* indicator.
|
||||
*
|
||||
* Métrica 0-1000 basada en RSSI. El controlador la muestra
|
||||
* como barra de calidad de señal del cliente.
|
||||
* CCQ = clamp((signal + 90) * 25, 0, 1000)
|
||||
*/
|
||||
|
||||
@@ -42,7 +46,7 @@ typedef struct {
|
||||
char mac[32];
|
||||
char ip[64];
|
||||
char hostname[64];
|
||||
int signal; /* RSSI dBm (negativo) */
|
||||
int signal; /* RSSI dBm (negative) */
|
||||
int noise; /* dBm */
|
||||
int rssi; /* SNR ≈ signal - noise */
|
||||
long tx_rate; /* kbps */
|
||||
@@ -51,7 +55,7 @@ typedef struct {
|
||||
long long rx_bytes;
|
||||
long long tx_packets;
|
||||
long long rx_packets;
|
||||
int uptime; /* segundos conectado */
|
||||
int uptime; /* seconds online */
|
||||
char radio[8]; /* "ng" / "na" / "6g" */
|
||||
int channel;
|
||||
char vap_name[32];
|
||||
@@ -60,15 +64,15 @@ typedef struct {
|
||||
bool is_wired;
|
||||
} sta_info_t;
|
||||
|
||||
/* Lee clientes WiFi de una interfaz. Devuelve nº de clientes. */
|
||||
/* Reads Wi-Fi clients from an interface. Returns the number of clients. */
|
||||
int clients_read_wifi(const char *wlan_iface,
|
||||
const char *radio_band,
|
||||
int channel,
|
||||
sta_info_t *out,
|
||||
int max_out);
|
||||
|
||||
/* Construye JSON array sta_table para un VAP.
|
||||
* El caller debe liberar con json_object_put(). */
|
||||
/* Creates a JSON array `sta_table` for a VAP.
|
||||
* The caller must free it using `json_object_put()` */
|
||||
struct json_object *clients_build_sta_table(const char *wlan_iface,
|
||||
const char *radio_band,
|
||||
int channel,
|
||||
@@ -76,10 +80,10 @@ struct json_object *clients_build_sta_table(const char *wlan_iface,
|
||||
int vlan_id,
|
||||
int is_11r);
|
||||
|
||||
/* Busca IP en /proc/net/arp dado un MAC. */
|
||||
/* Look up the IP address in /proc/net/arp given a MAC address. */
|
||||
int clients_mac_to_ip(const char *mac, char *ip_out, size_t sz);
|
||||
|
||||
/* Busca hostname en /tmp/dhcp.leases dado un MAC. */
|
||||
/* Find the hostname in /tmp/dhcp.leases given a MAC address */
|
||||
int clients_mac_to_hostname(const char *mac, char *out, size_t sz);
|
||||
|
||||
#endif /* OPENUF_CLIENTS_H */
|
||||
|
||||
+85
-85
@@ -1,50 +1,50 @@
|
||||
/*
|
||||
* openuf - inform.c
|
||||
*
|
||||
* Protocolo Inform de UniFi — implementación completa.
|
||||
* UniFi Inform Protocol — full implementation.
|
||||
*
|
||||
* ── CÓMO FUNCIONA ────────────────────────────────────────────────────
|
||||
* ── HOW IT WORKS ─────────────────────────────────────────────────────
|
||||
*
|
||||
* Cada 10 segundos el AP hace HTTP POST a http://<controller>:8080/inform
|
||||
* con un paquete binario TNBU que contiene JSON cifrado con AES-128-CBC.
|
||||
* Every 10 seconds the AP makes an HTTP POST to http://<controller>:8080/inform
|
||||
* with a binary TNBU packet containing JSON encrypted with AES-128-CBC.
|
||||
*
|
||||
* El controlador responde con otro paquete TNBU. El AP descifra, parsea
|
||||
* el JSON y ejecuta la acción (_type).
|
||||
* The controller responds with another TNBU packet. The AP decrypts, parses
|
||||
* the JSON, and executes the action (_type).
|
||||
*
|
||||
* ── PAQUETE BINARIO TNBU ─────────────────────────────────────────────
|
||||
* ── TNBU BINARY PACKET ───────────────────────────────────────────────
|
||||
*
|
||||
* Offset Bytes Campo
|
||||
* Offset Bytes Field
|
||||
* ------ ----- -----
|
||||
* 0 4 Magic "TNBU"
|
||||
* 4 4 Versión paquete (=0), uint32 BE
|
||||
* 8 6 MAC del AP
|
||||
* 14 2 Flags: bit0=cifrado, bit1=zlib
|
||||
* 16 16 IV de AES (cuando cifrado)
|
||||
* 32 4 Versión de datos (=1), uint32 BE
|
||||
* 36 4 Longitud del payload, uint32 BE
|
||||
* 40 N Payload JSON, cifrado con AES-128-CBC
|
||||
* 4 4 Packet version (=0), uint32 BE
|
||||
* 8 6 AP MAC address
|
||||
* 14 2 Flags: bit0=encrypted, bit1=zlib
|
||||
* 16 16 AES IV (when encrypted)
|
||||
* 32 4 Data version (=1), uint32 BE
|
||||
* 36 4 Payload length, uint32 BE
|
||||
* 40 N JSON payload, encrypted with AES-128-CBC
|
||||
*
|
||||
* ── CÓMO SE LEEN LOS PARÁMETROS ──────────────────────────────────────
|
||||
* ── HOW PARAMETERS ARE READ ──────────────────────────────────────────
|
||||
*
|
||||
* CPU: sysinfo_cpu_percent() → /proc/stat (delta 2 llamadas)
|
||||
* CPU: sysinfo_cpu_percent() → /proc/stat (delta across 2 calls)
|
||||
* RAM: sysinfo_mem() → /proc/meminfo
|
||||
* Interfaces: sysinfo_iface() → /proc/net/dev + /sys/class/net/
|
||||
* Radios: sysinfo_radio() → iw dev <iface> info + survey
|
||||
* VAPs UCI: wlan_get_vap_table() → libuci wireless.*
|
||||
* Clientes WiFi: clients_build_sta_table() → iw dev <iface> station dump
|
||||
* Clientes IP: clients_mac_to_ip() → /proc/net/arp
|
||||
* Clientes nombre: clients_mac_to_hostname() → /tmp/dhcp.leases
|
||||
* LLDP vecinos: lldp_read_neighbors() → lldpctl -f json
|
||||
* UCI VAPs: wlan_get_vap_table() → libuci wireless.*
|
||||
* WiFi clients: clients_build_sta_table() → iw dev <iface> station dump
|
||||
* IP clients: clients_mac_to_ip() → /proc/net/arp
|
||||
* Client names: clients_mac_to_hostname() → /tmp/dhcp.leases
|
||||
* LLDP neighbors: lldp_read_neighbors() → lldpctl -f json
|
||||
*
|
||||
* ── CICLO DE ADOPCIÓN ────────────────────────────────────────────────
|
||||
* ── ADOPTION CYCLE ───────────────────────────────────────────────────
|
||||
*
|
||||
* 1. AP envía inform con key=DEFAULT, default=true, state=1
|
||||
* 2. Controller responde: {_type:"cmd", cmd:"set-adopt",
|
||||
* key:"nuevaclave32hex", uri:"http://..."}
|
||||
* 3. AP guarda nueva clave + URL en state.json, adopted=true
|
||||
* 4. AP envía inform con nueva clave, state=4, default=false
|
||||
* 5. Controller responde: {_type:"setstate", radio_table:[...], vap_table:[...]}
|
||||
* 6. AP aplica config WiFi via wlan_apply_config() → libuci → wifi reload
|
||||
* 1. AP sends inform with key=DEFAULT, default=true, state=1
|
||||
* 2. Controller responds: {_type:"cmd", cmd:"set-adopt",
|
||||
* key:"new32hexkey", uri:"http://..."}
|
||||
* 3. AP saves the new key + URL to state.json, adopted=true
|
||||
* 4. AP sends inform with the new key, state=4, default=false
|
||||
* 5. Controller responds: {_type:"setstate", radio_table:[...], vap_table:[...]}
|
||||
* 6. AP applies WiFi config via wlan_apply_config() → libuci → wifi reload
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -95,10 +95,10 @@ static int valid_authkey(const char *key)
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
sys_stats — CPU y memoria del sistema
|
||||
sys_stats — CPU and memory of the system
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
El controlador muestra CPU y RAM en la vista del dispositivo.
|
||||
Leemos /proc/stat y /proc/meminfo directamente.
|
||||
The controller shows CPU and RAM in the device view.
|
||||
We read /proc/stat and /proc/meminfo directly.
|
||||
*/
|
||||
static struct json_object *build_sys_stats(void)
|
||||
{
|
||||
@@ -121,7 +121,7 @@ static struct json_object *build_sys_stats(void)
|
||||
json_object_object_add(o, "mem_buffer", json_object_new_int(0));
|
||||
}
|
||||
|
||||
/* CPU — delta respecto a llamada anterior (cada ~10s da buen promedio) */
|
||||
/* CPU — delta relative to the previous call (every ~10s gives a good average) */
|
||||
json_object_object_add(o, "cpu",
|
||||
json_object_new_int(sysinfo_cpu_percent()));
|
||||
|
||||
@@ -129,11 +129,11 @@ static struct json_object *build_sys_stats(void)
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
if_table — estadísticas de interfaces de red
|
||||
if_table — network interface statistics
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
Reportamos todos los puertos ethernet del modelo.
|
||||
Leemos /proc/net/dev para contadores y /sys/class/net/<iface>/
|
||||
para velocidad, duplex y estado del enlace.
|
||||
All Ethernet ports on the model are reported.
|
||||
/proc/net/dev is read for counters, and /sys/class/net/<iface>/
|
||||
for speed, duplex, and link status.
|
||||
*/
|
||||
static struct json_object *build_if_table(const uf_model_t *m,
|
||||
const openuf_state_t *st)
|
||||
@@ -184,10 +184,10 @@ static struct json_object *build_if_table(const uf_model_t *m,
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
radio_table — definición estática del hardware de radio
|
||||
radio_table — static definition of the radio hardware
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
Describe las capacidades físicas de cada radio al controlador.
|
||||
El controlador usa esto para saber qué frecuencias y modos soporta.
|
||||
Describes the physical capabilities of each radio to the controller.
|
||||
The controller uses this to know which frequencies and modes it supports.
|
||||
*/
|
||||
static void build_radio_table(struct json_object *root,
|
||||
const uf_model_t *m)
|
||||
@@ -215,12 +215,12 @@ static void build_radio_table(struct json_object *root,
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
radio_table_stats — estadísticas dinámicas de canal
|
||||
radio_table_stats — dynamic channel statistics
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
Leemos en tiempo real la utilización del canal con:
|
||||
Channel utilization is read in real time using:
|
||||
iw dev wlan0 survey dump → active/busy/tx/rx time
|
||||
iw dev wlan0 info → canal actual, potencia
|
||||
El controlador muestra estos datos en la vista de RF.
|
||||
iw dev wlan0 info → current channel, power
|
||||
The controller displays this data in the RF view.
|
||||
*/
|
||||
static struct json_object *build_radio_table_stats(const uf_model_t *m)
|
||||
{
|
||||
@@ -229,13 +229,13 @@ static struct json_object *build_radio_table_stats(const uf_model_t *m)
|
||||
for (int i = 0; i < m->radio_map_len; i++) {
|
||||
const uf_radio_map_t *rm = &m->radio_map[i];
|
||||
|
||||
/* Mapear "radio0" → "wlan0" por convención OpenWrt */
|
||||
/* Map "radio0" → "wlan0" by OpenWrt convention */
|
||||
char wlan_iface[32];
|
||||
int ridx = 0;
|
||||
sscanf(rm->device, "radio%d", &ridx);
|
||||
snprintf(wlan_iface, sizeof(wlan_iface), "wlan%d", ridx);
|
||||
|
||||
/* Nombre del radio en la tabla estática */
|
||||
/* Radio name in the static table */
|
||||
const char *radio_name = (i < m->radio_table_len)
|
||||
? m->radio_table[i].name : wlan_iface;
|
||||
int default_ch = (i < m->radio_table_len)
|
||||
@@ -272,10 +272,10 @@ static struct json_object *build_radio_table_stats(const uf_model_t *m)
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
port_table — estado real de los puertos ethernet
|
||||
port_table — Real/actual status of the ethernet ports
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
Leemos /sys/class/net/<iface>/speed y operstate para
|
||||
reflejar el estado real de cada puerto en el controlador.
|
||||
/sys/class/net/<iface>/speed and operstate are read to
|
||||
reflect the actual status of each port on the controller.
|
||||
*/
|
||||
static void build_port_table(struct json_object *root,
|
||||
const uf_model_t *m)
|
||||
@@ -328,24 +328,24 @@ static void build_eth_table(struct json_object *root, const uf_model_t *m)
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
vap_table — VAPs activas con clientes conectados (sta_table)
|
||||
vap_table — active VAPs with connected clients (sta_table)
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
Para cada VAP activa en UCI:
|
||||
1. Leemos estadísticas de la interfaz wlan con sysinfo_iface()
|
||||
2. Obtenemos el canal actual con sysinfo_radio()
|
||||
3. Enumeramos clientes con clients_build_sta_table()
|
||||
→ iw dev wlan0 station dump (señal, bitrate, bytes, uptime)
|
||||
For each active VAP in UCI:
|
||||
1. Interface statistics for the wlan are read with sysinfo_iface()
|
||||
2. The current channel is obtained with sysinfo_radio()
|
||||
3. Clients are enumerated with clients_build_sta_table()
|
||||
→ iw dev wlan0 station dump (signal, bitrate, bytes, uptime)
|
||||
→ /proc/net/arp (MAC → IP)
|
||||
→ /tmp/dhcp.leases (MAC → hostname)
|
||||
|
||||
El sta_table anidado es lo que el controlador usa para:
|
||||
- Mostrar clientes en el dashboard
|
||||
- Calcular estadísticas por cliente
|
||||
- Dibujar la topología de la red
|
||||
The nested sta_table is what the controller uses to:
|
||||
- Display clients on the dashboard
|
||||
- Calculate per-client statistics
|
||||
- Draw the network topology
|
||||
*/
|
||||
static struct json_object *build_vap_table(const uf_model_t *m)
|
||||
{
|
||||
/* Obtener lista de VAPs desde UCI */
|
||||
/* Get list of VAPs from UCI */
|
||||
struct json_object *uci_vaps = wlan_get_vap_table(m);
|
||||
int nvaps = json_object_array_length(uci_vaps);
|
||||
|
||||
@@ -374,7 +374,7 @@ static struct json_object *build_vap_table(const uf_model_t *m)
|
||||
if (json_object_object_get_ex(vap, "fast_roaming_enabled", &v))
|
||||
is_11r = json_object_get_boolean(v);
|
||||
|
||||
/* Mapear banda → interfaz wlan y canal actual */
|
||||
/* Map band → wlan interface and current channel */
|
||||
char wlan_iface[32] = "phy0-ap0";
|
||||
if (ifname && ifname[0])
|
||||
snprintf(wlan_iface, sizeof(wlan_iface), "%s", ifname);
|
||||
@@ -394,17 +394,17 @@ static struct json_object *build_vap_table(const uf_model_t *m)
|
||||
}
|
||||
}
|
||||
|
||||
/* Estadísticas de la interfaz inalámbrica */
|
||||
/* Wireless interface statistics */
|
||||
iface_stats_t iface_st;
|
||||
sysinfo_iface(wlan_iface, &iface_st);
|
||||
|
||||
/* Clientes conectados a esta VAP */
|
||||
/* Clients connected to this VAP */
|
||||
struct json_object *sta_tbl =
|
||||
clients_build_sta_table(wlan_iface, radio, channel, vap_name,
|
||||
vlan_id, is_11r);
|
||||
int num_sta = json_object_array_length(sta_tbl);
|
||||
|
||||
/* Calcular tx_power del radio correspondiente */
|
||||
/* Calculate tx_power of the corresponding radio */
|
||||
int tx_pwr = 20;
|
||||
radio_stats_t rs2;
|
||||
if (sysinfo_radio(wlan_iface, &rs2) == 0 && rs2.tx_power)
|
||||
@@ -452,7 +452,7 @@ static struct json_object *build_vap_table(const uf_model_t *m)
|
||||
json_object_new_string("user"));
|
||||
json_object_object_add(o, "ccq",
|
||||
json_object_new_int(0));
|
||||
/* sta_table anidado — clientes de ESTA VAP */
|
||||
/* Nested sta_table — clients of THIS VAP */
|
||||
json_object_object_add(o, "sta_table", sta_tbl);
|
||||
|
||||
json_object_array_add(arr, o);
|
||||
@@ -481,13 +481,13 @@ static struct json_object *collect_sta_table(struct json_object *vap_table)
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
build_payload — ensamblado completo del JSON inform
|
||||
build_payload — Complete assembly of the inform JSON
|
||||
═══════════════════════════════════════════════════════════════════ */
|
||||
static char *build_payload(const openuf_state_t *st,
|
||||
const uf_model_t *m,
|
||||
long uptime)
|
||||
{
|
||||
/* MAC sin colones → serial (uppercase) */
|
||||
/* MAC without colons → serial (uppercase) */
|
||||
char mac_clean[32] = {0};
|
||||
{
|
||||
const char *s = st->mac; int j = 0;
|
||||
@@ -511,7 +511,7 @@ static char *build_payload(const openuf_state_t *st,
|
||||
|
||||
struct json_object *root = json_object_new_object();
|
||||
|
||||
/* ── Identidad del dispositivo ──────────────────────────────── */
|
||||
/* ── Device identity ──────────────────────────────── */
|
||||
json_object_object_add(root, "mac",
|
||||
json_object_new_string(st->mac));
|
||||
json_object_object_add(root, "serial",
|
||||
@@ -564,17 +564,17 @@ static char *build_payload(const openuf_state_t *st,
|
||||
/* ── CPU + RAM ──────────────────────────────────────────────── */
|
||||
json_object_object_add(root, "sys_stats", build_sys_stats());
|
||||
|
||||
/* ── Interfaces ethernet con contadores reales ──────────────── */
|
||||
/* ── Ethernet interfaces with real counters ──────────────── */
|
||||
json_object_object_add(root, "if_table", build_if_table(m, st));
|
||||
|
||||
/* ── Capacidades de radio (estático del modelo) ─────────────── */
|
||||
/* ── Radio capabilities (static, from the model) ─────────────── */
|
||||
build_radio_table(root, m);
|
||||
|
||||
/* ── Utilización de canal en tiempo real ────────────────────── */
|
||||
/* ── Real-time channel utilization ────────────────────── */
|
||||
json_object_object_add(root, "radio_table_stats",
|
||||
build_radio_table_stats(m));
|
||||
|
||||
/* ── Puertos ethernet con estado real ───────────────────────── */
|
||||
/* ── Ethernet ports with actual status ───────────────────────── */
|
||||
build_port_table(root, m);
|
||||
build_eth_table(root, m);
|
||||
|
||||
@@ -585,10 +585,10 @@ static char *build_payload(const openuf_state_t *st,
|
||||
json_object_object_add(root, "vap_table", vap_table);
|
||||
json_object_object_add(root, "sta_table", sta_table);
|
||||
|
||||
/* ── Vecinos LLDP para topología visual ─────────────────────── */
|
||||
/* ── LLDP neighbors for visual topology ─────────────────────── */
|
||||
json_object_object_add(root, "lldp_table", lldp_read_neighbors());
|
||||
|
||||
/* Contadores globales */
|
||||
/* Global counters */
|
||||
json_object_object_add(root, "bytes_r", json_object_new_int(0));
|
||||
json_object_object_add(root, "bytes_d", json_object_new_int(0));
|
||||
json_object_object_add(root, "num_sta", json_object_new_int(station_count));
|
||||
@@ -610,7 +610,7 @@ static char *build_payload(const openuf_state_t *st,
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
Paquete binario TNBU
|
||||
TNBU binary packet
|
||||
═══════════════════════════════════════════════════════════════════ */
|
||||
static unsigned char *build_packet(const char *mac_hex,
|
||||
const char *key_hex,
|
||||
@@ -668,7 +668,7 @@ static unsigned char *build_packet(const char *mac_hex,
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
Parsear respuesta binaria del controlador
|
||||
Parse binary response from the controller
|
||||
═══════════════════════════════════════════════════════════════════ */
|
||||
static char *parse_packet(const unsigned char *data, size_t data_len,
|
||||
const char *key_hex)
|
||||
@@ -717,13 +717,13 @@ static char *parse_packet(const unsigned char *data, size_t data_len,
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
Procesar comando JSON del controlador
|
||||
Process JSON command from the controller
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
_type == "noop" → no hacer nada
|
||||
_type == "noop" → do nothing
|
||||
_type == "cmd" → set-adopt / reboot / reset / locate
|
||||
_type == "setstate" → aplicar radio_table + vap_table via UCI
|
||||
_type == "setparam" → cambiar un parámetro individual
|
||||
_type == "setstate" → apply radio_table + vap_table via UCI
|
||||
_type == "setparam" → change a single parameter
|
||||
*/
|
||||
static void handle_response(openuf_state_t *st,
|
||||
const uf_model_t *model,
|
||||
@@ -896,7 +896,7 @@ static void handle_response(openuf_state_t *st,
|
||||
system("reboot &");
|
||||
|
||||
} else if (!strcmp(cmd, "locate")) {
|
||||
/* Parpadear LED — en OpenWrt: echo 1 > /sys/class/leds/.../trigger */
|
||||
/* Blink LED — on OpenWrt: echo 1 > /sys/class/leds/.../trigger */
|
||||
strcpy(action_out, "locate");
|
||||
} else {
|
||||
snprintf(action_out, 64, "cmd:%s", cmd);
|
||||
@@ -904,7 +904,7 @@ static void handle_response(openuf_state_t *st,
|
||||
return;
|
||||
}
|
||||
|
||||
/* ── setstate — configuración WiFi del controlador ──────────── */
|
||||
/* ── setstate — WiFi configuration from the controller ──────────── */
|
||||
if (!strcmp(type, "setstate")) {
|
||||
if (json_object_object_get_ex(resp, "cfgversion", &v))
|
||||
snprintf(st->cfgversion, sizeof(st->cfgversion),
|
||||
@@ -937,7 +937,7 @@ static void handle_response(openuf_state_t *st,
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
inform_send — función principal pública
|
||||
inform_send — main public function
|
||||
═══════════════════════════════════════════════════════════════════ */
|
||||
int inform_send(openuf_state_t *st,
|
||||
const uf_model_t *model,
|
||||
@@ -961,7 +961,7 @@ int inform_send(openuf_state_t *st,
|
||||
LOG("Sending inform: adopted=%d, authkey=%.8s..., inform_url=%s",
|
||||
st->adopted, key_hex, st->inform_url);
|
||||
|
||||
/* MAC sin colones */
|
||||
/* MAC without colons */
|
||||
char mac_hex[32] = {0};
|
||||
{
|
||||
const char *s = st->mac; int j = 0;
|
||||
|
||||
+28
-29
@@ -1,33 +1,33 @@
|
||||
/*
|
||||
* openuf - lldp.c
|
||||
*
|
||||
* LLDP completo: envío de frames propios + lectura de vecinos.
|
||||
* Complete LLDP: sending of own frames + reading of neighbors.
|
||||
*
|
||||
* ── Construcción del frame ────────────────────────────────────────
|
||||
* ── Frame construction ─────────────────────────────────────────────
|
||||
*
|
||||
* Los TLVs LLDP tienen cabecera de 2 bytes:
|
||||
* bit 15..9 → tipo (7 bits)
|
||||
* bit 8..0 → longitud (9 bits, max 511 bytes)
|
||||
* LLDP TLVs have a 2-byte header:
|
||||
* bit 15..9 → type (7 bits)
|
||||
* bit 8..0 → length (9 bits, max 511 bytes)
|
||||
*
|
||||
* uint16_t header_be = (type << 9) | (len & 0x1ff)
|
||||
*
|
||||
* Ejemplo: Chassis ID TLV (type=1), 7 bytes de valor:
|
||||
* Example: Chassis ID TLV (type=1), 7 bytes of value:
|
||||
* header = (1 << 9) | 7 = 0x0207
|
||||
* → bytes: 0x02 0x07 [subtype=4] [MAC 6 bytes]
|
||||
*
|
||||
* ── Envío con AF_PACKET ───────────────────────────────────────────
|
||||
* ── Sending with AF_PACKET ────────────────────────────────────────
|
||||
*
|
||||
* 1. socket(AF_PACKET, SOCK_RAW, htons(0x88cc))
|
||||
* 2. ioctl(SIOCGIFINDEX) → ifindex
|
||||
* 3. Construir frame completo en buffer
|
||||
* 4. sendto() con sockaddr_ll
|
||||
* 3. Build the complete frame in a buffer
|
||||
* 4. sendto() with sockaddr_ll
|
||||
*
|
||||
* Sin CAP_NET_RAW (no root) → socket() devuelve EPERM.
|
||||
* Lo ignoramos silenciosamente (LLDP es opcional).
|
||||
* Without CAP_NET_RAW (not root) → socket() returns EPERM.
|
||||
* This is silently ignored (LLDP is optional).
|
||||
*
|
||||
* ── Lectura de vecinos con lldpctl ───────────────────────────────
|
||||
* ── Reading neighbors with lldpctl ────────────────────────────────
|
||||
*
|
||||
* lldpctl -f json retorna:
|
||||
* lldpctl -f json returns:
|
||||
* {
|
||||
* "lldp": {
|
||||
* "interface": [
|
||||
@@ -66,12 +66,12 @@
|
||||
|
||||
#include "lldp.h"
|
||||
|
||||
/* ─── Constantes ────────────────────────────────────────────────── */
|
||||
/* ─── Constants ────────────────────────────────────────────────── */
|
||||
static const uint8_t LLDP_DST[6] = {0x01,0x80,0xc2,0x00,0x00,0x0e};
|
||||
#define LLDP_ETHERTYPE 0x88cc
|
||||
#define CAP_WLAN_AP 0x0040
|
||||
|
||||
/* ─── Escribir TLV en buffer ────────────────────────────────────── */
|
||||
/* ─── Write TLV to buffer ────────────────────────────────────── */
|
||||
static int tlv_write(uint8_t *buf, int pos, int maxlen,
|
||||
int type, const uint8_t *val, int vlen)
|
||||
{
|
||||
@@ -90,7 +90,7 @@ static int tlv_str(uint8_t *buf, int pos, int maxlen,
|
||||
(const uint8_t*)str, (int)strlen(str));
|
||||
}
|
||||
|
||||
/* ─── Parsear MAC "aa:bb:cc:dd:ee:ff" → bytes ──────────────────── */
|
||||
/* ─── Parse MAC "aa:bb:cc:dd:ee:ff" → bytes ──────────────────── */
|
||||
static void parse_mac(const char *s, uint8_t out[6])
|
||||
{
|
||||
unsigned int b[6]={0};
|
||||
@@ -109,7 +109,7 @@ int lldp_send_frame(const char *ifname,
|
||||
{
|
||||
/* Socket raw — requiere root */
|
||||
int fd = socket(AF_PACKET, SOCK_RAW, htons(LLDP_ETHERTYPE));
|
||||
if (fd < 0) return -1; /* EPERM sin root → silencioso */
|
||||
if (fd < 0) return -1; /* EPERM without root → silent */
|
||||
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
@@ -162,9 +162,9 @@ int lldp_send_frame(const char *ifname,
|
||||
0x00, (uint8_t)(CAP_WLAN_AP >> 8),
|
||||
0x00, (uint8_t)(CAP_WLAN_AP & 0xff)
|
||||
};
|
||||
/* Corregir: CAP_WLAN_AP = 0x0040, un solo byte basta */
|
||||
/* Fix: CAP_WLAN_AP = 0x0040, a single byte is enough */
|
||||
v[1] = 0x00; v[0] = 0x00;
|
||||
/* bit 6 de los 16 bits de capabilities */
|
||||
/* bit 6 of the 16 capability bits */
|
||||
uint16_t cap = CAP_WLAN_AP;
|
||||
v[0] = (cap >> 8) & 0xff; v[1] = cap & 0xff;
|
||||
v[2] = v[0]; v[3] = v[1]; /* enabled = same */
|
||||
@@ -197,16 +197,15 @@ bool lldp_available(void)
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
lldp_read_neighbors — parsea JSON de lldpctl
|
||||
lldp_read_neighbors — parses JSON from lldpctl
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Navega: root → "lldp" → "interface" (array) → cada vecino.
|
||||
Por cada vecino extrae: chassis.id, chassis.name, chassis.descr,
|
||||
port.id, port.descr, y el nombre de la interfaz local.
|
||||
|
||||
El resultado se incluye en lldp_table[] del payload inform.
|
||||
El controlador lo usa para dibujar las líneas de conexión en
|
||||
la topología visual (qué switch/puerto conecta a este AP).
|
||||
Navigate: root → "lldp" → "interface" (array) → each neighbor.
|
||||
For each neighbor, extract: chassis.id, chassis.name, chassis.descr,
|
||||
port.id, port.descr, and the name of the local interface.
|
||||
The result is included in lldp_table[] of the inform payload.
|
||||
The controller uses it to draw the connection lines in
|
||||
the visual topology (which switch/port this AP connects to).
|
||||
*/
|
||||
struct json_object *lldp_read_neighbors(void)
|
||||
{
|
||||
@@ -230,7 +229,7 @@ struct json_object *lldp_read_neighbors(void)
|
||||
struct json_object *root = json_tokener_parse(buf);
|
||||
if (!root) return result;
|
||||
|
||||
/* Navegar: root.lldp.interface[] */
|
||||
/* Browse: root.lldp.interface[] */
|
||||
struct json_object *lldp_o, *iface_arr;
|
||||
if (!json_object_object_get_ex(root, "lldp", &lldp_o)) goto done;
|
||||
if (!json_object_object_get_ex(lldp_o, "interface", &iface_arr)) goto done;
|
||||
@@ -241,7 +240,7 @@ struct json_object *lldp_read_neighbors(void)
|
||||
struct json_object *iface = json_object_array_get_idx(iface_arr, i);
|
||||
if (!iface) continue;
|
||||
|
||||
/* Puerto local */
|
||||
/* Local port */
|
||||
struct json_object *tmp_o;
|
||||
const char *local_port = "";
|
||||
if (json_object_object_get_ex(iface, "name", &tmp_o))
|
||||
|
||||
+20
-20
@@ -6,18 +6,18 @@
|
||||
*
|
||||
* LLDP (Link Layer Discovery Protocol — IEEE 802.1AB)
|
||||
*
|
||||
* ── ENVÍO de frames LLDP propios ────────────────────────────────
|
||||
* ── SENDING our own LLDP frames ────────────────────────────────
|
||||
*
|
||||
* El AP transmite frames LLDP por cada puerto ethernet.
|
||||
* Esto permite al switch upstream registrar al AP como vecino,
|
||||
* y al controlador UniFi construir el mapa de topología visual.
|
||||
* The AP transmits LLDP frames on each Ethernet port.
|
||||
* This allows the upstream switch to register the AP as a neighbor,
|
||||
* and lets the UniFi controller build the visual topology map.
|
||||
*
|
||||
* Frame Ethernet:
|
||||
* dst = 01:80:c2:00:00:0e (multicast LLDP estándar)
|
||||
* dst = 01:80:c2:00:00:0e (standard multicast LLDP)
|
||||
* src = MAC del AP
|
||||
* type = 0x88cc
|
||||
*
|
||||
* Payload (TLVs encadenados):
|
||||
* Payload (TLVs chained):
|
||||
* Header TLV = [type:7bits | len_hi:1bit][len_lo:8bits]
|
||||
*
|
||||
* TLV type=1 Chassis ID subtype=4(MAC), value=MAC[6]
|
||||
@@ -28,12 +28,12 @@
|
||||
* TLV type=7 Capabilities cap=0x0040(WLAN-AP), en=0x0040
|
||||
* TLV type=0 End of LLDPDU len=0
|
||||
*
|
||||
* ── LECTURA de vecinos: lldpctl -f json ─────────────────────────
|
||||
* ── READING of neighbors: lldpctl -f json ─────────────────────────
|
||||
*
|
||||
* Si lldpd está instalado, leemos los vecinos detectados
|
||||
* y los incluimos en lldp_table del payload inform.
|
||||
* If lldpd is installed, the detected neighbors are read
|
||||
* and included in lldp_table of the inform payload.
|
||||
*
|
||||
* lldp_table en el JSON inform:
|
||||
* lldp_table in the inform JSON:
|
||||
* [{
|
||||
* "local_port": "eth0",
|
||||
* "chassis_id": "aa:bb:cc:...",
|
||||
@@ -43,30 +43,30 @@
|
||||
* "port_desc": "to-AP"
|
||||
* }]
|
||||
*
|
||||
* ── SIN lldpd ───────────────────────────────────────────────────
|
||||
* ── Without lldpd ───────────────────────────────────────────────────
|
||||
*
|
||||
* lldp_send_frame() funciona sin lldpd (usa raw socket directo).
|
||||
* lldp_read_neighbors() retorna array vacío si no hay lldpctl.
|
||||
* lldp_send_frame() works without lldpd (it uses a direct raw socket).
|
||||
* lldp_read_neighbors() returns an empty array if lldpctl is not present.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
/* Envía un frame LLDP por raw socket AF_PACKET.
|
||||
* Requiere ejecutar como root (CAP_NET_RAW).
|
||||
* Devuelve 0 si ok, -1 si error (sin root → error silencioso). */
|
||||
/* Sends an LLDP frame via raw AF_PACKET socket.
|
||||
* Requires running as root (CAP_NET_RAW).
|
||||
* Returns 0 on success, -1 on error (without root → silent error). */
|
||||
int lldp_send_frame(const char *ifname,
|
||||
const char *mac_str,
|
||||
const char *hostname,
|
||||
const char *model_desc,
|
||||
int ttl);
|
||||
|
||||
/* Lee vecinos LLDP de lldpctl y retorna JSON array lldp_table.
|
||||
* Si lldpctl no está, retorna array vacío (no falla).
|
||||
* Caller libera con json_object_put(). */
|
||||
/* Reads LLDP neighbors from lldpctl and returns a JSON array lldp_table.
|
||||
* If lldpctl is not present, returns an empty array (does not fail).
|
||||
* Caller frees it with json_object_put(). */
|
||||
struct json_object *lldp_read_neighbors(void);
|
||||
|
||||
/* true si lldpctl está instalado */
|
||||
/* true if lldpctl is installed */
|
||||
bool lldp_available(void);
|
||||
|
||||
#endif /* OPENUF_LLDP_H */
|
||||
|
||||
+9
-9
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
* openuf - main.c
|
||||
*
|
||||
* Daemon principal. Bucle con tres tareas:
|
||||
* 1. Announce – UDP broadcast+multicast cada 10s (descubrimiento L2)
|
||||
* 2. Inform – HTTP POST cifrado cada 10s (adopción + telemetría)
|
||||
* 3. LLDP – Raw frame L2 cada 30s (topología visual en UniFi)
|
||||
* Main daemon. Loop with three tasks:
|
||||
* 1. Announce – UDP broadcast+multicast each 10s (discovery L2)
|
||||
* 2. Inform – HTTP POST cifrado each 10s (adoption + telemetrics)
|
||||
* 3. LLDP – Raw frame L2 each 30s (visual topology in UniFi)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -127,13 +127,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Descripción LLDP del dispositivo ───────────────────────── */
|
||||
/* ── LLDP device description ───────────────────────── */
|
||||
char lldp_desc[128];
|
||||
snprintf(lldp_desc, sizeof(lldp_desc),
|
||||
"%s %s%s (openuf)",
|
||||
model->model_display, model->fw_pre, model->fw_ver);
|
||||
|
||||
/* ── Bucle principal ─────────────────────────────────────────── */
|
||||
/* ── Main loop ─────────────────────────────────────────── */
|
||||
time_t start_time = time(NULL);
|
||||
time_t last_announce = 0;
|
||||
time_t last_inform = 0;
|
||||
@@ -154,12 +154,12 @@ int main(int argc, char *argv[])
|
||||
last_announce = now;
|
||||
}
|
||||
|
||||
/* LLDP frames por cada interfaz ethernet */
|
||||
/* LLDP frames for each Ethernet interface */
|
||||
if ((now - last_lldp) >= LLDP_INTERVAL) {
|
||||
LOG("Sending LLDP frames");
|
||||
for (int i = 0; i < model->port_table_len; i++) {
|
||||
const char *iface = model->port_table[i].ifname;
|
||||
/* Leer MAC real de la interfaz si disponible */
|
||||
/* Read the actual MAC of the interface if available */
|
||||
char iface_mac[32];
|
||||
if (get_mac(iface, iface_mac, sizeof(iface_mac)) != 0)
|
||||
strncpy(iface_mac, mac_str, sizeof(iface_mac)-1);
|
||||
@@ -178,7 +178,7 @@ int main(int argc, char *argv[])
|
||||
last_inform = now;
|
||||
LOG("Sending inform");
|
||||
|
||||
/* Actualizar IP en cada ciclo */
|
||||
/* Update IP each cycle */
|
||||
char new_ip[64] = {0};
|
||||
if (get_ip(cfg.lan_if, new_ip, sizeof(new_ip)) == 0 ||
|
||||
get_ip("eth0", new_ip, sizeof(new_ip)) == 0)
|
||||
|
||||
+1
-1
@@ -166,7 +166,7 @@ const uf_model_t model_uapg2aclr = {
|
||||
.radio_map=uapg2aclr_rmap, .radio_map_len=2,
|
||||
};
|
||||
|
||||
/* ─── Registro de modelos ─────────────────────────────────────── */
|
||||
/* ─── Model Registry ─────────────────────────────────────── */
|
||||
static const uf_model_t *all_models[] = {
|
||||
&model_u6inwall,
|
||||
&model_u6lite,
|
||||
|
||||
+23
-23
@@ -1,26 +1,26 @@
|
||||
/*
|
||||
* openuf - sysinfo.c
|
||||
*
|
||||
* Lee estadísticas del sistema para el payload inform.
|
||||
* Reads system statistics for the inform payload.
|
||||
*
|
||||
* ── CPU: /proc/stat ──────────────────────────────────────────────────
|
||||
*
|
||||
* Formato: cpu user nice system idle iowait irq softirq steal
|
||||
* Format: cpu user nice system idle iowait irq softirq steal
|
||||
*
|
||||
* El uso se calcula con dos snapshots separados en el tiempo:
|
||||
* activo = user + nice + system + irq + softirq + steal
|
||||
* total = activo + idle + iowait
|
||||
* uso % = (Δactivo / Δtotal) × 100
|
||||
* Usage is calculated with two snapshots taken at different times:
|
||||
* active = user + nice + system + irq + softirq + steal
|
||||
* total = active + idle + iowait
|
||||
* usage % = (Δactive / Δtotal) × 100
|
||||
*
|
||||
* ── Memoria: /proc/meminfo ───────────────────────────────────────────
|
||||
* ── Memory: /proc/meminfo ────────────────────────────────────────────
|
||||
*
|
||||
* MemTotal, MemFree, Buffers, Cached
|
||||
* used = total - free - buffers - cached
|
||||
*
|
||||
* ── Interfaces: /proc/net/dev + /sys/class/net/<iface>/ ─────────────
|
||||
*
|
||||
* /proc/net/dev → contadores acumulados rx/tx
|
||||
* /sys/class/net/speed → velocidad negociada (Mbps)
|
||||
* /proc/net/dev → cumulative rx/tx counters
|
||||
* /sys/class/net/speed → negotiated speed (Mbps)
|
||||
* /sys/class/net/duplex → "full" / "half"
|
||||
* /sys/class/net/operstate → "up" / "down" / "unknown"
|
||||
* /sys/class/net/address → MAC
|
||||
@@ -28,8 +28,8 @@
|
||||
*
|
||||
* ── Radio: iw dev <iface> info + survey dump ─────────────────────────
|
||||
*
|
||||
* info: canal actual, potencia TX
|
||||
* survey dump: active/busy/tx/rx time → calcular % utilización
|
||||
* info: current channel, TX power
|
||||
* survey dump: active/busy/tx/rx time → calculate % utilization
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
@@ -47,7 +47,7 @@
|
||||
#include "sysinfo.h"
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
Memoria
|
||||
Memory
|
||||
═══════════════════════════════════════════════════════════════════ */
|
||||
int sysinfo_mem(mem_stats_t *out)
|
||||
{
|
||||
@@ -109,7 +109,7 @@ int sysinfo_cpu_percent(void)
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
Interfaz de red
|
||||
Network interface
|
||||
═══════════════════════════════════════════════════════════════════ */
|
||||
static int read_sysfs_str(const char *iface, const char *file,
|
||||
char *out, size_t sz)
|
||||
@@ -167,7 +167,7 @@ int sysinfo_iface(const char *ifname, iface_stats_t *out)
|
||||
/* IP */
|
||||
read_ip_ioctl(ifname, out->ip, sizeof(out->ip));
|
||||
|
||||
/* Contadores de /proc/net/dev */
|
||||
/* Counters of /proc/net/dev */
|
||||
FILE *f = fopen("/proc/net/dev", "r");
|
||||
if (!f) return 0;
|
||||
|
||||
@@ -179,7 +179,7 @@ int sysinfo_iface(const char *ifname, iface_stats_t *out)
|
||||
char *colon = strchr(line, ':');
|
||||
if (!colon) continue;
|
||||
|
||||
/* Extraer nombre de interfaz (puede tener espacios al inicio) */
|
||||
/* Extract interface name (may have leading spaces) */
|
||||
size_t end = colon - line;
|
||||
while (end > 0 && line[end-1] == ' ') end--;
|
||||
size_t start = 0;
|
||||
@@ -210,24 +210,24 @@ int sysinfo_iface(const char *ifname, iface_stats_t *out)
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
Radio WiFi
|
||||
WiFi Radio
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
1. iw dev wlan0 info → canal y potencia
|
||||
Ejemplo:
|
||||
1. iw dev wlan0 info → channel and power
|
||||
Example:
|
||||
Interface wlan0
|
||||
channel 6 (2437 MHz), width: 20 MHz
|
||||
txpower 20.00 dBm
|
||||
|
||||
2. iw dev wlan0 survey dump → utilización del canal
|
||||
Buscamos el bloque con "[in use]":
|
||||
2. iw dev wlan0 survey dump → channel utilization
|
||||
We look for the block with "[in use]":
|
||||
frequency: 2437 MHz [in use]
|
||||
channel active time: 12345 ms
|
||||
channel busy time: 987 ms
|
||||
channel transmit time: 456 ms
|
||||
channel receive time: 321 ms
|
||||
|
||||
Calculamos:
|
||||
We calculate:
|
||||
cu_total = busy/active × 100
|
||||
cu_self_tx = transmit/active × 100
|
||||
cu_self_rx = receive/active × 100
|
||||
@@ -268,7 +268,7 @@ int sysinfo_radio(const char *iface, radio_stats_t *out)
|
||||
in_use = 1; active=busy=tx_t=rx_t=0; continue;
|
||||
}
|
||||
if (!in_use) continue;
|
||||
/* Nueva frecuencia sin [in use] resetea el bloque */
|
||||
/* New frequency without [in use] resets the block */
|
||||
if (strstr(line, "frequency:") && !strstr(line, "[in use]")) {
|
||||
in_use = 0; continue;
|
||||
}
|
||||
@@ -287,7 +287,7 @@ int sysinfo_radio(const char *iface, radio_stats_t *out)
|
||||
out->cu_self_rx = (int)(rx_t * 100 / active);
|
||||
}
|
||||
|
||||
/* Número de clientes asociados */
|
||||
/* Number of associated clients */
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
"iw dev %s station dump 2>/dev/null | grep -c '^Station'",
|
||||
iface);
|
||||
|
||||
+14
-14
@@ -4,15 +4,15 @@
|
||||
/*
|
||||
* openuf - sysinfo.h
|
||||
*
|
||||
* Lee estadísticas del sistema (CPU, RAM, interfaces, radios).
|
||||
* Todas las lecturas son del kernel Linux directamente:
|
||||
* Reads system statistics (CPU, RAM, interfaces, radios).
|
||||
* All readings come directly from the Linux kernel:
|
||||
*
|
||||
* /proc/stat → uso CPU (deltas entre dos snapshots)
|
||||
* /proc/meminfo → memoria total/libre/buffer/cache
|
||||
* /proc/net/dev → contadores rx/tx por interfaz
|
||||
* /proc/stat → CPU usage (deltas between two snapshots)
|
||||
* /proc/meminfo → total/free/buffer/cache memory
|
||||
* /proc/net/dev → rx/tx counters per interface
|
||||
* /sys/class/net/ → speed, duplex, operstate, MAC
|
||||
* iw dev <if> info → canal actual, potencia TX
|
||||
* iw dev <if> survey dump → utilización del canal
|
||||
* iw dev <if> info → current channel, TX power
|
||||
* iw dev <if> survey dump → channel utilization
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
@@ -28,9 +28,9 @@ typedef struct {
|
||||
int sysinfo_mem(mem_stats_t *out);
|
||||
|
||||
/* ── CPU ─────────────────────────────────────────────────────────── */
|
||||
/* Retorna % uso CPU (0-100). Primera llamada retorna 0 (toma snapshot).
|
||||
* Las siguientes calculan el delta respecto a la anterior.
|
||||
* Con intervalo de 10s da un buen promedio de uso. */
|
||||
/* Returns % CPU usage (0-100). The first call returns 0 (takes a snapshot).
|
||||
* Subsequent calls compute the delta relative to the previous one.
|
||||
* With a 10s interval this gives a good average of usage. */
|
||||
int sysinfo_cpu_percent(void);
|
||||
|
||||
/* ── Interfaz de red ─────────────────────────────────────────────── */
|
||||
@@ -39,7 +39,7 @@ typedef struct {
|
||||
char mac[32];
|
||||
char ip[64];
|
||||
bool up;
|
||||
int speed; /* Mbps: 10/100/1000; -1 si no disponible */
|
||||
int speed; /* Mbps: 10/100/1000; -1 if not available */
|
||||
bool full_duplex;
|
||||
long long rx_bytes;
|
||||
long long tx_bytes;
|
||||
@@ -60,9 +60,9 @@ typedef struct {
|
||||
char iface[32];
|
||||
int channel;
|
||||
int tx_power;
|
||||
int cu_total; /* % uso canal total */
|
||||
int cu_self_tx; /* % tiempo transmitiendo */
|
||||
int cu_self_rx; /* % tiempo recibiendo */
|
||||
int cu_total; /* % total channel usage */
|
||||
int cu_self_tx; /* % time spent transmitting */
|
||||
int cu_self_rx; /* % time spent receiving */
|
||||
int num_sta;
|
||||
int noise; /* dBm */
|
||||
} radio_stats_t;
|
||||
|
||||
+49
-48
@@ -373,7 +373,7 @@ void wlan_clear(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Recopilar secciones a eliminar (no modificar durante iteración) */
|
||||
/* Collect sections to remove (do not modify during iteration) */
|
||||
char *to_del[64];
|
||||
int ndel = 0;
|
||||
struct uci_element *e;
|
||||
@@ -403,14 +403,14 @@ void wlan_clear(void)
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
wlan_apply_radio — aplicar config de radio (canal, HT, potencia)
|
||||
wlan_apply_radio — apply radio config (channel, HT, power)
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Lectura de parámetros del JSON del controlador:
|
||||
Reading parameters from the controller's JSON:
|
||||
channel → wireless.<device>.channel
|
||||
ht → wireless.<device>.htmode ("HT20" / "HT40" / "HT80" / "HE80")
|
||||
tx_power → wireless.<device>.txpower
|
||||
min_rssi → no se mapea a UCI (requiere daemon externo)
|
||||
min_rssi → not mapped to UCI (requires an external daemon)
|
||||
*/
|
||||
void wlan_apply_radio(struct json_object *radio_json,
|
||||
const char *device_name)
|
||||
@@ -452,7 +452,7 @@ void wlan_apply_radio(struct json_object *radio_json,
|
||||
|
||||
RP("ht", "htmode");
|
||||
|
||||
/* Canal: 0 = auto en UniFi */
|
||||
/* Channel: 0 = auto in UniFi */
|
||||
if (json_object_object_get_ex(radio_json, "channel", &v)) {
|
||||
int ch = json_object_get_int(v);
|
||||
if (ch == 0) {
|
||||
@@ -474,7 +474,7 @@ void wlan_apply_radio(struct json_object *radio_json,
|
||||
uci_set(ctx, &ptr);
|
||||
}
|
||||
|
||||
/* Habilitar el radio */
|
||||
/* Enable the radio */
|
||||
snprintf(path, sizeof(path), "wireless.%s.disabled=0", device_name);
|
||||
struct uci_ptr ptr;
|
||||
if (uci_lookup_ptr(ctx, &ptr, path, true) == UCI_OK)
|
||||
@@ -488,22 +488,22 @@ void wlan_apply_radio(struct json_object *radio_json,
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
Crear una VAP (wifi-iface UCI) desde un JSON VAP del controlador
|
||||
Create a VAP (wifi-iface UCI) from a controller VAP JSON
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Parámetros del controlador que leemos y cómo los mapeamos:
|
||||
Controller parameters we read and how we map them:
|
||||
|
||||
essid → wireless.openuf_X.ssid
|
||||
x_passphrase → wireless.openuf_X.key
|
||||
security → wireless.openuf_X.encryption (via sec_to_uci)
|
||||
hide_ssid → wireless.openuf_X.hidden
|
||||
guest_policy → wireless.openuf_X.isolate (aislamiento de clientes)
|
||||
guest_policy → wireless.openuf_X.isolate (client isolation)
|
||||
fast_roaming_enabled → ieee80211r, ft_over_ds, mobility_domain, ft_psk_generate_local
|
||||
band_steering → ieee80211k, ieee80211v, rrm_neighbor_report, bss_transition
|
||||
pmf_mode → ieee80211w (0/1/2)
|
||||
wpa3_support → añadir "sae-mixed" si WPA2+WPA3
|
||||
wpa3_support → add "sae-mixed" if WPA2+WPA3
|
||||
uapsd → uapsd (U-APSD power saving)
|
||||
vlan_id → wireless.openuf_X.vlan_id (si ≠ 0)
|
||||
vlan_id → wireless.openuf_X.vlan_id (if ≠ 0)
|
||||
*/
|
||||
static int apply_vap(struct uci_context *ctx,
|
||||
struct uci_package *pkg,
|
||||
@@ -535,7 +535,7 @@ static int apply_vap(struct uci_context *ctx,
|
||||
snprintf(target_network, sizeof(target_network), "vlan%d", vid);
|
||||
}
|
||||
|
||||
/* Nombre de sección: openuf_<idx>_<ssid_safe> */
|
||||
/* Section name: openuf_<idx>_<ssid_safe> */
|
||||
char safe[16] = {0};
|
||||
safe_section_name(essid, safe, sizeof(safe));
|
||||
char sec_name[48];
|
||||
@@ -571,23 +571,23 @@ static int apply_vap(struct uci_context *ctx,
|
||||
if (vap_id)
|
||||
UCI_SET(ctx, "wireless", sec_name, "openuf_vap_id", vap_id);
|
||||
|
||||
/* Contraseña */
|
||||
/* Password */
|
||||
if (pass && pass[0] && strcmp(security,"open") != 0)
|
||||
UCI_SET(ctx, "wireless", sec_name, "key", pass);
|
||||
|
||||
/* SSID oculto */
|
||||
/* hidden SSID */
|
||||
int hidden = 0;
|
||||
if (json_object_object_get_ex(vap_json, "hide_ssid", &v))
|
||||
hidden = json_object_get_boolean(v) ? 1 : 0;
|
||||
UCI_SET_INT(ctx, "wireless", sec_name, "hidden", hidden);
|
||||
|
||||
/* Aislamiento de clientes (guest network) */
|
||||
/* Client isolation (guest network) */
|
||||
int isolate = 0;
|
||||
if (json_object_object_get_ex(vap_json, "guest_policy", &v))
|
||||
isolate = json_object_get_boolean(v) ? 1 : 0;
|
||||
UCI_SET_INT(ctx, "wireless", sec_name, "isolate", isolate);
|
||||
|
||||
/* U-APSD (ahorro de energía para clientes móviles) */
|
||||
/* U-APSD (power saving for mobile clients) */
|
||||
int uapsd = 1;
|
||||
if (json_object_object_get_ex(vap_json, "uapsd", &v))
|
||||
uapsd = json_object_get_boolean(v) ? 1 : 0;
|
||||
@@ -595,22 +595,22 @@ static int apply_vap(struct uci_context *ctx,
|
||||
|
||||
/* ── PMF (Protected Management Frames / 802.11w) ──────────────
|
||||
* "disabled" → 0, "optional" → 1, "required" → 2
|
||||
* WPA3 (sae/sae-mixed) siempre requiere ieee80211w=2 */
|
||||
* WPA3 (sae/sae-mixed) always requires "optional" or " required" ieee80211w=2 */
|
||||
int pmf = 0;
|
||||
if (json_object_object_get_ex(vap_json, "pmf_mode", &v)) {
|
||||
const char *pm = json_object_get_string(v);
|
||||
if (!strcmp(pm, "optional")) pmf = 1;
|
||||
if (!strcmp(pm, "required")) pmf = 2;
|
||||
}
|
||||
/* WPA3 obliga PMF=2 */
|
||||
/* WPA3 forces PMF=2 */
|
||||
if (!strcmp(security,"wpa3") || !strcmp(security,"wpa3transition") ||
|
||||
!strcmp(security,"wpa3enterprise"))
|
||||
pmf = 2;
|
||||
UCI_SET_INT(ctx, "wireless", sec_name, "ieee80211w", pmf);
|
||||
|
||||
/* ── Fast Roaming (802.11r FT) ────────────────────────────────
|
||||
* Permite que los clientes se muevan entre APs sin re-autenticación
|
||||
* completa. El handshake FT sólo tarda ~50ms vs ~200-300ms normal. */
|
||||
* Allows clients to move between APs without re-authentication
|
||||
* complete. The FT handshake only takes ~50ms vs ~200-300ms for a normal one. */
|
||||
const char *ft_keys[] = {
|
||||
"fast_roaming_enabled", "fast_roaming", "ft_enabled", "ieee80211r"
|
||||
};
|
||||
@@ -629,10 +629,10 @@ static int apply_vap(struct uci_context *ctx,
|
||||
}
|
||||
|
||||
/* ── Band Steering (802.11k/v) ────────────────────────────────
|
||||
* 802.11k: Neighbor Reports → el AP informa al cliente qué otros
|
||||
* APs existen para facilitar el roaming.
|
||||
* 802.11v: BSS Transition Management → el AP puede "sugerir" al
|
||||
* cliente que se mueva a otro AP con mejor señal. */
|
||||
* 802.11k: Neighbor Reports → the AP tells the client what other
|
||||
* APs exist to facilitate roaming.
|
||||
* 802.11v: BSS Transition Management → the AP can "suggest" to the
|
||||
* client to move to another AP with better signal.*/
|
||||
int band_steer = 0;
|
||||
if (json_object_object_get_ex(vap_json, "band_steering", &v))
|
||||
band_steer = json_object_get_boolean(v) ? 1 : 0;
|
||||
@@ -669,18 +669,18 @@ static int apply_vap(struct uci_context *ctx,
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
wlan_apply_config — aplicar configuración completa del controlador
|
||||
wlan_apply_config — apply the controller's full configuration
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Llamado desde inform.c → handle_response() cuando _type=="setstate".
|
||||
config_json es el JSON completo del controlador.
|
||||
Called from inform.c → handle_response() when _type=="setstate".
|
||||
config_json is the controller's complete JSON.
|
||||
|
||||
Proceso:
|
||||
1. Eliminar VAPs antiguas (prefijo openuf_)
|
||||
2. Aplicar radio_table (canal, potencia, htmode) por radio
|
||||
3. Crear una VAP por cada entrada en vap_table
|
||||
4. Hacer commit UCI
|
||||
5. Ejecutar "wifi reload" para aplicar sin reiniciar el AP
|
||||
Process:
|
||||
1. Remove old VAPs (openuf_ prefix)
|
||||
2. Apply radio_table (channel, power, htmode) per radio
|
||||
3. Create one VAP for each entry in vap_table
|
||||
4. Commit UCI
|
||||
5. Run "wifi reload" to apply without rebooting the AP
|
||||
*/
|
||||
int wlan_apply_config(struct json_object *config_json,
|
||||
const uf_model_t *model)
|
||||
@@ -689,7 +689,7 @@ int wlan_apply_config(struct json_object *config_json,
|
||||
json_object_object_get_ex(config_json, "radio_table", &rt_arr);
|
||||
json_object_object_get_ex(config_json, "vap_table", &vt_arr);
|
||||
|
||||
/* Obtener MAC del AP para mobility_domain */
|
||||
/* Get the AP's MAC for mobility_domain */
|
||||
char mac_str[32] = "00:00:00:00:00:00";
|
||||
{
|
||||
char path[128];
|
||||
@@ -706,13 +706,13 @@ int wlan_apply_config(struct json_object *config_json,
|
||||
/* Remove every existing VAP so UniFi becomes the sole Wi-Fi owner. */
|
||||
wlan_clear();
|
||||
|
||||
/* 2. Aplicar radio_table */
|
||||
/* 2. Apply radio_table */
|
||||
if (rt_arr && json_object_is_type(rt_arr, json_type_array)) {
|
||||
int nr = json_object_array_length(rt_arr);
|
||||
for (int i = 0; i < nr; i++) {
|
||||
struct json_object *r = json_object_array_get_idx(rt_arr, i);
|
||||
if (!r) continue;
|
||||
/* Buscar el device UCI correspondiente a esta banda */
|
||||
/* Find the UCI device corresponding to this band */
|
||||
const char *radio_band = "";
|
||||
if (json_object_object_get_ex(r, "radio", &v))
|
||||
radio_band = json_object_get_string(v);
|
||||
@@ -766,7 +766,7 @@ int wlan_apply_config(struct json_object *config_json,
|
||||
printf("[openuf] Disabled %d default OpenWrt VAPs\n",
|
||||
disabled_defaults);
|
||||
|
||||
/* 3. Crear VAPs */
|
||||
/* 3. Create VAPs */
|
||||
if (vt_arr && json_object_is_type(vt_arr, json_type_array)) {
|
||||
int nv = json_object_array_length(vt_arr);
|
||||
for (int i = 0; i < nv; i++) {
|
||||
@@ -974,24 +974,25 @@ int wlan_apply_system_cfg(const char *system_cfg,
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
wlan_get_vap_table — leer VAPs activas desde UCI
|
||||
wlan_get_vap_table — read active VAPs from UCI
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Itera todas las wifi-iface con prefijo "openuf_" en /etc/config/wireless
|
||||
y construye el JSON vap_table para incluir en el payload inform.
|
||||
Iterates over all wifi-iface entries with the "openuf_" prefix in
|
||||
/etc/config/wireless and builds the vap_table JSON to include in
|
||||
the inform payload.
|
||||
|
||||
Campos que leemos de UCI → campos en el JSON:
|
||||
Fields we read from UCI → fields in the JSON:
|
||||
ssid → essid
|
||||
device → (usado para buscar radio y BSSID)
|
||||
device → (used to look up radio and BSSID)
|
||||
encryption → security (via sec_to_unifi)
|
||||
hidden → hide_ssid
|
||||
ieee80211r → fast_roaming_enabled
|
||||
ieee80211k → band_steering
|
||||
ieee80211w → pmf_mode ("disabled"/"optional"/"required")
|
||||
disabled → up (inverso)
|
||||
disabled → up (inverse)
|
||||
|
||||
También intentamos leer el BSSID real de la interfaz wlan
|
||||
desde /sys/class/net/<iface>/address.
|
||||
We also try to read the actual BSSID of the wlan interface
|
||||
from /sys/class/net/<iface>/address.
|
||||
*/
|
||||
/* Resolve a configured VAP to the live interface reported by nl80211. */
|
||||
static int find_runtime_vap(int phy_index, const char *ssid,
|
||||
@@ -1044,7 +1045,7 @@ struct json_object *wlan_get_vap_table(const uf_model_t *model)
|
||||
uci_foreach_element(&pkg->sections, e) {
|
||||
struct uci_section *sec = uci_to_section(e);
|
||||
if (strcmp(sec->type, "wifi-iface") != 0) continue;
|
||||
/* Solo reportar VAPs gestionadas por openuf */
|
||||
/* Only report VAPs managed by openuf */
|
||||
if (strncmp(sec->e.name, "openuf_", 7) != 0) continue;
|
||||
|
||||
#define UCI_GET(opt) uci_lookup_option_string(ctx, sec, opt)
|
||||
@@ -1063,7 +1064,7 @@ struct json_object *wlan_get_vap_table(const uf_model_t *model)
|
||||
if (!ssid) ssid = "";
|
||||
if (!device) device = "radio0";
|
||||
|
||||
/* Banda de este radio */
|
||||
/* Band of this radio */
|
||||
const char *radio_band = "ng";
|
||||
for (int j = 0; j < model->radio_map_len; j++) {
|
||||
if (!strcmp(model->radio_map[j].device, device)) {
|
||||
@@ -1079,7 +1080,7 @@ struct json_object *wlan_get_vap_table(const uf_model_t *model)
|
||||
if (find_runtime_vap(ridx, ssid, wlan_iface, sizeof(wlan_iface)) != 0)
|
||||
snprintf(wlan_iface, sizeof(wlan_iface), "phy%d-ap0", ridx);
|
||||
|
||||
/* Leer BSSID real desde sysfs */
|
||||
/* Read the actual BSSID from sysfs */
|
||||
char bssid[32] = "00:00:00:00:00:00";
|
||||
{
|
||||
char path[128];
|
||||
|
||||
Reference in New Issue
Block a user