Main Page | Modules | Class List | Directories | File List | Class Members | File Members | Related Pages

server_stats.c

Go to the documentation of this file.
00001 /**
00002  * @file   server_stats.c
00003  * @author Chris Green <cmg@sourcefire.com>
00004  * @date   Fri Jun 13 14:28:50 2003
00005  * 
00006  * @brief  "policy" learning portion of portscan detector
00007  * 
00008  * This keeps a table of (dip+dport+dprotocol) -> count to help
00009  * identify what is a normal looking portscan versus what is pretty
00010  * far outta whack.
00011  *
00012  */
00013 
00014 #include "server_stats.h"
00015 #include "flowps.h"
00016 #include "sfxhash.h"
00017 
00018 #include <sys/types.h>
00019 #include <sys/stat.h>
00020 #include <fcntl.h>
00021 #include <unistd.h> 
00022 #include <string.h>
00023 
00024 static void server_stats_init_entry(void);
00025 
00026 typedef struct _SERVER_KEY
00027 {
00028     u_int32_t address;
00029     u_int32_t port;
00030     u_int32_t protocol;  
00031 } SERVER_KEY;
00032 
00033 static SERVER_KEY s_key; 
00034 static int s_debug = 0;
00035 
00036 /** 
00037  * Print out the entirety of the server cache.
00038  * 
00039  * @param ssp server stats pointer
00040  */
00041 void server_stats_dump(SERVER_STATS *ssp)
00042 {
00043     SFXHASH_NODE *nodep;
00044     
00045     if(ssp && ssp->ipv4_table)
00046     {
00047         for( nodep = sfxhash_ghead(ssp->ipv4_table);
00048              nodep != NULL;
00049              nodep = sfxhash_gnext(nodep) )
00050         {
00051             SERVER_KEY *kp = (SERVER_KEY *) nodep->key;
00052             u_int32_t count = *(u_int32_t *) nodep->data;
00053             
00054             flow_printf("hits: %u proto: %3u port: %5u ip: %s\n",
00055                         count,
00056                         kp->protocol,
00057                         kp->port,
00058                         inet_ntoa(*(struct in_addr *)&kp->address));
00059         }
00060     }
00061     else
00062     {
00063         flow_printf("nothing to dump!\n");
00064     }
00065 }
00066 
00067 void server_stats(SERVER_STATS *ssp, int dumpall)
00068 {
00069     unsigned total, fail, success, nodes, anr, overhead, memcap;
00070 
00071     memcap = overhead = nodes = anr = total = fail = success = 0;
00072     
00073     if(ssp && ssp->ipv4_table)
00074     {
00075         total    = sfxhash_find_total(ssp->ipv4_table);
00076         fail     = sfxhash_find_fail(ssp->ipv4_table);
00077         success  = sfxhash_find_success(ssp->ipv4_table);
00078         nodes    = sfxhash_count(ssp->ipv4_table);
00079         anr      = sfxhash_anr_count(ssp->ipv4_table);
00080         memcap   = server_stats_memcap(ssp);
00081         overhead = server_stats_overhead_bytes(ssp);
00082     }    
00083 
00084     flow_printf(",-----[SERVER STATS]------------\n");
00085     flow_printf("   Memcap: %u  Overhead Bytes: %u\n",
00086                 memcap, overhead);
00087     
00088     flow_printf("   Finds: %u (Sucessful: %u(%%%lf) Unsucessful: %u(%%%lf))\n",
00089                 total,
00090                 success, calc_percent(success,total),
00091                 fail, calc_percent(fail,total));
00092 
00093     flow_printf("   Nodes: %u\n", nodes);
00094     
00095     flow_printf("   Recovered Nodes: %u\n", anr);
00096     flow_printf("`-------------------------------\n");
00097 
00098     if(dumpall)
00099         server_stats_dump(ssp);
00100 }
00101 
00102 /** 
00103  * Initialize the server stats structure
00104  *
00105  * If we do not specify a watchnet, then we have no use for this
00106  * structure
00107  * 
00108  * @param ssp server stats structure to initialize
00109  * @param watchnet what network we're watching for information
00110  * @param rows how many rows the underlying table should use
00111  * @param memcap what our total memory limit is
00112  * 
00113  * @return FLOW_SUCCESS on success
00114  */
00115 int server_stats_init(SERVER_STATS *ssp, IPSET *watchnetv4,
00116                       unsigned int rows, int memcap)
00117 {
00118     if(!ssp || !watchnetv4)
00119         return FLOW_ENULL;
00120 
00121     server_stats_init_entry();
00122     
00123     memset(ssp, 0, sizeof(SERVER_STATS));
00124 
00125     
00126     if(ipset_family(watchnetv4) != IPV4_FAMILY)
00127     {
00128         return FLOW_EINVALID;
00129     }
00130 
00131     /* what size should we do? */
00132     ssp->ipv4_table = sfxhash_new(rows,               /* # of rows in HT*/
00133                                   sizeof(SERVER_KEY), /* size of the key  */
00134                                   sizeof(u_int32_t),   /* data size */
00135                                   memcap,            /* how much memory is alloted */
00136                                   1,                 /* auto recover nodes */
00137                                   NULL,              /* autorecovery function */
00138                                   NULL,              /* free function for the data */
00139                                   1);                /* recycle old nodes */
00140 
00141     if(ssp->ipv4_table == NULL)
00142     {
00143         return FLOW_ENOMEM;
00144     }
00145 
00146     ssp->ipv4_watch = ipset_copy(watchnetv4);
00147     
00148     if(!ssp->ipv4_watch)
00149     {
00150         sfxhash_delete(ssp->ipv4_table);        
00151         return FLOW_ENOMEM;
00152     }
00153 
00154     return FLOW_SUCCESS;
00155 }
00156 
00157 int server_stats_destroy(SERVER_STATS *ssp)
00158 {
00159     if(!ssp)
00160     {
00161         return FLOW_ENULL;
00162     }
00163     
00164     sfxhash_delete(ssp->ipv4_table);
00165     ipset_free(ssp->ipv4_watch);
00166 
00167     return FLOW_SUCCESS;
00168 }
00169 
00170 /** 
00171  * See if we are watching this particular IP 
00172  * 
00173  * @param ssp server stats pointer
00174  * @param address ipv4 address in NETWORK BYTE ORDER
00175  * 
00176  * @return 1 if this SERVER_STATS is watching this network
00177  */
00178 int server_stats_contains(SERVER_STATS *ssp, u_int32_t address)
00179 {
00180     if(ssp->ipv4_watch)
00181     {
00182         u_int32_t hostaddress = ntohl(address);
00183 
00184         if(ipset_contains(ssp->ipv4_watch, &hostaddress, IPV4_FAMILY))
00185         {
00186             return FLOW_SUCCESS;
00187         }
00188     }
00189     
00190     return FLOW_DISABLED;        
00191 }
00192 
00193 
00194 u_int32_t server_stats_hitcount_ipv4(SERVER_STATS *ssp, u_int8_t ip_proto, u_int32_t address, u_int16_t port)
00195 {
00196     SERVER_KEY *kp = &s_key;
00197     u_int32_t *hitcountp;
00198 #ifdef DEBUG
00199     u_int32_t hostaddress = ntohl(address);
00200 #endif /* DEBUG */
00201 
00202     /* OK, IPSETs are acting in HOST ORDER */
00203     FLOWASSERT(ipset_contains(ssp->ipv4_watch, &hostaddress, IPV4_FAMILY));
00204 
00205     /* make a key */
00206     kp->address = address;
00207     kp->port = port;
00208     kp->protocol = ip_proto;
00209     
00210     hitcountp = (u_int32_t *) sfxhash_find(ssp->ipv4_table, kp);
00211     
00212     if(hitcountp != NULL)
00213     {
00214         return *hitcountp;
00215     }
00216 
00217     return 0;    
00218 }
00219 
00220 int server_stats_add_ipv4(SERVER_STATS *ssp, u_int8_t ip_proto, u_int32_t address, u_int16_t port,
00221                           u_int32_t *retcount)
00222 {
00223     SERVER_KEY *kp = &s_key;
00224     u_int32_t one = 1;
00225     u_int32_t *hitcountp = NULL;
00226     int ret;
00227 #ifdef DEBUG
00228     u_int32_t hostaddress = ntohl(address);
00229 #endif /* DEBUG */
00230     
00231     if(ssp == NULL || retcount == NULL)
00232         return FLOW_ENULL;
00233 
00234     /* calls to this subsystem should only be made if we are really watching this. */
00235     FLOWASSERT(ipset_contains(ssp->ipv4_watch, &hostaddress, IPV4_FAMILY));
00236     
00237     /* make the key */
00238     kp->address  = address;
00239     kp->port     = port;
00240     kp->protocol = ip_proto;
00241     
00242     /* find the key, add 1 to it or add a new node to the table */
00243     ret = sfxhash_add(ssp->ipv4_table, kp, &one);
00244     
00245     switch(ret)
00246     {
00247     case SFXHASH_NOMEM:
00248         /* NOMEM means that we would add it if we could but we're
00249          *  hard-core out of space.  So, just assume we added it.
00250          */
00251     case SFXHASH_OK:
00252         *retcount = 1;        
00253         break;
00254     case SFXHASH_INTABLE:
00255         hitcountp = (u_int32_t *) sfxhash_mru(ssp->ipv4_table);
00256         
00257         /* never let us wrap around to less hits */
00258         if(!hitcountp)
00259         {
00260             /* this is an odd error! */
00261             return FLOW_BADJUJU;
00262         }
00263         else
00264         {
00265             if((*hitcountp) < SERVER_STATS_MAX_HITCOUNT)
00266             {
00267                 (*hitcountp)++;
00268             }            
00269         }
00270         break;
00271     }
00272     
00273     return FLOW_SUCCESS;
00274 }
00275 
00276 int server_stats_remove_ipv4(SERVER_STATS *ssp, u_int8_t ip_proto,
00277                              u_int32_t address, u_int16_t port)
00278 {
00279     SERVER_KEY *kp = &s_key;
00280 
00281     if(!ssp)
00282         return FLOW_ENULL;
00283        
00284     kp->address = address;
00285     kp->port = port;
00286     kp->protocol = ip_proto;
00287 
00288     /* not like we can do anything if this failed */
00289     sfxhash_remove(ssp->ipv4_table, kp);
00290 
00291     return FLOW_SUCCESS;
00292 }
00293 
00294 
00295 /* start of parsing helpers */
00296 #define FAMILY_SIZE     1
00297 #define FAMILY_OFFSET   0
00298 
00299 #define IPV4_SIZE       4
00300 #define IPV4_OFFSET     (FAMILY_SIZE)
00301 
00302 #define PORT_SIZE       2
00303 #define PORT_OFFSET     (IPV4_OFFSET + IPV4_SIZE)
00304 
00305 #define IP_PROTO_SIZE   1
00306 #define IP_PROTO_OFFSET (PORT_OFFSET + PORT_SIZE)
00307 
00308 #define COUNT_SIZE      4
00309 #define COUNT_OFFSET    (IP_PROTO_OFFSET + IP_PROTO_SIZE)
00310 
00311 #define STATSREC_SIZE (FAMILY_SIZE + IPV4_SIZE + PORT_SIZE + IP_PROTO_SIZE + COUNT_SIZE)
00312 /* end parsing helpers */
00313 
00314 int server_stats_save(SERVER_STATS *ssp, char *filename)
00315 {
00316     SFXHASH_NODE *nodep;
00317     unsigned char buf[STATSREC_SIZE];
00318     int fd;
00319     
00320     if(!filename || !ssp)
00321         return FLOW_ENULL;
00322 #ifndef O_SYNC
00323 #define O_SYNC O_FSYNC
00324 #endif
00325 
00326     /* open this description, create it if necessary, always wait on
00327      * sync to disk w/ every write, only write */
00328     fd = open(filename, O_CREAT|O_TRUNC|O_SYNC|O_WRONLY);
00329 
00330     if(fd < 0)
00331     {
00332         if(s_debug)
00333         {
00334             flow_printf("%s was not found\n", filename);
00335         }
00336         return FLOW_NOTFOUND;
00337     }
00338 
00339     /* this is a crappy parser... that's par for the course */
00340     for( nodep = sfxhash_ghead(ssp->ipv4_table);
00341          nodep != NULL;
00342          nodep = sfxhash_gnext(nodep) )
00343     {
00344         SERVER_KEY *kp = (SERVER_KEY *) nodep->key;
00345         u_int32_t count = *(u_int32_t *) nodep->data;
00346         u_int8_t  family = '4';
00347         u_int32_t ipv4_address;
00348         u_int16_t port;
00349         u_int8_t  protocol;
00350         ssize_t  wbytes = 0;
00351         ssize_t wsize;
00352             
00353         
00354         count        = ntohl(count);       
00355         ipv4_address = htonl(kp->address);
00356         port         = htons(kp->port);
00357         protocol     = kp->protocol;
00358 
00359         memcpy(buf + FAMILY_OFFSET,   &family,        FAMILY_SIZE);
00360         memcpy(buf + IPV4_OFFSET,     &ipv4_address,  IPV4_SIZE);       
00361         memcpy(buf + PORT_OFFSET,     &port,          PORT_SIZE);
00362         memcpy(buf + IP_PROTO_OFFSET, &protocol,      IP_PROTO_SIZE);
00363         memcpy(buf + COUNT_OFFSET,    &count,         COUNT_SIZE);
00364 
00365         /* now make sure we get a full record on disk */
00366         while(wbytes < STATSREC_SIZE)
00367         {
00368             /* write the number of bytes we already have - the #
00369              * already written */
00370             wsize = write(fd, buf, (STATSREC_SIZE - wbytes));
00371 
00372             if(wsize < 0)
00373             {
00374                 /* this record was truncated */
00375                 flow_printf("Truncated Server Record!\n");
00376                 return FLOW_EINVALID;
00377             }
00378             else
00379             {
00380                 wbytes += wsize;
00381             }
00382         }
00383     }
00384     
00385     return FLOW_SUCCESS;
00386 }
00387 
00388 
00389 
00390 /** 
00391  * load a server stats file
00392  *
00393  * fmt:
00394  *
00395  * 1 char for the family
00396  * hex network representation of the IP  (8 chars)
00397  * hex network representation of the port (2 chars)
00398  * 1 char for the ip_proto                (1 char)
00399  * hex network representation of the hit count (4 chars)
00400  *
00401  * yes this record format is hard to use but it's easy to parse! :>
00402  * 
00403  * @param ssp server stats pointer
00404  * @param filename filename to load
00405  * 
00406  * @return FLOW_SUCCESS on sucess
00407  */
00408 int server_stats_load(SERVER_STATS *ssp, char *filename)
00409 {
00410     SERVER_KEY *kp = &s_key;
00411     size_t rsize;
00412     unsigned char buf[STATSREC_SIZE];
00413     int fd;
00414     
00415     if(!filename || !ssp)
00416         return FLOW_ENULL;
00417 
00418 
00419     fd = open(filename, O_RDONLY);
00420 
00421     if(fd < 0)
00422     {
00423         return FLOW_NOTFOUND;
00424     }
00425 
00426     /* this is a crappy parser... that's par for the course */
00427     while((rsize = read(fd, &buf, STATSREC_SIZE) == STATSREC_SIZE) > 0)
00428     {
00429         u_int8_t  family;
00430         u_int32_t ipv4_address;
00431         u_int16_t port;
00432         u_int8_t  protocol;
00433         u_int32_t count;
00434         
00435         if(rsize != STATSREC_SIZE)
00436         {
00437             /* this record was truncated */
00438             close(fd);
00439             return FLOW_EINVALID;
00440         }
00441 
00442         memcpy(&family,       buf + FAMILY_OFFSET,   FAMILY_SIZE);
00443         memcpy(&ipv4_address, buf + IPV4_OFFSET,     IPV4_SIZE);       
00444         memcpy(&port,         buf + PORT_OFFSET,     PORT_SIZE);
00445         memcpy(&protocol,     buf + IP_PROTO_OFFSET, IP_PROTO_SIZE);
00446         memcpy(&count,        buf + COUNT_OFFSET,    COUNT_SIZE);
00447 
00448         /* make sure that we're reading a format we understand */
00449         
00450         if(family != '4')
00451         {
00452             close(fd);
00453             return FLOW_EINVALID;
00454         }
00455 
00456         kp->protocol = ntohs(protocol);
00457         kp->address  = ntohl(ipv4_address);
00458         kp->port     = ntohs(port);
00459         count        = ntohl(count);
00460         
00461         if(sfxhash_add(ssp->ipv4_table, kp, &count) != 0)
00462         {
00463             close(fd);
00464             return FLOW_BADJUJU;
00465         }
00466     }
00467 
00468     close(fd);
00469     return FLOW_SUCCESS;
00470 }
00471 
00472 
00473 /** 
00474  * initialize the static s_init_key variable once and only once.This
00475  * is used to zero out the key so that if the compiler pads the
00476  * structure, we still have 0's in this keylookup.
00477  * 
00478  */
00479 static void server_stats_init_entry(void)
00480 {
00481     static int init_once = 1;
00482 
00483     if(init_once)
00484     {
00485         init_once = 0;
00486         memset(&s_key, 0, sizeof(SERVER_KEY));
00487     }
00488 }
00489 
00490 
00491 /** 
00492  * get the memcap
00493  * 
00494  * @param sbp server_stats ptr to return the memcap of
00495  * 
00496  * @return memcap or -1
00497  */
00498 int server_stats_memcap(SERVER_STATS *sbp)
00499 {
00500     if(sbp != NULL && sbp->ipv4_table != NULL)        
00501         return sbp->ipv4_table->mc.memcap;
00502 
00503     return -1;            
00504 }
00505 
00506 /** 
00507  * get the node count
00508  * 
00509  * @param sbp server_stats ptr to return the memcap of
00510  * 
00511  * @return nrows or -1
00512  */
00513 int server_stats_row_count(SERVER_STATS *sbp)
00514 {
00515     if(sbp != NULL && sbp->ipv4_table != NULL)        
00516         return sbp->ipv4_table->nrows;
00517 
00518     return -1;            
00519 }
00520 
00521 
00522 /** 
00523  * get the overhead # of bytes
00524  * 
00525  * @param sbp server_stats ptr to return the memcap of
00526  * 
00527  * @return nrows or -1
00528  */
00529 int server_stats_overhead_bytes(SERVER_STATS *sbp)
00530 {
00531     if(sbp != NULL && sbp->ipv4_table != NULL)
00532         return sfxhash_overhead_bytes(sbp->ipv4_table);
00533 
00534     return -1;            
00535 }
00536 
00537 

Generated on Sun May 14 14:51:15 2006 by  doxygen 1.4.2