Menu

#114 Handling neighbors with multiple CDP/LLDP entries

closed
cdp (1) lldp (2)
2015-02-21
2014-04-30
No

Hi,

Not sure if this would be called a bug, or a feature request -- you decide. :-)

For some neighbor devices, CDP.pm and LLDP.pm find and return multiple dissimilar SNMP entries. The order of the entries can be different each time, and Netdisco keeps only the last (processed) entry. This can make two similar neighbors appear dissimilar, and also makes Netdisco results vary slightly, each time a discovery is run.

Perhaps Netdisco could concatenate all (dissimilar) results, or could select a constant choice (eg: alphanumerically lowest/highest) from the entries?


For example, using snmpget to query an HP Procurve 5400 switch for its neighboring HP controlled MSM access point gives 4 entries:

cdpCacheDeviceId.22.1 = STRING: "CN32DWY23L"
cdpCacheDeviceId.22.2 = Hex-STRING: 84 34 97 B6 B8 DA
cdpCacheDevicePort.22.1 = STRING: "eth0"
cdpCacheDevicePort.22.2 = STRING: "Port 1"
cdpCachePlatform.22.1 = STRING: "5.7.3.0-sr2-13986E-MSM430"
cdpCachePlatform.22.2 = STRING: "HP AP Controlled,CN32DWY23L,J9650-60001:[...]"
lldpRemChassisId.0.22.1 = STRING: "CN32DWY23L"
lldpRemChassisId.0.22.2 = Hex-STRING: 84 34 97 B6 B8 DA
lldpRemPortDesc.22.1 = STRING: "eth0"
lldpRemPortId.22.2 = Hex-STRING: 84 34 97 B6 B8 DA
lldpRemPortDesc.8.0.22.1 = ""
lldpRemPortDesc.8.0.22.2 = STRING: "Port 1"
lldpRemSysDesc.10.0.22.1 = STRING: "5.7.3.0-sr2-13986E-MSM430"
lldpRemSysDesc.10.0.22.2 = STRING: "HP AP Controlled,CN32DWY23L,J9650-60001:[...]"

Values returned from c_id, c_port, c_type:

LLDP: id CN32DWY23L        port eth0   type 5.7.3.0-sr2-13986E-MSM430
CDP : id CN32DWY23L        port eth0   type 5.7.3.0-sr2-13986E-MSM430
LLDP: id 84:34:97:b6:b8:da port 1      type HP AP Controlled,CN32DWY23L,J9650-60001:[...]
CDP:  id 84:34:97:b6:b8:da port Port 1 type HP AP Controlled,CN32DWY23L,J9650-60001:[...]

CDP.pm and LLDP.pm each return 2 of the above entries, and the order does not seem to be a constant (possibly the unpredictability comes from discovery protocol packet timing?).

Currently, Netdisco will display one of the above 4 entries. Perhaps instead, Netdisco could concatenate all (dissimilar) entries, resulting in something like:

 id   = CN32DWY23L | 84:34:97:b6:b8:da
 port = eth0 | Port 1 | 1
 type = HP AP Controlled,CN32DWY23L,J9650-60001:[...] | 5.7.3.0-sr2-13986E-MSM430

Or perhaps a simpler/cleaner solution: Netdisco could always choose a constant entry (eg: alphabetically lowest or highest). This would lose some information (same as is done now), but would be a simple way to reduce/eliminate variability with time and between devices. Discover.pm could add something like:

  if ((defined $portrow->remote_ip) and ($portrow->remote_ip eq $remote_ip)) {
    if ($remote_id   gt $portrow->remote_id  ) {$remote_id   = $portrow->remote_id  };
    if ($remote_port gt $portrow->remote_port) {$remote_port = $portrow->remote_port};
    if ($remote_type gt $portrow->remote_type) {$remote_type = $portrow->remote_type};
  }

Environment:

- Device being SNMP-polled: HP J8698A Switch E5412zl, revision K.15.07.0008
- Neighbor device: HP AP Controlled, J9650-60001:54-A, 5.7.3.0-sr2-13986E-MSM430
- CDP and LLDP enabled on both devices.

$ uname -a
Linux NetDisco 3.13.0-24-generic #46-Ubuntu SMP Thu Apr 10 19:11:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

$ perl -v
This is perl 5, version 18, subversion 2 (v5.18.2) built for x86_64-linux-gnu-thread-multi
(with 41 registered patches, see perl -V for more detail)

$ egrep '^\$VERSION' .../perl5/SNMP/Info.pm
$VERSION = '3.13';

$ grep VERSION .../perl5/App/Netdisco.pm
our $VERSION = '2.026000';

Discussion

  • Oliver Gorwits

    Oliver Gorwits - 2014-04-30

    Ticket moved from /p/netdisco/netdisco2/113/

     
  • Oliver Gorwits

    Oliver Gorwits - 2014-04-30

    Ticket moved from /p/snmp-info/bugs/55/

    Can't be converted:

    • _milestone: None
    • _priority: None
     
  • Joel Leonhardt

    Joel Leonhardt - 2014-06-30

    The following patch handles the multiple LLDP/CDP entries, and also:

    • keeps available information, by concatenating entries
    • minimizes string length, by removing duplicates
    • avoids randomly ordering changes, by sorting entries
    • maximizes port matches, by prioritizing known port names
    bash> cd .../perl5/lib/perl5/App/Netdisco/Core
    bash> diff Discover.pm.original.v2.027004 Discover.pm.patched
    687c687,688
    <               $remote_type = 'AP: '. $remote_type;
    ---
    >               $remote_type = 'AP: '. $remote_type
    >                 if $remote_type !~ /\bAP\b/; # avoid unnecessarily making LLDP differ from CDP
    690c691,692
    <           $portrow->update({remote_type => $remote_type});
    ---
    >           # save remote_type immediately, only if no previous value, else save later
    >           $portrow->update({remote_type => $remote_type}) unless ($portrow->remote_type);
    783a786,820
    >       # handle multiple records for same neighbor (eg: due to CDP and LLDP, or multiple CDP)
    >       if ((defined $portrow->remote_ip) and ($portrow->remote_ip eq $remote_ip)) {
    >         debug sprintf
    >           ' [%s] neigh - port %s has more %s info: port (%s) & (%s), id (%s) & (%s), type (%s) & (%s)',
    >           $device->ip, $port, $portrow->remote_ip,
    >           $portrow->remote_port, $remote_port,
    >           $portrow->remote_id  , $remote_id,
    >           $portrow->remote_type, $remote_type;
    >
    >         # for remote_id and remote_type, concatenate new info with existing, de-duplicate and sort
    >         $remote_id  =join(' | ',sort(keys{map{$_, 1}(split(/ \| /,$portrow->remote_id  ),$remote_id  )}));
    >         $remote_type=join(' | ',sort(keys{map{$_, 1}(split(/ \| /,$portrow->remote_type),$remote_type)}));
    >
    >         # for remote_port, try to find a valid (known) port
    >         my $old=$portrow->remote_port;
    >         my $new=$remote_port;
    >         if ( $old && $new && ($old ne $new) &&
    >              (index($old, " | ") == -1) &&
    >              (schema('netdisco')->resultset('DevicePort')
    >                  ->single({ip => $remote_ip, port => $old})) ) {
    >             $remote_port = $old; #already had valid port; keep it and throw away new info
    >         }
    >         elsif ( $old && $new && ($old ne $new) &&
    >                 (index("| $old |", "| $new |") == -1) &&
    >                 (schema('netdisco')->resultset('DevicePort')
    >                    ->single({ip => $remote_ip, port => $new})) ) {
    >             $remote_port = $new; #new port is valid; keep it and throw away old info
    >         }
    >         else {
    >           # neither is a valid port; concatenate new info with existing, de-duplicate and sort
    >           $remote_port=join(' | ',sort(keys{map{$_, 1}(split(/ \| /,$portrow->remote_port),$remote_port)}));
    >         }
    >
    >      }
    >
    
     
  • Oliver Gorwits

    Oliver Gorwits - 2014-07-29

    Hi Joel,

    Unfortunately your patch does not cleanly apply to my copy of the source tree (even with offsets, there is an error in the third hunk).

    Please would you be able to send the output of diff -urN as an attachment?

    regards,
    oliver.

     
  • Oliver Gorwits

    Oliver Gorwits - 2014-07-29
    • status: new --> taken
    • assigned_to: Oliver Gorwits
     
  • Joel Leonhardt

    Joel Leonhardt - 2014-07-29

    Hi Oliver,

    Here is an updated diff, with the patch applied to App-Netdisco-2.028012.

    Attachment: diff-urN_v2.028012.txt

     
  • Oliver Gorwits

    Oliver Gorwits - 2014-07-31

    Many thanks Joel,

    We discussed this in the dev team and we'll probably apply parts of the patch at first, and consider the implications of other parts separately. The sorting is a good idea, as is checking the port on the remote device. The concatenation of IDs needs more discussion.

    Thanks once again,

     
  • Oliver Gorwits

    Oliver Gorwits - 2014-07-31

    Hi, in the end I've taken a different tack, and I'd appreciate you letting us know whether it makes a difference.

    I've applied a sort to the processing of neighbor data, so that should make it at least consistent. I've added a version of your patch to match APs with "AP" in the remote type.

    As mentioned above, I've left the concatenation to one side for now. We need to think a bit more about the implications. In you case it seems actually that the HP is simply making some questionable decisions about how it presents CDP and LLDP data in the SNMP MIB. What would be better is for us to handle receiving legitimate multiple neighbors where there's e.g. an unmanaged switch passing through the data. That would require something a bit smarter in Netdisco and possibly a DB Schema modification.

    Please do feed back whether the next release helps with the consistency and AP detection (wifi icon should appear in ports view), at least.

    regards,
    oliver.

     
  • Oliver Gorwits

    Oliver Gorwits - 2014-07-31
    • status: taken --> closed