/* * cdp-listen is part of cdp-tools. * added vlan & port_id - 2008 Allen Rueter * cdp-tools is (c) 2003-2006 Internet Connection, Inc. * * cdp-listen 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, or (at your option) * any later version. * * cdp-listen 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 cdp-tools; see the file LICENSE. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include /* Define the constants and text values for the 'type' field: */ #define TYPE_DEVICE_ID 0x0001 #define TYPE_ADDRESS 0x0002 #define TYPE_PORT_ID 0x0003 #define TYPE_CAPABILITIES 0x0004 #define TYPE_IOS_VERSION 0x0005 #define TYPE_PLATFORM 0x0006 #define TYPE_IP_PREFIX 0x0007 #define TYPE_VTP_MGMT_DOMAIN 0x0009 #define TYPE_NATIVE_VLAN 0x000a #define TYPE_DUPLEX 0x000b typedef struct _cdp_packet_header { u_int8_t version; u_int8_t time_to_live; u_int16_t checksum; } CDP_HDR; typedef struct _cfp_packet_data { u_int16_t type; u_int16_t length; } CDP_DATA; static int usage() { fprintf(stderr, "Usage: cdp-listen interfaces... | logsomething\n"); return 0; }; static int yankdo(int look_type, CDP_DATA *d, unsigned int len, void *x, int (*e)(void *x, unsigned char *data, unsigned int len)) { int hits = 0; while (len > sizeof(CDP_DATA)) { int type; int length; u_char *v; int vlen; 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 (vlen < 0 || vlen > len) { break; } if (type == look_type) { if (e) { if (e(x, v, vlen)) hits++; } else { hits++; } } len -= length; d = (CDP_DATA *)(((u_char *)d) + length); } return hits; } static int n_device_id(void *ignored, unsigned char *vdata, unsigned int len) { putchar('\t'); while (len > 0) { if (isalnum(*vdata) || *vdata == '-' || *vdata == '.') { putchar(*vdata); vdata++; len--; } else { break; } } return 1; } static int n_port_id(void *ignored, unsigned char *vdata, unsigned int len) { while (len > 0) { if (isalnum(*vdata) || *vdata == '/' ) { putchar(*vdata); vdata++; len--; } else { break; } } return 1; } static int n_device_ip4(void *cx, unsigned char *vdata, unsigned int len) { u_int32_t i, number; unsigned int q; if (len < 4) return 0; if (!cx) return 0; q = *((unsigned int *)cx); memcpy(&number, vdata, sizeof(number)); number = ntohl(number); vdata += sizeof(u_int32_t); len -= sizeof(u_int32_t); for (i = 0; len >= 3 && i < number; i++) { u_char protocol_len = vdata[1]; u_char *protocol_value = vdata+2; u_int16_t address_len; u_char *address = ((vdata+4)+protocol_len); if (len < protocol_len+4) return 0; /* failed I guess... */ memcpy(&address_len, vdata+2+protocol_len, sizeof(address_len)); address_len = ntohs(address_len); if (len < protocol_len+address_len+4) return 0; /*failed I guess*/ if (protocol_len == 1 && *protocol_value == 0xCC && address_len == 4) { if (q > 0) { q--; continue; } printf("%u.%u.%u.%u", (unsigned int)address[0], (unsigned int)address[1], (unsigned int)address[2], (unsigned int)address[3]); *((unsigned int *)cx) = (*((unsigned int *)cx)) + 1; return 1; } } return 0; } static int n_device_caps(void *cx, unsigned char *vdata, unsigned int len) { unsigned long *q = (unsigned long *)cx; u_int32_t cap; if (!q) return 0; if (len >= 4) { memcpy(∩, vdata, 4); } else if (len < 4) { memset(∩, 0, sizeof(cap)); memcpy(∩, vdata, len); } (*q) |= ntohl(cap); return 1; } static int n_nvlan_id(void *cx, unsigned char *vdata, unsigned int len) { unsigned long *q = (unsigned long *)cx; u_int16_t vlan; if (!q) return 0; if (len == 2) { memset(&vlan, 0, sizeof(vlan)); memcpy(&vlan, vdata, len); } else printf(" nvlanlen=%d ",len); (*q) |= ntohs(vlan); return 1; } static int n_ip_prefix(void *cx, unsigned char *vdata, unsigned int len) { unsigned int i, q; if (len < 4) return 0; if (!cx) return 0; q = *((unsigned int *)cx); for (i = 0; i < len; i += 5) { if (i+4 >= len) break; if (q > 0) { q--; continue; } printf("%u.%u.%u.%u/%u", (unsigned int)vdata[i], (unsigned int)vdata[i+1], (unsigned int)vdata[i+2], (unsigned int)vdata[i+3], (unsigned int)vdata[i+4]); *((unsigned int *)cx) = (*((unsigned int *)cx)) + 1; return 1; } return 0; } static void doit(char *interface, struct pcap_pkthdr *pcap_h, register u_char *pcap_p) { CDP_HDR *h; CDP_DATA *d; unsigned int len; unsigned short nvlan; unsigned long cap; h = (CDP_HDR *) (((unsigned char *)pcap_p) + 22); d = (CDP_DATA *)((((unsigned char *)pcap_p) + 22) + sizeof(CDP_HDR)); len = (pcap_h->len - 22) - sizeof(CDP_HDR);; printf("# Interface:\t%s\n# Hostname:", interface); if (!yankdo(TYPE_DEVICE_ID, d, len, 0, n_device_id)) printf("(unknown)"); putchar('\n'); { /* draw something that looks something like a /etc/hosts * record */ unsigned int i = 0; printf("# Address:\t"); while (yankdo(TYPE_ADDRESS, d, len, &i, n_device_ip4)) { printf("\n#\t\t"); } putchar('\n'); }; printf("# Port:\t"); yankdo(TYPE_PORT_ID, d, len, 0, n_port_id) ; printf("\n"); printf("# Native VLAN:\t"); yankdo( TYPE_NATIVE_VLAN, d, len, &nvlan, n_nvlan_id); printf("%d",nvlan); printf("\n"); printf("# TimeToLive:\t%u\n", h->time_to_live); cap = 0; yankdo(TYPE_CAPABILITIES, d, len, ∩, n_device_caps); printf("# Capabilities:"); if (cap & 0x01) printf(" L3R(router)"); if (cap & 0x02) printf(" L2TB(bridge)"); if (cap & 0x04) printf(" L2SRB(bridge)"); if (cap & 0x08) printf(" L2SW(switch)"); if (cap & 0x10) printf(" L3TXRX(host)"); if (cap & 0x40) printf(" L1(repeater)"); if (cap & 0x20) printf(" IGRP"); if (cap & 0xFFFFFF80) printf(" unknown(%08lX)", cap); printf("\n#\n"); { /* draw something that looks like it could call route :) */ unsigned int i = 0; printf("# Networks:\t"); while (yankdo(TYPE_IP_PREFIX, d, len, &i, n_ip_prefix)) { printf("\n#\t\t"); } printf("\n"); } printf("\n"); fflush(stdout); } int main(int argc, char *argv[]) { int c, i, j, m; char errbuf[PCAP_ERRBUF_SIZE]; struct bpf_program filter; char *filter_app = "ether multicast and ether[20:2] = 0x2000"; pcap_t **h; while((c=getopt(argc,argv,""))!=EOF) { switch (c) { default: usage(); exit(1); }; } if (argc == optind) { usage(); exit(1); } h = (pcap_t **)malloc(sizeof(pcap_t *) * ((argc - optind)+1)); if (!h) { perror("malloc"); exit(1); } for (c = 0, i = optind; i < argc; i++, c++) { bpf_u_int32 net, mask; pcap_lookupnet(argv[i], &net, &mask, errbuf); h[c] = pcap_open_live(argv[i], 65535, 1, 0, errbuf); if (!h[c]) { fprintf(stderr, "Could not open %s: %s\n", argv[i], errbuf); exit(1); } pcap_compile(h[c], &filter, filter_app, 0, net); pcap_setfilter(h[c], &filter); pcap_freecode(&filter); } if (c > 1) { u_char *packet; struct pcap_pkthdr header; fd_set rfds; for (i = 0; i < c; i++) { j = pcap_fileno(h[i]); fcntl(j, F_SETFL, fcntl(j, F_GETFL, 0) | O_NONBLOCK); } for (;;) { FD_ZERO(&rfds); for (i = 0, m = -1; i < c; i++) { j = pcap_fileno(h[i]); FD_SET(j, &rfds); if (j > m) m = j; } (void) select(m+1, &rfds, 0, 0, 0); for (i = 0; i < c; i++) { packet = (u_char *)pcap_next(h[i], &header); if (packet) { doit(argv[i-optind], &header, packet); } } } } else { if (pcap_loop(h[0], -1, (pcap_handler) doit, argv[optind]) < 0) { pcap_perror(h[0], NULL); exit(1); } } exit(0); }