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

bandwidthd.c

#include "bandwidthd.h"
#include  <netinet/if_ether.h>

// We must call regular exit to write out profile data, but child forks are supposed to usually
// call _exit?
#ifdef PROFILE
#define _exit(x) exit(x)
#endif

/*
#ifdef DEBUG
#define fork() (0)
#endif
*/

// ****************************************************************************************
// ** Global Variables
// ****************************************************************************************

#define SNAPLEN 100

static pcap_t *pd;

unsigned int GraphIntervalCount = 0;
unsigned int IpCount = 0;
unsigned int SubnetCount = 0;
int IntervalFinished = FALSE;
time_t IntervalStart;
time_t ProgramStart;
int RotateLogs = FALSE;
int PacketCallbackLock = 0;
pid_t pidGraphingChild = 0;

struct SubnetData SubnetTable[SUBNET_NUM];
struct IPData IpTable[IP_NUM];

int DataLink;
int IP_Offset;

struct IPDataStore *IPDataStore = NULL;
extern int bdconfig_parse(void);
void BroadcastState(int fd);
extern FILE *bdconfig_in;

struct config config;

struct Broadcast *Broadcasts = NULL;

pid_t workerchildpids[NR_WORKER_CHILDS];

void ResetTrafficCounters(void);
void CloseInterval(void);

void signal_handler(int sig)
      {
      switch (sig) 
            {
            case SIGHUP:
                  signal(SIGHUP, signal_handler);
                  RotateLogs++;
                  if (config.tag == '1') 
                        {
                        int i;

                        /* signal children */
                        for (i=0; i < NR_WORKER_CHILDS; i++) 
                              if (workerchildpids[i]) // this is a child
                                    kill(workerchildpids[i], SIGHUP);
                        }
                  break;
            case SIGTERM:
                  if (config.tag == '1') 
                        {
                        int i;

                        /* send term signal to children */
                        for (i=0; i < NR_WORKER_CHILDS; i++) 
                              if (workerchildpids[i])
                                    kill(workerchildpids[i], SIGTERM);
                        }
                  // TODO: Might want to make sure we're not in the middle of wrighting out a log file
                  exit(0);
                  break;
            }
      }

void bd_CollectingData()
      {
      char FileName[4][MAX_FILENAME];
      FILE *index;
      int Counter;

      snprintf(FileName[0], MAX_FILENAME, "%s/index.html", config.htdocs_dir);
      snprintf(FileName[1], MAX_FILENAME, "%s/index2.html", config.htdocs_dir);
      snprintf(FileName[2], MAX_FILENAME, "%s/index3.html", config.htdocs_dir);     
      snprintf(FileName[3], MAX_FILENAME, "%s/index4.html", config.htdocs_dir);

      for(Counter = 0; Counter < 4; Counter++)
            {
            index = fopen(FileName[Counter], "wt");   
            if (index)
                  {
                  fprintf(index, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
                  fprintf(index, "<HTML><HEAD><TITLE>Bandwidthd</TITLE>\n");
      
                  if (config.meta_refresh)
                        fprintf(index, "<META HTTP-EQUIV=\"REFRESH\" content=\"%u\">\n",
                                    config.meta_refresh);
                  fprintf(index, "<META HTTP-EQUIV=\"EXPIRES\" content=\"-1\">\n");
                  fprintf(index, "<META HTTP-EQUIV=\"PRAGMA\" content=\"no-cache\">\n");
                  fprintf(index, "</HEAD>\n<BODY><center><img src=\"logo.gif\" ALT=\"Logo\"><BR>\n");
                  fprintf(index, "<BR>\n - <a href=\"index.html\">Daily</a> -- <a href=\"index2.html\">Weekly</a> -- ");
                  fprintf(index, "<a href=\"index3.html\">Monthly</a> -- <a href=\"index4.html\">Yearly</a><BR>\n");
                  fprintf(index, "</CENTER><BR>bandwidthd has nothing to graph.  This message should be replaced by graphs in a few minutes.  If it's not, please see the section titled \"Known Bugs and Troubleshooting\" in the README");          
                  fprintf(index, "</BODY></HTML>\n");
                  fclose(index);
                  }
            else
                  {
                  syslog(LOG_ERR, "Cannot open %s for writing", FileName[Counter]);
                  exit(1);
                  }
            }
      }

pid_t WriteOutWebpages(long int timestamp)
{
      struct IPDataStore *DataStore = IPDataStore;
      struct SummaryData **SummaryData;
      int NumGraphs = 0;
      pid_t graphpid;
      int Counter;

      /* Did we catch any packets since last time? */
      if (!DataStore) 
            return -2;

      // break off from the main line so we don't miss any packets while we graph
      graphpid = fork();

      switch (graphpid) {
            case 0: /* we're the child, graph. */
                  {
#ifdef PROFILE
                  // Got this incantation from a message board.  Don't forget to set
                  // GMON_OUT_PREFIX in the shell
                  extern void _start(void), etext(void);
                  syslog(LOG_INFO, "Calling profiler startup...");
                  monstartup((u_long) &_start, (u_long) &etext);
#endif
            signal(SIGHUP, SIG_IGN);
                  
          nice(4); // reduce priority so I don't choke out other tasks

                  // Count Number of IP's in datastore
                  for (DataStore = IPDataStore, Counter = 0; DataStore; Counter++, DataStore = DataStore->Next);

                  // +1 because we don't want to accidently allocate 0
                  SummaryData = malloc(sizeof(struct SummaryData *)*Counter+1);

                  DataStore = IPDataStore;
                  while (DataStore) // Is not null
                        {
                        if (DataStore->FirstBlock->NumEntries > 0)
                              {
                              SummaryData[NumGraphs] = (struct SummaryData *) malloc(sizeof(struct SummaryData));
                              GraphIp(DataStore, SummaryData[NumGraphs++], timestamp+LEAD*config.range);
                              }
                      DataStore = DataStore->Next;
                        }

                  MakeIndexPages(NumGraphs, SummaryData);
      
                  _exit(0);
                  }
            break;

            case -1:
                  syslog(LOG_ERR, "Forking grapher child failed!");
                  return -1;
            break;

            default: /* parent + successful fork, assume graph success */
                  return(graphpid);
            break;
      }
}

void setchildconfig (int level) {
      static unsigned long long graph_cutoff;

      switch (level) {
            case 0:
                  config.range = RANGE1;
                  config.interval = INTERVAL1;
                  config.tag = '1';
                  graph_cutoff = config.graph_cutoff;
            break;
            case 1:
                  // Overide skip_intervals for children
                  config.skip_intervals = CONFIG_GRAPHINTERVALS;
                  config.range = RANGE2;
                  config.interval = INTERVAL2;
                  config.tag = '2';
                  config.graph_cutoff = graph_cutoff*(RANGE2/RANGE1);   
            break;
            case 2:
                  // Overide skip_intervals for children
                  config.skip_intervals = CONFIG_GRAPHINTERVALS;
                  config.range = RANGE3;
                  config.interval = INTERVAL3;
                  config.tag = '3';
                  config.graph_cutoff = graph_cutoff*(RANGE3/RANGE1);
            break;
            case 3:
                  // Overide skip_intervals for children
                  config.skip_intervals = CONFIG_GRAPHINTERVALS;
                  config.range = RANGE4;
                  config.interval = INTERVAL4;
                  config.tag = '4';
                  config.graph_cutoff = graph_cutoff*(RANGE4/RANGE1);
            break;

            default:
                  syslog(LOG_ERR, "setchildconfig got an invalid level argument: %d", level);
                  _exit(1);
      }
}

void makepidfile(pid_t pid) 
      {
      FILE *pidfile;

      pidfile = fopen("/var/run/bandwidthd.pid", "wt");
      if (pidfile) 
            {
            if (fprintf(pidfile, "%d\n", pid) == 0) 
                  {
                  syslog(LOG_ERR, "Bandwidthd: failed to write '%d' to /var/run/bandwidthd.pid", pid);
                  fclose(pidfile);
                  unlink("/var/run/bandwidthd.pid");
                  }
            else
                  fclose(pidfile);        
            }
      else
            syslog(LOG_ERR, "Could not open /var/run/bandwidthd.pid for write");
      }

void PrintHelp(void)
      {
      printf("\nUsage: bandwidthd [OPTION]\n\nOptions:\n");
      printf("\t-D\t\tDo not fork to background\n");
      printf("\t-l\t\tList detected devices\n");
      printf("\t-c filename\tAlternate configuration file\n");
      printf("\t--help\t\tShow this help\n");
      printf("\n");
      exit(0);
      }

static void handle_interval(int signal)
      {
      if (!PacketCallbackLock)
            CloseInterval();
      else // Set flag to have main loop close interval
            IntervalFinished = TRUE;
      }

int main(int argc, char **argv)
    {
    struct bpf_program fcode;
    u_char *pcap_userdata = 0;
#ifdef HAVE_PCAP_FINDALLDEVS
      pcap_if_t *Devices;
#endif
      char Error[PCAP_ERRBUF_SIZE];
      struct stat StatBuf;
      int i;
      int ForkBackground = TRUE;
      int ListDevices = FALSE;
      int Counter;
      char *bd_conf = NULL;
      struct in_addr addr, addr2;

      signal(SIGHUP, SIG_IGN);
      signal(SIGTERM, SIG_IGN);

      ProgramStart = time(NULL);

      // Init worker child pids to zero so we don't kill random things when we shut down
      for (Counter = 0; Counter < NR_WORKER_CHILDS; Counter++)
            workerchildpids[Counter] = 0;

      for(Counter = 1; Counter < argc; Counter++)
            {
            if (argv[Counter][0] == '-')
                  {
                  switch(argv[Counter][1])
                        {
                        case 'D':
                              ForkBackground = FALSE;
                              break;
                        case 'l':
                              ListDevices = TRUE; 
                              break;
                        case 'c':
                              if (argv[Counter+1])
                                    {
                                    bd_conf = argv[Counter+1];
                                    Counter++;
                                    }
                              else
                                    PrintHelp();
                              break;
                        default:
                              printf("Improper argument: %s\n", argv[Counter]);
                        case '-':
                              PrintHelp();
                        }
                  }
            }

      config.dev = NULL;
      config.description = "No Description";
      config.management_url = "https://127.0.0.1";
      config.filter = "ip or ether proto 1537";
      //config.filter = "ip";
      config.skip_intervals = CONFIG_GRAPHINTERVALS;
      config.graph_cutoff = CONFIG_GRAPHCUTOFF;
      config.promisc = TRUE;
      config.extensions = FALSE;
      config.graph = TRUE;
      config.output_cdf = FALSE;
      config.recover_cdf = FALSE;
      config.meta_refresh = CONFIG_METAREFRESH;
      config.output_database = FALSE;
      config.db_connect_string = NULL;
      config.sensor_name = "unset";  
      config.log_dir = LOG_DIR;
      config.htdocs_dir = HTDOCS_DIR;

      openlog("bandwidthd", LOG_CONS, LOG_DAEMON);

      // Locate configuration file
      if (!(bd_conf && !stat(bd_conf, &StatBuf)))
            {
            if (bd_conf)
                  {
                  printf("Could not find %s\n", bd_conf);
                  exit(1);
                  }
            else
                  if (!stat("bandwidthd.conf", &StatBuf))
                        bd_conf = "bandwidthd.conf";
                  else
                        if (!stat("./etc/bandwidthd.conf", &StatBuf))
                              bd_conf = "./etc/bandwidthd.conf";
                        else
                              if (!stat(CONFIG_FILE, &StatBuf))
                                    bd_conf = CONFIG_FILE;
                              else
                                    {
                                    printf("Cannot find bandwidthd.conf, ./etc/bandwidthd.conf or %s\n", CONFIG_FILE);
                                    syslog(LOG_ERR, "Cannot find bandwidthd.conf, ./etc/bandwidthd.conf or %s", CONFIG_FILE);
                                    exit(1);
                                    }
            }

      bdconfig_in = fopen(bd_conf, "rt");
      if (!bdconfig_in)
            {
            syslog(LOG_ERR, "Cannot open bandwidthd.conf");
            printf("Cannot open ./etc/bandwidthd.conf\n");
            exit(1);
            }
      bdconfig_parse();

      // Log list of monitored subnets
      for (Counter = 0; Counter < SubnetCount; Counter++)
            {
            addr.s_addr = ntohl(SubnetTable[Counter].ip);
            addr2.s_addr = ntohl(SubnetTable[Counter].mask);
            syslog(LOG_INFO, "Monitoring subnet %s with netmask %s", inet_ntoa(addr), inet_ntoa(addr2));
            }

#ifdef HAVE_PCAP_FINDALLDEVS
      pcap_findalldevs(&Devices, Error);
      if (config.dev == NULL && Devices && Devices->name)
            config.dev = strdup(Devices->name);
      if (ListDevices)
            {     
            while(Devices)
                  {
                  printf("Description: %s\nName: \"%s\"\n\n", Devices->description, Devices->name);
                  Devices = Devices->next;
                  }
            exit(0);
            }
#else
      if (ListDevices)
            {
            printf("List devices is not supported by you version of libpcap\n");
            exit(0);
            }
#endif      

      if (config.graph)
            bd_CollectingData();

      /* detach from console. */
      if (ForkBackground)
            if (fork2())
                  exit(0);

      makepidfile(getpid());

      setchildconfig(0); /* initialize first (day graphing) process config */

      if (config.graph || config.output_cdf)
            {
            /* fork processes for week, month and year graphing. */
            for (i=0; i<NR_WORKER_CHILDS; i++) 
                  {
                  workerchildpids[i] = fork();

                  /* initialize children and let them start doing work,
                   * while parent continues to fork children.
                   */

                  if (workerchildpids[i] == 0) 
                        { /* child */
                        setchildconfig(i+1);
                        break;
                        }

                  if (workerchildpids[i] == -1) 
                        { /* fork failed */
                        syslog(LOG_ERR, "Failed to fork graphing child (%d)", i);
                        /* i--; ..to retry? -> possible infinite loop */
                        continue;
                        }
                  }
            }     

      signal(SIGHUP, signal_handler);
      signal(SIGTERM, signal_handler);

    if(config.recover_cdf)
          RecoverDataFromCDF();

    IntervalStart = time(NULL);

      syslog(LOG_INFO, "Opening %s", config.dev);     
      pd = pcap_open_live(config.dev, SNAPLEN, config.promisc, 1000, Error);
    if (pd == NULL) 
            {
            syslog(LOG_ERR, "%s", Error);
            exit(0);
            }

    if (pcap_compile(pd, &fcode, config.filter, 1, 0) < 0)
            {
        pcap_perror(pd, "Error");
            printf("Malformed libpcap filter string in bandwidthd.conf\n");
            syslog(LOG_ERR, "Malformed libpcap filter string in bandwidthd.conf");
            exit(1);
            }

    if (pcap_setfilter(pd, &fcode) < 0)
        pcap_perror(pd, "Error");

      switch (DataLink = pcap_datalink(pd))
            {
            default:
                  if (config.dev)
                        printf("Unknown Datalink Type %d, defaulting to ethernet\nPlease forward this error message and a packet sample (captured with \"tcpdump -i %s -s 2000 -n -w capture.cap\") to hinkle@derbyworks.com\n", DataLink, config.dev);
                  else
                        printf("Unknown Datalink Type %d, defaulting to ethernet\nPlease forward this error message and a packet sample (captured with \"tcpdump -s 2000 -n -w capture.cap\") to hinkle@derbyworks.com\n", DataLink);
                  syslog(LOG_INFO, "Unkown datalink type, defaulting to ethernet");
            case DLT_EN10MB:
                  syslog(LOG_INFO, "Packet Encoding: Ethernet");
                  IP_Offset = 14; //IP_Offset = sizeof(struct ether_header);
                  break;      
#ifdef DLT_LINUX_SLL 
            case DLT_LINUX_SLL:
                  syslog(LOG_INFO, "Packet Encoding: Linux Cooked Socket");
                  IP_Offset = 16;
                  break;
#endif
#ifdef DLT_RAW
            case DLT_RAW:
                  printf("Untested Datalink Type %d\nPlease report to hinkle@derbyworks.net if bandwidthd works for you\non this interface\n", DataLink);
                  printf("Packet Encoding:\n\tRaw\n");
                  syslog(LOG_INFO, "Untested packet encoding: Raw");
                  IP_Offset = 0;
                  break;
#endif
            case DLT_IEEE802:
                  printf("Untested Datalink Type %d\nPlease report to hinkle@derbyworks.net if bandwidthd works for you\non this interface\n", DataLink);
                  printf("Packet Encoding:\nToken Ring\n");
                  syslog(LOG_INFO, "Untested packet encoding: Token Ring");
                  IP_Offset = 22;
                  break;
            }

      if (ForkBackground)
            {                                           
            fclose(stdin);
            fclose(stdout);
            fclose(stderr);
            }

      if (IPDataStore)  // If there is data in the datastore draw some initial graphs
            {
            syslog(LOG_INFO, "Drawing initial graphs");
            pidGraphingChild = WriteOutWebpages(IntervalStart+config.interval);
            }

      ResetTrafficCounters();
      // This is also set in CloseInterval because it gets overwritten in some commit modules
      signal(SIGALRM, handle_interval);
      alarm(config.interval);
      nice(1);
      while (1)
            {
            // Bookeeping
          if (IntervalFinished)  // Then write out this intervals data and possibly kick off the grapher
                  CloseInterval();

            // Process 1 buffer full of data
      if (pcap_dispatch(pd, -1, PacketCallback, pcap_userdata) == -1) 
                  {
            syslog(LOG_ERR, "Bandwidthd: pcap_dispatch: %s",  pcap_geterr(pd));
            exit(1);
            }
            }

    pcap_close(pd);
    exit(0);        
    }
   
void CloseInterval(void)
    {
      time_t current_time;

#ifdef PROFILE
      // Exit and write out profiling information before we commit the data
      exit(0);
#endif
      current_time = time(NULL);

      // Sanity check for time getting set forward
      if ((current_time > IntervalStart+(config.interval*100)) || (current_time < IntervalStart-(config.interval*100)))
            {
            IntervalStart = current_time-config.interval;
            ProgramStart = current_time;  
            }

    GraphIntervalCount++;
    CommitData(IntervalStart+config.interval);
      BroadcastState(pcap_fileno(pd));  
      ResetTrafficCounters();
      // Make sure the signal hasn't been overwritten by one of our CommitData modules
      // pgsql module does overwrite it
    signal(SIGALRM, handle_interval);
      alarm(config.interval);
    }

// Write an ethernet packet describing us out the given socket
void BroadcastState(int fd)
      {
      char buf[SNAPLEN];
      char    enet_broadcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
      struct ether_header *eptr;

      if (DataLink != DLT_EN10MB)
            return;

      if ((14+strlen(config.sensor_name)+strlen(config.dev)) > SNAPLEN)
            {
            syslog(LOG_ERR, "Sensor and device name too long for broadcast");
            return;
            }

      bzero(buf, sizeof(buf));
    eptr = (struct ether_header *) buf;         /* start of 16-byte Ethernet header */

    // Always broadcast
    memcpy(&buf[0], enet_broadcast, 6);
    memcpy(&buf[6], enet_broadcast, 6); // Would rather use our own mac address

      eptr->ether_type = htons(1537);     // Random packet type we'll use for bandwidthd  
      
      memcpy(buf+14, config.sensor_name, strlen(config.sensor_name)+1);
      memcpy(buf+14+strlen(config.sensor_name)+1, config.dev, strlen(config.dev)+1);

    if (write(fd, buf, SNAPLEN) != SNAPLEN)
      syslog(LOG_ERR, "Write error during bandwidthd broadcast");
      }

void ParseBroadcast(const u_char *in)
      {
      char *p = (char *)in+14; // Skip ethernet header
      struct Broadcast *bc;
      struct Broadcast *bc2;
      char *sensor_name;
      char *interface;

      sensor_name = p;
      interface = p+strlen(p)+1;

      // Sanity check
      if (strlen(sensor_name) > SNAPLEN || strlen(interface) > SNAPLEN)
            {
            syslog(LOG_ERR, "Bandwidthd broadcast packet failed sanity check, discarding");
            return;
            }

      if ((!strcmp(sensor_name, config.sensor_name)) && (!strcmp(interface, config.dev)))
            return; // Our own packet

      for (bc = Broadcasts; bc; bc = bc->next)
            {
            if ((!strcmp(sensor_name, bc->sensor_name)) && (!strcmp(interface, bc->interface)))
                  {
                  // Found this link
                  bc->received = time(NULL); 
                  return;
                  }
            }

      bc2 = malloc(sizeof(struct Broadcast));
      bc2->sensor_name = strdup(sensor_name);
      bc2->interface = strdup(interface); 
      bc2->received = time(NULL);
      bc2->next = NULL;
                        
      if (Broadcasts == NULL)
            {
            Broadcasts = bc2;
            return;
            }
      else
            {
            for (bc = Broadcasts; bc->next; bc = bc->next);
            bc->next = bc2;
            return;
            }
      }

void PacketCallback(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
    {
    unsigned int Counter;

    u_int caplen = h->caplen;
      struct ether_header *eptr;
    const struct ip *ip;

    uint32_t srcip;
    uint32_t dstip;

    struct IPData *ptrIPData;

      int AlreadyTotaled = FALSE;
      PacketCallbackLock = TRUE;

      eptr = (struct ether_header *) p;
      if (eptr->ether_type == htons(1537))
            ParseBroadcast(p);

    caplen -= IP_Offset;  // We're only measuring ip size, so pull off the ethernet header
    p += IP_Offset; // Move the pointer past the datalink header

    ip = (const struct ip *)p; // Point ip at the ip header

      if (ip->ip_v != 4) // then not an ip packet so skip it
            return;

    srcip = ntohl(*(uint32_t *) (&ip->ip_src));
    dstip = ntohl(*(uint32_t *) (&ip->ip_dst));
      
    for (Counter = 0; Counter < SubnetCount; Counter++)
        {    
            // Packets from a monitored subnet to a monitored subnet will be
            // credited to both ip's

            // Totals are checked for first to speed up FindIp in the total case
            // That way the 0 entry is always first in the FindIp table
        if (SubnetTable[Counter].ip == (srcip & SubnetTable[Counter].mask))
            {
                  if (!AlreadyTotaled)
                        {
                  Credit(&(IpTable[0].Send), ip);
                        AlreadyTotaled = TRUE;
                        }

            ptrIPData = FindIp(srcip);  // Return or create this ip's data structure
                  if (ptrIPData)
                  Credit(&(ptrIPData->Send), ip);
            }
    
        if (SubnetTable[Counter].ip == (dstip & SubnetTable[Counter].mask))
            {
                  if (!AlreadyTotaled)
                        {
                    Credit(&(IpTable[0].Receive), ip);
                        AlreadyTotaled = TRUE;
                        }

            ptrIPData = FindIp(dstip);
            if (ptrIPData)
                    Credit(&(ptrIPData->Receive), ip);
            }                        
        }
      PacketCallbackLock = FALSE;
    }


// Eliminates duplicate entries and fully included subnets so packets don't get
// counted multiple times
void MonitorSubnet(unsigned int ip, unsigned int mask)
      {
      unsigned int subnet = ip & mask;
      int Counter, Counter2;
      struct in_addr addr, addr2;

    addr.s_addr = ntohl(subnet);
    addr2.s_addr = ntohl(mask);

      for (Counter = 0; Counter < SubnetCount; Counter++)
            {
            if ((SubnetTable[Counter].ip == subnet) && (SubnetTable[Counter].mask == mask))
                  {
                  syslog(LOG_ERR, "Subnet %s/%s already exists, skipping.", inet_ntoa(addr), inet_ntoa(addr2));
                  return; 
                  }
            }

      for (Counter = 0; Counter < SubnetCount; Counter++)
            {
            if ((SubnetTable[Counter].ip == (ip & SubnetTable[Counter].mask)) && (SubnetTable[Counter].mask < mask))
                  {
                  syslog(LOG_ERR, "Subnet %s/%s is already included, skipping.", inet_ntoa(addr), inet_ntoa(addr2));
                  return; 
                  }
            }
      
      for (Counter = 0; Counter < SubnetCount; Counter++)
            {
            if (((SubnetTable[Counter].ip & mask) == subnet) && (SubnetTable[Counter].mask > mask))
                  {
                  syslog(LOG_ERR, "Subnet %s/%s includes already listed subnet, removing smaller entry", inet_ntoa(addr), inet_ntoa(addr2));
                  // Shift everything down
                  for (Counter2 = Counter; Counter2 < SubnetCount-1; Counter2++)
                        {
                        SubnetTable[Counter2].ip = SubnetTable[Counter2+1].ip;
                        SubnetTable[Counter2].mask = SubnetTable[Counter2+1].mask;
                        }
                  SubnetCount--;
                  Counter--; // Retest this entry because we replaced it 
                  }
            }
            
      SubnetTable[SubnetCount].mask = mask;
      SubnetTable[SubnetCount].ip = subnet;
      SubnetCount++;
      }

inline void Credit(struct Statistics *Stats, const struct ip *ip)
    {
    unsigned long size;
    const struct tcphdr *tcp;
    uint16_t port;
      int Counter;

    size = ntohs(ip->ip_len);

    Stats->total += size;
      Stats->packet_count++;
    
    switch(ip->ip_p)
        {
        case 6:     // TCP
            tcp = (struct tcphdr *)(ip+1);
                  tcp = (struct tcphdr *) ( ((char *)tcp) + ((ip->ip_hl-5)*4) ); // Compensate for IP Options
            Stats->tcp += size;

                  // This is a wierd way to do this, I know, but the old "if/then" structure burned alot more CPU
                  for (port = ntohs(tcp->TCPHDR_DPORT), Counter=2 ; Counter ; port = ntohs(tcp->TCPHDR_SPORT), Counter--)
                        {
                        switch(port)
                              {
                              case 110:
                              case 25:
                              case 143:
                              case 587:
                                    Stats->mail += size;
                                    return;
                              case 80:
                              case 443:
                                    Stats->http += size;
                                    return;
                              case 20:
                              case 21:
                                    Stats->ftp += size;
                                    return;                             
                              case 1044:  // Direct File Express
                              case 1045:  // ''  <- Dito Marks
                              case 1214:  // Grokster, Kaza, Morpheus
                              case 4661:  // EDonkey 2000
                              case 4662:  // ''
                              case 4665:  // ''
                              case 5190:  // Song Spy
                              case 5500:  // Hotline Connect
                              case 5501:  // ''
                              case 5502:  // ''
                              case 5503:  // ''
                              case 6346:  // Gnutella Engine
                              case 6347:  // ''
                              case 6666:  // Yoink
                              case 6667:  // ''
                              case 7788:  // Budy Share
                              case 8888:  // AudioGnome, OpenNap, Swaptor
                              case 8889:  // AudioGnome, OpenNap
                              case 28864: // hotComm                    
                              case 28865: // hotComm
                                    Stats->p2p += size;
                                    return;     
                              }
                        }
            return;
        case 17:
            Stats->udp += size;
            return;
        case 1: 
            Stats->icmp += size;
            return;
        }
    }

// TODO:  Throw away old data!
void DropOldData(long int timestamp)      // Go through the ram datastore and dump old data
      {
      struct IPDataStore *DataStore;
      struct IPDataStore *PrevDataStore;  
      struct DataStoreBlock *DeletedBlock;
      
      PrevDataStore = NULL;
    DataStore = IPDataStore;

      // Progress through the linked list until we reach the end
      while(DataStore)  // we have data
            {
            // If the First block is out of date, purge it, if it is the only block
            // purge the node
        while(DataStore->FirstBlock->LatestTimestamp < timestamp - config.range)
                  {
            if ((!DataStore->FirstBlock->Next) && PrevDataStore) // There is no valid block of data for this ip, so unlink the whole ip
                        {                                                                       // Don't bother unlinking the ip if it's the first one, that's to much
                                                                                                // Trouble
                        PrevDataStore->Next = DataStore->Next;    // Unlink the node
                        free(DataStore->FirstBlock->Data);      // Free the memory
                        free(DataStore->FirstBlock);
                        free(DataStore);                                                                    
                        DataStore = PrevDataStore->Next;    // Go to the next node
                        if (!DataStore) return; // We're done
                        }                       
                  else if (!DataStore->FirstBlock->Next)
                        {
                        // There is no valid block of data for this ip, and we are 
                        // the first ip, so do nothing 
                        break; // break out of this loop so the outside loop increments us
                        } 
                  else // Just unlink this block
                        {
                        DeletedBlock = DataStore->FirstBlock;
                        DataStore->FirstBlock = DataStore->FirstBlock->Next;  // Unlink the block
                        free(DeletedBlock->Data);
                        free(DeletedBlock);
                      }
                  }

            PrevDataStore = DataStore;                      
            DataStore = DataStore->Next;
            }
      }

void StoreIPDataInDatabase(struct IPData IncData[], struct extensions *extension_data)
      {
      if (config.output_database == DB_PGSQL)
            pgsqlStoreIPData(IncData, extension_data);
      }

void StoreIPDataInCDF(struct IPData IncData[])
      {
      struct IPData *IPData;
      unsigned int Counter;
      FILE *cdf;
      struct Statistics *Stats;
      char IPBuffer[50];
      char logfile[MAX_FILENAME];
      
      snprintf(logfile, MAX_FILENAME, "%s/log.%c.0.cdf", config.log_dir, config.tag);

      cdf = fopen(logfile, "at");

      for (Counter=0; Counter < IpCount; Counter++)
            {
            IPData = &IncData[Counter];
            HostIp2CharIp(IPData->ip, IPBuffer);
            fprintf(cdf, "%s,%lu,", IPBuffer, IPData->timestamp);
            Stats = &(IPData->Send);
            fprintf(cdf, "%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu", Stats->total, Stats->icmp, Stats->udp, Stats->tcp, Stats->ftp, Stats->http, Stats->mail, Stats->p2p); 
            Stats = &(IPData->Receive);
            fprintf(cdf, "%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", Stats->total, Stats->icmp, Stats->udp, Stats->tcp, Stats->ftp, Stats->http, Stats->mail, Stats->p2p);         
            }
      fclose(cdf);
      }

void _StoreIPDataInRam(struct IPData *IPData)
      {
      struct IPDataStore *DataStore;
      struct DataStoreBlock *DataStoreBlock;

      if (!IPDataStore) // we need to create the first entry
            {
            // Allocate Datastore for this IP
          IPDataStore = malloc(sizeof(struct IPDataStore));
                  
            IPDataStore->ip = IPData->ip;
            IPDataStore->Next = NULL;
                              
            // Allocate it's first block of storage
            IPDataStore->FirstBlock = malloc(sizeof(struct DataStoreBlock));
            IPDataStore->FirstBlock->LatestTimestamp = 0;

            IPDataStore->FirstBlock->NumEntries = 0;
            IPDataStore->FirstBlock->Data = calloc(IPDATAALLOCCHUNKS, sizeof(struct IPData));
            IPDataStore->FirstBlock->Next = NULL;                                                                                                           
        if (!IPDataStore->FirstBlock || ! IPDataStore->FirstBlock->Data)
            {
            syslog(LOG_ERR, "Could not allocate datastore! Exiting!");
            exit(1);
            }
            }

      DataStore = IPDataStore;

      // Take care of first case
      while (DataStore) // Is not null
            {
            if (DataStore->ip == IPData->ip) // then we have the right store
                  {
                  DataStoreBlock = DataStore->FirstBlock;

                  while(DataStoreBlock) // is not null
                        {
                        if (DataStoreBlock->NumEntries < IPDATAALLOCCHUNKS) // We have a free spot
                              {
                              memcpy(&DataStoreBlock->Data[DataStoreBlock->NumEntries++], IPData, sizeof(struct IPData));
                              DataStoreBlock->LatestTimestamp = IPData->timestamp;
                              return;
                              }
                else
                              {
                              if (!DataStoreBlock->Next) // there isn't another block, add one
                                    {
                          DataStoreBlock->Next = malloc(sizeof(struct DataStoreBlock));
                                    DataStoreBlock->Next->LatestTimestamp = 0;
                                    DataStoreBlock->Next->NumEntries = 0;
                                    DataStoreBlock->Next->Data = calloc(IPDATAALLOCCHUNKS, sizeof(struct IPData));
                                    DataStoreBlock->Next->Next = NULL;                                                                                                                    
                                    }

                              DataStoreBlock = DataStoreBlock->Next;
                              }
                        }                                   

                  return;
                  }
            else
                  {
                  if (!DataStore->Next) // there is no entry for this ip, so lets make one.
                        {
                        // Allocate Datastore for this IP
              DataStore->Next = malloc(sizeof(struct IPDataStore));
                        
                      DataStore->Next->ip = IPData->ip;
                        DataStore->Next->Next = NULL;
                              
                        // Allocate it's first block of storage
                        DataStore->Next->FirstBlock = malloc(sizeof(struct DataStoreBlock));
                        DataStore->Next->FirstBlock->LatestTimestamp = 0;
                        DataStore->Next->FirstBlock->NumEntries = 0;
                        DataStore->Next->FirstBlock->Data = calloc(IPDATAALLOCCHUNKS, sizeof(struct IPData));
                        DataStore->Next->FirstBlock->Next = NULL;                                                                                                       
                        }
      
                  DataStore = DataStore->Next;              
                  }
            }
      }

void StoreIPDataInRam(struct IPData IncData[])
      {
      unsigned int Counter;

    for (Counter=0; Counter < IpCount; Counter++)
            _StoreIPDataInRam(&IncData[Counter]);
      }

void CommitData(time_t timestamp)
    {
      int MayGraph = FALSE;
    unsigned int Counter;
      struct stat StatBuf;
      char logname1[MAX_FILENAME];
      char logname2[MAX_FILENAME];
      int offset;
      struct extensions *extension_data;


      // Set the timestamps
      for (Counter=0; Counter < IpCount; Counter++)
        IpTable[Counter].timestamp = timestamp;

      // Call extensions
      if (config.extensions)
            extension_data = execute_extensions();
      else
            extension_data = NULL;

      // Output modules
      // Only call this from first thread
      if (config.output_database && config.tag == '1')
            StoreIPDataInDatabase(IpTable, extension_data);

      if (config.output_cdf)
            {
            // TODO: This needs to be moved into the forked section, but I don't want to 
            //    deal with that right now (Heavy disk io may make us drop packets)
            StoreIPDataInCDF(IpTable);

            if (RotateLogs >= config.range/RANGE1) // We set this++ on HUP
                  {
                  snprintf(logname1, MAX_FILENAME, "%s/log.%c.%n4.cdf", config.log_dir, config.tag, &offset); 
                  snprintf(logname2, MAX_FILENAME, "%s/log.%c.5.cdf", config.log_dir, config.tag); 

                  if (!stat(logname2, &StatBuf)) // File exists
                        unlink(logname2);

                  if (!stat(logname1, &StatBuf)) // File exists
                        rename(logname1, logname2);
                  logname1[offset] = '3';
                  logname2[offset] = '4';             
                  if (!stat(logname1, &StatBuf)) // File exists
                        rename(logname1, logname2);
            logname1[offset] = '2';
            logname2[offset] = '3';             
                  if (!stat(logname1, &StatBuf)) // File exists
                        rename(logname1, logname2);
            logname1[offset] = '1';
            logname2[offset] = '2';             
                  if (!stat(logname1, &StatBuf)) // File exists
                        rename(logname1, logname2);
            logname1[offset] = '0';
            logname2[offset] = '1';             
                  if (!stat(logname1, &StatBuf)) // File exists
                        rename(logname1, logname2); 
                  fclose(fopen(logname1, "at")); // Touch file
                  RotateLogs = FALSE;
                  }
            }

      if (config.graph)
            {
            StoreIPDataInRam(IpTable);

            // If there is no child to wait on or child returned or waitpid produced an error
            if (pidGraphingChild < 1 || waitpid(pidGraphingChild, NULL, WNOHANG))
                  MayGraph = TRUE;                    

            if (GraphIntervalCount%config.skip_intervals == 0 && MayGraph)
                  pidGraphingChild = WriteOutWebpages(timestamp);
            else if (GraphIntervalCount%config.skip_intervals == 0)
                  syslog(LOG_INFO, "Previouse graphing run not complete... Skipping current run");

            DropOldData(timestamp);
            }

      destroy_extension_data(extension_data);
    }


int RCDF_Test(char *filename)
      {
      // Determine if the first date in the file is before the cutoff
      // return FALSE on error
      FILE *cdf;
      char ipaddrBuffer[16];
      time_t timestamp;

      if (!(cdf = fopen(filename, "rt"))) 
            return FALSE;
      fseek(cdf, 10, SEEK_END); // fseek to near end of file
      while (fgetc(cdf) != '\n') // rewind to last newline
            {
            if (fseek(cdf, -2, SEEK_CUR) == -1)
                  break;
            }
      if(fscanf(cdf, " %15[0-9.],%lu,", ipaddrBuffer, &timestamp) != 2)
            {
            syslog(LOG_ERR, "%s is corrupted, skipping", filename); 
            return FALSE;
            }
      fclose(cdf);
      if (timestamp < time(NULL) - config.range)
            return FALSE; // There is no data in this file from after the cutoff
      else
            return TRUE; // This file has data from after the cutoff
      }


void RCDF_PositionStream(FILE *cdf)
      {
    time_t timestamp;
      time_t current_timestamp;
      char ipaddrBuffer[16];

      current_timestamp = time(NULL);

      fseek(cdf, 0, SEEK_END);
      timestamp = current_timestamp;
      while (timestamp > current_timestamp - config.range)
            {
            // What happenes if we seek past the beginning of the file?
            if (fseek(cdf, -512*1024,SEEK_CUR) == -1 || ferror(cdf))
                  { // fseek returned error, just seek to beginning
                  clearerr(cdf);
                  fseek(cdf, 0, SEEK_SET);
                  return;
                  }
            while (fgetc(cdf) != '\n' && !feof(cdf)); // Read to next line
            ungetc('\n', cdf);  // Just so the fscanf mask stays identical
        if(fscanf(cdf, " %15[0-9.],%lu,", ipaddrBuffer, &timestamp) != 2)
                  {
                  syslog(LOG_ERR, "Unknown error while scanning for beginning of data...\n");
                  return;     
                  }
            }
      while (fgetc(cdf) != '\n' && !feof(cdf));
      ungetc('\n', cdf); 
      }

void ResetTrafficCounters(void)
      {
      // Set to 1 because "totals" are the 0 slot
      IpCount = 1;
    IntervalStart=time(NULL);
    IntervalFinished = FALSE;
      memset(IpTable, 0, sizeof(struct IPData)*IP_NUM);
      }

void RCDF_Load(FILE *cdf)
      {
    time_t timestamp;
      time_t current_timestamp = 0;
      struct in_addr ipaddr;
      struct IPData *ip=NULL;
      char ipaddrBuffer[16];
      unsigned long int Counter = 0;
      unsigned long int IntervalsRead = 0;

    for(Counter = 0; !feof(cdf) && !ferror(cdf); Counter++)
          {
            if(fscanf(cdf, " %15[0-9.],%lu,", ipaddrBuffer, &timestamp) != 2) 
                  goto End_RecoverDataFromCdf;

            if (!timestamp) // First run through loop
                  current_timestamp = timestamp;

            if (timestamp != current_timestamp)
                  { // Dump to datastore
                  StoreIPDataInRam(IpTable);
                  IpCount = 0;
                  current_timestamp = timestamp;
                  IntervalsRead++;
                  }           
            inet_aton(ipaddrBuffer, &ipaddr);
            ip = FindIp(ntohl(ipaddr.s_addr));
            ip->timestamp = timestamp;

        if (fscanf(cdf, "%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,",
            &ip->Send.total, &ip->Send.icmp, &ip->Send.udp,
            &ip->Send.tcp, &ip->Send.ftp, &ip->Send.http, &ip->Send.mail, &ip->Send.p2p) != 7
          || fscanf(cdf, "%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu",
            &ip->Receive.total, &ip->Receive.icmp, &ip->Receive.udp,
            &ip->Receive.tcp, &ip->Receive.ftp, &ip->Receive.http, &ip->Receive.mail, &ip->Receive.p2p) != 7)
                  goto End_RecoverDataFromCdf;        
            }

End_RecoverDataFromCdf:
      StoreIPDataInRam(IpTable);
      syslog(LOG_INFO, "Finished recovering %lu records", Counter);     
      DropOldData(time(NULL)); // Dump the extra data
    if(!feof(cdf))
       syslog(LOG_ERR, "Failed to parse part of log file. Giving up on the file");
      IpCount = 0; // Reset traffic Counters
    fclose(cdf);
      }

void RecoverDataFromCDF(void)
      {
      FILE *cdf;
      char index[] = "012345";
      char logname[MAX_FILENAME];
      int offset;
      int Counter;
      int First = FALSE;

      snprintf(logname, MAX_FILENAME, "%s/log.%c.%n0.cdf", config.log_dir, config.tag, &offset);

      for (Counter = 5; Counter >= 0; Counter--)
            {
            logname[offset] = index[Counter];
            if (RCDF_Test(logname))
                  break;
            }
      
      First = TRUE;
      for (; Counter >= 0; Counter--)
            {
            logname[offset] = index[Counter];
            if ((cdf = fopen(logname, "rt")))
                  {
                  syslog(LOG_INFO, "Recovering from %s", logname);
                  if (First)
                        {
                        RCDF_PositionStream(cdf);
                        First = FALSE;
                        }
                  RCDF_Load(cdf);
                  }
            }
      }

// ****** FindIp **********
// ****** Returns or allocates an Ip's data structure

inline struct IPData *FindIp(uint32_t ipaddr)
    {
    unsigned int Counter;
      static time_t last_error = 0;
    
    for (Counter=0; Counter < IpCount; Counter++)
        if (IpTable[Counter].ip == ipaddr)
            return (&IpTable[Counter]);
    
    if (IpCount >= IP_NUM)
        {
            time_t current_error = time(NULL);
            if (current_error > last_error + 30)
                  {
                  last_error = current_error;
              syslog(LOG_ERR, "Maximum tracked IP's (%d) is too low, some ips will be ignored.  Possible network scan?", IP_NUM);
                  }
            return(NULL);
        }
      
    IpTable[IpCount].ip = ipaddr;
    return (&IpTable[IpCount++]);
    }

size_t ICGrandTotalDataPoints = 0;

char inline *HostIp2CharIp(unsigned long ipaddr, char *buffer)
    {
      struct in_addr in_addr;
      char *s;

      in_addr.s_addr = htonl(ipaddr);     
    s = inet_ntoa(in_addr);
      strncpy(buffer, s, 16);
      buffer[15] = '\0';
      return(buffer);
/*  uint32_t ip = *(uint32_t *)ipaddr;

      sprintf(buffer, "%d.%d.%d.%d", (ip << 24)  >> 24, (ip << 16) >> 24, (ip << 8) >> 24, (ip << 0) >> 24);
*/
    }


// Add better error checking
int fork2()
    {
    pid_t pid;

    if (!(pid = fork()))
        {
        if (!fork())
            {
#ifdef PROFILE
                        // Got this incantation from a message board.  Don't forget to set
                        // GMON_OUT_PREFIX in the shell
                        extern void _start(void), etext(void);
                        syslog(LOG_INFO, "Calling profiler startup...");
                        monstartup((u_long) &_start, (u_long) &etext);
#endif
            return(0);
            }        

        _exit(0);
        }
    
    waitpid(pid, NULL, 0);
    return(1);
    }


Generated by  Doxygen 1.6.0   Back to index