Snort mailing list archives

Re: RE: Network Behaviour Anomoly Detection


From: "Lawrence Reed" <Lawrence.Reed () noaa gov>
Date: Wed, 13 Oct 2004 12:03:07 +0000

Larry Reed wrote:
I have a chunk of code to do just that. It was written some time ago for BY 0.1.0. Spits out the stream stats in a csv format, similar to alert_csv output.
b
If anyone is interested I'll clean it up for BY 0.2.0 and post it.

Someone expressed interest so here it is:

This patch will allow BY 0.2.0 to read snort stream stat unified files. The output is written to a csv file as define in barynard.conf. The default filename is stats-csv.out and the default format is:

starttime, endtime, client-ip, client-port, server-ip, server-port, server-bytes, server-packets, client-bytes,client-packets


My barnyard.conf contains only two lines:

   processor dp_stream_stat
   output stream_stat_csv


The files are created with the following snort.conf entry:

   preprocessor stream4: disable_evasion_alerts, keepstats binary

I patched a few files and created two new files:

Patched:
/barnyard-0.2.0/src/input-plugins/dp_stream_stat.h
/barnyard-0.2.0/src/output-plugins/op_plugbase.c
/barnyard-0.2.0/src/output-plugins/Makefile.in

New:
/barnyard-0.2.0/src/output-plugins/op_stream_stat_csv.c
/barnyard-0.2.0/src/output-plugins/op_stream_stat_csv.h


I have been using this code for several weeks. It works with my configuration. I have not tested everything and consider this beta code.

The patches were created from BY 0.2.0 build 32.

Use at your own risk.


Larry


--- ../../../barnyard-0.2.0/src/output-plugins/op_stream_stat_csv.h     Thu Jan  1 00:00:00 1970
+++ op_stream_stat_csv.h        Fri Mar 26 04:15:17 2004
@@ -0,0 +1,9 @@
+
+
+#ifndef __OP_STREAM_STAT_CSV_H__
+#define __OP_STREAM_STAT_CSV_H__
+
+void OpStreamStatCSV_Init();
+
+#endif  /* __OP_STREAM_STAT_CSV_H__ */
+
--- ../../../barnyard-0.2.0/src/input-plugins/dp_stream_stat.h  Fri Feb 20 01:59:48 2004
+++ dp_stream_stat.h    Wed Sep 22 12:21:10 2004
@@ -18,6 +18,7 @@
 #include <sys/types.h>
 
 #define STREAM_STAT_MAGIC  0xDEAD5747   /* dead alert (ok, so it's stupid) */
+#define STREAM_STAT_MAGIC  0xdead029a   /* dead alert (ok, so it's stupid) */
 
 typedef struct _StreamStatFileHeader
 {
--- ../../../barnyard-0.2.0/src/output-plugins/Makefile.in      Sat May  1 16:52:16 2004
+++ Makefile.in Wed Jul 14 20:51:07 2004
@@ -87,7 +87,7 @@
 libop_a_LIBADD = 
 libop_a_OBJECTS =  op_decode.o op_fast.o op_plugbase.o op_logdump.o \
 op_alert_syslog.o op_log_pcap.o op_acid_db.o op_alert_csv.o op_sguil.o \
-op_alert_syslog2.o op_alert_console.o
+op_alert_syslog2.o op_alert_console.o op_stream_stat_csv.o
 AR = ar
 CFLAGS = @CFLAGS@
 COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
--- ../../../barnyard-0.2.0/src/output-plugins/op_plugbase.c    Mon Mar 29 00:14:19 2004
+++ op_plugbase.c       Wed Jul 14 20:47:27 2004
@@ -33,6 +33,7 @@
 #include "op_alert_csv.h"
 #include "op_alert_syslog2.h"
 #include "op_alert_console.h"
+#include "op_stream_stat_csv.h"
 
 /* ----------------------- Global Data --------------------------*/
 OutputPluginListNode *outputPlugins = NULL;
@@ -53,6 +54,7 @@
     OpAlertCSV_Init();
     OpAlertSyslog2_Init();
     OpAlertConsole_Init();
+    OpStreamStatCSV_Init();
     return;
 }
 
--- ../../../barnyard-0.2.0/src/output-plugins/op_stream_stat_csv.c     Thu Jan  1 00:00:00 1970
+++ op_stream_stat_csv.c        Wed Jul 14 21:42:18 2004
@@ -0,0 +1,388 @@
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include "strlcpyu.h"
+#include "ConfigFile.h"
+#include "plugbase.h"
+#include "op_plugbase.h"
+#include "mstring.h"
+#include "util.h"
+#include "sid.h"
+#include "classification.h"
+#include "input-plugins/dp_stream_stat.h"
+#include "barnyard.h"
+
+
+/* KEYWORD DEFINES */
+#define CSV_START_TIME       1 
+#define CSV_END_TIME         2 
+#define CSV_CLIENT_BYTES     3 
+#define CSV_SERVER_BYTES     4 
+#define CSV_CLIENT_IP        5
+#define CSV_SERVER_IP        6 
+#define CSV_CLIENT_PORT      7 
+#define CSV_SERVER_PORT      8 
+#define CSV_SERVER_PACKETS   9 
+#define CSV_CLIENT_PACKETS   10 
+
+/*  D A T A   S T R U C T U R E S  **************************************/
+typedef struct _AlertCSVOpData 
+{
+    char *filepath;
+    FILE *file;
+    int num_entries;
+    u_int32_t *entry_defs;
+} StreamStatCSVOpData;
+
+
+/*  P R O T O T Y P E S  ************************************************/
+static int OpStreamStatCSV_Setup(OutputPlugin *, char *args);
+static int OpStreamStatCSV_Exit(OutputPlugin *);
+static int OpStreamStatCSV_Start(OutputPlugin *, void *);
+static int OpStreamStatCSV_Stop(OutputPlugin *);
+static int OpStreamStatCSV(void *, void *);
+static int OpStreamStatCSV_LogConfig(OutputPlugin *);
+
+static StreamStatCSVOpData *StreamStatCSVOpParseArgs(char *);
+static void StreamStatCSVOpParseCustomFormat(StreamStatCSVOpData *data, char *format);
+static char *CSVEscape(char *);
+
+/* init routine makes this processor available for dataprocessor directives */
+void OpStreamStatCSV_Init()
+{
+    OutputPlugin *outputPlugin;
+
+    /* 
+     * register the plugin name, its setup function, its type and the plugin 
+     * information
+     */
+    outputPlugin = RegisterOutputPlugin("stream_stat_csv", "stream_stat");
+
+    outputPlugin->setupFunc = OpStreamStatCSV_Setup;
+    outputPlugin->exitFunc = OpStreamStatCSV_Exit;
+    outputPlugin->startFunc = OpStreamStatCSV_Start;
+    outputPlugin->stopFunc = OpStreamStatCSV_Stop;
+    outputPlugin->outputFunc = OpStreamStatCSV;
+    outputPlugin->logConfigFunc = OpStreamStatCSV_LogConfig;
+    
+    /* tell people you're installed */
+    LogMessage("StreamStatCSV initialized\n");
+}
+
+static int OpStreamStatCSV_LogConfig ( OutputPlugin *outputPlugin)
+{   
+
+    StreamStatCSVOpData *data = (StreamStatCSVOpData *)outputPlugin->data;
+
+    if (!outputPlugin || !outputPlugin->data)
+        return -1;
+
+    LogMessage("OpStreamStatCSV configured\n");
+    LogMessage("   Filepath: %s\n",data->filepath);
+    LogMessage("   Format: %s\n","");
+
+    return 0;
+}
+
+
+/* Setup the output plugin, process any arguments, link the functions to
+ * the output functional node
+ */
+static int OpStreamStatCSV_Setup(OutputPlugin *outputPlugin, char *args)
+{
+    /* setup the run time context for this output plugin */
+    outputPlugin->data = StreamStatCSVOpParseArgs(args);
+
+    return 0;
+}
+
+/* Inverse of the setup function, free memory allocated in Setup 
+ * can't free the outputPlugin since it is also the list node itself
+ */
+static int OpStreamStatCSV_Exit(OutputPlugin *outputPlugin)
+{
+    StreamStatCSVOpData *data = (StreamStatCSVOpData *)outputPlugin->data;
+    
+    if(data != NULL)
+    {
+        if(data->filepath != NULL)
+            free(data->filepath);
+        if(data->entry_defs != NULL)
+            free(data->entry_defs);
+    }
+    
+    return 0;
+}
+
+/* 
+ * this function gets called at start time, you should open any output files
+ * or establish DB connections, etc, here
+ */
+static int OpStreamStatCSV_Start(OutputPlugin *outputPlugin, void *spool_header)
+{
+    StreamStatCSVOpData *data = (StreamStatCSVOpData *)outputPlugin->data;
+
+    if(data == NULL)
+        FatalError("ERROR: Unable to find context for StreamStatCSV startup!\n");
+
+    /*if(pv.verbose >= 2) */
+       OpStreamStatCSV_LogConfig(outputPlugin);
+           
+    /* Open file */
+    if((data->file = fopen(data->filepath, "a")) == NULL)
+    {
+        FatalError("ERROR: Unable to open '%s' (%s)\n", data->filepath, 
+                strerror(errno));
+    }
+    
+    OpStreamStatCSV_LogConfig(outputPlugin); 
+    return 0;
+}
+
+static int OpStreamStatCSV_Stop(OutputPlugin *outputPlugin)
+{
+    StreamStatCSVOpData *data = (StreamStatCSVOpData *)outputPlugin->data;
+
+    if(data == NULL)
+        FatalError("ERROR: Unable to find context for StreamStatCSV startup!\n");
+    
+    /* close file */
+    fclose(data->file);
+
+    return 0;
+}
+
+/* 
+ * this is the primary output function for the plugin, this is what gets called
+ * for every record read 
+ */
+static int OpStreamStatCSV(void *context, void *data)
+{
+    int i = 0;
+    Sid *sid = NULL;
+    ClassType *class_type = NULL;
+    char timestamp[TIMEBUF_SIZE];
+    StreamStatRecord *record = (StreamStatRecord *)data;
+    StreamStatCSVOpData *op_data = (StreamStatCSVOpData *)context;
+    char timestamp1[TIMEBUF_SIZE];
+    char timestamp2[TIMEBUF_SIZE];
+    FILE *file = op_data->file;
+    char *escaped_string;
+
+    if(op_data->num_entries == 0)
+    {    
+        /* default output mode */
+        RenderTimestamp(record->start_time, timestamp1, TIMEBUF_SIZE);
+        RenderTimestamp(record->end_time, timestamp2, TIMEBUF_SIZE);
+        fprintf(op_data->file, "%s,%s,%u.%u.%u.%u,%u,%u.%u.%u.%u,%u,%u,%u,%u,%u\n", 
+                timestamp1,
+                timestamp2,
+                (record->client_ip & 0x000000ff),
+                (record->client_ip & 0x0000ff00) >> 8,
+                (record->client_ip & 0x00ff0000) >> 16,
+                (record->client_ip & 0xff000000) >> 24,
+               record->client_port, 
+               (record->server_ip & 0x000000ff),
+               (record->server_ip & 0x0000ff00) >> 8,
+               (record->server_ip & 0x00ff0000) >> 16,
+               (record->server_ip & 0xff000000) >> 24,
+               record->server_port,
+                record->server_bytes, record->server_packets, 
+               record->client_bytes, record->client_packets);
+    }
+    for(i = 0; i < op_data->num_entries; ++i)
+    {
+        switch(op_data->entry_defs[i])
+        {
+            case CSV_START_TIME:
+                RenderTimestamp(record->start_time, timestamp1, TIMEBUF_SIZE);
+                fprintf(file, "%s", timestamp1);
+                break;
+            case CSV_END_TIME:
+                RenderTimestamp(record->start_time, timestamp1, TIMEBUF_SIZE);
+                fprintf(file, "%s", timestamp1);
+                break;
+            case CSV_CLIENT_BYTES:
+                fprintf(file, "%u", record->client_bytes);
+                break;
+            case CSV_SERVER_BYTES:
+                fprintf(file, "%u", record->server_bytes);
+                break;
+            case CSV_SERVER_IP:
+                fprintf(file, "%u.%u.%u.%u", 
+                        record->server_ip & 0x000000ff,
+                        (record->server_ip & 0x0000ff00) >> 8,
+                        (record->server_ip & 0x00ff0000) >> 16,
+                        (record->server_ip & 0xff000000) >> 24);
+                break;
+            case CSV_CLIENT_IP:
+                fprintf(file, "%u.%u.%u.%u", 
+                        record->client_ip & 0x000000ff,
+                        (record->client_ip & 0x0000ff00) >> 8,
+                        (record->client_ip & 0x00ff0000) >> 16,
+                        (record->client_ip & 0xff000000) >> 24);
+                break;
+            case CSV_SERVER_PORT:
+                fprintf(file, "%u", record->server_port);
+                break;
+            case CSV_CLIENT_PORT:
+                fprintf(file, "%u", record->client_port);
+                break;
+            case CSV_CLIENT_PACKETS:
+                fprintf(file, "%u", record->client_packets);
+                break;
+            case CSV_SERVER_PACKETS:
+                fprintf(file, "%u", record->server_packets);
+                break;
+        }
+        if(i < op_data->num_entries - 1)
+            fprintf(file, ",");
+        else
+            fprintf(file, "\n");
+    }
+    fflush(file);        
+    return 0;
+}
+
+/* initialize the output processor for this particular instantiation */
+StreamStatCSVOpData *StreamStatCSVOpParseArgs(char *args)
+{
+    StreamStatCSVOpData *data;
+
+    data = (StreamStatCSVOpData *)SafeAlloc(sizeof(StreamStatCSVOpData));
+
+    if(args != NULL)
+    {
+        char **toks;
+        int num_toks;
+        /* parse out your args */
+        toks = mSplit(args, " ", 2, &num_toks, 0);
+        switch(num_toks)
+        {
+            case 2:
+                StreamStatCSVOpParseCustomFormat(data, toks[1]);
+            case 1:
+                data->filepath = strdup(toks[0]);
+                break;
+            case 0:
+                data->filepath = strdup("stats-csv.out");
+                break;
+            default:
+                FatalError("ERROR %s (%d) => Invalid arguments for StreamStatCSV "
+                        "plugin: %s\n", file_name, file_line, args);
+        }       
+        /* free your mSplit tokens */
+        FreeToks(toks, num_toks);
+    }
+    else    
+    {
+        data->filepath = strdup("stats-csv.out");
+    }
+
+    return data;
+}
+
+
+void StreamStatCSVOpParseCustomFormat(StreamStatCSVOpData *data, char *format)
+{
+    char **toks;
+    int num_toks;
+    int i;
+    toks = mSplit(format, ",", 128, &num_toks, 0);
+    data->num_entries = num_toks;
+    data->entry_defs = (u_int32_t *)calloc(num_toks, sizeof(u_int32_t));
+    for(i = 0; i < num_toks; ++i)
+    {
+        if(strcasecmp("start_time", toks[i]) == 0)
+        {
+            data->entry_defs[i] = CSV_START_TIME;
+        }
+        else if(strcasecmp("end_time", toks[i]) == 0)
+        {
+            data->entry_defs[i] = CSV_END_TIME;
+        }
+        else if(strcasecmp("client_ip", toks[i]) == 0)
+        {
+            data->entry_defs[i] = CSV_CLIENT_IP;
+        }
+        else if(strcasecmp("client_port", toks[i]) == 0)
+        {
+            data->entry_defs[i] = CSV_CLIENT_PORT;
+        }
+        else if(strcasecmp("server_ip", toks[i]) == 0)
+        {
+            data->entry_defs[i] = CSV_SERVER_IP;
+        }
+        else if(strcasecmp("server_port", toks[i]) == 0)
+        {
+            data->entry_defs[i] = CSV_SERVER_PORT;
+        }
+        else if(strcasecmp("client_bytes", toks[i]) == 0)
+        {
+            data->entry_defs[i] = CSV_CLIENT_BYTES;
+        }
+        else if(strcasecmp("client_packets", toks[i]) == 0)
+        {
+            data->entry_defs[i] = CSV_CLIENT_PACKETS;
+       }
+        else if(strcasecmp("server_packets", toks[i]) == 0)
+        {
+            data->entry_defs[i] = CSV_SERVER_PACKETS;
+       }
+        else if(strcasecmp("server_bytes", toks[i]) == 0)
+        {
+            data->entry_defs[i] = CSV_SERVER_BYTES;
+        }
+        else
+        {
+            fprintf(stderr, "WARNING %s (%u) => Unrecognized keyword in "
+                    "StreamStatCSV: %s\n", file_name, file_line, toks[i]);
+        }
+    }
+    FreeToks(toks, num_toks);
+}
+
+char *CSVEscape(char *input)
+{
+    size_t strLen;
+    char *buffer;
+    char *current;
+    if((strchr(input, ',') == NULL) && (strchr(input, '"') == NULL))
+        return strdup(input);
+    /* max size of escaped string is 2*size + 3, so we allocate that much */
+    strLen = strlen(input);
+    buffer = (char *)SafeAlloc((strLen * 2) + 3);
+    current = buffer;
+    *current = '"';
+    ++current;
+    while(*input != '\0')
+    {
+        switch(*input)
+        {
+            case '"':
+                *current = '\\';
+                ++current;
+                *current = '"';
+                ++current;
+                break;
+            case '\\':
+                *current = '\\';
+                ++current;
+                *current = '\\';
+                ++current;
+                break;
+            default:
+                *current = *input;
+                ++current;
+                break;
+        }
+        ++input;
+    }
+    *current = '"';
+    return buffer;
+}

Current thread: