Skip to content

Instantly share code, notes, and snippets.

@sokratisg
Created June 19, 2014 09:44
Show Gist options
  • Save sokratisg/cd3aab9de401f1d9d6c8 to your computer and use it in GitHub Desktop.
Save sokratisg/cd3aab9de401f1d9d6c8 to your computer and use it in GitHub Desktop.
listen & print Cisco CDP through tcpdump or snoop
#!/usr/bin/perl -w
#
# Listen for Cisco Discovery Protocol (CDP) packets
# and print out key values such as switch, port and vlan.
#
# This script depends on either "snoop" (Solaris) or
# "tcpdump" (Linux, AIX, and others). Both of those programs generally
# must be run as root.
#
# It has been tested on Solaris 10 and Linux (CentOS, Ubuntu)
#
#
# This work is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or any later
# version.
# This work is distributed in the hope that it will be useful, but without
# any warranty; without even the implied warranty of merchantability or
# fitness for a particular purpose. See version 2 and version 3 of the GNU
# General Public License for more details.
#
# Latest update: Dariusz Ankowski
# Version 1.2
# May 2008
# Some code cleaning, this version works also on Linux.
#
# Original code: Andy Welter
# Version 1.1
# July 2007
# Support timeout values while waiting on the cdp packet.
#
# Version 1.0
# December 2006
# Initial Version.
use Getopt::Std;
my $cmd = undef;
my $idev;
my $verbose;
my $timeout=60;
my $temp_pkt=undef; # Workaround for tcpdump
our ($opt_h, $opt_i, $opt_t);
sub usage {
print<<EOF;
Usage: $0 -i devX [-t tmout] [-v]
-i devX : Use the devX device name for the interface to watch.
-t tmout : Timeout value in seconds. Don't wait for a CDP packet longer than this.
Default is 60 seconds. 0 means no limit.
-v : Verbose output.
-h : This help message.
EOF
exit 1;
}
sub timeout {
die "TIMEOUT";
};
sub hexprint {
(my $str = shift) =~ s/(.|\n)/sprintf("%02lx ", ord $1)/eg;
return $str;
}
# Parse output for the packet
sub snoop {
my ($cmd)=@_;
my $packet=$temp_pkt;
open (GETPACKET, "$cmd") || die "Cannot open $cmd\n";
while ( $_ = <GETPACKET> ) {
chomp;
$verbose && print "--> $_\n";
if (s/^\s*[\dxa-fA-F]+:?\s+//) {
@data=split /\s+/,$_,9;
pop @data;
foreach $bytes (@data) {
$packet=$packet . pack "H4", $bytes;
};
};
};
close GETPACKET;
return $packet;
};
# Parse the acquired CDP packet for key values.
sub decodePacket {
# decode the packet
# ethernet layout:
# 0-7 8 byte preamble
# 8-13 6 byte dest mac addr
# 14-19 6 byte source mac addr
# 20-21 2 byte type field
# 22-23 2 byte check sum
# 24-25 2 byte ???
# 26-27 2 byte first CDP data field
# 28-29 2 byte field length (including field type and length)
# 30-- Variable data.
# 4 byte CRC field.
#
# Field type indicators
# Device-ID => 0x01
# Version-String => 0x05
# Platform => 0x06
# Address => 0x02
# Port-ID => 0x03
# Capability => 0x04
# VTP-Domain => 0x09
# VLAN-ID => 0x0a
# Duplex => 0x0b
# AVVID-Trust => 0x12
# AVVID-CoS => 0x13);
my ($packet)=@_;
my ($plen,$string,$ii,$flength,$switchName,$switchPort,$ftype,$vlan) = undef;
$verbose && printf "packet len=%d\n",length($packet);
# The CDP packet data starts at offset 26
$ii=26;
$plen=length ($packet);
while ( $ii < $plen-4) {
$ftype=unpack "n", substr ($packet, $ii, 2);
$flength=unpack "n", substr ($packet, $ii+2, 2);
if ($ftype==1) {
$switchName=substr ($packet,$ii+4,$flength-4);
} elsif ($ftype==3) {
$switchPort=substr ($packet,$ii+4,$flength-4);
} elsif ($ftype==10) {
$vlan=unpack "n",substr ($packet,$ii+4,$flength-4);
}
if ($verbose) {
$string=substr ($packet,$ii+4,$flength-4);
$fvalue=hexprint ($string);
$string=~s/[[:^print:]]/./g;
printf "\noffset=%d, type 0x%04x, length 0x%04x\nHex Value:\n%s\nASCII value:\n%s\n\n",
$ii,$ftype, $flength-4,$fvalue,$string;
}
if ($flength == 0 ) {
$ii=$plen;
};
$ii=$ii+$flength;
};
# Print results
print "Switch: $switchName\n";
print "Port: $switchPort\n";
print "VLAN: $vlan\n";
};
# Check for parameters
getopts('vht:i:');
usage if defined $opt_h;
$timeout=$opt_t if defined $opt_t;
$verbose=$opt_v if defined $opt_v;
if (defined $opt_i) {
$idev=$opt_i;
} else {
usage;
}
#
# MAIN ROUTINE
#
# determine whether we are a snoop or tcpdump kinda system
$cmd=`which tcpdump 2>/dev/null`;
chomp $cmd;
if ( -x "$cmd" ) {
$cmd="$cmd -i $idev -s 1500 -X -c 1 'ether [20:2] = 0x2000' 2>/dev/null |";
$temp_pkt="01234567890123";
} else {
$cmd=`which snoop 2>/dev/null`;
chomp $cmd;
if ( -x "$cmd" ) {
$cmd="$cmd -d $idev -s 1500 -x0 -c 1 'ether[20:2] = 0x2000' 2>/dev/null |";
} else {
print "ERROR: neither snoop nor tcpdump in my path\n";
exit 1;
}
}
$SIG{ALRM}=\&timeout;
eval {
alarm ($timeout);
$packet=snoop($cmd);
alarm(0);
};
if ($@ =~ "TIMEOUT") {
print "No CDP packet - sorry\n";
exit 1;
};
#
# Decode the acquired packet and print the results.
decodePacket($packet);
# The End.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment