Skip to content

Instantly share code, notes, and snippets.

@exeral
Created October 30, 2025 16:17
Show Gist options
  • Select an option

  • Save exeral/90762c0aea005876b84db6c6e7e7969e to your computer and use it in GitHub Desktop.

Select an option

Save exeral/90762c0aea005876b84db6c6e7e7969e to your computer and use it in GitHub Desktop.
LLDP parser
import struct
import sys
import json # Importing json module for JSON formatting
def byte_to_int(b):
"""Handles byte to integer conversion for Python 2 and 3 compatibility."""
return ord(b) if isinstance(b, str) else b
def mac_address_format(byte_sequence):
"""Format MAC address bytes for both Python 2 and 3."""
return ":".join("{:02x}".format(byte_to_int(b)) for b in byte_sequence)
def parse_lldp(data):
"""Parse LLDP packet and extract key TLVs, including VLAN information."""
lldp_info = {}
lldp_vlans = [] # List of VLANs
offset = 0
while offset < len(data):
if offset + 2 > len(data):
break
tlv_header = struct.unpack(">H", data[offset:offset + 2])[0]
tlv_type = (tlv_header >> 9) & 0x7F
tlv_length = tlv_header & 0x1FF
offset += 2
if offset + tlv_length > len(data):
break
tlv_value = data[offset:offset + tlv_length]
offset += tlv_length
if tlv_type == 1: # Chassis ID
subtype = byte_to_int(tlv_value[0])
if subtype == 4:
lldp_info['Chassis ID'] = mac_address_format(tlv_value[1:])
else:
lldp_info['Chassis ID'] = tlv_value[1:].decode('utf-8', errors='ignore')
elif tlv_type == 2: # Port ID
subtype = byte_to_int(tlv_value[0])
if subtype == 3:
lldp_info['Port ID'] = tlv_value[1:].decode('utf-8', errors='ignore')
else:
lldp_info['Port ID Raw'] = tlv_value[1:].decode('utf-8', errors='ignore')
elif tlv_type == 4: # Port desc
lldp_info['Port description'] = tlv_value.decode('utf-8', errors='ignore')
elif tlv_type == 5: # System Name
lldp_info['System Name'] = tlv_value.decode('utf-8', errors='ignore')
elif tlv_type == 6: # System Description
lldp_info['System Description'] = tlv_value.decode('utf-8', errors='ignore')
elif tlv_type == 127: # Organizationally Specific TLV
if tlv_length >= 3 and tlv_value[:3] == b'\x00\x80\xc2':
subtype = byte_to_int(tlv_value[3])
if subtype == 3: # VLAN Name TLV
vlan_id = struct.unpack(">H", tlv_value[4:6])[0]
vlan_name = tlv_value[6:].decode('utf-8', errors='ignore')
lldp_vlans.append({'VLAN ID': vlan_id, 'VLAN Name': vlan_name})
if lldp_vlans:
lldp_info['VLANs'] = lldp_vlans
return lldp_info
def parse_pcap(file_path):
"""Parse a pcap file to extract LLDP packets."""
with open(file_path, 'rb') as f:
f.read(24) # Skip pcap header
packets = []
while True:
packet_header = f.read(16)
if len(packet_header) < 16:
break
_, _, incl_len, _ = struct.unpack("IIII", packet_header)
packet_data = f.read(incl_len)
if len(packet_data) < incl_len:
break
packets.append(packet_data)
return packets
def extract_lldp_packets(packets):
"""Filter and parse LLDP packets (EtherType 0x88cc)."""
lldp_packets = []
for packet in packets:
ethertype = struct.unpack(">H", packet[12:14])[0]
if ethertype == 0x88cc:
lldp_payload = packet[14:]
lldp_packets.append(parse_lldp(lldp_payload))
return lldp_packets
def main():
if len(sys.argv) != 2:
print("Usage: python lldp_parser.py <path_to_pcap_file>")
sys.exit(1)
file_path = sys.argv[1]
packets = parse_pcap(file_path)
lldp_packets = extract_lldp_packets(packets)
# Output LLDP information as JSON
print(json.dumps(lldp_packets, indent=4))
if __name__ == "__main__":
main()
@exeral
Copy link
Author

exeral commented Oct 30, 2025

This script will parse a packet capture file (pcap) from tool like tcpdump
it will search for lldp packets and will nicely print the content of the LLDP packet

can be used like this on ESXi:
pktcap-uw --uplink vmnic0 -c 1 --ethtype 0x88cc -s 0 -o /tmp/lldp.pcap > /dev/null && wget -qO- http://example.org/lldp-parser.py | python - /tmp/lldp.pcap && rm -f /tmp/lldp.pcap

and will output something like

[
    {
        "Chassis ID": "08:05:e2:00:00:00",
        "Port ID Raw": "xe-0/0/29",
        "Port description": "the port description",
        "System Name": "switch-name.example.org",
        "System Description": "Juniper Networks, Inc. qfx5100-48t-6q Ethernet Switch, kernel JUNOS 21.4R3-S3.4, Build date: 2023-03-09 00:31:55 UTC Copyright (c) 1996-2023 Juniper Networks, Inc.",
        "VLANs": [
            {
                "VLAN ID": 166,
                "VLAN Name": "\bvlan-166"
            },
            {
                "VLAN ID": 190,
                "VLAN Name": "\bvlan-190"
            },
            {
                "VLAN ID": 191,
                "VLAN Name": "\bvlan-191"
            },
            {
                "VLAN ID": 558,
                "VLAN Name": "\bvlan-558"
            },
            {
                "VLAN ID": 69,
                "VLAN Name": "\u0007vlan-69"
            }
        ]
    }
]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment