/* * openuf - main.c * * 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 #include #include #include #include #include #include #include #include #include #include "config.h" #include "state.h" #include "ufmodel.h" #include "announce.h" #include "inform.h" #include "lldp.h" #if ENABLE_LOGGING FILE *log_fp = NULL; #endif #define LLDP_INTERVAL 30 /* seconds between LLDP frames */ #define LLDP_TTL 120 /* LLDP record lifetime in seconds */ static int get_mac(const char *iface, char *out, size_t sz) { char path[128]; snprintf(path, sizeof(path), "/sys/class/net/%s/address", iface); FILE *f = fopen(path, "r"); if (!f) return -1; char buf[32] = {0}; fgets(buf, sizeof(buf), f); fclose(f); buf[strcspn(buf, "\r\n")] = '\0'; if (strlen(buf) < 11) return -1; strncpy(out, buf, sz-1); return 0; } static int get_ip(const char *iface, char *out, size_t sz) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) return -1; struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, iface, IFNAMSIZ-1); int ret = -1; if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { struct sockaddr_in *sa = (struct sockaddr_in *)&ifr.ifr_addr; strncpy(out, inet_ntoa(sa->sin_addr), sz-1); ret = 0; } close(fd); return ret; } int main(int argc, char *argv[]) { for (int i = 1; i < argc-1; i++) if (!strcmp(argv[i], "-c")) setenv("OPENUF_CONF", argv[i+1], 1); openuf_config_t cfg; config_load(&cfg); #if ENABLE_LOGGING if (cfg.enable_logging) { log_fp = fopen("/var/log/openuf.log", "a"); if (log_fp) { LOG("Logging enabled"); } } #endif const uf_model_t *model = ufmodel_find(cfg.ufmodel); char mac_str[32] = "00:00:00:00:00:00"; char ip_str[64] = "192.168.1.1"; if (get_mac(cfg.lan_if, mac_str, sizeof(mac_str)) != 0) get_mac("eth0", mac_str, sizeof(mac_str)); if (get_ip(cfg.lan_if, ip_str, sizeof(ip_str)) != 0) get_ip("eth0", ip_str, sizeof(ip_str)); openuf_state_t state; state_load(&state); strncpy(state.mac, mac_str, sizeof(state.mac)-1); strncpy(state.ip, ip_str, sizeof(state.ip)-1); if (!state.hostname[0]) strncpy(state.hostname, model->display_name, sizeof(state.hostname)-1); /* Log initial state */ LOG("Initial device state: adopted=%d, authkey=%.8s...", state.adopted, state.authkey[0] ? state.authkey : "DEFAULT"); if (!state.adopted || !state.inform_url[0]) snprintf(state.inform_url, sizeof(state.inform_url), "http://%s:%d%s", cfg.controller_ip, INFORM_PORT, INFORM_PATH); state_save(&state); printf("[openuf] Starting model=%-8s MAC=%s IP=%s\n", model->model, mac_str, ip_str); printf("[openuf] Controller: %s\n", state.inform_url); printf("[openuf] Adopted: %s\n", state.adopted ? "yes" : "no"); printf("[openuf] LLDP available: %s\n", lldp_available() ? "yes (lldpd)" : "no (transmit only)"); fflush(stdout); LOG("Daemon started"); /* ── Announce socket ────────────────────────────────────────── */ announce_ctx_t ann; if (cfg.enable_announce) { if (announce_init(&ann, model, mac_str, ip_str) != 0) { LOG("Failed to initialize announce service"); cfg.enable_announce = 0; } } /* ── 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); /* ── Main loop ─────────────────────────────────────────── */ time_t start_time = time(NULL); time_t last_announce = 0; time_t last_inform = 0; time_t last_lldp = 0; printf("[openuf] Main loop started\n"); fflush(stdout); while (1) { time_t now = time(NULL); /* Announce L2/UDP */ if (cfg.enable_announce && (now - last_announce) >= ANNOUNCE_INTERVAL) { LOG("Sending announce"); int announce_rc = announce_send(&ann); LOG("Announce completed with result=%d", announce_rc); last_announce = now; } /* 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; /* 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); int lldp_rc = lldp_send_frame(iface, iface_mac, state.hostname, lldp_desc, LLDP_TTL); LOG("LLDP frame interface=%s mac=%s result=%d", iface, iface_mac, lldp_rc); } last_lldp = now; } /* Inform HTTP POST */ if (cfg.enable_inform && (now - last_inform) >= cfg.inform_interval) { last_inform = now; LOG("Sending inform"); /* 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) strncpy(state.ip, new_ip, sizeof(state.ip)-1); long uptime = (long)(now - start_time); char err[128] = {0}; if (inform_send(&state, model, uptime, err) != 0) { LOG("inform error: %s", err); } } sleep(1); } announce_close(&ann); #if ENABLE_LOGGING if (log_fp) { LOG("Shutting down"); fclose(log_fp); } #endif return 0; }