fix(ui): use source in LLDP neighbor key

This commit is contained in:
Siyuan 2025-11-10 16:45:52 +00:00
parent 8957a65cae
commit 15484f889e
2 changed files with 133 additions and 1 deletions

131
internal/lldp/tlv.go Normal file
View File

@ -0,0 +1,131 @@
package lldp
import (
"encoding/binary"
"fmt"
"net"
"github.com/google/gopacket/layers"
)
var (
capabilityMap = map[string]uint16{
"other": layers.LLDPCapsOther,
"repeater": layers.LLDPCapsRepeater,
"bridge": layers.LLDPCapsBridge,
"wlanap": layers.LLDPCapsWLANAP,
"router": layers.LLDPCapsRouter,
"phone": layers.LLDPCapsPhone,
"docsis": layers.LLDPCapsDocSis,
"station_only": layers.LLDPCapsStationOnly,
"cvlan": layers.LLDPCapsCVLAN,
"svlan": layers.LLDPCapsSVLAN,
"tmpr": layers.LLDPCapsTmpr,
}
)
func tlvMgmtAddressToBytes(m *layers.LLDPMgmtAddress) []byte {
var b []byte
b = append(b, byte(len(m.Address))+1) // TLV Length
b = append(b, byte(m.Subtype)) // Address Subtype
b = append(b, m.Address...) // Address
b = append(b, byte(m.InterfaceSubtype)) // Interface Subtype
ifIndex := make([]byte, 4) // 4 bytes for the interface number
binary.BigEndian.PutUint32(ifIndex, m.InterfaceNumber)
b = append(b, ifIndex...)
b = append(b, 0) // OID type
return b
}
func tlvMgmtAddress(m *layers.LLDPMgmtAddress) layers.LinkLayerDiscoveryValue {
return layers.LinkLayerDiscoveryValue{
Type: layers.LLDPTLVMgmtAddress,
Value: tlvMgmtAddressToBytes(m),
Length: uint16(len(tlvMgmtAddressToBytes(m))),
}
}
// if err := checkLLDPTLVLen(v, 9); err != nil {
// return err
// }
// mlen := v.Value[0]
// if err := checkLLDPTLVLen(v, int(mlen+7)); err != nil {
// return err
// }
// info.MgmtAddress.Subtype = IANAAddressFamily(v.Value[1])
// info.MgmtAddress.Address = v.Value[2 : mlen+1]
// info.MgmtAddress.InterfaceSubtype = LLDPInterfaceSubtype(v.Value[mlen+1])
// info.MgmtAddress.InterfaceNumber = binary.BigEndian.Uint32(v.Value[mlen+2 : mlen+6])
// olen := v.Value[mlen+6]
// if err := checkLLDPTLVLen(v, int(mlen+7+olen)); err != nil {
// return err
// }
// info.MgmtAddress.OID = string(v.Value[mlen+7 : mlen+7+olen])
func checkLLDPTLVLen(v layers.LinkLayerDiscoveryValue, l int) (err error) {
if len(v.Value) < l {
err = fmt.Errorf("invalid TLV %v length %d (wanted mimimum %v)", v.Type, len(v.Value), l)
}
return
}
// parseTlvMgmtAddress parses the Management Address TLV and returns the Management Address
// structure.
// we don't parse the OID here, as it's not needed for the neighbor cache
func parseTlvMgmtAddress(v layers.LinkLayerDiscoveryValue) *layers.LLDPMgmtAddress {
if err := checkLLDPTLVLen(v, 9); err != nil {
return nil
}
mlen := v.Value[0]
if err := checkLLDPTLVLen(v, int(mlen+7)); err != nil {
return nil
}
return &layers.LLDPMgmtAddress{
Subtype: layers.IANAAddressFamily(v.Value[1]),
Address: v.Value[2 : mlen+1],
InterfaceSubtype: layers.LLDPInterfaceSubtype(v.Value[mlen+1]),
InterfaceNumber: binary.BigEndian.Uint32(v.Value[mlen+2 : mlen+6]),
}
}
func lldpMgmtAddressToSerializable(m *layers.LLDPMgmtAddress) ManagementAddress {
var addrString string
switch m.Subtype {
case layers.IANAAddressFamilyIPV4:
addrString = net.IP(m.Address).String()
case layers.IANAAddressFamilyIPV6:
addrString = net.IP(m.Address).String()
default:
addrString = string(m.Address)
}
return ManagementAddress{
AddressFamily: m.Subtype.String(),
Address: addrString,
InterfaceSubtype: m.InterfaceSubtype.String(),
InterfaceNumber: m.InterfaceNumber,
}
}
func tlvStringValue(tlvType layers.LLDPTLVType, value string) layers.LinkLayerDiscoveryValue {
return layers.LinkLayerDiscoveryValue{
Type: tlvType,
Value: []byte(value),
Length: uint16(len(value)),
}
}
func toLLDPCapabilitiesBytes(capabilities []string) uint16 {
r := uint16(0)
for _, capability := range capabilities {
mask, ok := capabilityMap[capability]
if ok {
r |= mask
}
}
return r
}

View File

@ -37,7 +37,8 @@ export default function LLDPNeighborsCard({
<div className="space-y-3 pt-2">
{neighbors.map(neighbor => {
const displayName = neighbor.system_name || neighbor.port_description || neighbor.mac;
return <div className="space-y-3" key={neighbor.mac}>
const key = `${neighbor.mac}-${neighbor.source}`;
return <div className="space-y-3" key={key}>
<h4 className="text-sm font-semibold font-mono">{displayName}</h4>
<div
className="rounded-md rounded-l-none border border-slate-500/10 border-l-blue-700/50 bg-white p-4 pl-4 backdrop-blur-sm dark:bg-transparent"