diff --git a/internal/scans/runner.go b/internal/scans/runner.go index 8bc4964..4f359eb 100644 --- a/internal/scans/runner.go +++ b/internal/scans/runner.go @@ -259,6 +259,11 @@ func lldpRemRowsFromWalk(rows map[string]*lldpRemAggRow, pduName string, val any case "9": r.sysName = v } + // Индекс lldpRemEntry: { timeMark, localPortNum, remIndex } — часть прошивок не отдаёт колонку 2, но порт есть в суффиксе OID. + idx := parts[1:] + if len(idx) >= 3 && strings.TrimSpace(r.localPort) == "" { + r.localPort = idx[1] + } } // probeLLDPViaRemTableWalk один раз обходит всю lldpRemTable (стандарт IEEE 802.1AB). @@ -395,9 +400,13 @@ func probeLLDPViaColumnWalks(client *gosnmp.GoSNMP, ip string, checkedAt time.Ti out := make([]LLDPResult, 0, len(keys)) for k := range keys { + lp := strings.TrimSpace(ports[k]) + if lp == "" { + lp = lldpRemIndexLocalPortFromKey(k) + } item := LLDPResult{ IP: ip, - LocalPortNum: ports[k], + LocalPortNum: lp, RemoteChassisID: chassis[k], RemotePortID: remotePorts[k], RemoteSysName: sysNames[k], @@ -419,9 +428,65 @@ func probeLLDP(client *gosnmp.GoSNMP, ip string, checkedAt time.Time) []LLDPResu if len(out) == 0 { out = probeLLDPViaGetNextRemTable(client, ip, checkedAt) } + return enrichLLDPLocalPortLabels(client, out) +} + +// lldpRemIndexLocalPortFromKey суффикс ключа walk: timeMark.localPortNum.remIndex (IEEE LLDP remTable). +func lldpRemIndexLocalPortFromKey(key string) string { + parts := strings.Split(key, ".") + if len(parts) >= 3 { + return parts[1] + } + return "" +} + +// lldpLocPortDescOIDs — lldpLocPortDesc по номеру порта (RFC 4502 и встречающийся в поле вариант). +var lldpLocPortDescOIDs = []string{ + ".1.0.8802.1.1.2.1.2.1.7.1.3", + ".1.0.8802.1.1.2.1.3.7.1.3", +} + +func probeLLDPLocPortDescByPortNum(client *gosnmp.GoSNMP) map[string]string { + out := make(map[string]string) + for _, base := range lldpLocPortDescOIDs { + for k, v := range walkAsMap(client, base) { + v = strings.TrimSpace(v) + if v != "" { + out[k] = v + } + } + } return out } +func formatLocalPortLabel(portNum, ifDescr string) string { + portNum = strings.TrimSpace(portNum) + ifDescr = strings.TrimSpace(ifDescr) + if ifDescr != "" && portNum != "" && ifDescr != portNum { + return portNum + " — " + ifDescr + } + if ifDescr != "" { + return ifDescr + } + return portNum +} + +// enrichLLDPLocalPortLabels подставляет lldpLocPortDesc (как в CLI) к номеру локального порта. +func enrichLLDPLocalPortLabels(client *gosnmp.GoSNMP, items []LLDPResult) []LLDPResult { + if len(items) == 0 { + return items + } + descByPort := probeLLDPLocPortDescByPortNum(client) + if len(descByPort) == 0 { + return items + } + for i := range items { + num := strings.TrimSpace(items[i].LocalPortNum) + items[i].LocalPortNum = formatLocalPortLabel(num, descByPort[num]) + } + return items +} + func snmpOIDTrim(s string) string { return strings.TrimPrefix(strings.TrimSpace(s), ".") }