/* Copyright (c) 2009 Victor Julien */ #if LIBPCAP_VERSION_MAJOR == 1 #include #else #include #endif #include "eidps-common.h" #include "eidps.h" #include "decode.h" #include "packet-queue.h" #include "threads.h" #include "threadvars.h" #include "tm-queuehandlers.h" #include "tm-modules.h" #include "tm-threads.h" #include "source-pcap.h" #include "util-debug.h" /** * \brief Structure to hold thread specific variables. */ typedef struct PcapThreadVars_ { /* thread specific handle */ pcap_t *pcap_handle; /* data link type for the thread */ int datalink; /* counters */ uint32_t pkts; uint64_t bytes; uint32_t errs; ThreadVars *tv; } PcapThreadVars; int ReceivePcap(ThreadVars *, Packet *, void *, PacketQueue *); int ReceivePcapThreadInit(ThreadVars *, void *, void **); void ReceivePcapThreadExitStats(ThreadVars *, void *); int ReceivePcapThreadDeinit(ThreadVars *, void *); int DecodePcapThreadInit(ThreadVars *, void *, void **); int DecodePcap(ThreadVars *, Packet *, void *, PacketQueue *); /** * \brief Registration Function for RecievePcap. * \todo Unit tests are needed for this module. */ void TmModuleReceivePcapRegister (void) { tmm_modules[TMM_RECEIVEPCAP].name = "ReceivePcap"; tmm_modules[TMM_RECEIVEPCAP].ThreadInit = ReceivePcapThreadInit; tmm_modules[TMM_RECEIVEPCAP].Func = ReceivePcap; tmm_modules[TMM_RECEIVEPCAP].ThreadExitPrintStats = ReceivePcapThreadExitStats; tmm_modules[TMM_RECEIVEPCAP].ThreadDeinit = NULL; tmm_modules[TMM_RECEIVEPCAP].RegisterTests = NULL; } /** * \brief Registration Function for DecodePcap. * \todo Unit tests are needed for this module. */ void TmModuleDecodePcapRegister (void) { tmm_modules[TMM_DECODEPCAP].name = "DecodePcap"; tmm_modules[TMM_DECODEPCAP].ThreadInit = DecodePcapThreadInit; tmm_modules[TMM_DECODEPCAP].Func = DecodePcap; tmm_modules[TMM_DECODEPCAP].ThreadExitPrintStats = NULL; tmm_modules[TMM_DECODEPCAP].ThreadDeinit = NULL; tmm_modules[TMM_DECODEPCAP].RegisterTests = NULL; } /** * \brief Pcap callback function. * * This function fills in our packet structure from libpcap. * From here the packets are picked up by the DecodePcap thread. * * \param user pointer to PcapThreadVars passed from pcap_dispatch * \param h pointer to pcap packet header * \param pkt pointer to raw packet data */ void PcapCallback(char *user, struct pcap_pkthdr *h, u_char *pkt) { //printf("PcapCallback: user %p, h %p, pkt %p\n", user, h, pkt); PcapThreadVars *ptv = (PcapThreadVars *)user; ThreadVars *tv = ptv->tv; mutex_lock(&mutex_pending); if (pending > MAX_PENDING) { pthread_cond_wait(&cond_pending, &mutex_pending); } mutex_unlock(&mutex_pending); Packet *p = tv->tmqh_in(tv); p->ts.tv_sec = h->ts.tv_sec; p->ts.tv_usec = h->ts.tv_usec; ptv->pkts++; ptv->bytes += h->caplen; p->datalink = ptv->datalink; p->pktlen = h->caplen; memcpy(p->pkt, pkt, p->pktlen); //printf("PcapCallback: p->pktlen: %" PRIu32 " (pkt %02x, p->pkt %02x)\n", p->pktlen, *pkt, *p->pkt); /* pass on... */ tv->tmqh_out(tv, p); } /** * \brief Recieves packets from an interface via libpcap. * * This function recieves packets from an interface and passes * the packet on to the pcap callback function. * * \param tv pointer to ThreadVars * \param data pointer that gets cast into PcapThreadVars for ptv * \param pq pointer to the PacketQueue (not used here but part of the api) */ int ReceivePcap(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq) { PcapThreadVars *ptv = (PcapThreadVars *)data; /// Just read one packet at a time for now. int r = 0; while (r == 0) { //printf("ReceivePcap: call pcap_dispatch %" PRIu32 "\n", tv->flags); r = pcap_dispatch(ptv->pcap_handle, 1, (pcap_handler)PcapCallback, (u_char *)ptv); if (r < 0) { printf("ReceivePcap: error %s\n", pcap_geterr(ptv->pcap_handle)); break; } if (TmThreadsCheckFlag(tv, THV_KILL) || TmThreadsCheckFlag(tv, THV_PAUSE)) { SCLogInfo("pcap packet reading interrupted"); return 0; } } return 0; } /** * \brief Init function for RecievePcap. * * This is a setup function for recieving packets * via libpcap. There are two versions of this function * depending on the major version of libpcap used. * For versions prior to 1.x we use open_pcap_live, * for versions 1.x and greater we use pcap_create + pcap_activate. * * \param tv pointer to ThreadVars * \param initdata pointer to the interface passed from the user * \param data pointer gets populated with PcapThreadVars * * \todo Create a general pcap setup function. */ #if LIBPCAP_VERSION_MAJOR == 1 int ReceivePcapThreadInit(ThreadVars *tv, void *initdata, void **data) { if (initdata == NULL) { printf("ReceivePcapThreadInit error: initdata == NULL\n"); return -1; } PcapThreadVars *ptv = malloc(sizeof(PcapThreadVars)); if (ptv == NULL) { return -1; } memset(ptv, 0, sizeof(PcapThreadVars)); ptv->tv = tv; SCLogInfo("using interface %s", (char *)initdata); /* XXX create a general pcap setup function */ char errbuf[PCAP_ERRBUF_SIZE]; ptv->pcap_handle = pcap_create((char *)initdata, errbuf); if (ptv->pcap_handle == NULL) { printf("error %s\n", pcap_geterr(ptv->pcap_handle)); exit(1); } /* set Snaplen, Promisc, and Timeout. Must be called before pcap_activate */ int pcap_set_snaplen_r = pcap_set_snaplen(ptv->pcap_handle,LIBPCAP_SNAPLEN); //printf("ReceivePcapThreadInit: pcap_set_snaplen(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_set_snaplen_r); if (pcap_set_snaplen_r != 0) { printf("ReceivePcapThreadInit: error is %s\n", pcap_geterr(ptv->pcap_handle)); exit(1); } int pcap_set_promisc_r = pcap_set_promisc(ptv->pcap_handle,LIBPCAP_PROMISC); //printf("ReceivePcapThreadInit: pcap_set_promisc(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_set_promisc_r); if (pcap_set_promisc_r != 0) { printf("ReceivePcapThreadInit: error is %s\n", pcap_geterr(ptv->pcap_handle)); exit(1); } int pcap_set_timeout_r = pcap_set_timeout(ptv->pcap_handle,LIBPCAP_COPYWAIT); //printf("ReceivePcapThreadInit: pcap_set_timeout(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_set_timeout_r); if (pcap_set_timeout_r != 0) { printf("ReceivePcapThreadInit: error is %s\n", pcap_geterr(ptv->pcap_handle)); exit(1); } /* activate the handle */ int pcap_activate_r = pcap_activate(ptv->pcap_handle); //printf("ReceivePcapThreadInit: pcap_activate(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_activate_r); if (pcap_activate_r != 0) { printf("ReceivePcapThreadInit: error is %s\n", pcap_geterr(ptv->pcap_handle)); exit(1); } ptv->datalink = pcap_datalink(ptv->pcap_handle); *data = (void *)ptv; return 0; } #else /* implied LIBPCAP_VERSION_MAJOR == 0 */ int ReceivePcapThreadInit(ThreadVars *tv, void *initdata, void **data) { if (initdata == NULL) { printf("ReceivePcapThreadInit error: initdata == NULL\n"); return -1; } PcapThreadVars *ptv = malloc(sizeof(PcapThreadVars)); if (ptv == NULL) { return -1; } memset(ptv, 0, sizeof(PcapThreadVars)); ptv->tv = tv; SCLogInfo("using interface %s", (char *)initdata); char errbuf[PCAP_ERRBUF_SIZE] = ""; ptv->pcap_handle = pcap_open_live((char *)initdata, LIBPCAP_SNAPLEN, LIBPCAP_PROMISC, LIBPCAP_COPYWAIT, errbuf); if (ptv->pcap_handle == NULL) { printf("error %s\n", errbuf); exit(1); } ptv->datalink = pcap_datalink(ptv->pcap_handle); *data = (void *)ptv; return 0; } #endif /* LIBPCAP_VERSION_MAJOR */ /** * \brief This function prints stats to the screen at exit. * \param tv pointer to ThreadVars * \param data pointer that gets cast into PcapThreadVars for ptv */ void ReceivePcapThreadExitStats(ThreadVars *tv, void *data) { PcapThreadVars *ptv = (PcapThreadVars *)data; SCLogInfo("(%s) Packets %" PRIu32 ", bytes %" PRIu64 "", tv->name, ptv->pkts, ptv->bytes); return; } /** * \brief DeInit function closes pcap_handle at exit. * \param tv pointer to ThreadVars * \param data pointer that gets cast into PcapThreadVars for ptv */ int ReceivePcapThreadDeinit(ThreadVars *tv, void *data) { PcapThreadVars *ptv = (PcapThreadVars *)data; pcap_close(ptv->pcap_handle); return 0; } /** * \brief This function passes off to link type decoders. * * DecodePcap reads packets from the PacketQueue and passes * them off to the proper link type decoder. * * \param t pointer to ThreadVars * \param p pointer to the current packet * \param data pointer that gets cast into PcapThreadVars for ptv * \param pq pointer to the current PacketQueue */ int DecodePcap(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq) { DecodeThreadVars *dtv = (DecodeThreadVars *)data; /* update counters */ PerfCounterIncr(dtv->counter_pkts, tv->pca); PerfCounterAddUI64(dtv->counter_bytes, tv->pca, p->pktlen); PerfCounterAddUI64(dtv->counter_avg_pkt_size, tv->pca, p->pktlen); PerfCounterSetUI64(dtv->counter_max_pkt_size, tv->pca, p->pktlen); /* call the decoder */ switch(p->datalink) { case LINKTYPE_LINUX_SLL: DecodeSll(tv, dtv, p, p->pkt, p->pktlen, pq); break; case LINKTYPE_ETHERNET: DecodeEthernet(tv, dtv, p,p->pkt, p->pktlen, pq); break; case LINKTYPE_PPP: DecodePPP(tv, dtv, p, p->pkt, p->pktlen, pq); break; default: printf("Error: datalink type %" PRId32 " not yet supported in module DecodePcap.\n", p->datalink); break; } return 0; } int DecodePcapThreadInit(ThreadVars *tv, void *initdata, void **data) { DecodeThreadVars *dtv = NULL; if ( (dtv = malloc(sizeof(DecodeThreadVars))) == NULL) { printf("Error Allocating memory\n"); return -1; } memset(dtv, 0, sizeof(DecodeThreadVars)); /* register counters */ dtv->counter_pkts = PerfTVRegisterCounter("decoder.pkts", tv, TYPE_UINT64, "NULL"); dtv->counter_bytes = PerfTVRegisterCounter("decoder.bytes", tv, TYPE_UINT64, "NULL"); dtv->counter_ipv4 = PerfTVRegisterCounter("decoder.ipv4", tv, TYPE_UINT64, "NULL"); dtv->counter_ipv6 = PerfTVRegisterCounter("decoder.ipv6", tv, TYPE_UINT64, "NULL"); dtv->counter_eth = PerfTVRegisterCounter("decoder.ethernet", tv, TYPE_UINT64, "NULL"); dtv->counter_sll = PerfTVRegisterCounter("decoder.sll", tv, TYPE_UINT64, "NULL"); dtv->counter_tcp = PerfTVRegisterCounter("decoder.tcp", tv, TYPE_UINT64, "NULL"); dtv->counter_udp = PerfTVRegisterCounter("decoder.udp", tv, TYPE_UINT64, "NULL"); dtv->counter_icmpv4 = PerfTVRegisterCounter("decoder.icmpv4", tv, TYPE_UINT64, "NULL"); dtv->counter_icmpv6 = PerfTVRegisterCounter("decoder.icmpv6", tv, TYPE_UINT64, "NULL"); dtv->counter_ppp = PerfTVRegisterCounter("decoder.ppp", tv, TYPE_UINT64, "NULL"); dtv->counter_pppoe = PerfTVRegisterCounter("decoder.pppoe", tv, TYPE_UINT64, "NULL"); dtv->counter_gre = PerfTVRegisterCounter("decoder.gre", tv, TYPE_UINT64, "NULL"); dtv->counter_avg_pkt_size = PerfTVRegisterAvgCounter("decoder.avg_pkt_size", tv, TYPE_DOUBLE, "NULL"); dtv->counter_max_pkt_size = PerfTVRegisterMaxCounter("decoder.max_pkt_size", tv, TYPE_UINT64, "NULL"); tv->pca = PerfGetAllCountersArray(&tv->pctx); PerfAddToClubbedTMTable(tv->name, &tv->pctx); *data = (void *)dtv; return 0; } /* eof */