hackedteam/vector-ipa

View on GitHub
src/capture.c

Summary

Maintainability
Test Coverage
/*
    MODULE -- iface and capture functions

    Copyright (C) Alberto Ornaghi

    $Id: capture.c 3422 2011-02-09 12:55:25Z alor $
*/

#include <main.h>
#include <decode.h>
#include <threads.h>
#include <capture.h>
#include <ui.h>
#include <inet.h>

#include <pcap/pcap.h>
#ifdef OS_LINUX
   /* for wireless extensions */
   #include <iwlib.h>
#endif

#if defined(OS_LINUX)
   /* LINUX needs 1 */
   #define PCAP_TIMEOUT 1
   #include <net/if.h>
   #include <sys/ioctl.h>
#elif defined(OS_SOLARIS)
   /* SOLARIS needs > 1 */
   #define PCAP_TIMEOUT 10
#else
   /* FREEBSD needs 1 */
   /* MACOSX  needs 1 */
   #define PCAP_TIMEOUT 10
#endif

/* globals */

/* protos */

void capture_init(void);
void capture_close(void);
void capture_start(void);
void capture_stop(void);

void capture_getifs(void);
int is_pcap_file(char *file, char *errbuf);

int iface_is_wireless(char *iface);
int iface_set_status(char *iface, u_char status);
int iface_set_monitor(char *iface);
int iface_set_channel(char *iface, int channel);

/*******************************************/

/*
 * set up the pcap to capture from the specified interface
 * set up even the first dissector by looking at DLT_*
 */

void capture_init(void)
{
   pcap_t *pd;
   struct bpf_program bpf;
   char pcap_errbuf[PCAP_ERRBUF_SIZE];

   /*
    * if the user didn't specified the interface,
    * we have to found one...
    */
   if (!GBL_OPTIONS->read && GBL_OPTIONS->Siface == NULL && GBL_CONF->sniffing_iface == NULL) {
      char *ifa = pcap_lookupdev(pcap_errbuf);
      ON_ERROR(ifa, NULL, "No suitable interface found...");

      GBL_OPTIONS->Siface = strdup(ifa);
   }

   /* if the iface is not set on command line, get it form the file */
   if (GBL_CONF->sniffing_iface && GBL_OPTIONS->Siface == NULL) {
      GBL_OPTIONS->Siface = GBL_CONF->sniffing_iface;
      GBL_OPTIONS->Siface_chan = GBL_CONF->sniffing_iface_channel;
   }

   if (GBL_OPTIONS->Siface)
      DEBUG_MSG(D_INFO, "capture_init %s", GBL_OPTIONS->Siface);
   else
      DEBUG_MSG(D_INFO, "capture_init (no interface)");


   if (GBL_OPTIONS->read) {
      USER_MSG("Reading from %s...\n", GBL_OPTIONS->pcapfile_in);
   } else {
      if (GBL_OPTIONS->Siface_chan)
         USER_MSG("Listening on %s (channel %d)...\n", GBL_OPTIONS->Siface, GBL_OPTIONS->Siface_chan);
      else
         USER_MSG("Listening on %s...\n", GBL_OPTIONS->Siface);
   }

   /* set the snaplen to maximum */
   GBL_PCAP->snaplen = 2048;

   /* open the interface from GBL_OPTIONS (user specified) */
   if (GBL_OPTIONS->read) {
      pd = pcap_open_offline(GBL_OPTIONS->pcapfile_in, pcap_errbuf);
   } else {
      // pd = pcap_open_live(GBL_OPTIONS->Siface, GBL_PCAP->snaplen, 1, PCAP_TIMEOUT, pcap_errbuf);
      /* slice the pcap_open_live function in pieces to insert the pcap_set_rfmon() */
      pd = pcap_create(GBL_OPTIONS->Siface, pcap_errbuf);
      ON_ERROR(pd, NULL, "pcap_create: %s", pcap_errbuf);

      if (pcap_set_snaplen(pd, GBL_PCAP->snaplen) < 0)
         FATAL_ERROR("Cannot set snaplen on [%s]", GBL_OPTIONS->Siface);

      DEBUG_MSG(D_INFO, "Activating promisc mode on [%s]...", GBL_OPTIONS->Siface);
      if (pcap_set_promisc(pd, 1) < 0)
         FATAL_ERROR("ERROR: Cannot enable promisc mode [%s]", pcap_geterr(pd));

      /* if the interface is wireless, enable the monitor mode */
      if (iface_is_wireless(GBL_OPTIONS->Siface)) {
         DEBUG_MSG(D_INFO, "Activating monitor mode on [%s]...", GBL_OPTIONS->Siface);
#if 0
         /*
          * NOTICE: for some awkward reason, this does not work !!
          * in some case the interface DLT si set to NULL (BSD loopback)
          * in other cases a mon0 interface is created...
          * don't mess with this and use the "granny-style" monitor mode
          */
         /* try first with the gentle manner... */
         if (pcap_can_set_rfmon(pd)) {
            if (pcap_set_rfmon(pd, 1) < 0)
               DEBUG_MSG(D_ERROR, "ERROR: Monitor mode failed [%s]", pcap_geterr(pd));
         } else {
            /* then try the rude way */
            DEBUG_MSG(D_INFO, "Cannot set the monitor mode, trying bringing it down and up again...");
#endif
            do {
               /* bring the interface DOWN */
               if (iface_set_status(GBL_OPTIONS->Siface, IFACE_DOWN) != ESUCCESS) {
                  DEBUG_MSG(D_ERROR, "ERROR: Failed to bring [%s] DOWN", GBL_OPTIONS->Siface);
                  break;
               }
               /* try to set the rfmon now */
               if (iface_set_monitor(GBL_OPTIONS->Siface) < 0) {
                  DEBUG_MSG(D_ERROR, "ERROR: Monitor mode failed [%s]", pcap_geterr(pd));
                  /* don't break here, we need to restore the interface in UP state */
               }
               /* bring the interface UP */
               if (iface_set_status(GBL_OPTIONS->Siface, IFACE_UP) != ESUCCESS) {
                  FATAL_ERROR("Failed to bring [%s] UP, cannot continue", GBL_OPTIONS->Siface);
                  break;
               }
            } while (0);
#if 0
         }
#endif
         /* set the channel to listen on */
         if (GBL_OPTIONS->Siface_chan) {
            DEBUG_MSG(D_INFO, "Setting wireless channel %d on [%s]...", GBL_OPTIONS->Siface_chan, GBL_OPTIONS->Siface);
            if (iface_set_channel(GBL_OPTIONS->Siface, GBL_OPTIONS->Siface_chan) < 0) {
               DEBUG_MSG(D_ERROR, "ERROR: Failed to set channel %d [%s]", GBL_OPTIONS->Siface_chan, pcap_geterr(pd));
            }
         }
      }

      if (pcap_set_timeout(pd, PCAP_TIMEOUT) < 0)
         FATAL_ERROR("Cannot set timeout to %d on [%s]", PCAP_TIMEOUT, GBL_OPTIONS->Siface);

      if (pcap_activate(pd) < 0)
         FATAL_ERROR("Cannot activate [%s]", GBL_OPTIONS->Siface);
   }
   ON_ERROR(pd, NULL, "pcap_open: %s", pcap_errbuf);

   /*
    * update to the reap assigned snapshot.
    * this may be different reading from files
    */
   DEBUG_MSG(D_INFO, "requested snapshot: %d assigned: %d", GBL_PCAP->snaplen, pcap_snapshot(pd));
   GBL_PCAP->snaplen = pcap_snapshot(pd);

   /* get the file size */
   if (GBL_OPTIONS->read) {
      struct stat st;
      fstat(fileno(pcap_file(pd)), &st);
      GBL_PCAP->dump_size = st.st_size;
   }

   /* set the pcap filters */
   if (GBL_PCAP->filter != NULL && strcmp(GBL_PCAP->filter, "")) {

      DEBUG_MSG(D_INFO, "pcap_filter: %s", GBL_PCAP->filter);

      if (pcap_compile(pd, &bpf, GBL_PCAP->filter, 1, 0) < 0)
         ERROR_MSG("%s", pcap_errbuf);

      if (pcap_setfilter(pd, &bpf) == -1)
         ERROR_MSG("pcap_setfilter");

      pcap_freecode(&bpf);
   }

   /* set the right dlt type for the iface */
   GBL_PCAP->dlt = pcap_datalink(pd);

   DEBUG_MSG(D_INFO, "capture_init: %s [%d]", pcap_datalink_val_to_description(GBL_PCAP->dlt), GBL_PCAP->dlt);
   USER_MSG("Link layer is %s\n", pcap_datalink_val_to_description(GBL_PCAP->dlt));

   /* check if we support this media */
   if (get_decoder(LINK_LAYER, GBL_PCAP->dlt) == NULL) {
      if (GBL_OPTIONS->read)
         FATAL_ERROR("Dump file not supported [%02X](%s)", GBL_PCAP->dlt, pcap_datalink_val_to_description(GBL_PCAP->dlt));
      else
         FATAL_ERROR("Network Inteface \"%s\" not supported [%02X](%s)", GBL_OPTIONS->Siface, GBL_PCAP->dlt, pcap_datalink_val_to_description(GBL_PCAP->dlt));
   }

   /* set the global descriptor for the capture interface */
   GBL_PCAP->pcap = pd;

   /* on exit clean up the structures */
   atexit(capture_close);

}

void capture_close(void)
{

   if (GBL_PCAP->pcap)
      pcap_close(GBL_PCAP->pcap);

   DEBUG_MSG(D_DEBUG, "ATEXIT: capture_closed");
}

void capture_start(void)
{
   int ret;

   DEBUG_MSG(D_DEBUG, "capture_start");

   /*
    * infinite loop
    * dispatch packets to decode
    */
   ret = pcap_loop(GBL_PCAP->pcap, -1, decode_captured, NULL);
   ON_ERROR(ret, -1, "Error while capturing: %s", pcap_geterr(GBL_PCAP->pcap));

   DEBUG_MSG(D_DEBUG, "capture_start: [%d] exited the infinite loop", ret);
}

void capture_stop(void)
{
   DEBUG_MSG(D_DEBUG, "capture_stop");

   pcap_breakloop(GBL_PCAP->pcap);
}

/*
 * get the list of all network interfaces
 */
void capture_getifs(void)
{
   pcap_if_t *ifs;
   pcap_if_t *dev, *pdev, *ndev;
   char pcap_errbuf[PCAP_ERRBUF_SIZE];

   DEBUG_MSG(D_DEBUG, "capture_getifs");

   /* retrieve the list */
   if (pcap_findalldevs(&ifs, pcap_errbuf) == -1)
      ERROR_MSG("%s", pcap_errbuf);

   /* analize the list and remove unwanted entries */
   for (pdev = dev = ifs; dev != NULL; dev = ndev) {

      /* the next entry in the list */
      ndev = dev->next;

      /* set the description for the local loopback */
      if (dev->flags & PCAP_IF_LOOPBACK) {
         SAFE_FREE(dev->description);
         dev->description = strdup("Local Loopback");
      }

      /* fill the empty descriptions */
      if (dev->description == NULL)
         dev->description = dev->name;

      /* remove the pseudo device 'any' */
      if (!strcmp(dev->name, "any")) {
         /* check if it is the first in the list */
         if (dev == ifs)
            ifs = ndev;
         else
            pdev->next = ndev;

         SAFE_FREE(dev->name);
         SAFE_FREE(dev->description);
         SAFE_FREE(dev);

         continue;
      }

      /* remember the previous device for the next loop */
      pdev = dev;

      DEBUG_MSG(D_INFO, "capture_getifs: [%s] %s", dev->name, dev->description);
   }

}

/*
 * check if the given file is a pcap file
 */
int is_pcap_file(char *file, char *errbuf)
{
   pcap_t *pd;

   pd = pcap_open_offline(file, errbuf);
   if (pd == NULL)
      return -EINVALID;

   pcap_close(pd);

   return ESUCCESS;
}

/*
 * check if the interface is wireless
 */
int iface_is_wireless(char *iface)
{
#ifdef OS_MACOSX
   return 0;
#endif
#ifdef OS_LINUX
   struct ifreq ifr;
   int sk;

   if ((sk = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
      return 0;

   strncpy(ifr.ifr_name, iface, IFNAMSIZ);

   /* if the interface does not have a wireless name, it is not wireless :) */
   if (ioctl(sk, SIOCGIWNAME, &ifr) < 0) {
      close(sk);
      return 0;
   }

   close(sk);
   return 1;
#endif
}

/*
 * set the interface status
 */
int iface_set_status(char *iface, u_char status)
{
#ifdef OS_MACOSX
   return -EFAILURE;
#endif
#ifdef OS_LINUX
   struct ifreq ifr;
   int sk;

   if ((sk = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
      return -EFAILURE;

   strncpy(ifr.ifr_name, iface, IFNAMSIZ);

   /* get the current flags */
   if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
      close(sk);
      return -EFAILURE;
   }

   strncpy(ifr.ifr_name, iface, IFNAMSIZ);

   switch(status) {
      case IFACE_UP:
         ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
         break;
      case IFACE_DOWN:
         ifr.ifr_flags &= ~IFF_UP;
         break;
   }

   /* set the new one */
   if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
      close(sk);
      return -EFAILURE;
   }

   close(sk);
   return ESUCCESS;
#endif
}

/*
 * set the interface in monitor mode
 */
int iface_set_monitor(char *iface)
{
#ifdef OS_MACOSX
   return -EFAILURE;
#endif
#ifdef OS_LINUX
   struct iwreq wrq;
   int sk;

   if ((sk = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
      return -EFAILURE;

   strncpy(wrq.ifr_name, iface, IFNAMSIZ);

   /* magic number for monitor mode
    *
    * the values come from:
    *  { "Auto", "Ad-Hoc", "Managed", "Master", "Repeater", "Secondary", "Monitor", "Unknown/bug" }
    */
   wrq.u.mode = 6;

   if (ioctl(sk, SIOCSIWMODE, &wrq) < 0) {
      close(sk);
      return -EFAILURE;
   }

   close(sk);
   return ESUCCESS;
#endif
}

/*
 * set the interface channel (of frequence)
 */
int iface_set_channel(char *iface, int channel)
{
#ifdef OS_MACOSX
   return -EFAILURE;
#endif
#ifdef OS_LINUX
   struct iwreq wrq;
   int sk;

   if ((sk = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
      return -EFAILURE;

   strncpy(wrq.ifr_name, iface, IFNAMSIZ);

   wrq.u.freq.flags = IW_FREQ_FIXED;
   /* this way we support only channels and not real freqs */
   wrq.u.freq.m = channel;
   wrq.u.freq.e = 0;

   if (ioctl(sk, SIOCSIWFREQ, &wrq) < 0) {
      close(sk);
      return -EFAILURE;
   }

   close(sk);
   return ESUCCESS;
#endif
}


/* EOF */

// vim:ts=3:expandtab