Logo Search packages:      
Sourcecode: darkstat version File versions  Download package

acct.c

/* darkstat: a network traffic analyzer
 * (c) 2001-2003, Emil Mikulic.
 */

#include "acct.h"
#include "port_db.h"
#include "proto.h"
#include "dns.h"
#include "graph.h"

#include <pthread.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#if defined(sun) && (defined(__svr4__) || defined(__SVR4)) 
#include <sys/sockio.h>
#endif

int64 num_packets, total_data;
dword local_ip = 0, lan_ip = 0xFFFFFFFF, lan_mask = 0;
int   acct_linktype = 0;
time_t      t_start, t_already = 0, t_lastsave = 0;
pcap_t      *acct_pcap = NULL;
host_record *local_host_rec;

#define FAIL() { up_acct = 0; pthread_exit( (void*)EXIT_FAIL ); }
#define SUCCEED() { up_acct = 0; pthread_exit( (void*)EXIT_SUCCESS ); }



void interpret_linktype(int linktype, byte *pdata,
      byte **ptr, word *pkt_type)
{
      switch (linktype)
      {
      case DLT_EN10MB:
            *ptr = pdata;
            *pkt_type = eth_pkt_type(*ptr);
            break;

      /* Brian May */
      case DLT_RAW:
            *ptr = pdata - 14;
            *pkt_type = ETH_PKT_TYPE_IP;
            break;

      /* thanks to Petry Roman */
      case DLT_FDDI:
            *ptr = pdata + 13; /* skip FDDI header */
            *ptr += 6; /* some other garbage */
            *ptr -= 12; /* now it matches up with ether */
            *pkt_type = eth_pkt_type(*ptr);
            break;

      case DLT_LINUX_SLL:
            *ptr = pdata + 14; /* skip header */
            *ptr -= 12; /* now it matches up with ether */
            *pkt_type = eth_pkt_type(*ptr);
            break;

      /* Stefan Haenssgen */
      case DLT_PPP:
            *ptr = pdata + 4;
            *pkt_type = eth_pkt_type(*ptr);
            break;
#if 0
      case DLT_NULL:
            *ptr = pdata + 4;
            *pkt_type = eth_pkt_type(*ptr);
            break;
#endif
      /* Maxim Golov */
      case DLT_NULL:
            *ptr = pdata + 4; /* skip header */
            *ptr -= 14; /* now it matches up with ether */
            if (*(int*)pdata == AF_INET)
            {
                  *pkt_type = ETH_PKT_TYPE_IP;
            }
            else
            {
                  *pkt_type = eth_pkt_type(*(int*)pdata);
            }
            break;

      /* Jean-Edouard BABIN <Jeb@jeb.com.fr> */
      case DLT_PPP_SERIAL:
            *ptr  = pdata - 10;
            *pkt_type = ETH_PKT_TYPE_IP;
            break;

      default:
            freakout("Unknown link type!  Mail Emil.");
      }
}



/* disassemble a packet and account for it */
void handle_pkt(byte *user unused, const struct pcap_pkthdr *pheader,
      byte *pdata)
{
      byte *ptr;
      word pkt_type;
      dword pkt_len;
      byte pkt_proto;
      dword pkt_srcip, pkt_destip;
      port_record *p;

      /* update total packet and data counters */
      i64add32(num_packets, 1);
      i64add32(total_data, (dword)(pheader->len));

      interpret_linktype(acct_linktype, pdata, &ptr, &pkt_type);

      if (pkt_type != ETH_PKT_TYPE_IP)
      {
            if (verbose)
            {
                  putchar('(');
                  switch (pkt_type)
                  {
                  case 0x0806: printf("ARP"); break;
                  case 0x809b: printf("AppleTalk"); break;
                  case 0x8035: printf("RARP"); break;
                  case 0x814c: printf("SNMP"); break;
                  case 0x86dd: printf("IPv6"); break;
                  case 0x880b: printf("PPP"); break;
                  default: printf("unknown (type %04X)",
                              pkt_type);
                  }
                  printf(" packet -- ignored)\n");
                  fflush(stdout);
            }
            return;
      }

      ptr = eth_pkt_strip(ptr);

      if (verbose) printf("(IP) ");

      pkt_proto = ip_pkt_proto(ptr);
      pkt_srcip = ip_pkt_srcip(ptr);
      pkt_destip = ip_pkt_destip(ptr);
      pkt_len = (dword)(ip_pkt_len(ptr));

      /* de-mangle Linux 2.4.x NAT */
      if ((pkt_srcip & lan_mask) == lan_ip) pkt_srcip = local_ip;
      if ((pkt_destip & lan_mask) == lan_ip) pkt_destip = local_ip;

      if (verbose)
      {
            printf(proto_name_short(pkt_proto));
            putchar(' ');
            print_addr(pkt_srcip);
            printf("->");
            print_addr(pkt_destip);
      }

      /* start messing with DB */
      pthread_mutex_lock(&db_mutex);
      
      /* accounting for protocols */
      if (pkt_srcip == local_ip)
            proto_transfer_out(pkt_proto, pkt_len);
      if (pkt_destip == local_ip)
            proto_transfer_in(pkt_proto, pkt_len);
      if (pkt_srcip != local_ip && pkt_destip != local_ip)
            proto_transfer_other(pkt_proto, pkt_len);

      /* accounting for hosts */
      host_transfer_out(pkt_srcip, pkt_len);
      host_transfer_in(pkt_destip, pkt_len);

      /* accounting for ports */
      if (pkt_proto == IP_PROTO_TCP)
      {
            byte *tcp_ptr = ip_pkt_strip(ptr);
            word  srcport = tcp_pkt_srcport(tcp_ptr),
                  destport = tcp_pkt_destport(tcp_ptr);
            byte flags = tcp_flags(tcp_ptr);

            if (pkt_destip == local_ip)
            {
                  /* treat all ftp-data as single port -- jib */
                  if (srcport == 20) destport = 20;

                  if ((flags & TCP_SYN) && !(flags ^ TCP_SYN))
                        p = port_from_num(destport);
                  else
                        p = port_find(destport);

                  if (p) port_update_in(p, pkt_len);
            }
                  else if (pkt_srcip == local_ip)
            {
                  p = port_find(srcport);
                  if (p) port_update_out(p, pkt_len);
            }     

            if (verbose)
            {
                  printf(" %d:%d (", srcport,destport);
            
                  if (flags & TCP_URG) printf("URG ");
                  if (flags & TCP_ACK) printf("ACK ");
                  if (flags & TCP_PSH) printf("PSH ");
                  if (flags & TCP_RST) printf("RST ");
                  if (flags & TCP_SYN) printf("SYN ");
                  if (flags & TCP_FIN) printf("FIN ");
                  if (flags) putchar(8);
                  printf(")");
            }
      }

      /* stop messing with db */
      pthread_mutex_unlock(&db_mutex);

      /* update graph */
      pthread_mutex_lock(&graph_mutex);
      graph_rotate(pheader->ts.tv_sec);
      if (pkt_srcip == local_ip) graph_add_out(pkt_len); else
      if (pkt_destip == local_ip) graph_add_in(pkt_len);
      pthread_mutex_unlock(&graph_mutex);

      if (verbose)
      {
            printf(" size:%d\n", pkt_len);
            fflush(stdout);
      }
}



void init_db(void)
{
      host_db_init();
      port_db_init();
      proto_db_init();
}



void save_db(const char *filename)
{
      dword uptime = (dword)( t_already + (time(NULL) - t_start) );
      FILE *fp = fopen(filename, "wb");

      if (!fp)
      {
            printf("Error: save_db(): Can't open %s for writing.\n",
                  filename);
            exit(EXIT_FAIL);
      }

      fwrite(&uptime, sizeof(uptime), 1, fp);
      fwrite64(num_packets, fp);
      fwrite64(total_data, fp);

      pthread_mutex_lock(&db_mutex);
      host_db_save(fp);
      proto_db_save(fp);
      port_db_save(fp);
      pthread_mutex_unlock(&db_mutex);

      pthread_mutex_lock(&graph_mutex);
      graph_save(fp);
      pthread_mutex_unlock(&graph_mutex);

      fclose(fp);
}



int load_from_file(FILE *fp)
{
      int numread;
      dword uptime;

      numread = fread(&uptime, sizeof(uptime), 1, fp);
      t_already = (time_t)uptime;
      if (!numread) return 0;

      fread64(num_packets, fp, numread);
      if (!numread) return 0;

      fread64(total_data, fp, numread);
      if (!numread) return 0;

      if (!host_db_load(fp)) return 0;
      if (!proto_db_load(fp)) return 0;
      if (!port_db_load(fp)) return 0;
      if (!graph_load(fp)) return 0;

      return 1;
}



void load_db(char *filename)
{
      FILE *fp = fopen(filename, "r");
      
      if (!fp)
      {
            printf("Can't load db from %s, starting from scratch.\n",
                  filename);
            return;
      }

      if (!load_from_file(fp))
      {
            printf("Fatal error: corrupt db in %s.\n"
                  "Please remove database and restart darkstat.\n",
                  filename);
            fclose(fp);
            exit(EXIT_FAIL);
      }

      fclose(fp);
      printf("Loaded %s.\n", filename);
}



dword get_local_ip(char *inter)
{
      int tmp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
      struct ifreq ifr;
      struct sockaddr sa;

      strcpy(ifr.ifr_name, inter);
      ifr.ifr_addr.sa_family = AF_INET;
      if (ioctl(tmp, SIOCGIFADDR, &ifr) != 0)
      {
            printf("Error: Can't get own IP address on interface %s.\n",
                  inter);
            exit(EXIT_FAIL);
      }
      close(tmp);
      sa = ifr.ifr_addr;

      /* linux/socket.h:
       * struct sockaddr {
       *    sa_family_t     sa_family;      * address family, AF_xxx       
       *    char            sa_data[14];    * 14 bytes of protocol address 
       */

      /* linux/in.h:
       * struct sockaddr_in {
       * sa_family_t           sin_family;     * Address family
       *    unsigned short int    sin_port;       * Port number
       *    struct in_addr        sin_addr;       * Internet address
       * * Pad to size of `struct sockaddr'.
       *
       * * Internet address.
       * struct in_addr {
       *    __u32   s_addr;
       */

      return ntohl( ((struct sockaddr_in*)&sa)->sin_addr.s_addr );
}



void acct_main(void *ignored unused)
{
      char err[PCAP_ERRBUF_SIZE];

      if (!local_ip) local_ip = get_local_ip(acctdev);
      printf("Sniffing on device %s, local IP is ", acctdev);
      print_addr(local_ip);
      putchar('\n');

      init_db();
      init_graph();
      load_db(db_file);

      local_host_rec = host_from_ip(local_ip);

      err[0] = '\0'; /* zero length string */
      acct_pcap = pcap_open_live(acctdev, 100, promisc, PCAP_TIMEOUT, err);
      if (!acct_pcap)
      {
            printf("Error: pcap_open_live(%s): %s\n"
                  "Are you not running as root?\n", acctdev, err);
            FAIL();
      }

      if (err[0] != '\0') /* not zero length anymore -> warning */
            printf("pcap_open_live() warning: %s", err);

      if (acctexpr)
      {
            struct bpf_program expr_compiled;
            /*bpf_u_int32 net, mask;*/
            /*pcap_lookupnet(netdev, &net, &mask, err);*/

            if (pcap_compile(acct_pcap, &expr_compiled,
                  acctexpr, 1, 0 /*net*/) == -1)
            {
                  printf("Error: pcap_compile() failed: %s\n", err);
                  FAIL();
            }

            if (pcap_setfilter(acct_pcap, &expr_compiled) == -1)
            {
                  printf("Error: pcap_setfilter() failed: %s\n", err);
                  FAIL();
            }
#ifdef HAVE_FREECODE
            pcap_freecode(&expr_compiled);
#endif
      }



      t_lastsave = t_start = time(NULL);
      acct_linktype = pcap_datalink(acct_pcap);

      printf("ACCT: Capturing traffic...\n"
             "Point your browser at http://localhost:%d/ to see the stats."
             "\n\n", webport);
      up_acct = 1;

      while (!shutting_down)
      {
            struct pcap_stat ps;

            /* capture some packets for accounting */
            if (pcap_dispatch(acct_pcap, -1,
                  (pcap_handler)handle_pkt, NULL) == -1)
            {
                  printf("Error: pcap_dispatch(): %s\n",
                        pcap_geterr(acct_pcap));
                  FAIL();
            }

            /* print out cap statistics */      
            if (verbose)
            {
                  pcap_stats(acct_pcap, &ps);
                  printf("Packets: received %d, dropped %d (acct)\n",
                        ps.ps_recv, ps.ps_drop);
            }

            /* commit db to disk if enough time has passed */
            if (time(NULL) - t_lastsave > SAVE_TIME)
            {
                  t_lastsave = time(NULL);
                  save_db(db_file);
            }
      }

      pcap_close(acct_pcap);

      printf("ACCT: Out of capture loop.\n");
      SUCCEED(); /* implies up_acct = 0 */
}


Generated by  Doxygen 1.6.0   Back to index