Logo Search packages:      
Sourcecode: net-acct version File versions  Download package

capture-linux.c

/* 
 * Network accounting
 * capture-linux.c - capture raw packets - linux version
 * (C) 1994, 1995 Ulrich Callmeier
 * 27/5/1998 - modified to use /etc/protocols instead of constants -mk
 */

#include <sys/time.h>
#include <sys/wait.h>
#include "netacct.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <malloc.h>
#include <strings.h>
#include <signal.h>
#include <fcntl.h>
#include <utmp.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <asm/types.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>

char *rcs_revision_capture_c = "$Revision: 1.9 $";

void handle_ip(unsigned char buf[], char *devname, char *user);

static int capture_sd = -1;

/*
 * New global constants from /etc/protocols replace #defines from protocols.h
 */
int IP_ICMP = 0;
int IP_TCP = 0;
int IP_UDP = 0;

void init_capture()
/*
 * 1) Open our capture socket
 * 2) Set all the promisc devices to promiscous mode
 * 3) Read essential protocol numbers from /etc/protocols - mk
 */
{
    struct ifreq ifr;
    struct promisc_device *p;
    struct protoent *pr;

    if ((capture_sd = socket (AF_INET, SOCK_PACKET, htons (ETH_P_ALL))) < 0)
      {
          syslog(LOG_ERR, "can't get socket: %m\n");
          daemon_stop(0);
      }

    p = cfg -> promisc;
    
    while(p!=NULL)
      {
          strcpy (p -> oldifr.ifr_name, p -> name);
          
          if (ioctl (capture_sd, SIOCGIFFLAGS, &(p -> oldifr)) < 0)
            {
                syslog(LOG_ERR, "can't get flags: %m\n");
                daemon_stop(0);
            }
          
          p -> reset = 1;
          
          ifr = p -> oldifr;
          ifr.ifr_flags |= IFF_PROMISC;
          
          if (ioctl (capture_sd, SIOCSIFFLAGS, &ifr) < 0)
            {
                syslog(LOG_ERR, "can't set flags: %m\n");
                daemon_stop(0);
            }

          DEBUG(DBG_MISC, sprintf(dbg, "%s set to promiscous mode\n", p -> name));
          
          p = p -> next;
        }
    if((pr=getprotobyname("tcp")) == NULL) {
        syslog(LOG_ERR, "Can't get TCP protocol entry.\n");
        daemon_stop(0);
    }
    IP_TCP = pr->p_proto;
    if((pr=getprotobyname("udp")) == NULL) {
        syslog(LOG_ERR, "Can't get UDP protocol entry.\n");
        daemon_stop(0);
    }
    IP_UDP = pr->p_proto;
    if((pr=getprotobyname("icmp")) == NULL) {
        syslog(LOG_ERR, "Can't get ICMP protocol entry.\n");
        daemon_stop(0);
    }
    IP_ICMP = pr->p_proto;
}

void exit_capture(void)
{
    struct promisc_device *p;

    /* do we have to check (capture_sd >= 0) ? */

    p = cfg -> promisc;
    
    while(p != NULL)
      {
          if (ioctl (capture_sd, SIOCSIFFLAGS, &(p -> oldifr)) < 0)
            {
                syslog(LOG_ERR, "can't reset flags: %m\n");
            }
          
          p = p -> next;
      }
    
    close (capture_sd);
}

inline int onnet(unsigned long int addr, struct ipnetwork *net)
{
  return ((addr & net -> netmask) == net -> netnumber);
}

int onnetlist(unsigned long int addr, struct ipnetwork *netlist)
{
    while(netlist!=NULL)
      {
          if(onnet(addr, netlist))
            {
                return 1;
            }
          netlist = netlist -> next;
      }
    return 0;
}

struct dynadat *dynadat = NULL;

char *check_user_dev2line(char *devname)
/*
 * Find username corresponding to devname
 */
{
    struct dev2line *d2l;
    char *line;
    struct dynadat *dd;
    int found;

    d2l = dev2line;
    line = NULL;

    while(d2l!=NULL)
      {
          if(strcmp(d2l -> netinterface, devname)==0)
            {
                line = d2l -> line;
                break;
            }
          
          d2l = d2l -> next;
      }
    
    if(line == NULL)
      return NULL;
    
    dd = dynadat;

    found = 0;
    while(dd != NULL)
      {
          if(strcmp(dd -> netinterface, devname) == 0)
            {
                found = 1;
                break;
            }
          dd = dd -> next;
      }
          
    if(!found)
      {
          /* We don't have an entry for this device yet. Add one */

          dd = malloc(sizeof(struct dynadat));
          
          dd -> netinterface = strdup(devname);
          dd -> last_stat = 0; /* force reading of utmp */
          dd -> mtime = 0;
          dd -> user = NULL;
          dd -> next = dynadat;
          dynadat = dd;
      }

    /* dd now points to the right dynadat entry */
    /* maybe this is out of date, so we check */
          
    if ((now - dd->last_stat) > FORCE_STAT_TIME )
      {
          struct stat statbuf;
          
          /* it could be invalid, lets stat utmp */
          
          if(stat(_PATH_UTMP, &statbuf)==0)
            {
            DEBUG(DBG_UTMP, sprintf(dbg, "%d: did a stat of %s\n",(int) now,_PATH_UTMP));
            
            dd -> last_stat = now;
            
            if((statbuf.st_mtime - dd->mtime) > 0)
              {
                struct utmp *ut_rec; /* utmp record */
                
                /* we have to wade through utmp */
                DEBUG(DBG_UTMP, sprintf(dbg, "%d: wading through utmp %s\n",(int) now, _PATH_UTMP));
                
                dd -> mtime = statbuf.st_mtime;
                
                while ((ut_rec = getutent()))
                  {
                  if ((ut_rec->ut_type == USER_PROCESS) &&
                      (ut_rec->ut_name[0] != '\000') &&
                      (strcmp(ut_rec->ut_line,line)==0))
                    {
                      if(dd -> user) free(dd -> user);
                      dd -> user = malloc(10);
                      strncpy(dd -> user, ut_rec->ut_user, 8);
                      dd->user[8] = 0;
      
                      DEBUG(DBG_DYNAMIC, sprintf(dbg, "found %s for %s\n",dd->user, line));

                      break;
                    }
                  }
                endutent();
              }
            }
          else
            {
            syslog(LOG_ERR,"couldn't stat %s: %m\n",_PATH_UTMP);
            return NULL;
            }
        }
    
    return dd -> user;
}

char *check_user_dynamicip(__u32 addr)
/*
 * Find username corresponding to addr
 */
{
    struct dynadat *dd;
    int found;

    DEBUG(DBG_ANNOYING, sprintf(dbg, "check_user_dynamicip(%s)\n", intoa(addr)));

    dd = dynadat;

    found = 0;
    while(dd != NULL)
      {
      if(dd -> addr == addr)
        {
          found = 1;
          break;
        }
      dd = dd -> next;
      }
          
    if(!found)
      {
          /* We don't have an entry for this addr yet. Add one */

          dd = malloc(sizeof(struct dynadat));
          
          dd -> addr = addr;
          dd -> last_stat = 0; /* force reading of dir */
          dd -> mtime = 0;
          dd -> user = NULL;
          dd -> next = dynadat;
          dynadat = dd;

          DEBUG(DBG_DYNAMIC, sprintf(dbg, "added entry for %s to dynadat list\n", intoa(addr)));
          
      }

    /* dd now points to the right dynadat entry */
    /* maybe this is out of date, so we check */
          
    if ((now - dd->last_stat) > FORCE_STAT_TIME )
      {
      struct stat statbuf;
      
      /* it could be invalid, lets stat dir */
          
      if(stat(cfg -> dynamicip, &statbuf)==0)
        {
          DEBUG(DBG_DYNAMIC, sprintf(dbg, "%d: did a stat of %s, last_stat was %d, mtime was %d\n",(int) now, cfg->dynamicip, (int) dd->last_stat, (int) dd -> mtime));
          
          dd -> last_stat = now;
          
          if((statbuf.st_mtime - dd->mtime) > 0)
            {
            FILE *f;
            char *s;
            /* we have to read dir */
            
            s = malloc(strlen(cfg->dynamicip) + 1 + 15 + 1 );
            
            strcpy(s, cfg->dynamicip);
            strcat(s, "/");
            strcat(s, intoa(addr));
            
            DEBUG(DBG_DYNAMIC, sprintf(dbg, "%d: reading %s\n",(int) now, s));
            
            if(dd -> user) free(dd -> user);
            dd -> user = NULL;
            dd -> mtime = statbuf.st_mtime;
            
            f = fopen(s, "r");
            
            if(f == NULL)
              {
                /* syslog(LOG_ERR,"couldn't fopen %s: %m\n",s); */
                DEBUG(DBG_DYNAMIC, sprintf(dbg, "%d: couldn't fopen %s: %s\n",(int) now, s, strerror(errno)));
                return NULL;
              }
            
            dd -> user = malloc(BUFSIZ);
            
            fgets(dd -> user, BUFSIZ, f);
            if(dd->user[strlen(dd->user)-1]=='\n') dd->user[strlen(dd->user)-1]='\0';
            
            fclose(f);
            
            DEBUG(DBG_DYNAMIC, sprintf(dbg, "found %s for %s\n",dd->user, intoa(addr)));
            
            }
        }
      else
        {
          syslog(LOG_ERR,"couldn't stat %s: %m\n",cfg->dynamicip);
          return NULL;
        }
      }
    
    return dd -> user;
}

void packet_loop()
{
  struct sockaddr saddr;
  int sizeaddr;
  unsigned char buff[1600];
  unsigned char *buf;
  int hardheader;
  int length;
  static struct iphdr *tmp_iphdr;
  int type;
  int dynamicstyle;
  int do_user;
  __u32 dynamicaddr, otheraddr;
  char *user;
  struct promisc_device *p;
  struct headerdat *h;
  int found = 0;
  struct mon_host_struct *ptr;    

  dynamicstyle = (dev2line != NULL) ? 1 : ((cfg->dynamicip != NULL) ? 2 : 0);

  buf = &buff[20];

  while (running)
    {
      sizeaddr = 14;
      length = recvfrom (capture_sd, buf, 127, 0, &saddr, &sizeaddr);
      if (length == -1)
      {
        if(errno != EINTR)
          DEBUG(DBG_SYSCALL, sprintf(dbg, "recvfrom: %s\n", strerror(errno)));
        continue;
      }
      
      do_user = 0;
      
      p = cfg -> notdev;
      
      while(p!=NULL)
      {
        if (!strcmp(p -> name, saddr.sa_data)) {
          packets->notdev++;
          break;
        }
        p = p -> next;
      }
      if (p)
      continue;

      found = 0;
      if(cfg->iflist) { /* we specified at least one iflimit tag */
          /* if we don't monitor this interface, bail now - mk */
          for(p=cfg->iflist;p && !found;p=p->next) {
                if(!strcmp(p->name, saddr.sa_data))
                    found = 1;
          }
          if(!found) {
                packets->ignored++;
              continue;
          }
      }                                                                    

      h = cfg -> headers;
      
      hardheader = -1;
      while (h)
      {
        if(strncmp(saddr.sa_data,h->name,h->l) == 0)
          {
            hardheader = h -> offset;
            if (h -> type != 0)
            type = buf[h -> type] * 256 + buf[h -> type + 1];
            else
            type = ETH_P_IP;
            break;
          }
        h = h -> next;
      }

      if (hardheader == -1)
      {
        /* ASSUMES: interface - line just with ppp and slip etc. */
        if(dynamicstyle == 1) do_user = 1;
        
        hardheader = 0;
        type = 0;
        packets->unenc++;
#ifdef IGNORE_UNENC
        continue; /* ignore ppp/slip */
#endif
      }
      else
      {
        if(type != ETH_P_IP)
          {
            /* ETH_P_ARP, ETH_P_RARP, ETH_P_IPX, etc. */
            packets->ignored++;
            continue;
          }
      }

      tmp_iphdr = (void *) (buf+hardheader);

      found = 0;
      if(cfg->hostlist) { /* we specified at least one hostlimit tag */
          /* if we don't monitor this IP, bail now - mk */
          for(ptr=cfg->hostlist;ptr && !found;ptr=ptr->next) {
                if(ptr->ipaddr == tmp_iphdr->saddr 
                || ptr->ipaddr == tmp_iphdr->daddr)
                    found = 1;
          }
          if(!found) {
                packets->ignored++;
              continue;
          }
      }                                                                    
      if((tmp_iphdr->saddr & cfg->ignoremask) == (tmp_iphdr->daddr & cfg->ignoremask))
      {
        packets->local++;
        continue;
      }
      else
      {
        if(onnetlist(tmp_iphdr->saddr,cfg->ignorenet) || onnetlist(tmp_iphdr->daddr, cfg->ignorenet))
          {
            if(!(onnetlist(tmp_iphdr->saddr,cfg->dontignore) || onnetlist(tmp_iphdr->daddr, cfg->dontignore)))
            {
              if(debug_level & DBG_IGNORE)
                {
                  char tmp[18];
                  strcpy(tmp, intoa(tmp_iphdr->saddr));
                  DEBUG(DBG_IGNORE, sprintf(dbg, "netignored: %s -> %s\n",
                                    tmp,intoa(tmp_iphdr->daddr)));
                }
              packets->netignored++;
              continue;
            }
          }
        packets->ip++;
        user = NULL;
        
        switch(dynamicstyle)
          {
          case 1:
            if(do_user) user = check_user_dev2line(saddr.sa_data);
            break;
          case 2:
            dynamicaddr = otheraddr = 0;
            if(onnet(tmp_iphdr->saddr, &cfg->dynamicnet)) 
            {
              dynamicaddr = tmp_iphdr->saddr;
              if (onnet(tmp_iphdr->daddr, &cfg->dynamicnet)) otheraddr = tmp_iphdr->daddr;
              DEBUG(DBG_ANNOYING, sprintf(dbg, "source %s is on dynamicnet\n", intoa(dynamicaddr)));
              
            }
            else if (onnet(tmp_iphdr->daddr, &cfg->dynamicnet))
            {
              dynamicaddr = tmp_iphdr->daddr;
              if(onnet(tmp_iphdr->saddr, &cfg->dynamicnet)) otheraddr = tmp_iphdr->saddr;
              DEBUG(DBG_ANNOYING, sprintf(dbg, "destination %s is on dynamicnet\n", intoa(dynamicaddr)));
            }
            
            if(dynamicaddr != 0)
            {
              if(onnetlist(dynamicaddr, cfg->excludenamelookup))
                {
                  DEBUG(DBG_ANNOYING, sprintf(dbg, "BUT: %s is excluded from name lookup\n", intoa(dynamicaddr)));
                  
                  dynamicaddr = otheraddr;
                  otheraddr = 0;
                  if(onnetlist(dynamicaddr, cfg->excludenamelookup))
                  {
                    DEBUG(DBG_ANNOYING, sprintf(dbg, "prev. bug: %s is excluded from name lookup, too\n", intoa(dynamicaddr)));
                    dynamicaddr = 0;
                  }
                }
              if(dynamicaddr != 0)
                {
                  user = check_user_dynamicip(dynamicaddr); 
                  if((user == NULL) && (otheraddr != 0))
                  {
                    user = check_user_dynamicip(otheraddr); 
                  }
                }
            }
            break;
          }
        handle_ip(buf+hardheader, saddr.sa_data, user);
      }
    }
}

void handle_ip(unsigned char buf[], char *devname, char *user)
{
    static struct iphdr *tmp_iphdr;
    static struct tcphdr *tmp_tcphdr;
    static struct icmphdr *tmp_icmphdr;
    unsigned short srcport, dstport;
    tmp_iphdr = (void *) &buf[0];
    tmp_tcphdr = (void *) &buf[tmp_iphdr->ihl*4];
    /* relevant headers of udphdr and tcphdr are identical */
    tmp_icmphdr = (void *) &buf[tmp_iphdr->ihl*4];

    if(tmp_iphdr->protocol == IP_UDP)
      {
          packets->ip_udp++;
          
          srcport = ntohs(tmp_tcphdr->source);
          dstport = ntohs(tmp_tcphdr->dest);
      }
    else if(tmp_iphdr->protocol == IP_TCP)
      {
          packets->ip_tcp++;

          srcport = ntohs(tmp_tcphdr->source);
          dstport = ntohs(tmp_tcphdr->dest);
      }
    else if(tmp_iphdr->protocol == IP_ICMP)
      {
          packets->ip_icmp++;
          srcport = tmp_icmphdr->type; 
          dstport = tmp_icmphdr->code;
      }
    else
      {
          packets->ip_other++;
          srcport = dstport = 0;
      }
    
    register_packet(tmp_iphdr->saddr,tmp_iphdr->daddr,tmp_iphdr->protocol, srcport, dstport, ntohs(tmp_iphdr->tot_len), devname, user);
}

#ifdef TCP_USER_INFO

/* Originally by Vlad Seriakov (vlad@torn.ktts.kharkov.ua) */
/* Adapted by Ulrich Callmeier */

int get_tcp_info(struct ipdata *ip, uid_t *uid)
{
  FILE *procinfo;
  char buffer[1024];
  int num, local_port, rem_port, d, state, tmout;
  long localaddr, remaddr;
  unsigned long rxq, txq, time_len, retr, inode;
  int timer_run;
  
  if ((procinfo = fopen(PATH_PROCNET_TCP, "r")) == NULL)
    {
      syslog(LOG_ERR, "error opening %s: %s", PATH_PROCNET_TCP,strerror(errno));
      DEBUG(DBG_ERR, sprintf(dbg,"error opening %s: %s", PATH_PROCNET_TCP,strerror(errno)));
      return(-1);
    }
  
  fgets(buffer, sizeof(buffer), procinfo); /* skip header line */
  
  while (!feof(procinfo))
    {
      if(fgets(buffer, sizeof(buffer), procinfo)!=NULL)
      {
        num = sscanf(buffer,
                   "%d: %lX:%X %lX:%X %X %lX:%lX %X:%lX %lX %d %d %ld\n",
                   &d, &localaddr, &local_port,
                   &remaddr, &rem_port, &state,
                   &txq, &rxq, &timer_run, &time_len, &retr, (int*) uid, &tmout, &inode);
        if (num < 11) continue;           /* 13 ? */
        if(localaddr == ip->src && remaddr == ip->dst &&
           local_port == ip->srcport && rem_port == ip->dstport)
          {
            fclose(procinfo);
            return 1;
          }
      }
    }
  
  fclose(procinfo);
  
  return(0);
}

#endif

Generated by  Doxygen 1.6.0   Back to index