mirror of
https://github.com/BlackLight/Snort_AIPreproc.git
synced 2024-11-13 04:07:15 +01:00
346 lines
9.1 KiB
C
346 lines
9.1 KiB
C
|
/*
|
||
|
* =====================================================================================
|
||
|
*
|
||
|
* Filename: cluster.c
|
||
|
*
|
||
|
* Description: Module for managing alarm clustering and cluter hierarchies
|
||
|
*
|
||
|
* Version: 1.0
|
||
|
* Created: 12/08/2010 12:43:28
|
||
|
* Revision: none
|
||
|
* Compiler: gcc
|
||
|
*
|
||
|
* Author: BlackLight (http://0x00.ath.cx), <blacklight@autistici.org>
|
||
|
* Licence: GNU GPL v.3
|
||
|
* Company: DO WHAT YOU WANT CAUSE A PIRATE IS FREE, YOU ARE A PIRATE!
|
||
|
*
|
||
|
* =====================================================================================
|
||
|
*/
|
||
|
|
||
|
#include "spp_ai.h"
|
||
|
#include <stdio.h>
|
||
|
#include <unistd.h>
|
||
|
#include <pthread.h>
|
||
|
|
||
|
PRIVATE hierarchy_node *src_port_root = NULL;
|
||
|
PRIVATE hierarchy_node *src_addr_root = NULL;
|
||
|
PRIVATE hierarchy_node *dst_port_root = NULL;
|
||
|
PRIVATE hierarchy_node *dst_addr_root = NULL;
|
||
|
PRIVATE AI_config *_config = NULL;
|
||
|
PRIVATE AI_snort_alert *alert_log = NULL;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* FUNCTION: _hierarchy_node_new
|
||
|
* \brief Create a new clustering hierarchy node
|
||
|
* \param label Label for the node
|
||
|
* \param min_val Minimum value for the range represented by the node
|
||
|
* \param max_val Maximum value for the range represented by the node
|
||
|
* \return The brand new node if the allocation was ok, otherwise abort the application
|
||
|
*/
|
||
|
|
||
|
PRIVATE hierarchy_node*
|
||
|
_hierarchy_node_new ( char *label, int min_val, int max_val )
|
||
|
{
|
||
|
hierarchy_node *n = NULL;
|
||
|
|
||
|
if ( !( n = ( hierarchy_node* ) malloc ( sizeof ( hierarchy_node )) ))
|
||
|
{
|
||
|
_dpd.fatalMsg ( "Dynamic memory allocation failure at %s:%d\n", __FILE__, __LINE__ );
|
||
|
}
|
||
|
|
||
|
n->min_val = min_val;
|
||
|
n->max_val = max_val;
|
||
|
n->nchildren = 0;
|
||
|
n->children = NULL;
|
||
|
n->parent = NULL;
|
||
|
strncpy ( n->label, label, sizeof ( n->label ));
|
||
|
|
||
|
return n;
|
||
|
} /* ----- end of function _hierarchy_node_new ----- */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* FUNCTION: _hierarchy_node_append
|
||
|
* \brief Append a node to a clustering hierarchy node
|
||
|
* \param parent Parent node
|
||
|
* \param child Child node
|
||
|
*/
|
||
|
|
||
|
PRIVATE void
|
||
|
_hierarchy_node_append ( hierarchy_node *parent, hierarchy_node *child )
|
||
|
{
|
||
|
if ( !( parent->children = ( hierarchy_node** ) realloc ( parent->children, (++(parent->nchildren)) * sizeof ( hierarchy_node* )) ))
|
||
|
{
|
||
|
_dpd.fatalMsg ( "Dynamic memory allocation failure at %s:%d\n", __FILE__, __LINE__ );
|
||
|
}
|
||
|
|
||
|
parent->children[ parent->nchildren - 1 ] = child;
|
||
|
child->parent = parent;
|
||
|
} /* ----- end of function _hierarchy_node_append ----- */
|
||
|
|
||
|
|
||
|
/* PRIVATE void */
|
||
|
/* _hierarchy_node_free ( hierarchy_node *n ) */
|
||
|
/* { */
|
||
|
/* int i; */
|
||
|
/* */
|
||
|
/* if ( !n ) */
|
||
|
/* return; */
|
||
|
/* */
|
||
|
/* for ( i=0; i < n->nchildren; i++ ) */
|
||
|
/* { */
|
||
|
/* if ( n->children[i] ) */
|
||
|
/* _hierarchy_node_free ( n->children[i] ); */
|
||
|
/* } */
|
||
|
/* */
|
||
|
/* free ( n ); */
|
||
|
/* n = NULL; */
|
||
|
/* } */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* FUNCTION: _AI_get_min_hierarchy_node
|
||
|
* \brief Get the minimum node in a hierarchy tree that matches a certain value
|
||
|
* \param val Value to be matched in the range
|
||
|
* \param root Root of the hierarchy
|
||
|
* \return The minimum node that matches the value if any, NULL otherwise
|
||
|
*/
|
||
|
PRIVATE hierarchy_node*
|
||
|
_AI_get_min_hierarchy_node ( int val, hierarchy_node *root )
|
||
|
{
|
||
|
int i;
|
||
|
hierarchy_node *next = NULL;
|
||
|
|
||
|
if ( !root )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if ( (unsigned) val < (unsigned) root->min_val || (unsigned) val > (unsigned) root->max_val )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
for ( i=0; i < root->nchildren && !next; i++ )
|
||
|
{
|
||
|
if ( root->children[i]->min_val <= val && root->children[i]->max_val >= val )
|
||
|
{
|
||
|
next = root->children[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !next )
|
||
|
return root;
|
||
|
|
||
|
return _AI_get_min_hierarchy_node ( val, next );
|
||
|
} /* ----- end of function _AI_get_min_hierarchy_node ----- */
|
||
|
|
||
|
/**
|
||
|
* FUNCTION: _AI_cluster_thread
|
||
|
* \brief Thread for periodically clustering the log information
|
||
|
*/
|
||
|
PRIVATE void*
|
||
|
_AI_cluster_thread ( void* arg )
|
||
|
{
|
||
|
AI_snort_alert *tmp;
|
||
|
hierarchy_node *node, *child;
|
||
|
char label[256];
|
||
|
|
||
|
while ( 1 )
|
||
|
{
|
||
|
sleep ( _config->alertClusteringInterval );
|
||
|
|
||
|
if ( !( alert_log = AI_get_alerts() ))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
FILE *fp = fopen ( "/home/blacklight/LOG", "a" );
|
||
|
|
||
|
for ( tmp = alert_log; tmp; tmp = tmp->next )
|
||
|
{
|
||
|
if ( src_addr_root && !tmp->src_addr_node )
|
||
|
{
|
||
|
node = _AI_get_min_hierarchy_node ( ntohl ( tmp->src_addr ), src_addr_root );
|
||
|
|
||
|
if ( node )
|
||
|
{
|
||
|
if ( node->min_val < node->max_val )
|
||
|
{
|
||
|
inet_ntop ( AF_INET, &(tmp->src_addr), label, INET_ADDRSTRLEN );
|
||
|
child = _hierarchy_node_new ( label, ntohl ( tmp->src_addr ), ntohl ( tmp->src_addr ));
|
||
|
_hierarchy_node_append ( node, child );
|
||
|
node = child;
|
||
|
}
|
||
|
|
||
|
tmp->src_addr_node = node;
|
||
|
fprintf ( fp, "minimum range holding %s: %s (prev: %s)\n", label, tmp->src_addr_node->label, tmp->src_addr_node->parent->label );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( dst_addr_root && !tmp->dst_addr_node )
|
||
|
{
|
||
|
node = _AI_get_min_hierarchy_node ( ntohl ( tmp->dst_addr ), dst_addr_root );
|
||
|
|
||
|
if ( node )
|
||
|
{
|
||
|
if ( node->min_val < node->max_val )
|
||
|
{
|
||
|
/* snprintf ( label, sizeof(label), "%d", ntohl ( tmp->dst_addr )); */
|
||
|
inet_ntop ( AF_INET, &(tmp->src_addr), label, INET_ADDRSTRLEN );
|
||
|
child = _hierarchy_node_new ( label, ntohl ( tmp->dst_addr ), ntohl ( tmp->dst_addr ));
|
||
|
_hierarchy_node_append ( node, child );
|
||
|
node = child;
|
||
|
}
|
||
|
|
||
|
tmp->dst_addr_node = node;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( src_port_root && !tmp->src_port_node )
|
||
|
{
|
||
|
node = _AI_get_min_hierarchy_node ( ntohs ( tmp->src_port ), src_port_root );
|
||
|
|
||
|
if ( node )
|
||
|
{
|
||
|
if ( node->min_val < node->max_val )
|
||
|
{
|
||
|
snprintf ( label, sizeof(label), "%d", ntohs ( tmp->src_port ));
|
||
|
child = _hierarchy_node_new ( label, ntohs ( tmp->src_port ), ntohs ( tmp->src_port ));
|
||
|
_hierarchy_node_append ( node, child );
|
||
|
node = child;
|
||
|
}
|
||
|
|
||
|
tmp->src_port_node = node;
|
||
|
fprintf ( fp, "minimum range holding %d: %s (prev: %s)\n", ntohs(tmp->src_port), tmp->src_port_node->label, tmp->src_port_node->parent->label );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( dst_port_root && !tmp->dst_port_node )
|
||
|
{
|
||
|
node = _AI_get_min_hierarchy_node ( ntohs ( tmp->dst_port ), dst_port_root );
|
||
|
|
||
|
if ( node )
|
||
|
{
|
||
|
if ( node->min_val < node->max_val )
|
||
|
{
|
||
|
snprintf ( label, sizeof(label), "%d", ntohs ( tmp->dst_port ));
|
||
|
child = _hierarchy_node_new ( label, ntohs ( tmp->dst_port ), ntohs ( tmp->dst_port ));
|
||
|
_hierarchy_node_append ( node, child );
|
||
|
node = child;
|
||
|
}
|
||
|
|
||
|
tmp->dst_port_node = node;
|
||
|
fprintf ( fp, "minimum range holding %d: %s (prev: %s)\n", ntohs(tmp->dst_port), tmp->dst_port_node->label, tmp->dst_port_node->parent->label );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fclose ( fp );
|
||
|
}
|
||
|
|
||
|
return (void*) 0;
|
||
|
} /* ----- end of function AI_cluster_thread ----- */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* FUNCTION: AI_hierarchies_build
|
||
|
* \brief Build the clustering hierarchy trees
|
||
|
* \param conf Reference to the configuration of the module
|
||
|
* \param nodes Nodes containing the information about the clustering ranges
|
||
|
* \param n_nodes Number of nodes
|
||
|
*/
|
||
|
|
||
|
void
|
||
|
AI_hierarchies_build ( AI_config *conf, hierarchy_node **nodes, int n_nodes )
|
||
|
{
|
||
|
int i, j;
|
||
|
int min_range = 0;
|
||
|
pthread_t cluster_thread;
|
||
|
hierarchy_node *root = NULL;
|
||
|
hierarchy_node *cover = NULL;
|
||
|
_config = conf;
|
||
|
|
||
|
for ( i=0; i < n_nodes; i++ )
|
||
|
{
|
||
|
switch ( nodes[i]->type )
|
||
|
{
|
||
|
case src_port:
|
||
|
if ( !src_port_root )
|
||
|
src_port_root = _hierarchy_node_new ( "1-65535", 1, 65535 );
|
||
|
|
||
|
root = src_port_root;
|
||
|
min_range = 65534;
|
||
|
break;
|
||
|
|
||
|
case dst_port:
|
||
|
if ( !dst_port_root )
|
||
|
dst_port_root = _hierarchy_node_new ( "1-65535", 1, 65535 );
|
||
|
|
||
|
root = dst_port_root;
|
||
|
min_range = 65534;
|
||
|
break;
|
||
|
|
||
|
case src_addr:
|
||
|
if ( !src_addr_root )
|
||
|
src_addr_root = _hierarchy_node_new ( "0.0.0.0/0",
|
||
|
0x0, 0xffffffff );
|
||
|
|
||
|
root = src_addr_root;
|
||
|
min_range = 0xffffffff;
|
||
|
break;
|
||
|
|
||
|
case dst_addr:
|
||
|
if ( !dst_addr_root )
|
||
|
dst_addr_root = _hierarchy_node_new ( "0.0.0.0/0",
|
||
|
0x0, 0xffffffff );
|
||
|
|
||
|
root = dst_addr_root;
|
||
|
min_range = 0xffffffff;
|
||
|
break;
|
||
|
|
||
|
/* TODO Manage range for timestamps (and something more?) */
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
cover = NULL;
|
||
|
|
||
|
for ( j=0; j < n_nodes; j++ )
|
||
|
{
|
||
|
if ( i != j )
|
||
|
{
|
||
|
if ( (unsigned) nodes[j]->min_val <= (unsigned) nodes[i]->min_val &&
|
||
|
(unsigned) nodes[j]->max_val >= (unsigned) nodes[i]->max_val )
|
||
|
{
|
||
|
if (( (unsigned) nodes[i]->min_val - (unsigned) nodes[j]->min_val +
|
||
|
(unsigned) nodes[j]->max_val - (unsigned) nodes[i]->max_val ) <= min_range )
|
||
|
{
|
||
|
cover = nodes[j];
|
||
|
min_range = nodes[i]->min_val - nodes[j]->min_val +
|
||
|
nodes[j]->max_val - nodes[i]->max_val;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( cover )
|
||
|
{
|
||
|
_hierarchy_node_append ( cover, nodes[i] );
|
||
|
} else {
|
||
|
if ( (unsigned) nodes[i]->min_val >= (unsigned) root->min_val && (unsigned) nodes[i]->max_val <= (unsigned) root->max_val &&
|
||
|
( (unsigned) nodes[i]->min_val != (unsigned) root->min_val || (unsigned) nodes[i]->max_val != (unsigned) root->max_val ))
|
||
|
{
|
||
|
_hierarchy_node_append ( root, nodes[i] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( pthread_create ( &cluster_thread, NULL, _AI_cluster_thread, NULL ) != 0 )
|
||
|
{
|
||
|
_dpd.fatalMsg ( "Failed to create the hash cleanup thread\n" );
|
||
|
}
|
||
|
} /* ----- end of function AI_hierarchies_build ----- */
|
||
|
|