/* * cdpr - Cisco Discovery Protocol Reporter * Copyright (c) 2002-2006 MonkeyMental.com * * This program will show you which Cisco device your machine is * connected to based on CDP packets received. * * This program 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 (at your option) any later version. * * This program 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 the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Version History: * * 1.0.0 LO 02-06-30 Initial Release * 1.0.1 LO 02-07-01 ifdef/endif DLT types to deal with older bpf.h headers * 1.0.2 LO 02-07-02 New filter to better identify CDP packets * 1.0.3 LO 02-07-03 loop on pcap_next until a valid packet is received. * (patch provided Martin Buck ) * 1.0.4 LO 02-07-10 Added 1ms timeout to pcap_open_live to fix *BSD * 1.0.5 LO 02-07-14 Copy packet data to local struct to resolve Bus Errors * on Sun machines. * 1.0.6 LO 02-07-15 More alignment fixes. * 1.0.7 LO 02-10-29 Port to Win32, autodetection and list generation of * PCAP capable devices. * 1.0.8 LO 03-02-13 Port to arm processor (Zaurus) using a filter specific * to arm processors to work around pcap bug * 1.1.0 LO 03-04-25 Add central server reporting code * 1.1.1 LO 03-04-26 Fix compile errors for Win32 on server code * 1.1.2 LO 03-04-27 Add config file support * 1.1.3 LO 03-04-28 Add location/description flag for extended info on server * Split server code and conf file code into seperate source files * Cleanup old server/config file code. Remove cond. compile for CDPRS * Create cdpr.h for ext. functions and common defines. * Add DNS support to the config file so you can specify a hostname * 1.1.4 LO 03-06-20 Added -n flag to override hostname sent to server. Added error * checking to the socket code. * 1.1.5 LO 03-06-23 Fix bug where hostname would not be transmitted w/o location set * 2.0.0 LO 03-06-25 Release - Major revision change due to server reporting code * 2.0.1 LO 03-07-01 Add sys/types.h to the includes in conffile.c for BSD support * 2.0.2 LO 03-08-03 Enable timeouts so that cdpr won't wait forever looking for a packet * 2.0.3 LO 03-08-04 Better cleanup after aborting due to timeout. * 2.0.4 LO 03-08-05 Timeout code that actually timesout. Set device to nonblocking * mode and loop until packet or timeout. Before, pcap_next would * block so the signal handler couldn't acutally exit. * 2.0.5 LO 03-10-22 Went back to pcap_next() to get the packet, add a default timeout * of 5 minutes. Got rid of pktcap.c and timeout.c as they are no * longer needed. * 2.1.0 LO 03-11-08 Release - Got timeout code working on WIN32 port * 2.2.0 LO 04-04-14 Added the ability specify the port to send the cdpr updates to. * 2.2.1 LO 06-07-28 Fixed segfault when not providing port to a command line selected server (-s) */ /*#include "pcap.h" */ #include #include #include #include #ifdef WIN32 #include "xgetopt.h" #include "time.h" #else #include #endif #ifdef SOLARIS #include "u_ints.h" #endif #include "cdpr.h" #include "cdp.h" void dump_ip (const u_char *ip) { char switch_ip[100]; if(cdprs>=1) { sprintf (switch_ip, "switch_ip=%d.%d.%d.%d", (int) ip[0], (int) ip[1], (int) ip[2], (int) ip[3]); cdprs_action(CDPRS_DATA, switch_ip, 0); } printf ("%d.%d.%d.%d", (int) ip[0], (int) ip[1], (int) ip[2], (int) ip[3]); } void dump_hex (const u_char *p, int len) { while (len--) { printf ("%02X ", *p++); } } void dump_ascii (const u_char *p, int len) { while (len--) { printf ("%c", (*p < ' ' || *p > '~') ? '.' : *p); ++p; } } void dump_data (const u_char *p, int len) { int i; for (i = 0; i < len; i += 16, p += 16) { printf ("%p: ", p); dump_hex (p, 16); printf (" "); dump_ascii (p, 16); printf ("\n"); } } char * get_cdp_type (int type) { int i; for (i = 0; type_vals[i].type != 0; ++i) { if (type == type_vals[i].type) { return type_vals[i].val; } } return "Unknown type"; } void print_cdp_address (u_char *v, int verbose) { u_int32_t i; u_int32_t number; memcpy (&number, v, sizeof (number)); number = ntohl (number); if (verbose > 0) { printf (" number: %d\n", number); } v += sizeof (u_int32_t); for (i = 0; i < number; ++i) { u_char protocol = *v; u_char protocol_len = *(v+1); u_char *protocol_val = v+2; /* int address_len = ntohs(*((u_int16_t *)(v+2+protocol_len)));*/ u_int16_t address_len; u_char *address_val = v+2+protocol_len+2; memcpy (&address_len, (v+2+protocol_len), sizeof(address_len)); address_len = ntohs(address_len); if (verbose > 0) { printf (" protocol: %02x\n", protocol); printf (" protocol len: %02x\n", protocol_len); printf (" protocol val: "); dump_hex (protocol_val, protocol_len); printf ("\n"); printf (" address len: %02x\n", address_len); printf (" address val: "); } else { printf (" value: "); } if (protocol_len == 1 && *protocol_val == 0xCC && address_len == 4) dump_ip (address_val); else dump_hex (address_val, address_len); printf ("\n"); v += (2+protocol_len+2+address_len); } } void print_cdp_capabilities (u_char *v) { u_int32_t cap; memcpy (&cap, v, sizeof (cap)); cap = ntohl (cap); printf (" value: %08x\n", cap); if (cap & 0x01) printf (" Performs level 3 routing for at least one network layer protocol.\n"); if (cap & 0x02) printf (" Performs level 2 transparent bridging.\n"); if (cap & 0x04) printf (" Performs level 2 source-route bridging.\n"); if (cap & 0x08) printf (" Performs level 2 switching.\n"); if (cap & 0x10) printf (" Sends and receives packets for at least one network layer protocol.\n"); if (cap & 0x20) printf (" The bridge or switch does not forward IGMP Report packets on nonrouter ports.\n"); if (cap & 0x40) printf (" Provides level 1 functionality.\n"); } void print_cdp_packet (const u_char *p, unsigned int plen, int verbose) { CDP_HDR *h; CDP_DATA *d; h = (CDP_HDR *) p; // dump_data (p, 128); if (verbose > 1 ) { printf ("\ncdp packet:\n"); printf (" version: %02x\n", h->version); printf (" time to live: %02x\n", h->time_to_live); printf (" checksum: %04x\n", ntohs (h->checksum)); } d = (CDP_DATA *) (p + sizeof (CDP_HDR)); plen -= sizeof (CDP_HDR); while (plen > sizeof (CDP_DATA)) { int type; int length; u_char *v; /* variable data */ int vlen; /* length of variable data */ CDP_DATA data; memcpy (&data, d, sizeof (CDP_DATA)); type = ntohs (data.type); length = ntohs (data.length); v = (u_char *) d + sizeof (CDP_DATA); vlen = length - sizeof (CDP_DATA); if(verbose > 0 ) { printf ("\ncdp type/len/val:\n"); printf (" type: %04x - %s\n", type, get_cdp_type (type)); printf (" length: %04x\n", length); } switch (type) { case TYPE_DEVICE_ID: printf ("%s\n", get_cdp_type (type)); printf (" value: %.*s\n", vlen, v); break; case TYPE_ADDRESS: printf ("%s\n", get_cdp_type (type)); print_cdp_address (v, verbose); break; case TYPE_PORT_ID: printf ("%s\n", get_cdp_type (type)); printf (" value: %.*s\n", vlen, v); if(cdprs >= 1) { char port[1024]; char *portval; int portlen; portval = urlencode(v, strlen(v), &portlen); sprintf(port, "&port=%.*s", portlen, portval); free(portval); cdprs_action(CDPRS_DATA, port, verbose); } break; case TYPE_CAPABILITIES: if(verbose > 0) { printf ("%s\n", get_cdp_type (type)); print_cdp_capabilities (v); } break; case TYPE_IOS_VERSION: if(verbose > 0) { printf ("%s\n", get_cdp_type (type)); printf (" value: %.*s\n", vlen, v); } break; case TYPE_PLATFORM: if(verbose > 0) { printf ("%s\n", get_cdp_type (type)); printf (" value: %.*s\n", vlen, v); } break; case TYPE_IP_PREFIX: if(verbose > 0) { printf ("%s\n", get_cdp_type (type)); printf (" value: "); dump_hex (v, vlen); printf ("\n"); } break; case TYPE_VTP_MGMT_DOMAIN: if(verbose > 0) { printf ("%s\n", get_cdp_type (type)); printf (" value: %.*s\n", vlen, v); } break; case TYPE_NATIVE_VLAN: if(verbose > 0) { printf ("%s\n", get_cdp_type (type)); printf (" value: "); dump_hex (v, vlen); printf ("\n"); } break; case TYPE_DUPLEX: if(verbose > 0) { printf ("%s\n", get_cdp_type (type)); printf (" value: "); dump_hex (v, vlen); printf ("\n"); } break; default: if(verbose > 1) { printf ("%s\n", get_cdp_type (type)); printf (" value: "); dump_hex (v, vlen); printf ("\n"); } } plen -= length; d = (CDP_DATA *) ((u_char *) d + length); } } int print_dlt(pcap_t *handle) { int type; /* Print the DLL Type*/ switch(type = pcap_datalink(handle)) { #ifdef DLT_NULL case DLT_NULL: printf("Data Link Type: BSD Loopback encapsulation.\n"); break; #endif #ifdef DLT_EN10MB case DLT_EN10MB: printf("Data Link Type: Ethernet (10MB, 100MB, 1000MB and up).\n"); break; #endif #ifdef DLT_IEEE802 case DLT_IEEE802: printf("Data Link Type: IEEE 802.5 Token Ring.\n"); break; #endif #ifdef DLT_ARCNET case DLT_ARCNET: printf("Data Link Type: ARCNET.\n"); break; #endif #ifdef DLT_PPP case DLT_PPP: printf("Data Link Type: PPP (Point-to-Point Protocol).\n"); break; #endif #ifdef DLT_FDDI case DLT_FDDI: printf("Data Link Type: FDDI.\n"); break; #endif #ifdef DLT_ATM_RFC1483 case DLT_ATM_RFC1483: printf("Data Link Type: RFC 1483 LLC/SNAP-encapsulated ATM.\n"); break; #endif #ifdef DLT_RAW case DLT_RAW: printf("Data Link Type: raw IP.\n"); break; #endif #ifdef DLT_PPP_SERIAL case DLT_PPP_SERIAL: printf("Data Link Type: PPP in HDLC-like framing.\n"); break; #endif #ifdef DLT_PPP_ETHER case DLT_PPP_ETHER: printf("Data Link Type: PPPoE.\n"); break; #endif #ifdef DLT_C_HDLC case DLT_C_HDLC: printf("Data Link Type: Cisco PPP with HDLC framing.\n"); break; #endif #ifdef DLT_IEEE802_11 case DLT_IEEE802_11: printf("Data Link Type: IEEE 802.11 wireless LAN.\n"); break; #endif #ifdef DLT_LOOP case DLT_LOOP: printf("Data Link Type: OpenBSD loopback encapsulation.\n"); break; #endif #ifdef DLT_LTALK case DLT_LTALK: printf("Data Link Type: Apple LocalTalk.\n"); break; #endif default: printf("%d is an unknown Data Link Transport\n", type); } return 0; } int usage(void) { puts("d: Specify device to use (eth0, hme0, etc.)"); puts("h: Print this usage"); puts("t: time in seconds to abort waiting for a packet (should be > 60)"); puts("v[vv]: Set verbose mode"); puts("\n** Options dealing with server updates: **"); puts(" u: Send cdpr information to a cdpr server\n requires config file as arg"); puts(" l: Location/description of this port for use with -u or -s and -p"); puts(" n: Override the hostname reported to the server for use with -u or -s and -p"); puts(" s: Server to send information to specify port with a : after Server/IP\n Example: 192.168.1.20:88 (default: 80) requires -p (overridden by -u)"); puts(" p: Path of server script to send data to requires -s (overridden by -u)"); exit(0); } int main(int argc, char *argv[]) { pcap_t *handle; char *dev = NULL; char *conf = NULL; char *loc = NULL; char *name = NULL; char *svr = NULL; char *path = NULL; char errbuf[PCAP_ERRBUF_SIZE]; struct bpf_program filter; /* ** Filter Expression: 01:00:0c:cc:cc:cc Multicast Mac Address ** This filter doesn't work on the arm, so use all multicast ** ether[20:2] = 0x2000: CDP signature in LLC */ #ifdef ZAURUS char filter_app[] = "ether multicast and ether[20:2] = 0x2000"; #else char filter_app[] = "ether host 01:00:0c:cc:cc:cc and ether[20:2] = 0x2000"; #endif bpf_u_int32 mask; bpf_u_int32 net; struct pcap_pkthdr header; const u_char *packet; char version[] = "2.2.1"; int c; int verbose=0; int locdetail=0; int nameoverride=0; int cdprs_config=0; int cdprs_cmdline=0; unsigned int seconds=300; time_t start_time=0; /* Zero out some global variables */ timeout=1; cdprs=0; memset (errbuf, 0, sizeof (errbuf)); /* Print out header */ printf("cdpr - Cisco Discovery Protocol Reporter\nVersion %s\n", version); printf("Copyright (c) 2002-2006 - MonkeyMental.com\n\n"); /* Check command-line options */ while((c = getopt(argc, argv, "d:t:vhu:l:n:s:p:")) !=EOF) switch(c) { case 'd': dev = optarg; break; case 'v': verbose++; break; case 't': seconds = atoi(optarg); printf("Timeout enabled for %u seconds\n", seconds); break; case 'u': conf = optarg; cdprs = 1; cdprs_config=1; if(cdprs_cmdline) { puts("Overriding command line server arguments with config file\n"); cdprs_cmdline = 0; } cdprs_action(CDPRS_INIT, conf, 0); break; case 'l': loc = optarg; locdetail = 1; break; case 'n': name = optarg; nameoverride = 1; break; case 's': svr = optarg; if(cdprs_config) { puts("Config file overrides cmd line"); } else { if(cdprs != 1) cdprs = 1; cdprs_cmdline++; } break; case 'p': path = optarg; if(cdprs_config) { puts("Config file overrides cmd line"); } else { if(cdprs != 1) cdprs = 1; cdprs_cmdline++; } break; case 'h': case '?': usage(); break; } /* ** If we are using command line server paths, validate and buld ** build data structure using functions that are normally used ** when parsing the config file */ if(cdprs && (cdprs_cmdline == 2)) { cdprs_action(CDPRS_INIT, "", 0); do_something_with(svr, path); } else if(cdprs && (cdprs_cmdline != 0)) { puts("Error only -s or -p was specified but not both"); exit(2); } /* Get a pcap capable device */ if(dev == NULL) { int i = 0; int inum; pcap_if_t *d; pcap_if_t *alldevs; /* The user didn't provide a packet source: Retrieve the device list */ if (pcap_findalldevs(&alldevs, errbuf) == -1) { fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf); exit(1); } /* Print the list */ for(d=alldevs; d; d=d->next) { printf("%d. %s", ++i, d->name); if (d->description) printf(" (%s)\n", d->description); else printf(" (No description available)\n"); } if(i==0) { printf("\nNo interfaces found! Make sure pcap is installed.\n"); return -1; } printf("Enter the interface number (1-%d):",i); scanf("%d", &inum); if(inum < 1 || inum > i) { printf("\nInterface number out of range.\n"); /* Free the device list */ pcap_freealldevs(alldevs); return -1; } /* Jump to the selected adapter */ for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++) { ; } dev = d->name; } printf("Using Device: %s\n", dev); /* Get the network number and netmask */ pcap_lookupnet(dev, &net, &mask, errbuf); /* Open the pcap device */ if((handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf)) == NULL) { printf("Error opening device (%s)\n", errbuf); exit(1); } else if (strlen(errbuf)) { printf("Warning opening device (%s)\n", errbuf); } /* Compile the pcap filter */ pcap_compile(handle, &filter, filter_app, 0, net); /* Activate the pcap filter */ pcap_setfilter(handle, &filter); pcap_freecode(&filter); /* Set non-blocking mode */ if(pcap_setnonblock(handle, 1, errbuf)) { pcap_perror(handle, NULL); } /* Get the next packet that comes in, we only need one */ printf("Waiting for CDP advertisement:\n"); printf("(default config is to transmit CDP packets every 60 seconds)\n"); /* Get current time to check for timeout */ start_time = time(NULL); do { packet = pcap_next(handle, &header); #ifdef WIN32 Sleep(10000); #else usleep(10000); #endif } while ((!packet) && ( timeout=((start_time+seconds) > (unsigned int)time(NULL))) ); /* ** timeout expired. clean up and exit */ if(timeout == 0) { puts("Aborting due to timeout"); pcap_close(handle); return(2); } /* Print its length */ if(verbose > 0) { printf("Received a CDP packet, header length: %d\n", header.len); } // print cdp packet, 22 bytes into packet print_cdp_packet (packet+22, header.len-22, verbose); if (verbose > 1) { print_dlt(handle); } pcap_close(handle); if(cdprs >=1) { if(locdetail >=1) { set_location(loc); } if(nameoverride >= 1) { get_hostname(nameoverride, name); } else { get_hostname(0, NULL); } cdprs_action(CDPRS_SEND, "", verbose); } return(0); }