Skip to content

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.

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:

CapabilityDocker containerFreeSDN Agent
Raw sockets / ARPNoYes
L2 frames (LLDP, CDP)NoYes
Promiscuous modeNoYes
SNMP trap receiver (UDP 162)Port-map onlyYes
Syslog receiver (UDP 514)Port-map onlyYes
DHCP snoopingNoYes
Multi-VLAN accessNoYes
mDNS / SSDP / ONVIF multicastPartialYes

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.

DaemonDesktop app
ModeHeadless system service, runs 24/7Interactive PySide6 GUI application
Target userSysadmin deploying at a remote siteNetwork technician on-site
ConnectionPersistent WebSocket to control planeREST API
PlatformsWindows, 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).

ScannerProtocol / LayerRoot required?
arpARP (L2)Yes - raw L2 sockets via Scapy
pingICMP via subprocessNo
tcp_portTCP connectNo
http_serviceHTTP/HTTPS (L7)No
bannerSSH/Telnet banner grabNo
snmpSNMP v1/v2c (UDP 161)No
netbiosNetBIOS/SMB (UDP 137)No
mdnsmDNS/Bonjour (UDP 5353)No
ssdpSSDP/UPnP (UDP 1900)No
sipSIP OPTIONS (UDP 5060)No
dnsDNS reverse lookupNo
rtspRTSP (TCP 554)No
onvifONVIF WS-Discovery (UDP 3702)No (Scapy library needed)
sadpHikvision SADP (UDP 37020)No (Scapy library needed)

Passive listeners run as long-lived background tasks alongside the daemon. They are off by default and must be enabled in PassiveConfig.

ListenerWhat it capturesRoot required?
lldpLLDP neighbor frames (ethertype 0x88CC)Yes
cdpCisco CDP frames (multicast 01:00:0C:CC:CC:CC)Yes
snmp_trapSNMP trap PDUs (UDP 162)Yes (privileged port)
syslogSyslog messages (UDP 514)Yes (privileged port)
dhcpDHCP traffic (UDP 67/68) - detects new devices via DHCP DISCOVER/REQUEST/ACKYes

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.

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.

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.