Skip to content

[Bug]: snmp_topology selects 127.0.0.1 as every device's management IP, collapsing all devices into a single actor #23480

Description

@cshuttle

Bug description

snmp_topology picks 127.0.0.1 as the management IP of every SNMP device that exposes a loopback row in ipAddrTable. Because it picks the same address for every device, the map's default nodes_identity: ip then collapses the entire managed estate into a single actor.

On a 7-device UniFi estate (1 gateway, 3 switches, 3 APs, all polled fine, all with correct sysName), the topology:snmp function returns:

nodes_identity: ip   ->  managed SNMP devices: 1
    sys_name = LivingRoomAP, management_ip = 127.0.0.1, type = access_point,
    display_name = coreswitch.int.intergrem.com

nodes_identity: mac  ->  managed SNMP devices: 7
    ...every one of them with management_ip = 127.0.0.1

One node, wearing one device's hostname, another's sysName, and a third's device type. Endpoint actors render correctly throughout, so nothing else about the map looks broken.

Two defects compound. Either one alone would be harmless; together they produce the collapse.

1. No loopback exclusion when harvesting management addresses.

topology_cache_interfaces.go:81-95updateIfIndexByIP() appends every address from the ip_if_index walk (IP-MIB ipAddrTable) to localDevice.ManagementAddresses, with no filter for loopback, link-local or unspecified addresses:

c.ifIndexByIP[ip] = ifIndex
c.localDevice.ManagementAddresses = appendManagementAddress(c.localDevice.ManagementAddresses, topologymodel.ManagementAddress{
    Address:     ip,
    AddressType: managementAddressTypeFromIP(ip),
    Source:      "ip_mib",
})

Most agents list 127.0.0.1 in ipAddrTable. Every UniFi device here does:

$ snmpwalk ... 192.168.1.240 1.3.6.1.2.1.4.20.1.2
iso.3.6.1.2.1.4.20.1.2.127.0.0.1     = INTEGER: 1
iso.3.6.1.2.1.4.20.1.2.192.168.1.240 = INTEGER: 3

2. Management IP is chosen by lexicographic string sort.

topology_management_address.go:92-128pickManagementIP() sorts the candidates as strings and returns the first:

if len(ipValues) > 0 {
    sort.Strings(ipValues)
    return ipValues[0]
}

"127.0.0.1" sorts below "192.168.1.240", so the loopback wins — on every device, identically, which is what merges them via normalizeTopologyDevice() (topology_device.go:27).

This defect bites independently of loopback. A device with several real addresses gets an arbitrary one. Our gateway's ipAddrTable is:

97.165.236.22, 127.0.0.1, 192.161.76.43, 192.168.0.0, 192.168.1.1, 192.168.2.1

Even with loopback filtered, lexicographic order returns 192.161.76.43 — the public WAN address — in preference to the actual management address 192.168.1.1. String order also puts "9x.x.x.x" last, so any 9x/8x-prefixed address loses to any 1x one regardless of what it is.

Expected behavior

snmp_topology should select a real management address per device, and distinct devices should stay distinct actors under the default nodes_identity: ip.

Concretely:

  1. Exclude loopback, link-local (169.254/16, fe80::/10) and unspecified addresses when building ManagementAddresses from IP-MIB — or at minimum skip them in pickManagementIP().
  2. Replace the lexicographic sort with a preference order, then compare numerically as IPs rather than as strings. A reasonable order: the target the collector actually polls the device on (already available as DeviceConnectionInfo.Hostname, and already passed into refreshDeviceTopology, though it may be a DNS name rather than a literal IP) > LLDP/CDP-advertised management address > private/RFC1918 > other global > link-local > loopback.

Point 2 alone would fix both symptoms and is the more valuable half — preferring the polled address is both obviously correct and information the collector already has.

Steps to reproduce

  1. Configure go.d/snmp.conf with two or more SNMP devices whose ipAddrTable includes 127.0.0.1 (any stock net-snmp agent, and all UniFi switches/APs/gateways).
  2. Ensure the matched profile supplies the ip_if_index topology kind — e.g. one extending _std-topology-fdb-arp-mib.yaml.
  3. Let snmp_topology complete a refresh, then call the topology:snmp function with the defaults (map_type: high_confidence_inferred, nodes_identity: ip).
  4. Every device reports management_ip: 127.0.0.1, and the devices are merged into one actor. Switching nodes_identity: mac separates them again but the management IP is still 127.0.0.1 on each.

Installation method

kickstart.sh

System info

Linux 7.0.14-6-pve x86_64 GNU/Linux
Debian GNU/Linux 13 (trixie)  (Proxmox VE 9.2.5)

SNMP devices: UniFi UCG Fiber gateway, 2x USW Lite 16 PoE, 1x USW Pro Max 24 PoE, 2x UAP-AC-LR, 1x U6 Mesh — SNMPv3 authPriv.

Netdata build info

Netdata Version _____ : v2.11.0
Package Architecture  : x86_64
Package Distro _______ : debian 13
Kernel Version _______ : 7.0.14-6-pve

Both functions are byte-identical on master as of filing, so this is not fixed there.

Additional info

Workaround, for anyone hitting this before it is fixed. updateIfIndexByIP() returns early when a row yields no topo_ip_addr tag, and the tag processor emits no tag at all when a match_pattern fails to match (tag_processor.go — a non-match is not a passthrough). So re-declaring the ip_if_index kind with a pattern constrained to the real management subnet drops the loopback row before it reaches either defect:

topology:
  - kind: ip_if_index
    MIB: IP-MIB
    table:
      OID: 1.3.6.1.2.1.4.20
      name: ipAddrTable
    symbols:
      - OID: 1.3.6.1.2.1.4.20.1.2
        name: ip_if_index
    metric_tags:
      - tag: topo_ip_addr
        symbol:
          OID: 1.3.6.1.2.1.4.20.1.1
          name: ipAdEntAddr
          match_pattern: '^(192\.168\.[0-9]{1,3}\.[1-9][0-9]{0,2})$'
          match_value: "$1"
      - tag: topo_if_index
        symbol:
          OID: 1.3.6.1.2.1.4.20.1.2
          name: ipAdEntIfIndex
      - tag: topo_ip_netmask
        symbol:
          OID: 1.3.6.1.2.1.4.20.1.3
          name: ipAdEntNetMask

With that in place, same devices, same defaults: 1 managed device becomes 7, each with its real address, and actors/links go from 108/110 to 118/123. It obviously only works if you can hardcode your management subnet, which is why it is a workaround and not a fix.

Unrelated observation from the same estate, mentioned in case it is useful rather than as part of this report: none of the shipped ubiquiti-* profiles declare a topology: section or extend any _std-topology-* profile, so a UniFi estate produces a device-only map with zero links out of the box. UniFi also does not implement LLDP-MIB over SNMP (lldpLocSysName/lldpRemTable return No Such Object on the gateway, the switches and the APs), while BRIDGE-MIB/Q-BRIDGE/STP/ARP are all present — so FDB-based inference works well for these devices once a profile asks for it. Happy to open that separately or send a PR adding the topology extends to the Ubiquiti profiles if that would be welcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions