8000
Skip to content

buraglio/lldp2map

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lldp2map

A Go tool that recursively walks SNMP LLDP neighbor tables across network devices and generates a topology diagram as PNG, PDF, Draw.io, or Excalidraw. Available as both a CLI and a cross-platform GUI.

Features

  • SNMPv2c and SNMPv3 support (MD5/SHA/SHA256/SHA512 auth; DES/AES/AES192/AES256 priv)
  • Recursive BFS discovery — follows management addresses from each LLDP neighbor, with chassis ID and ARP/NDP-table fallback for devices that do not advertise explicit management addresses
  • IPv6 NDP neighbor cache — resolves MAC-based chassis IDs to IPv6 addresses via the RFC 4293 unified neighbor table, falling back to the legacy IPv4-only ARP table
  • Address family filtering--addr-family ipv4|ipv6|both limits both displayed labels and next-hop discovery to one address family
  • Prefix exclusion--ignore-prefix accepts CIDR ranges to exclude from node labels and BFS discovery (repeatable)
  • Verbose discovery log — each BFS step explains why a device is queued, skipped, filtered, or cannot be recursed into
  • Interface address labels — optionally annotate each node with its IPv4/IPv6 addresses via --show-addrs
  • Four output formats — PNG and PDF (via Graphviz), Draw.io XML, Excalidraw JSON
  • Cross-platform GUI — launch with --gui or build the dedicated lldp2map-gui binary for a point-and-click interface with live log output and a Cancel button
  • Configurable hop depth, timeout, retries, and port
  • Port labels on edges (local port → remote port)
  • Full IPv6 support for both SNMP transport and LLDP management address discovery

Requirements

  • Go 1.21+
  • Graphviz (dot binary must be in PATH) — required for PNG/PDF output only
# macOS
brew install graphviz

# Debian / Ubuntu
sudo apt install graphviz

# RHEL / Fedora
sudo dnf install graphviz

The GUI depends on OpenGL and (on Linux) X11. These are only needed when building with GUI support (the default).

# Debian / Ubuntu
sudo apt install libgl1-mesa-dev xorg-dev

# RHEL / Fedora
sudo dnf install mesa-libGL-devel libX11-devel

Build

CLI + GUI (single binary, default)

git clone https://github.com/buraglio/lldp2map.git
cd lldp2map
go build -o lldp2map .

CLI only — no GUI, no C dependencies

Use the nogui build tag to produce a pure-Go CLI binary with no OpenGL or X11 requirements. This is the right choice for headless servers and CI environments.

go build -tags nogui -o lldp2map .

Standalone GUI binary

go build -o lldp2map-gui ./cmd/lldp2map-gui

macOS app bundle (requires the Fyne CLI)

go install fyne.io/fyne/v2/cmd/fyne@latest
fyne package -os darwin -appID io.github.buraglio.lldp2map -name lldp2map

Install to $GOPATH/bin

go install github.com/buraglio/lldp2map@latest

Usage

CLI

lldp2map <host> [flags]

GUI

# Via the combined binary
lldp2map --gui

# Via the dedicated GUI binary
lldp2map-gui

Launches a Fyne-based desktop window with all flags exposed as form fields, a live scrolling discovery log, an infinite progress bar, and Cancel / Open Result buttons.

GUI screenshot

Flags

Flag Default Description
-c, --community public SNMPv2c community string
-v, --version 2c SNMP version: 2c or 3
--username SNMPv3 username
--auth-proto SHA SNMPv3 auth protocol: MD5, SHA, SHA256, SHA512
--auth-pass SNMPv3 authentication passphrase
--priv-proto AES SNMPv3 priv protocol: DES, AES, AES192, AES256
--priv-pass SNMPv3 privacy passphrase
--sec-level authpriv SNMPv3 security level: noauth, auth, authpriv
--port 161 SNMP UDP port
--timeout 5 SNMP timeout in seconds
--retries 2 SNMP retries per request
--max-hops 10 Maximum BFS depth for recursive discovery
--show-addrs false Annotate nodes with interface IPv4/IPv6 addresses (walks IP-MIB on each device)
--addr-family both Address family for --show-addrs labels and next-hop discovery: ipv4, ipv6, or both
--ignore-prefix CIDR prefix to exclude from labels and discovery (repeatable)
-o, --output network-map.png Output file path
-f, --format png Output format: png, pdf, drawio, excalidraw
--gui Launch the graphical interface (must be the first and only argument)

Examples

SNMPv2c, default community:

lldp2map -c public 3fff::1

SNMPv2c, PDF output, limit to 3 hops:

lldp2map -c public -f pdf -o topology.pdf --max-hops 3 3fff::1

SNMPv3 with auth and privacy (recommended):

lldp2map -v 3 \
  --username netops \
  --auth-proto SHA \
  --auth-pass MyAuthPass \
  --priv-proto AES \
  --priv-pass MyPrivPass \
  --sec-level authpriv \
  -o network.png \
  3fff:1::1

SNMPv3 auth-only, show interface addresses:

lldp2map -v 3 \
  --username monitor \
  --auth-proto SHA256 \
  --auth-pass MyAuthPass \
  --sec-level auth \
  --show-addrs \
  3fff:1::1

Show only IPv6 addresses, ignore documentation and loopback prefixes:

lldp2map -c public \
  --show-addrs \
  --addr-family ipv6 \
  --ignore-prefix 3fff::/20 \
  --ignore-prefix ::1/128 \
  3fff::1

Exclude RFC1918 and loopback from discovery and labels:

lldp2map -c public \
  --ignore-prefix 10.0.0.0/8 \
  --ignore-prefix 172.16.0.0/12 \
  --ignore-prefix 192.168.0.0/16 \
  --ignore-prefix 127.0.0.0/8 \
  10.0.0.1

Export to Draw.io:

lldp2map -c public -f drawio 3fff::1

Export to Excalidraw:

lldp2map -c public -f excalidraw 3fff::1

Launch GUI:

lldp2map --gui
# or
lldp2map-gui

Example Output

Example topology diagram

The diagram above was generated from a synthetic topology using lldp2map --show-addrs. Each node shows the device name and, when --show-addrs is set, its interface addresses. Port labels appear near the originating device on each link.

How It Works

  1. Connects to the seed device via SNMP and walks the LLDP-MIB remote neighbor table
  2. Extracts neighbor system names, local/remote port descriptions, and management addresses (IPv4 and IPv6)
  3. Enqueues each discovered management address into a BFS queue for recursive discovery
  4. Optionally walks the IP-MIB address table (--show-addrs) to collect all interface addresses per device
  5. Repeats until the queue is empty or --max-hops depth is reached
  6. Renders the completed graph to the requested output format

Management Address Resolution

For recursive discovery, lldp2map uses a four-tier fallback to locate a reachable IP for each neighbor:

  1. LLDP management address (lldpRemManAddrIfId) — explicit management IP advertised by the remote device
  2. Chassis networkAddress (subtype 5) — IP encoded directly in the LLDP chassis ID field
  3. Chassis MAC → RFC 4293 NDP/ARP lookup (subtype 4) — resolves a MAC-addressed chassis ID to an IP via the queried device's unified IPv4+IPv6 neighbor table (ipNetToPhysicalPhysAddress); prefers global-unicast IPv6 over IPv4 when both exist for the same MAC
  4. Chassis MAC → legacy ARP fallback — if the RFC 4293 table is unavailable, falls back to the IPv4-only ARP table (ipNetToMediaPhysAddress)

Neighbors for which no IP can be resolved are still added to the topology map but are logged as non-recursable. Devices that are only IPv6-reachable (common in dual-stack and IPv6-only networks) are handled by tier 3. This covers devices such as MikroTik routers that advertise a MAC chassis ID without an explicit management address.

If LLDP walk fails partway (e.g., the device responds to SNMP but does not implement the LLDP MIB), the device is still added to the topology using its IP and whatever system name was retrievable.

Address Filtering

--addr-family and --ignore-prefix are applied consistently in two places:

  • Node labels — addresses collected by --show-addrs are filtered before being written to the diagram
  • BFS discovery — management addresses are filtered before being enqueued as next-hop targets; if all management addresses for a neighbor are excluded, the neighbor is logged as filtered and not recursed into

Interface Address Discovery (--show-addrs)

When --show-addrs is set, each device is additionally queried for its full interface address list using the IP-MIB:

  • Primary: ipAddressTable (RFC 4293, 1.3.6.1.2.1.4.34) — covers both IPv4 and IPv6
  • Fallback: ipAddrTable (RFC 1213, 1.3.6.1.2.1.4.20) — IPv4 only, used if the modern table is unavailable

Loopback (127.0.0.0/8, ::1) and link-local (fe80::/10) addresses are excluded. All other unicast addresses are shown in the node label, subject to --addr-family and --ignore-prefix filtering.

Output Formats

Format Flag Extension Requires
PNG png .png Graphviz
PDF pdf .pdf Graphviz
Draw.io drawio .drawio Nothing
Excalidraw excalidraw .excalidraw Nothing

Draw.io and Excalidraw exports use a circular layout computed by lldp2map. Nodes can be freely repositioned in the editor after import. Draw.io edges re-route automatically when nodes are moved; Excalidraw lines do not (re-run the tool or drag endpoints manually).

LLDP MIB OIDs

OID Name Purpose
1.0.8802.1.1.2.1.3.3.0 lldpLocSysName Local device hostname
1.0.8802.1.1.2.1.3.7.1.4 lldpLocPortDesc Local port descriptions
1.0.8802.1.1.2.1.4.1.1.4 lldpRemChassisIdSubtype Remote chassis ID subtype (4=MAC, 5=networkAddress)
1.0.8802.1.1.2.1.4.1.1.5 lldpRemChassisId Remote chassis ID (used for management address fallback)
1.0.8802.1.1.2.1.4.1.1.7 lldpRemPortId Remote port identifier
1.0.8802.1.1.2.1.4.1.1.8 lldpRemPortDesc Remote port description
1.0.8802.1.1.2.1.4.1.1.9 lldpRemSysName Remote system name
1.0.8802.1.1.2.1.4.2.1.3 lldpRemManAddrIfId Remote management addresses
1.3.6.1.2.1.4.22.1.2 ipNetToMediaPhysAddress ARP table (MAC→IPv4, legacy fallback)
1.3.6.1.2.1.4.35.1.4 ipNetToPhysicalPhysAddress Unified neighbor table (MAC→IPv4+IPv6, RFC 4293)
1.3.6.1.2.1.4.34.1.3 ipAddressIfIndex Interface addresses, IPv4+IPv6 (RFC 4293)
1.3.6.1.2.1.4.20.1.1 ipAdEntAddr Interface addresses, IPv4 only (RFC 1213, fallback)

Project Structure

lldp2map/
├── main.go                       # Entry point (GUI+CLI); routes --gui to gui.Run()
├── main_nogui.go                 # Entry point (CLI only, build tag: nogui)
├── cmd/
│   ├── root.go                   # CLI flags and run() (Cobra)
│   └── lldp2map-gui/main.go      # Standalone GUI binary entry point
├── gui/app.go                    # Fyne GUI (--gui flag or lldp2map-gui binary)
├── internal/
│   ├── discover/discover.go      # BFS discovery engine (shared by CLI and GUI)
│   ├── filter/addr.go            # Address family and prefix filtering
│   ├── snmp/client.go            # SNMP v2c/v3 client (gosnmp)
│   ├── lldp/walker.go            # LLDP MIB walker, OID parser, IP address walker
│   ├── graph/topology.go         # In-memory topology graph
│   └── render/
│       ├── layout.go             # Circular layout engine (shared)
│       ├── graphviz.go           # PNG/PDF via Graphviz dot
│       ├── drawio.go             # Draw.io XML export
│       └── excalidraw.go         # Excalidraw JSON export
├── docs/
│   ├── gen-example.go            # Synthetic example diagram generator (go:build ignore)
│   └── example.png               # Example diagram embedded in this README
├── go.mod
└── go.sum

Dependencies

Linux lldpd and SNMP

Linux hosts running lldpd do not expose LLDP neighbor data via SNMP by default. lldpd must be configured to operate as an AgentX sub-agent alongside snmpd. Without this, lldp2map can still discover a Linux host as a neighbor (seen from an adjacent device's LLDP table), but it cannot recurse into that host to find its neighbors.

This is particularly useful on operating systems like proxmox which also house LXCs and VMs that may also be running lldpd. I don't know if this works on VMWare or HyperV, or if lldp is exposed under windows, althugh it can be enabled.

To enable SNMP on a Linux host running lldpd:

  1. Install and configure snmpd, then add AgentX master support to /etc/snmp/snmpd.conf:
master agentx
  1. Start lldpd with the -x flag to connect as an AgentX sub-agent:
lldpd -x

Or, in /etc/default/lldpd (Debian/Ubuntu) or /etc/sysconfig/lldpd (RHEL/Fedora):

DAEMON_ARGS="-x"
  1. Restart both services:
systemctl restart snmpd lldpd

Once configured, lldp2map can walk the LLDP-MIB on the Linux host just like any other device.

Caveats

  • Neighbors with no resolvable IP (no management address, no chassis networkAddress, and no ARP/NDP entry for their MAC) are added to the map but not recursed into. The discovery log explicitly reports this for each such neighbor.
  • --addr-family and --ignore-prefix affect both displayed labels and BFS discovery. If all management addresses for a neighbor are excluded by these filters, that neighbor will not be recursed into (this is logged clearly).
  • Duplicate edges (A→B and B→A) are automatically deduplicated.
  • If lldpLocSysName is not available, the device IP is used as the node label.
  • --show-addrs adds one extra SNMP walk per visited device. On large networks this increases discovery time.
  • Devices with SNMP ACLs must permit access from the host running lldp2map (you do have SNMP ACLs, right?).
  • The GUI requires a display. On headless servers use the CLI.
  • Example addresses in this README use the 3fff::/20 documentation prefix defined in RFC 9637.

About

A go utility for creating a map based on LLDP data obtained via SNMP

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

0