srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/packeteer/net/lldp.hpp92
-rw-r--r--include/packeteer/summarize.hpp6
2 files changed, 98 insertions, 0 deletions
diff --git a/include/packeteer/net/lldp.hpp b/include/packeteer/net/lldp.hpp
new file mode 100644
index 0000000..ccf9f13
--- /dev/null
+++ b/include/packeteer/net/lldp.hpp
@@ -0,0 +1,92 @@
+#pragma once
+
+#include <cstdint>
+#include <cstdio>
+#include <optional>
+#include <span>
+#include <string>
+
+#include "packeteer/byteio.hpp"
+
+// IEEE 802.1AB LLDP. Sent directly on Ethernet (ethertype 0x88CC), no
+// IP layer at all - the same reasoning ARP is dispatched by ethertype
+// in summarize.hpp rather than through anything IP-based. TLV-encoded:
+// each TLV is a 2-byte header (7-bit type, 9-bit length) followed by
+// that many bytes of value, terminated by an End of LLDPDU TLV (type
+// 0). Only the three mandatory TLVs (Chassis ID, Port ID, TTL) plus
+// System Name - usually the single most useful, human-readable field
+// in the whole frame - are decoded; the rest (Port/System
+// Description, Capabilities, Management Address, and any
+// organizationally-specific TLVs) are walked over correctly (so
+// nothing after them is missed) but not rendered.
+namespace packeteer::net {
+
+inline constexpr std::uint16_t kEthertypeLldp = 0x88CC;
+
+struct LldpInfo {
+ std::optional<std::string> chassis_id;
+ std::optional<std::string> port_id;
+ std::optional<std::uint16_t> ttl;
+ std::optional<std::string> system_name;
+};
+
+// Chassis ID and Port ID share the same subtype+value shape. Subtype 4
+// (MAC address) is rendered as hex-colon; everything else (interface
+// name/alias, component, locally-assigned string, etc.) is treated as
+// ASCII text - true for every subtype except "network address"
+// (subtype 5 for Chassis ID), which has its own AFI-prefixed encoding
+// this doesn't attempt to decode specially and would render as
+// mangled text instead. Uncommon enough in practice not to be worth a
+// separate code path for a one-line summary.
+inline std::string format_lldp_id(std::uint8_t subtype, std::span<const unsigned char> value) {
+ if (subtype == 4 && value.size() == 6) {
+ char buf[18];
+ std::snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", value[0], value[1],
+ value[2], value[3], value[4], value[5]);
+ return buf;
+ }
+ return std::string(reinterpret_cast<const char*>(value.data()), value.size());
+}
+
+inline std::optional<LldpInfo> parse_lldp(std::span<const unsigned char> bytes) {
+ LldpInfo info{};
+ std::size_t pos = 0;
+ constexpr int kMaxTlvs = 32; // real LLDPDUs rarely carry more than a handful
+
+ for (int i = 0; i < kMaxTlvs; ++i) {
+ if (pos + 2 > bytes.size()) break;
+ std::uint16_t tlv_header = read_be16(bytes, pos);
+ std::uint8_t type = static_cast<std::uint8_t>(tlv_header >> 9);
+ std::uint16_t length = tlv_header & 0x01FF;
+ pos += 2;
+ if (pos + length > bytes.size()) break; // truncated: stop, keep what was decoded
+ std::span<const unsigned char> value = bytes.subspan(pos, length);
+
+ if (type == 0) break; // End of LLDPDU
+ if (type == 1 && length >= 1) {
+ info.chassis_id = format_lldp_id(value[0], value.subspan(1));
+ } else if (type == 2 && length >= 1) {
+ info.port_id = format_lldp_id(value[0], value.subspan(1));
+ } else if (type == 3 && length == 2) {
+ info.ttl = read_be16(value, 0);
+ } else if (type == 5 && length >= 1) {
+ info.system_name = std::string(reinterpret_cast<const char*>(value.data()), value.size());
+ }
+
+ pos += length;
+ }
+
+ // The three mandatory TLVs (802.1AB 9.2) - anything missing one
+ // of these isn't really a well-formed LLDPDU.
+ if (!info.chassis_id || !info.port_id || !info.ttl) return std::nullopt;
+ return info;
+}
+
+inline std::string lldp_summary(const LldpInfo& info) {
+ std::string out = "LLDP chassis=" + *info.chassis_id + " port=" + *info.port_id +
+ " ttl=" + std::to_string(*info.ttl);
+ if (info.system_name) out += " name=" + *info.system_name;
+ return out;
+}
+
+} // namespace packeteer::net
diff --git a/include/packeteer/summarize.hpp b/include/packeteer/summarize.hpp
index 4bb2d24..c261083 100644
--- a/include/packeteer/summarize.hpp
+++ b/include/packeteer/summarize.hpp
@@ -28,6 +28,7 @@
#include "packeteer/net/igmp.hpp"
#include "packeteer/net/ipv4.hpp"
#include "packeteer/net/ipv6.hpp"
+#include "packeteer/net/lldp.hpp"
#include "packeteer/net/rtcp.hpp"
#include "packeteer/net/rtp.hpp"
#include "packeteer/net/tcp.hpp"
@@ -301,6 +302,11 @@ inline std::string summarize_packet(std::span<const unsigned char> bytes, int da
return out;
}
+ if (vlan.ethertype == net::kEthertypeLldp) {
+ if (auto lldp = net::parse_lldp(vlan.payload)) out += " | " + net::lldp_summary(*lldp);
+ return out;
+ }
+
if (vlan.ethertype != net::kEthertypeIPv4 && vlan.ethertype != net::kEthertypeIPv6) {
return out;
}