/* * (c) Dinko Korunic, kreator@esa.fer.hr, 2004. * * Compile: * gcc -O2 -s `glib-config --cflags` `glib-config --libs` -lpcap gw-stat.c * ie: gcc -O2 -Wall -UDEBUG -I/usr/include/glib-2.0 \ * -I/usr/lib/glib-2.0/include -L/usr/lib -lglib-2.0 * -lpcap gw-stat.c * * Required libs: * GLIB, PCAP, Libc * * Usage: * gw-stat work_dir net_iface net_regexp network/netmask * ie: ./a.out `pwd` eth0 "" 161.53.71.0/255.255.255.0 * * Reference: * http://developer.gnome.org/doc/API/glib/ * http://purl.org/net/darkstat * http://www.ntop.org/ * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * * Should you choose to use and/or modify this source code, please do so * under the terms of the GNU General Public License under which this * program is distributed. */ static char rcsid[] = "$Id: gw-stat.c,v 1.1 2004/12/05 17:55:01 kreator Exp kreator $"; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* we use 64bit int */ #if !defined G_HAVE_GINT64 #error "We don't have 64bit int. Sorry." #endif /* a bunch of health macros and defines */ #define PCAP_TIMEOUT 1000 #define ETH_PKT_TYPE_IP 0x0800 #define eth_pkt_type(pkt) ntohs(((unsigned short*)pkt)[6]) #define eth_pkt_strip(pkt) (pkt + 14) #define quad2ip(a, b, c, d) \ (((a & 255) << 24) | ((b & 255) << 16) | \ ((c & 255) << 8) | (d & 255)) #define ip_pkt_len(pkt) ntohs(((unsigned short *)pkt)[1]) #define ip_pkt_proto(pkt) pkt[9] #define ip_pkt_srcip(pkt) ntohl(((unsigned int *)(pkt + 12))[0]) #define ip_pkt_destip(pkt) ntohl(((unsigned int *)(pkt + 16))[0]) #define ip_pkt_strip(pkt)(pkt + 20) extern int sighold __P ((int __sig)); extern int sigrelse __P ((int __sig)); guint64 totalpackets = 0; guint64 totaldata = 0; GHashTable *hosthash = NULL; unsigned int localip = 0, lanip = 0xffffffff, lanmask = 0; char *listenexpr = NULL, *listendev = NULL; int runme = 1, listenlink = 0; static const char *prototable[255]; static struct { unsigned char number; const char *shortname, *longname; } protocols[] = { {0, "HOPOPT", "IPv6 Hop-by-Hop Option"}, {1, "ICMP", "Internet Control Message"}, {2, "IGMP", "Internet Group Management"}, {3, "GGP", "Gateway-to-Gateway"}, {4, "IP", "IP in IP (encapsulation)"}, {5, "ST", "Stream"}, {6, "TCP", "Transmission Control"}, {7, "CBT", "CBT"}, {8, "EGP", "Exterior Gateway Protocol"}, {9, "IGP", "any private interior gateway " "(used by Cisco for their IGRP)"}, {10, "BBN-RCC-MON", "BBN RCC Monitoring"}, {11, "NVP-II", "Network Voice Protocol"}, {12, "PUP", "PUP"}, {13, "ARGUS", "ARGUS"}, {14, "EMCON", "EMCON"}, {15, "XNET", "Cross Net Debugger"}, {16, "CHAOS", "Chaos"}, {17, "UDP", "User Datagram"}, {18, "MUX", "Multiplexing"}, {19, "DCN-MEAS", "DCN Measurement Subsystems"}, {20, "HMP", "Host Monitoring"}, {21, "PRM", "Packet Radio Measurement"}, {22, "XNS-IDP", "XEROX NS IDP"}, {23, "TRUNK-1", "Trunk-1"}, {24, "TRUNK-2", "Trunk-2"}, {25, "LEAF-1", "Leaf-1"}, {26, "LEAF-2", "Leaf-2"}, {27, "RDP", "Reliable Data Protocol"}, {28, "IRTP", "Internet Reliable Transaction"}, {29, "ISO-TP4", "ISO Transport Protocol Class 4"}, {30, "NETBLT", "Bulk Data Transfer Protocol"}, {31, "MFE-NSP", "MFE Network Services Protocol"}, {32, "MERIT-INP", "MERIT Internodal Protocol"}, {33, "SEP", "Sequential Exchange Protocol"}, {34, "3PC", "Third Party Connect Protocol"}, {35, "IDPR", "Inter-Domain Policy Routing Protocol"}, {36, "XTP", "XTP"}, {37, "DDP", "Datagram Delivery Protocol"}, {38, "IDPR-CMTP", "IDPR Control Message Transport Proto"}, {39, "TP++", "TP++ Transport Protocol"}, {40, "IL", "IL Transport Protocol"}, {41, "IPv6", "Ipv6"}, {42, "SDRP", "Source Demand Routing Protocol"}, {43, "IPv6-Route", "Routing Header for IPv6"}, {44, "IPv6-Frag", "Fragment Header for IPv6"}, {45, "IDRP", "Inter-Domain Routing Protocol"}, {46, "RSVP", "Reservation Protocol"}, {47, "GRE", "General Routing Encapsulation"}, {48, "MHRP", ""}, {49, "BNA", "BNA"}, {50, "ESP", "Encapsulating Security Payload"}, {51, "AH", "Authentication Header"}, {52, "I-NLSP", "Integrated Net Layer Security TUBA"}, {53, "SWIPE", "IP with Encryption"}, {54, "NARP", "NBMA Address Resolution Protocol"}, {55, "MOBILE", "IP Mobility"}, {56, "TLSP", "Transport Layer Security Protocol " "using Kryptonet key management"}, {57, "SKIP", "SKIP"}, /* for sending Australians */ {58, "IPv6-ICMP", "ICMP for IPv6"}, {59, "IPv6-NoNxt", "No Next Header for IPv6"}, {60, "IPv6-Opts", "Destination Options for IPv6"}, {61, "", "any host internal protocol"}, {62, "CFTP", "CFTP"}, {63, "", "any local network"}, {64, "SAT-EXPAK", "SATNET and Backroom EXPAK"}, {65, "KRYPTOLAN", "Kryptolan"}, {66, "RVD", "MIT Remote Virtual Disk Protocol"}, {67, "IPPC", "Internet Pluribus Packet Core"}, {68, "", "any distributed file system"}, {69, "SAT-MON", "SATNET Monitoring"}, {70, "VISA", "VISA Protocol"}, {71, "IPCV", "Internet Packet Core Utility"}, {72, "CPNX", "Computer Protocol Network Executive"}, {73, "CPHB", "Computer Protocol Heart Beat"}, {74, "WSN", "Wang Span Network"}, {75, "PVP", "Packet Video Protocol"}, {76, "BR-SAT-MON", "Backroom SATNET Monitoring"}, {77, "SUN-ND", "SUN ND PROTOCOL-Temporary"}, {78, "WB-MON", "WIDEBAND Monitoring"}, {79, "WB-EXPAK", "WIDEBAND EXPAK"}, {80, "ISO-IP", "ISO Internet Protocol"}, {81, "VMTP", "VMTP"}, {82, "SECURE-VMTP", "SECURE-VMTP"}, {83, "VINES", "VINES"}, {84, "TTP", "TTP"}, {85, "NSFNET-IGP", "NSFNET-IGP"}, {86, "DGP", "Dissimilar Gateway Protocol"}, {87, "TCF", "TCF"}, {88, "EIGRP", "EIGRP"}, {89, "OSPFIGP", "OSPFIGP"}, {90, "Sprite-RPC", "Sprite RPC Protocol"}, {91, "LARP", "Locus Address Resolution Protocol"}, {92, "MTP", "Multicast Transport Protocol"}, {93, "AX.25", "AX.25 Frames"}, {94, "IPIP", "IP-within-IP Encapsulation Protocol"}, {95, "MICP", "Mobile Internetworking Control Pro."}, {96, "SCC-SP", "Semaphore Communications Sec. Pro."}, {97, "ETHERIP", "Ethernet-within-IP Encapsulation"}, {98, "ENCAP", "Encapsulation Header"}, {99, "", "any private encryption scheme"}, {100, "GMTP", "GMTP"}, {101, "IFMP", "Ipsilon Flow Management Protocol"}, {102, "PNNI", "PNNI over IP"}, {103, "PIM", "Protocol Independent Multicast"}, {104, "ARIS", "ARIS"}, {105, "SCPS", "SCPS"}, {106, "QNX", "QNX"}, {107, "A/N", "Active Networks"}, {108, "IPComp", "IP Payload Compression Protocol"}, {109, "SNP", "Sitara Networks Protocol"}, {110, "Compaq-Peer", "Compaq Peer Protocol"}, {111, "IPX-in-IP", "IPX in IP"}, {112, "VRRP", "Virtual Router Redundancy Protocol"}, {113, "PGM", "PGM Reliable Transport Protocol"}, {114, "", "any 0-hop protocol"}, {115, "L2TP", "Layer Two Tunneling Protocol"}, {116, "DDX", "D-II Data Exchange (DDX)"}, {117, "IATP", "Interactive Agent Transfer Protocol"}, {118, "STP", "Schedule Transfer Protocol"}, {119, "SRP", "SpectraLink Radio Protocol"}, {120, "UTI", "UTI"}, {121, "SMP", "Simple Message Protocol"}, {122, "SM", "SM"}, {123, "PTP", "Performance Transparency Protocol"}, {124, "ISIS", "over IPv4"}, {125, "FIRE", ""}, {126, "CRTP", "Combat Radio Transport Protocol"}, {127, "CRUDP", "Combat Radio User Datagram"}, {128, "SSCOPMCE", ""}, {129, "IPLT", ""}, {130, "SPS", "Secure Packet Shield"}, {131, "PIPE", "Private IP Encapsulation within IP"}, {132, "SCTP", "Stream Control Transmission Protocol"}, {133, "FC", "Fibre Channel"}, {134, "RSVP-E2E-IGNORE",""}, {255, "Reserved", ""}, {0, NULL, NULL} }; void mysignalhandler(int signal) { sighold(signal); switch(signal) { case SIGINT: runme = 0; break; default: break; } sigrelse(signal); } char *ip2string(unsigned int *ip) { static char stringip[15]; sprintf(stringip, "%d.%d.%d.%d", (*ip >> 24) & 255, (*ip >> 16) & 255, (*ip >> 8) & 255, (*ip & 255)); return stringip; } unsigned int getlocalip(char *iface) { int tmp; struct ifreq ifr; struct sockaddr sa; tmp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); strcpy(ifr.ifr_name, iface); ifr.ifr_addr.sa_family = AF_INET; if (ioctl(tmp, SIOCGIFADDR, &ifr) != 0) { fprintf(stderr, "Cannot get local IP address for iface %s\n", iface); exit(EXIT_FAILURE); } close(tmp); sa = ifr.ifr_addr; return ntohl(((struct sockaddr_in*)&sa)->sin_addr.s_addr); } void init_all(char ***args) { char *workdir = (*args)[1]; char *network = (*args)[4]; int i1, i2, i3, i4, j1, j2, j3, j4; int i; listendev = (*args)[2]; listenexpr = (*args)[3]; hosthash = g_hash_table_new(g_int_hash, g_int_equal); if (chdir(workdir) == -1) { perror("Cannot chdir"); exit(EXIT_FAILURE); } umask(022); signal(SIGINT, mysignalhandler); signal(SIGHUP, mysignalhandler); sscanf(network, "%d.%d.%d.%d/%d.%d.%d.%d", &i1, &i2, &i3, &i4, &j1, &j2, &j3, &j4); lanip = quad2ip(i1, i2, i3, i4); lanmask = quad2ip(j1, j2, j3, j4); localip = getlocalip(listendev); #ifdef DEBUG fprintf(stdout, "LAN addr: %s, local IP addr: %s\n", network, ip2string(&localip)); #endif for (i = 0; protocols[i].shortname || protocols[i].longname; ++i) prototable[protocols[i].number] = protocols[i].shortname; } void hosthashforeach(gpointer key, gpointer data, gpointer userdata) { fprintf(stderr, "IP %s, traffic %lli octets\n", ip2string(key), *(long long *) data); } void uninit_all() { g_hash_table_foreach(hosthash, hosthashforeach, NULL); } void getpkt(unsigned char *user, const struct pcap_pkthdr *pkthdr, unsigned char *pktdata) { unsigned short pkttype = 0; unsigned char *pktptr = NULL; unsigned char pktproto = 0; unsigned int pktlen = 0, pktsrcip = 0, pktdstip = 0; guint32 pktip = 0; guint32 *oldpktip = NULL; guint64 *pkttotal = NULL; totalpackets++; totaldata += pkthdr->len; switch(listenlink) { case DLT_EN10MB: pktptr = pktdata; pkttype = eth_pkt_type(pktptr); break; case DLT_RAW: pktptr = pktdata - 14; pkttype = ETH_PKT_TYPE_IP; break; case DLT_FDDI: pktptr = pktdata + 13; pktptr += 6; pktptr -= 12; pkttype = eth_pkt_type(pktptr); break; case DLT_LINUX_SLL: pktptr = pktdata + 14; pktptr -= 12; pkttype = eth_pkt_type(pktptr); break; case DLT_PPP: pktptr = pktdata + 4; pkttype = eth_pkt_type(pktptr); break; case DLT_NULL: pktptr = pktdata + 4; pktptr -= 14; if (*(int *)pktdata == AF_INET) pkttype = ETH_PKT_TYPE_IP; else pkttype = eth_pkt_type((int *)pktdata); break; case DLT_PPP_SERIAL: pktptr = pktdata - 10; pkttype = ETH_PKT_TYPE_IP; break; default: return; } if (pkttype != ETH_PKT_TYPE_IP) { #ifdef DEBUG switch (pkttype) { case 0x0806: fprintf(stderr, "ARP\n"); break; case 0x809b: fprintf(stderr, "AppleTalk\n"); break; case 0x8035: fprintf(stderr, "RARP\n"); break; case 0x814c: fprintf(stderr, "SNMP\n"); break; case 0x86dd: fprintf(stderr, "IPv6\n"); break; case 0x880b: fprintf(stderr, "PPP\n"); break; default: fprintf(stderr, "unknown type %04X\n", pkttype); } #endif return; } pktptr = eth_pkt_strip(pktptr); pktproto = ip_pkt_proto(pktptr); pktsrcip = ip_pkt_srcip(pktptr); pktdstip = ip_pkt_destip(pktptr); pktlen = ip_pkt_len(pktptr); #if 0 if ((pktsrcip & lanmask) == lanip) pktsrcip = localip; if ((pktdstip & lanmask) == lanip) pktdstip = localip; #endif #ifdef DEBUG fprintf(stderr, "IP: proto %s(%d), src %s ", prototable[pktproto], pktproto, ip2string(&pktsrcip)); fprintf(stderr, "-> dst %s, len %u\n", ip2string(&pktdstip), pktlen); #endif if ((pktsrcip & lanmask) == lanip) { pktip = pktsrcip; } else if ((pktdstip & lanmask) == lanip) { pktip = pktdstip; } else return; if (g_hash_table_lookup_extended(hosthash, (gpointer)&pktip, (gpointer)&oldpktip, (gpointer)&pkttotal)) { *pkttotal += pktlen; g_hash_table_insert(hosthash, oldpktip, pkttotal); } else { guint32 *pktipptr = g_new(guint32, 1); *pktipptr = pktip; pkttotal = g_new(guint64, 1); *pkttotal = pktlen; g_hash_table_insert(hosthash, pktipptr, pkttotal); } } void capture() { char pcaperr[PCAP_ERRBUF_SIZE] = ""; pcap_t *listenpcap = NULL; listenpcap = pcap_open_live(listendev, 100, 1, PCAP_TIMEOUT, pcaperr); if (listenpcap == NULL) { fprintf(stderr, "Cannot pcap_open_live(): %s\n", pcaperr); exit(EXIT_FAILURE); } if (pcaperr[0]) fprintf(stderr, "pcap_open_live(): %s\n", pcaperr); if (listenexpr) { struct bpf_program complistenexpr; if (pcap_compile(listenpcap, &complistenexpr, listenexpr, 1, 0) == -1) { fprintf(stderr, "Cannot pcap_compile(): %s\n", pcaperr); exit(EXIT_FAILURE); } if (pcap_setfilter(listenpcap, &complistenexpr) == -1) { fprintf(stderr, "Cannot pcap_setfilter(): %s\n", pcaperr); exit(EXIT_FAILURE); } } listenlink = pcap_datalink(listenpcap); while (runme) { if (pcap_dispatch(listenpcap, -1, (pcap_handler)getpkt, NULL) == -1) { fprintf(stderr, "Cannot pcap_dispatch(): %s\n", pcap_geterr(listenpcap)); exit(EXIT_FAILURE); } } #ifdef DEBUG { struct pcap_stat listenstat; pcap_stats(listenpcap, &listenstat); fprintf(stderr, "Received packets: %d, dropped packets %d\n", listenstat.ps_recv, listenstat.ps_drop); } #endif pcap_close(listenpcap); } int main(int argc, char **argv) { switch(argc) { case 5: break; default: fputs("Usage: listener work_dir listen_device listen_expr lan_addr/lan_mask\n", stderr); exit(EXIT_FAILURE); } init_all(&argv); capture(); uninit_all(); return EXIT_SUCCESS; }