Self-hosted service
lldpd/lldpd avatar
lldpd/lldpd

lldpd and the open implementation of link layer discovery

implementation of IEEE 802.1ab (LLDP)

717 stars213 forksCNOASSERTION

At a glance

What is it?
lldpd is a C implementation of IEEE 802.1ab that both sends and receives LLDP, ships an SNMP subagent for net-snmp and partially implements LLDP-MED. It replaces the proprietary link layer protocols it was designed to supplant, and it does so with privilege separation and a daemon that has to be told which interfaces to listen on.
Who is it for?
lldpd fits a network team that needs inter-vendor link layer visibility, that already runs a mixed estate with switches from several vendors, and that is willing to run a daemon with raw socket privileges on the segments it monitors. It is the wrong tool on Windows, which the project does not support, and the wrong tool if you only need to see what is attached to one port, since a single sniffer session answers that.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 9 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

LLDP exists to replace the proprietary discovery protocols

LLDP, the Link Layer Discovery Protocol, is an industry standard protocol designed to supplant proprietary link layer protocols such as Extreme's EDP, the Extreme Discovery Protocol, and CDP, the Cisco Discovery Protocol. The goal of LLDP is to provide an inter-vendor compatible mechanism to deliver link layer notifications to adjacent network devices.

That goal is the whole reason lldpd exists as free software. In a mixed estate, the proprietary protocols do not cross vendor boundaries, so a network with gear from three manufacturers has three incompatible ways of answering the question of what is on the other end of the cable. LLDP is the neutral answer, and lldpd is an implementation of it you can run on your own machines rather than a feature you hope the switch vendor ships.

The project implements both reception and sending. That is the part that distinguishes it from a passive sniffer, and it matters for the practical use. A receiving daemon tells you about your neighbours, which is the discovery half. A sending daemon announces your own device, its capabilities and its port information, which is what lets a remote management system build a map. Neither half is much use alone.

The scope beyond plain LLDP is partial but not trivial. lldpd implements an SNMP subagent for net-snmp, so the collected information is reachable through the standard network management stack rather than only through the command line. The LLDP-MIB is partially implemented, with the most useful tables present, and lldpd also partially implements LLDP-MED, the extension that carries media capabilities such as voice and network policy.

It supports bridge, vlan and bonding, which covers the topologies where discovery gets interesting, where a port is not simply attached to one host.

Privilege separation means two processes and a chroot

lldpd uses privilege separation to increase its security, and the documentation describes the arrangement in enough detail to reason about it.

Two processes cooperate. One runs as root and does minimal stuff, and the other runs as an unprivileged user into a chroot doing most of the stuff. The point is that the process which does the parsing of received frames, which is the part that talks to untrusted input from the network, does not have root.

The setup burden that follows is not negligible. You need to create a user called `_lldpd` in a group `_lldpd`, and the documentation notes this name can be changed with `./configure`. You also need to create an empty directory `/usr/local/var/run/lldpd`, and it needs to be owned by root, not `_lldpd`, which is the kind of detail that produces a confusing failure if you get it backwards.

There is a further wrinkle with a memorable cause. If you get fuzzy timestamps from syslog, the fix is to copy `/etc/locatime` into the chroot. The documentation writes it that way, and the filename appears to be a typo for the `localtime` file that carries the local timezone, but either way the symptom it fixes is real and the fix is in the documentation.

The control side has a similar shape. `lldpcli` lets one query the information collected through the command line, and if you do not want to run it as root, the documentation says to install it setuid or setgid `_lldpd`. So the tool has a privileged daemon, a chroot for the unprivileged half, a Unix socket or similar control path, and a command line client that needs higher rights to talk to it.

The Dockerfile shows the same structure in its configure line, with `--with-privsep-user=_lldpd`, `--with-privsep-group=_lldpd` and `--with-privsep-chroot=/run/lldpd`, plus explicit paths for the control socket and the pid file, `--with-lldpd-ctl-socket=/run/lldpd.socket` and `--with-lldpd-pid-file=/run/lldpd.pid`. The runtime stage then creates the `_lldpd` group and user before the binary is copied in.

Compiling from Git is four commands and a chroot to create

For general instructions the documentation points at the website, including building from released tarballs, and then gives the from-Git path:

sh
./autogen.sh
./configure
make
sudo make install

That is the whole build. The autotools chain is in the repository, with `configure.ac`, `Makefile.am`, `autogen.sh` and the `m4/` directory, and the `libevent` entry is a submodule, which is why `.gitmodules` is present.

The four commands are simple, and what follows them is not. The `_lldpd` user and group have to exist, the chroot directory has to exist and be owned by root, and the daemon has to be started in a way that matches how your system expects daemons to start. The repository ships packaging for several systems, with `debian/` and `redhat/` directories, an `osx/` directory for the macOS installer work, and a `Dockerfile`.

The macOS path is its own small project. Homebrew is the quick route:

sh
brew install lldpd
# Or, for the latest version:
brew install https://raw.github.com/lldpd/lldpd/master/osx/lldpd.rb

Building an installer package for a specific macOS version is a separate procedure with its own configure line, using `--with-embedded-libevent` and `--without-snmp`, and the documentation stresses that it should work on the same version of macOS, and that it is important to use a separate build directory. For older OS X versions you add `CFLAGS` and `LDFLAGS` with `-mmacosx-version-min=11.1`, and for ARM64 you can configure a binary supporting both architectures by adding `ARCHS="arm64 x86_64"` to the arguments of the `make` command. You can check with `otool -l` that you got what you expected in term of supported versions.

The Android route is the most involved and the documentation is candid about why. You must not clone the repository or download the master branch from GitHub, but instead download the official release from the website and unpack it into a working directory, alongside the Android NDK version 22 or later. You install `automake`, `libtool` and `pkg-config`, then write a `compile.sh` file in the root of the `lldpd` directory that sets the toolchain, target and API variables and runs the same autotools chain with a cross-compile host, and finally you install with `make install DESTDIR=$PWD/install`.

Docker is the shortest route to a running daemon

The container path is the most concise way to get lldpd running, and the run command in the documentation shows exactly which capabilities the daemon needs:

sh
docker run --rm --net=host --uts=host \
           -v /etc/os-release:/etc/os-release \
           --cap-add=NET_RAW --cap-add=NET_ADMIN \
           --name lldpd \
           ghcr.io/lldpd/lldpd:latest

The flags are the interesting part. `--net=host` and `--uts=host` are needed because the daemon talks to the host's network stack and hostname, which is how it identifies itself in the announcements it sends. The two capability flags, `NET_RAW` and `NET_ADMIN`, are what the raw socket work requires. And the single bind mount of `/etc/os-release` is there so that the container reports the host operating system rather than the container's, which matters because the OS release information ends up in the LLDP advertisements.

The image is published at `ghcr.io/lldpd/lldpd`, and the tag has more structure than `latest`. In place of `latest` which provides the latest stable version, you may use `1`, `1.0`, `1.0.12` to match specific versions, or `master` to get the development version. So pinning to a specific patch version is a matter of changing the tag.

Querying the running daemon from the host is done with `docker exec`:

sh
docker exec lldpd lldpcli show neighbors

and for an interactive shell you use `docker exec -it lldpd lldpcli`.

The Dockerfile is a three stage Alpine build. The first stage installs autoconf, automake, libtool and alpine-sdk and runs `./autogen.sh`. The second stage installs the development libraries, libevent, libxml2, jansson, readline and libcap, plus the bsd-compat-headers, and then configures with `--enable-pie`, `--enable-hardening`, `--without-embedded-libevent`, `--without-snmp` and `--with-xml`, and builds. The final stage is the runtime image, adding the shared libraries and creating the `_lldpd` user and group, with `/etc/lldpd.d` declared as a volume for configuration.

Where the platform list is the boundary

The supported operating systems are FreeBSD, GNU/Linux, macOS, NetBSD, OpenBSD and Solaris. Windows is not supported, but you can use WinLLDPService as a transmit-only agent.

That list is the first filter for an adopter, and the Windows line is worth reading carefully. The substitute is transmit-only, so on a Windows host you would be announcing your machine to neighbours but not learning about them, which is the opposite of what a discovery tool is usually for.

The BSD and Solaris presence says something about the project's origin and audience, which is consistent with a tool that came out of the Unix network engineering tradition where link layer discovery was a sore point with proprietary protocols on the wire.

There is a second boundary in the same area, and it is not about the operating system at all. lldpd only discovers what other devices announce. If the switches in your environment do not have LLDP enabled, the daemon will run perfectly and report nothing, and there is no way for the daemon itself to fix that, because LLDP is disabled per port in the switch configuration. Several vendors ship LLDP off by default, and some expose it only through a global enable rather than per interface.

That makes the first real use of the tool a conversation with your switch vendor or with your own configuration. The daemon is the easy part. A further consequence is that LLDP traffic is a privacy consideration that the operational model makes real: the announcements your machine sends include its system name, capabilities and port description, and that information travels to every neighbour on the segment, including ones you do not own.

Against a one-off sniffer, the difference is continuous inventory

The realistic alternative is a packet capture on a specific port, and the difference in approach is about persistence rather than capability.

A sniffer gives you a point-in-time answer to a narrow question. You filter for the LLDP frames, you read the neighbours of one port, and you stop. Nothing is stored, so nothing has to be kept in sync, and there is no daemon to run on the switch or the server.

lldpd is a daemon that maintains a live view. The `lldpcli show neighbors` command reads the current state, and the SNMP subagent makes the same information available to net-snmp, which is how you get it into the monitoring and inventory systems that a network team already runs. Aimed at a documentation and inventory system rather than a one-off investigation, the value is in the continuity of the picture and in not having to be present when the device is plugged in.

There is also the sending half, which a sniffer does not do at all. Announcing your own device with LLDP is how the remote end of the link learns about you, and it is what makes management systems that consume LLDP work without vendor-specific agents.

So the decision comes down to scale and direction. One port, one question, no continuity needed: a sniffer is simpler and has fewer moving parts. A fleet of switches across several sites, with a need to know what is attached where and to expose that to a monitoring system: the daemon plus the SNMP subagent is the right shape, and the privilege separation and the chroot are the price of running it everywhere.

One point of comparison is worth naming for anyone choosing a management system: LLDP is the inter-vendor standard, while CDP and EDP are proprietary. A system built on LLDP is the one that keeps working when the switch vendor changes, which is the whole argument the standard exists to make.

Editorial conclusion

lldpd fits a network team that needs inter-vendor link layer visibility, that already runs a mixed estate with switches from several vendors, and that is willing to run a daemon with raw socket privileges on the segments it monitors. It is the wrong tool on Windows, which the project does not support, and the wrong tool if you only need to see what is attached to one port, since a single sniffer session answers that. Before adopting, verify three things: that the switches in your environment actually emit LLDP, since LLDP is not universally enabled on all vendor firmware, that your platform is on the supported list, and how you will run it under your init system, because the daemon has a chroot, a control socket and a pid file that the packaging has to arrange. The code is under a custom licence recorded in the LICENSE file, the most recent release is 1.0.22 from 2026-05-14, and the last push was on 2026-10-14.

Frequently asked questions

What is lldpd and what standard does it implement?

It is an implementation of IEEE 802.1ab, the Link Layer Discovery Protocol, written in C. It implements both reception and sending, so it can both learn about adjacent devices and announce its own, and it partially implements LLDP-MED.

Which operating systems does lldpd support?

FreeBSD, GNU/Linux, macOS, NetBSD, OpenBSD and Solaris. Windows is not supported, but the readme points to WinLLDPService as a transmit-only agent for that platform.

How do I build lldpd from source?

Run `./autogen.sh`, then `./configure`, `make` and `sudo make install`. You also need to create a user called `_lldpd` in a group `_lldpd`, and an empty directory `/usr/local/var/run/lldpd` owned by root rather than by `_lldpd`.

How do I run lldpd with Docker?

The image is `ghcr.io/lldpd/lldpd`, and the run command uses `--net=host --uts=host`, binds `/etc/os-release`, adds the `NET_RAW` and `NET_ADMIN` capabilities, and names the container `lldpd`. You can then query it with `docker exec lldpd lldpcli show neighbors`.

Can I get lldpd information into net-snmp?

Yes. lldpd implements an SNMP subagent for net-snmp to get local and remote LLDP information, and the LLDP-MIB is partially implemented with the most useful tables present. The Docker build disables SNMP with `--without-snmp`.

Official sources

  1. Issues
  2. lldpd/lldpd on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/lldpd-lldpd.svg)](https://hysenlabs.com/projects/lldpd-lldpd)

Was this useful?