Discovery Agent Overview
The FreeSDN Agent (freesdn-agent) is a lightweight network discovery daemon that runs on a machine inside your LAN and feeds real device intelligence back to the FreeSDN control plane.
Why an agent?
Section titled “Why an agent?”The FreeSDN control plane runs as a Docker stack. Docker containers operate in isolated network namespaces with Layer 3+ access only - they cannot open raw sockets, listen for Layer 2 frames (LLDP/CDP/ARP), enter promiscuous mode, or bind privileged ports like UDP 162 (SNMP traps) or UDP 514 (syslog) without special host-mode setup.
An agent running as a native system service on a host inside the LAN has full OS-level access:
| Capability | Docker container | FreeSDN Agent |
|---|---|---|
| Raw sockets / ARP | No | Yes |
| L2 frames (LLDP, CDP) | No | Yes |
| Promiscuous mode | No | Yes |
| SNMP trap receiver (UDP 162) | Port-map only | Yes |
| Syslog receiver (UDP 514) | Port-map only | Yes |
| DHCP snooping | No | Yes |
| Multi-VLAN access | No | Yes |
| mDNS / SSDP / ONVIF multicast | Partial | Yes |
A single control plane can manage agents at multiple sites simultaneously - each agent scans its local LAN and streams results back over an outbound HTTPS/WebSocket connection, with no inbound firewall holes required.
Two form factors, one scanner engine
Section titled “Two form factors, one scanner engine”| Daemon | Desktop app | |
|---|---|---|
| Mode | Headless system service, runs 24/7 | Interactive PySide6 GUI application |
| Target user | Sysadmin deploying at a remote site | Network technician on-site |
| Connection | Persistent WebSocket to control plane | REST API |
| Platforms | Windows, Linux, macOS (Py >= 3.11) | Windows, Linux, macOS |
| Approximate size | ~30 MB (no Qt) | ~150 MB (includes Qt) |
Both share the same scanner engine. The 14 active scanners in freesdn_agent/scanners/ are pure Python with zero Qt dependency - only the orchestration layer differs (asyncio event loop for the daemon, QThread for the desktop app).
The 14 active scanners
Section titled “The 14 active scanners”| Scanner | Protocol / Layer | Root required? |
|---|---|---|
arp | ARP (L2) | Yes - raw L2 sockets via Scapy |
ping | ICMP via subprocess | No |
tcp_port | TCP connect | No |
http_service | HTTP/HTTPS (L7) | No |
banner | SSH/Telnet banner grab | No |
snmp | SNMP v1/v2c (UDP 161) | No |
netbios | NetBIOS/SMB (UDP 137) | No |
mdns | mDNS/Bonjour (UDP 5353) | No |
ssdp | SSDP/UPnP (UDP 1900) | No |
sip | SIP OPTIONS (UDP 5060) | No |
dns | DNS reverse lookup | No |
rtsp | RTSP (TCP 554) | No |
onvif | ONVIF WS-Discovery (UDP 3702) | No (Scapy library needed) |
sadp | Hikvision SADP (UDP 37020) | No (Scapy library needed) |
The 5 passive listeners
Section titled “The 5 passive listeners”Passive listeners run as long-lived background tasks alongside the daemon. They are off by default and must be enabled in PassiveConfig.
| Listener | What it captures | Root required? |
|---|---|---|
lldp | LLDP neighbor frames (ethertype 0x88CC) | Yes |
cdp | Cisco CDP frames (multicast 01:00:0C:CC:CC:CC) | Yes |
snmp_trap | SNMP trap PDUs (UDP 162) | Yes (privileged port) |
syslog | Syslog messages (UDP 514) | Yes (privileged port) |
dhcp | DHCP traffic (UDP 67/68) - detects new devices via DHCP DISCOVER/REQUEST/ACK | Yes |
Capability advertisement
Section titled “Capability advertisement”With each heartbeat the agent sends a capability advertisement to the control plane so the UI knows exactly what this agent can run:
{ "scanners": ["arp", "icmp", "ports", "http", "banner", "netbios", "sip", "mdns", "ssdp", "snmp", "onvif", "sadp"], "listeners": ["lldp", "cdp"], "scan_types": ["quick", "camera", "voip", "iot", "port", "windows", "full"], "platform": "linux", "python_version": "3.12.3", "agent_version": "1.0.0", "has_scapy": true, "has_root": true, "interfaces": ["eth0", "eth1"]}The backend uses scan_types to filter the scan-type choices when creating a scheduled scan for a specific agent - if the agent lacks Scapy, the camera scan type is not offered.
Control plane connection
Section titled “Control plane connection”The agent maintains a persistent WebSocket to the control plane at:
wss://<server>/api/v1/agents/ws/<agent_id>The agent_id is embedded in the WebSocket URL path. Immediately after connecting, the agent sends a JSON authentication message:
{"agent_key": "<key>", "site_id": "<uuid>"}The backend verifies the key against the stored SHA-256 hash. The agent key is a 32-byte random token issued once at registration; the backend stores only its SHA-256 hash. The key is kept in the OS keyring (Windows Credential Manager, macOS Keychain, Linux Secret Service) - never in plaintext config files.
Commands flow server → agent (e.g. scan_network, fingerprint_device). Results, heartbeats, and scan progress flow agent → server over the same connection. If the connection drops, the agent reconnects with exponential backoff (initial 5 s, max 5 min).
Required outbound access: HTTPS (port 443) to your FreeSDN server. No inbound ports are needed on the agent machine.
Heartbeat
Section titled “Heartbeat”Every 30 seconds (configurable, 10 s-1 h) the agent sends a heartbeat containing CPU/memory/disk utilisation, active task count, uptime, and current subnet list. (A managed_devices field is present in the payload but is not available in this release; the daemon always emits 0 for that value.) Heartbeats are stored in the TimescaleDB time-series DB and drive the online/offline/error status indicator in the Agents UI.