/*** This Programs/Libraries are (C)opyright by Sebastian Krahmer. *** You may use it under the terms of the GPL. You should have *** already received the file COPYING that shows you your rights. *** Please look at COPYING for further license-details. *** *** THERE IS ABSOLUTELY NO WARRANTY. SO YOU USE IT AT YOUR OWN RISK. *** IT WAS WRITTEN IN THE HOPE THAT IT WILL BE USEFULL. I AM NOT RESPONSIBLE *** FOR ANY DAMAGE YOU MAYBE GET DUE TO USING MY PROGRAMS. ***/ #include "config.h" #include "usi++/usi-structs.h" #include "usi++/datalink.h" #include #include #include #include #ifdef IMMEDIATE #include #endif namespace usipp { Pcap::Pcap() : RX() { // Initialize memset(d_filter_string, 0, sizeof(d_filter_string)); d_pd = NULL; memset(&d_tv, 0, sizeof(d_tv)); memset(d_dev, 0, sizeof(d_dev)); d_timeout = false; start = 0; } /* This constructor should be used to * initialize raw-d_datalink-objects, means not IP/TCP/ICMP etc. * We need this b/c unlike in derived classes, d_datalink::init_device() * cannot set a filter! */ Pcap::Pcap(char *filterStr) { // Initialize memset(d_filter_string, 0, sizeof(d_filter_string)); strncpy(d_filter_string, filterStr, sizeof(d_filter_string)); d_pd = NULL; memset(d_dev, 0, sizeof(d_dev)); start = 0; } Pcap::~Pcap() { if (d_pd != NULL) pcap_close(d_pd); } Pcap::Pcap(const Pcap &rhs) { if (this == &rhs) return; d_datalink = rhs.d_datalink; d_framelen = rhs.d_framelen; d_filter = rhs.d_filter; d_phdr = rhs.d_phdr; d_ether = rhs.d_ether; strncpy(d_filter_string, rhs.d_filter_string, sizeof(d_filter_string)); strncpy(d_dev, rhs.d_dev, sizeof(d_dev)); d_has_promisc = rhs.d_has_promisc; d_snaplen = rhs.d_snaplen; start = rhs.start; if (rhs.d_pd) init_device(d_dev, d_has_promisc, d_snaplen); return; } Pcap &Pcap::operator=(const Pcap &rhs) { if (this == &rhs) return *this; d_datalink = rhs.d_datalink; d_framelen = rhs.d_framelen; d_filter = rhs.d_filter; d_phdr = rhs.d_phdr; d_ether = rhs.d_ether; strncpy(d_filter_string, rhs.d_filter_string, sizeof(d_filter_string)); strncpy(d_dev, rhs.d_dev, sizeof(d_dev)); d_has_promisc = rhs.d_has_promisc; d_snaplen = rhs.d_snaplen; start = rhs.start; if (rhs.d_pd) { if (d_pd) pcap_close(d_pd); init_device(d_dev, d_has_promisc, d_snaplen); } return *this; } /* Return the actual d_datalink of the object. */ int Pcap::get_datalink() { return d_datalink; } /* Return the actual framlen of the object. * (d_framelen depends on d_datalink) */ int Pcap::get_framelen() { return d_framelen; } /* Fill buffer with src-hardware-adress of actuall packet, * use 'd_datalink' to determine what HW the device is. * Now only ethernet s supportet, but it's extensinable. */ char *Pcap::get_hwsrc(char *hwaddr, size_t len) { unsigned char *s; memset(hwaddr, 0, len); switch (d_datalink) { case DLT_EN10MB: if (len < 2*ETH_ALEN) return NULL; s = d_ether.ether_shost; sprintf(hwaddr, "%02x:%02x:%02x:%02x:%02x:%02x", (u_char)*s, (u_char)*(s+1), (u_char)*(s+2), (u_char)*(s+3), (u_char)*(s+4), (u_char)*(s+5)); break; default: return NULL; } return hwaddr; } /* Fill buffer with dst-hardware-adress of actuall packet, * use 'd_datalink' to determine what HW the device is. * Now only ethernet s supportet. */ char *Pcap::get_hwdst(char *hwaddr, size_t len) { unsigned char *s; memset(hwaddr, 0, len); switch (d_datalink) { case DLT_EN10MB: if (len < 2*ETH_ALEN) return NULL; s = d_ether.ether_dhost; sprintf(hwaddr, "%02x:%02x:%02x:%02x:%02x:%02x", (u_char)*s, (u_char)*(s+1), (u_char)*(s+2), (u_char)*(s+3), (u_char)*(s+4), (u_char)*(s+5)); break; default: return NULL; } return hwaddr; } /* Get protocol-type of ethernet-frame * Maybe moves to ethernet-class in future? */ u_int16_t Pcap::get_etype() { return ntohs(d_ether.ether_type); } /* Initialize a device ("eth0" for example) for packet- * capturing. It MUST be called before sniffpack() is launched. * Set 'promisc' to 1 if you want the device running in promiscous mode. * Fetch at most 'd_snaplen' bytes per call. */ int Pcap::init_device(char *dev, int promisc, size_t d_snaplen) { char ebuf[PCAP_ERRBUF_SIZE]; memset(ebuf, 0, PCAP_ERRBUF_SIZE); if ((d_pd = pcap_open_live(dev, d_snaplen, promisc, 500, ebuf)) == NULL) { die(ebuf, STDERR, 1); } // Ehem, BSD workarounnd. BSD won't timeout on select() // unless we force immediate return for read() (in pcap) // for uncomplete packets (queue not full?) #ifdef IMMEDIATE int v = 1; if (ioctl(pcap_fileno(d_pd), BIOCIMMEDIATE, &v) < 0) { snprintf(ebuf, sizeof(ebuf), "Pcap::init_device::ioctl(..., BIOCIMMEDIATE, 1) %s", strerror(errno)); die(ebuf, STDERR, 1); } #endif if (pcap_lookupnet(dev, &d_localnet, &d_netmask, ebuf) < 0) { snprintf(ebuf, sizeof(ebuf), "Pcap::init_device::pcap_lookupnet: %s\n", pcap_geterr(d_pd)); die(ebuf, STDERR, 1); } /* The d_filter_string must be filled by derived classes, such * as IP, where the virtual init_device() simply sets d_filter_string * to "ip" and then calls Pcap::init_device(). */ if (pcap_compile(d_pd, &d_filter, d_filter_string, 1, d_netmask) < 0) { snprintf(ebuf, sizeof(ebuf), "Pcap::init_device::pcap_compile: %s\n", pcap_geterr(d_pd)); die(ebuf, STDERR, 1); } if (pcap_setfilter(d_pd, &d_filter) < 0) { snprintf(ebuf, sizeof(ebuf), "Pcap::init_device::pcap_setfilter: %s\n", pcap_geterr(d_pd)); die(ebuf, STDERR, 1); } if ((d_datalink = pcap_datalink(d_pd)) < 0) { snprintf(ebuf, sizeof(ebuf), "Pcap::init_device::pcap_d_datalink: %s\n", pcap_geterr(d_pd)); die(ebuf, STDERR, 1); } // turn d_datalink into d_framelen switch (d_datalink) { case DLT_EN10MB: d_framelen = sizeof(d_ether); break; case DLT_PPP: d_framelen = 4; /* shouldn't be 4 */ break; case DLT_PPP_BSDOS: d_framelen = 24; break; case DLT_SLIP: d_framelen = 24; break; case DLT_RAW: d_framelen = 0; break; // loopback #ifdef DLT_LOOP case DLT_LOOP: #endif case DLT_NULL: d_framelen = 4; break; #ifdef DLT_LINUX_SLL case DLT_LINUX_SLL: d_framelen = 16; break; #endif default: printf("%d %d\n", d_datalink, DLT_RAW); fprintf(stderr, "Datalink type: %i not supported. Report!\n", d_datalink); die("Pcap::init_device: Unknown d_datalink.\n", STDERR, 1); } strncpy(d_dev, dev, sizeof(d_dev)); d_has_promisc = promisc; d_snaplen = d_snaplen; set_nonblock(); return 0; } /* set a new filter for capturing */ int Pcap::setfilter(char *s) { char ebuf[PCAP_ERRBUF_SIZE]; memset(ebuf, 0, PCAP_ERRBUF_SIZE); if (!d_pd) die("Pcap::setfilter: Device not initialized.\n", STDERR, 1); memset(d_filter_string, 0, sizeof(d_filter_string)); snprintf(d_filter_string, sizeof(d_filter_string), "%s", s); if (pcap_compile(d_pd, &d_filter, d_filter_string, 1, d_netmask) < 0) { snprintf(ebuf, sizeof(ebuf), "Pcap::setfilter::pcap_compile: %s\n", pcap_geterr(d_pd)); die(ebuf, STDERR, 1); } if (pcap_setfilter(d_pd, &d_filter) < 0) { snprintf(ebuf, sizeof(ebuf), "Pcap::setfilter::pcap_setfilter: %s\n", pcap_geterr(d_pd)); die(ebuf, STDERR, 1); } set_nonblock(); return 0; } int Pcap::sniffpack(void *s, size_t len) { char *tmp; memset(s, 0, len); d_timeout = false; if (!d_pd) die("Pcap::sniffpack: Device not initialized.\n", STDERR, 1); /* XXX: with select() packets on loopback interface are lost * so we leave it out for now * if (d_tv.tv_sec != 0 || d_tv.tv_usec != 0) { // TO was set while (1) { fd_set rset; FD_ZERO(&rset); int fd = pcap_fileno(d_pd); FD_SET(fd, &rset); timeval tmp = d_tv; // wait for packet int sr; if ((sr=select(fd+1, &rset, NULL, NULL, &tmp)) < 0) { if (errno == EINTR) continue; else return -1; } else if (sr == 0) { // timed out d_timeout = true; return 0; } else // got packet break; } } */ /* XXX: there is a bug on linux when select() returns 1, but pcap_next() * returns NULL */ if (start == 0) // first time sniffpack() is called start = time(NULL); if (d_tv.tv_sec != 0 || d_tv.tv_usec != 0) { // timeout set /* while ((tmp = (char*)pcap_next(d_pd, &d_phdr)) == NULL) if ((time(NULL) - start) > d_tv.tv_sec) { d_timeout = true; start = 0; return 0; } */ while (1) { if ((time(NULL) - start) > d_tv.tv_sec) { d_timeout = true; start = 0; return 0; } tmp = (char*)pcap_next(d_pd, &d_phdr); if (tmp != NULL) { break; } } } else { // no timeout set, loop until we get some kind of packet while ((tmp = (char*)pcap_next(d_pd, &d_phdr)) == NULL) ; } switch (d_datalink) { case DLT_EN10MB: memcpy(&d_ether, tmp, d_framelen); break; case DLT_PPP: break; case DLT_PPP_BSDOS: break; case DLT_SLIP: break; case DLT_RAW: break; #ifdef DLT_LOOP case DLT_LOOP: #endif case DLT_NULL: break; #ifdef DLT_LINUX_SLL case DLT_LINUX_SLL: break; #endif default: die("Pcap::sniffpack: Unknown d_datalink.\n", STDERR, 1); } #ifdef USI_DEBUG cerr<<"Pcap::d_phdr.len="<