2010-08-14 14:30:41 +02:00
|
|
|
/*
|
|
|
|
* =====================================================================================
|
|
|
|
*
|
|
|
|
* Filename: stream.c
|
|
|
|
*
|
|
|
|
* Description: It manages the streams of TCP packets, keeping them in a hashtable
|
|
|
|
*
|
|
|
|
* Version: 0.1
|
|
|
|
* Created: 30/07/2010 15:02:54
|
|
|
|
* 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 <stdlib.h>
|
|
|
|
#include <unistd.h>
|
2010-10-04 17:48:07 +02:00
|
|
|
#include <time.h>
|
2010-08-14 14:30:41 +02:00
|
|
|
|
|
|
|
PRIVATE struct pkt_info *hash = NULL;
|
|
|
|
PRIVATE time_t start_time = 0;
|
|
|
|
|
2010-09-04 21:33:53 +02:00
|
|
|
/** pthread mutex for managing the access of multiple readers/writers to the hash table */
|
|
|
|
PRIVATE pthread_mutex_t hash_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
|
|
/** \defgroup stream Manage streams, sorting them into hash tables and linked lists
|
|
|
|
* @{ */
|
2010-08-14 14:30:41 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Remove a stream from the hash table (private function)
|
|
|
|
* \param stream Stream to be removed
|
|
|
|
*/
|
|
|
|
|
2010-09-04 21:33:53 +02:00
|
|
|
PRIVATE void
|
2010-10-11 17:00:03 +02:00
|
|
|
__AI_stream_free ( struct pkt_info* stream )
|
2010-09-04 21:33:53 +02:00
|
|
|
{
|
2010-08-14 14:30:41 +02:00
|
|
|
struct pkt_info *tmp = NULL;
|
|
|
|
|
2010-11-16 19:18:08 +01:00
|
|
|
/* If the provided stream is empty, or the hash table contains no element, just return */
|
2010-08-14 14:30:41 +02:00
|
|
|
if ( !stream || !hash || HASH_COUNT(hash) == 0 )
|
|
|
|
return;
|
|
|
|
|
2010-11-16 19:18:08 +01:00
|
|
|
/* Lock the mutex over the hash table and search for a stream having the provided key */
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_lock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
HASH_FIND ( hh, hash, &(stream->key), sizeof(struct pkt_key), tmp );
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_unlock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
|
2010-11-16 19:18:08 +01:00
|
|
|
/* If that key is not there, just return */
|
2010-08-14 14:30:41 +02:00
|
|
|
if ( !tmp )
|
|
|
|
return;
|
|
|
|
|
2010-11-16 19:18:08 +01:00
|
|
|
/* If the stream has no IP or TCP header, return */
|
2010-09-04 21:33:53 +02:00
|
|
|
if ( stream->pkt )
|
|
|
|
{
|
|
|
|
if ( !stream->pkt->ip4_header )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( stream->pkt->ip4_header->proto != IPPROTO_TCP || !stream->pkt->tcp_header )
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-11-16 19:18:08 +01:00
|
|
|
/* Remove the stream from the hash table */
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_lock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
HASH_DEL ( hash, stream );
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_unlock ( &hash_mutex );
|
|
|
|
|
2010-11-16 19:18:08 +01:00
|
|
|
/* Remove all the packets contained in the stream */
|
2010-09-04 21:33:53 +02:00
|
|
|
while ( stream )
|
|
|
|
{
|
2010-08-14 14:30:41 +02:00
|
|
|
tmp = stream->next;
|
|
|
|
|
2010-09-04 21:33:53 +02:00
|
|
|
if ( stream->pkt )
|
|
|
|
{
|
2010-08-14 14:30:41 +02:00
|
|
|
free ( stream->pkt );
|
|
|
|
stream->pkt = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
free ( stream );
|
|
|
|
stream = tmp;
|
|
|
|
}
|
2010-09-04 21:33:53 +02:00
|
|
|
|
|
|
|
stream = NULL;
|
2010-10-11 17:00:03 +02:00
|
|
|
} /* ----- end of function __AI_stream_free ----- */
|
2010-08-14 14:30:41 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Thread called for cleaning up the hash table from the traffic streams older than
|
|
|
|
* a certain threshold
|
|
|
|
*/
|
|
|
|
|
2010-09-04 21:33:53 +02:00
|
|
|
void*
|
|
|
|
AI_hashcleanup_thread ( void* arg )
|
|
|
|
{
|
2010-11-23 23:23:29 +01:00
|
|
|
struct pkt_info *h, *stream, *tmp;
|
|
|
|
time_t max_timestamp;
|
|
|
|
int pkt_count, pkt_rm;
|
2017-05-01 14:41:55 +02:00
|
|
|
bool has_old_streams;
|
2010-11-23 23:23:29 +01:00
|
|
|
|
|
|
|
if ( config->hashCleanupInterval == 0 )
|
|
|
|
{
|
|
|
|
pthread_exit ((void*) 0);
|
|
|
|
return (void*) 0;
|
|
|
|
}
|
2010-08-14 14:30:41 +02:00
|
|
|
|
|
|
|
while ( 1 ) {
|
|
|
|
/* Sleep for the specified number of seconds */
|
2010-09-21 21:47:48 +02:00
|
|
|
sleep ( config->hashCleanupInterval );
|
2010-08-14 14:30:41 +02:00
|
|
|
|
|
|
|
/* If the hash is empty, come back to sleep */
|
|
|
|
if ( !hash || !HASH_COUNT(hash) )
|
|
|
|
continue;
|
|
|
|
|
2010-11-23 23:23:29 +01:00
|
|
|
has_old_streams = true;
|
2010-08-14 14:30:41 +02:00
|
|
|
|
2010-11-23 23:23:29 +01:00
|
|
|
while ( has_old_streams )
|
|
|
|
{
|
|
|
|
has_old_streams = false;
|
2010-08-14 14:30:41 +02:00
|
|
|
|
2010-11-23 23:23:29 +01:00
|
|
|
/* Check all the streams in the hash */
|
|
|
|
for ( h = hash; h; h = (struct pkt_info*) h->next ) {
|
|
|
|
if ( h->observed )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2010-09-04 21:33:53 +02:00
|
|
|
|
2010-11-23 23:23:29 +01:00
|
|
|
if ( h->next )
|
2010-09-04 21:33:53 +02:00
|
|
|
{
|
2010-11-23 23:23:29 +01:00
|
|
|
if ( h->next->observed )
|
|
|
|
{
|
|
|
|
if ( config->max_hash_pkt_number != 0 )
|
|
|
|
{
|
|
|
|
if ( h->next->n_packets == 0 )
|
|
|
|
{
|
|
|
|
for ( stream = h->next, pkt_count=0; stream; stream = (struct pkt_info*) stream->next, pkt_count++ );
|
|
|
|
h->next->n_packets = pkt_count;
|
|
|
|
} else {
|
|
|
|
pkt_count = h->next->n_packets;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If this stream has too many packets inside, remove the oldest ones */
|
|
|
|
if ( pkt_count > config->max_hash_pkt_number )
|
|
|
|
{
|
|
|
|
for ( stream = h->next, pkt_rm = 0;
|
|
|
|
stream && pkt_rm < pkt_count - config->max_hash_pkt_number;
|
|
|
|
stream = stream->next, pkt_rm++ )
|
|
|
|
{
|
|
|
|
tmp = stream->next;
|
|
|
|
__AI_stream_free ( stream );
|
|
|
|
stream = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
h->next = stream;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
max_timestamp = 0;
|
|
|
|
|
|
|
|
/* Find the maximum timestamp in the flow */
|
|
|
|
for ( stream = h; stream; stream = (struct pkt_info*) stream->next ) {
|
|
|
|
if ( stream->timestamp > max_timestamp )
|
|
|
|
max_timestamp = stream->timestamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the most recent packet in the stream is older than the specified threshold, remove that stream */
|
|
|
|
|
|
|
|
if ( time (NULL) - max_timestamp > config->streamExpireInterval ) {
|
|
|
|
has_old_streams = true;
|
|
|
|
stream = h;
|
|
|
|
|
|
|
|
if ( stream )
|
|
|
|
{
|
|
|
|
__AI_stream_free ( stream );
|
|
|
|
}
|
2010-09-04 21:33:53 +02:00
|
|
|
}
|
2010-08-14 14:30:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hey we'll never reach this point unless 1 becomes != 1, but I have to place it
|
|
|
|
* for letting not gcc annoy us */
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_exit ((void*) 0);
|
2010-08-14 14:30:41 +02:00
|
|
|
return (void*) 0;
|
|
|
|
} /* ----- end of function AI_hashcleanup_thread ----- */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Function called for appending a new packet to the hash table,
|
|
|
|
* creating a new stream or appending it to an existing stream
|
|
|
|
* \param pkt Packet to be appended
|
|
|
|
*/
|
|
|
|
|
2010-09-04 21:33:53 +02:00
|
|
|
void
|
|
|
|
AI_pkt_enqueue ( SFSnortPacket* pkt )
|
2010-08-14 14:30:41 +02:00
|
|
|
{
|
|
|
|
struct pkt_key key;
|
|
|
|
struct pkt_info *info;
|
|
|
|
struct pkt_info *tmp;
|
|
|
|
struct pkt_info *found = NULL;
|
|
|
|
|
|
|
|
if ( start_time == 0 )
|
|
|
|
start_time = time (NULL);
|
|
|
|
|
2010-11-23 23:23:29 +01:00
|
|
|
/* If we are not using the stream hash table, just return */
|
|
|
|
if ( config->use_stream_hash_table == 0 )
|
|
|
|
return;
|
|
|
|
|
2010-08-14 14:30:41 +02:00
|
|
|
/* If this is not an IP and/or TCP packet, it's not for me */
|
|
|
|
if ( !( pkt->ip4_header && pkt->tcp_header ))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( !( info = (struct pkt_info*) malloc( sizeof(struct pkt_info) )) )
|
2010-11-23 23:23:29 +01:00
|
|
|
{
|
2010-10-03 04:18:43 +02:00
|
|
|
AI_fatal_err ( "Fatal dynamic memory allocation error", __FILE__, __LINE__ );
|
2010-11-23 23:23:29 +01:00
|
|
|
}
|
2010-08-14 14:30:41 +02:00
|
|
|
|
|
|
|
memset ( &key, 0, sizeof(struct pkt_key));
|
|
|
|
key.src_ip = pkt->ip4_header->source.s_addr;
|
|
|
|
key.dst_port = pkt->tcp_header->destination_port;
|
|
|
|
|
|
|
|
info->key = key;
|
|
|
|
info->timestamp = time(NULL);
|
|
|
|
info->observed = false;
|
2010-11-23 23:23:29 +01:00
|
|
|
info->n_packets = 0;
|
2010-08-14 14:30:41 +02:00
|
|
|
info->next = NULL;
|
|
|
|
|
|
|
|
if ( !( info->pkt = (SFSnortPacket*) malloc ( sizeof (SFSnortPacket) )) )
|
2010-11-23 23:23:29 +01:00
|
|
|
{
|
2010-10-03 04:18:43 +02:00
|
|
|
AI_fatal_err ( "Fatal dynamic memory allocation error", __FILE__, __LINE__ );
|
2010-11-23 23:23:29 +01:00
|
|
|
}
|
2010-08-14 14:30:41 +02:00
|
|
|
|
|
|
|
memcpy ( info->pkt, pkt, sizeof (SFSnortPacket) );
|
|
|
|
|
|
|
|
if ( hash ) {
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_lock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
HASH_FIND ( hh, hash, &key, sizeof(struct pkt_key), found );
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_unlock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If there is already an element of this traffic stream in my hash table,
|
|
|
|
* append the packet just received to this stream*/
|
|
|
|
if ( found ) {
|
2010-11-23 23:23:29 +01:00
|
|
|
/* If the current packet contains a RST or a FIN, just deallocate the stream */
|
|
|
|
if (
|
|
|
|
( info->pkt->tcp_header->flags & TCPHEADER_RST ) ||
|
|
|
|
(( info->pkt->tcp_header->flags & TCPHEADER_FIN ) &&
|
|
|
|
( info->pkt->tcp_header->flags & TCPHEADER_ACK ))
|
|
|
|
) {
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_lock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
HASH_FIND ( hh, hash, &key, sizeof(struct pkt_key), found );
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_unlock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
|
|
|
|
if ( found ) {
|
|
|
|
if ( !found->observed ) {
|
2010-10-11 17:00:03 +02:00
|
|
|
__AI_stream_free ( found );
|
2010-08-14 14:30:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tmp = NULL;
|
|
|
|
|
|
|
|
for ( ; found->next; found = found->next ) {
|
2010-11-23 18:42:20 +01:00
|
|
|
/* Stupid memory bug fixed in a stupid and unelegant way */
|
2011-02-09 03:22:17 +01:00
|
|
|
if ( (unsigned long int) found->next->pkt < 0x100 )
|
2010-11-23 18:42:20 +01:00
|
|
|
break;
|
|
|
|
|
2010-08-14 14:30:41 +02:00
|
|
|
/* If the sequence number of the next packet in the stream
|
|
|
|
* is bigger than the sequence number of the current packet,
|
|
|
|
* place the current packet before that */
|
|
|
|
if ( ntohl( found->next->pkt->tcp_header->sequence ) >
|
|
|
|
ntohl( info->pkt->tcp_header->sequence ) ) {
|
|
|
|
tmp = found->next;
|
|
|
|
found->next = info;
|
|
|
|
info->next = tmp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !tmp ) {
|
|
|
|
found->next = info;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* If the packet contains the ACK flag, no payload and it is
|
|
|
|
* associated to no active stream, just ignore it */
|
|
|
|
/* if ( pkt->tcp_header->flags & TCPHEADER_ACK ) { */
|
|
|
|
/* return; */
|
|
|
|
/* } */
|
|
|
|
|
|
|
|
/* If there is no stream associated to this packet, create
|
|
|
|
* a new node in the hash table */
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_lock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
HASH_ADD ( hh, hash, key, sizeof(struct pkt_key), info );
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_unlock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
} /* ----- end of function AI_pkt_enqueue ----- */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Get a TCP stream by key
|
|
|
|
* \param key Key of the stream to be picked up (struct pkt_key)
|
|
|
|
* \return A pkt_info pointer to the stream if found, NULL otherwise
|
|
|
|
*/
|
|
|
|
|
2017-05-01 14:41:55 +02:00
|
|
|
struct pkt_info*
|
2010-08-14 14:30:41 +02:00
|
|
|
AI_get_stream_by_key ( struct pkt_key key )
|
|
|
|
{
|
|
|
|
struct pkt_info *info = NULL;
|
2010-09-04 21:33:53 +02:00
|
|
|
|
|
|
|
pthread_mutex_lock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
HASH_FIND ( hh, hash, &key, sizeof (struct pkt_key), info );
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_unlock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
|
|
|
|
/* If no stream was found with that key, return */
|
|
|
|
if ( info == NULL )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* If the timestamp of the stream is older than the start time, return */
|
|
|
|
if ( info->timestamp < start_time )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return info;
|
|
|
|
} /* ----- end of function AI_get_stream_by_key ----- */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Set the flag "observed" on a stream associated to a security alert, so that it won't be removed from the hash table
|
|
|
|
* \param key Key of the stream to be set as "observed"
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
AI_set_stream_observed ( struct pkt_key key )
|
|
|
|
{
|
|
|
|
struct pkt_info *info = NULL;
|
2010-09-04 21:33:53 +02:00
|
|
|
|
|
|
|
pthread_mutex_lock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
HASH_FIND ( hh, hash, &key, sizeof (struct pkt_key), info );
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_unlock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
|
|
|
|
if ( info == NULL )
|
|
|
|
return;
|
|
|
|
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_lock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
info->observed = true;
|
2010-09-04 21:33:53 +02:00
|
|
|
pthread_mutex_unlock ( &hash_mutex );
|
2010-08-14 14:30:41 +02:00
|
|
|
} /* ----- end of function AI_set_stream_observed ----- */
|
|
|
|
|
2010-09-04 21:33:53 +02:00
|
|
|
/** @} */
|