From Jeff Snyder:

04-stream.diff
 A simplified packet reassembly API built on top of  fragment_add_seq_next for 
reassembling fragments that are delivered in-order, where fragments are 
identified by a framenum and an offset into that frame. Streams are attached 
to a conversation or a circuit and are unidirectional.

svn path=/trunk/; revision=16082
This commit is contained in:
Anders Broman 2005-10-03 18:34:21 +00:00
parent f11ef4b7a0
commit 758ceb58e8
4 changed files with 676 additions and 0 deletions

View File

@ -76,6 +76,7 @@ LIBETHEREAL_SRC = \
stat_cmd_args.c \
stats_tree.c \
strutil.c \
stream.c \
t35.c \
tap.c \
timestamp.c \
@ -165,6 +166,7 @@ LIBETHEREAL_INCLUDES = \
stat_cmd_args.h \
stats_tree.h \
stats_tree_priv.h \
stream.h \
strutil.h \
t35.h \
tap.h \

View File

@ -54,6 +54,7 @@
#include "emem.h"
#include <epan/reassemble.h>
#include <epan/stream.h>
static gint proto_malformed = -1;
static dissector_handle_t frame_handle = NULL;
@ -146,6 +147,9 @@ init_dissection(void)
may free up space for fragments, which they find by using the
data structures that "reassemble_init()" frees. */
reassemble_init();
/* Initialise the stream-handling tables */
stream_init();
}
void

532
epan/stream.c Normal file
View File

@ -0,0 +1,532 @@
/* stream.c
*
* Definititions for handling circuit-switched protocols
* which are handled as streams, and don't have lengths
* and IDs such as are required for reassemble.h
*
* $Id: stream.c,v 1.9 2005/07/27 22:47:55 richardv Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <glib.h>
#include <epan/packet.h>
#include <epan/reassemble.h>
#include <epan/stream.h>
#include <epan/tvbuff.h>
/* number of streams to allocate memory for at once */
#define MEMCHUNK_STREAM_COUNT 20
/* ditto pdus */
#define MEMCHUNK_PDU_COUNT 100
/* ditto fragments */
#define MEMCHUNK_FRAGMENT_COUNT 100
typedef struct {
fragment_data *fd_head; /* the reassembled data, NULL
* until we add the last fragment */
guint32 pdu_number; /* Number of this PDU within the stream */
/* id of this pdu (globally unique) */
guint32 id;
} stream_pdu_t;
struct stream_pdu_fragment
{
guint32 len; /* the length of this fragment */
stream_pdu_t *pdu;
gboolean final_fragment;
};
struct stream {
/* the key used to add this stream to stream_hash */
struct stream_key *key;
/* pdu to add the next fragment to, or NULL if we need to start
* a new PDU.
*/
stream_pdu_t *current_pdu;
/* number of PDUs added to this stream so far */
guint32 pdu_counter;
/* the framenumber and offset of the last fragment added;
used for sanity-checking */
guint32 lastfrag_framenum;
guint32 lastfrag_offset;
};
/*****************************************************************************
*
* Stream hash
*/
/* key */
typedef struct stream_key {
/* streams can be attached to circuits or conversations, and we note
that here */
gboolean is_circuit;
union {
const struct circuit *circuit;
const struct conversation *conv;
} circ;
int p2p_dir;
} stream_key_t;
/* hash func */
guint stream_hash_func(gconstpointer k)
{
const stream_key_t *key = (const stream_key_t *)k;
/* is_circuit is redundant to the circuit/conversation pointer */
return ((guint)key->circ.circuit) ^ key->p2p_dir;
}
/* compare func */
gboolean stream_compare_func(gconstpointer a,
gconstpointer b)
{
const stream_key_t *key1 = (const stream_key_t *)a;
const stream_key_t *key2 = (const stream_key_t *)b;
if( key1 -> p2p_dir != key2 -> p2p_dir ||
key1-> is_circuit != key2 -> is_circuit )
return FALSE;
if( key1 -> is_circuit )
return (key1 -> circ.circuit == key2 -> circ.circuit );
else
return (key1 -> circ.conv == key2 -> circ.conv );
}
/* value destroy func */
void stream_value_destroy_func(gpointer v)
{
stream_t *val = (stream_t *)v;
/* this is only called when the entire hash (and hence the entire
* "streams" GMemChunk is being freed, so there is no need to free
* v.
*/
}
/* memory pools */
static GMemChunk *stream_keys = NULL;
static GMemChunk *streams = NULL;
/* the hash table */
static GHashTable *stream_hash;
/* init/reset function, call from stream_init() */
static void init_stream_hash( void ) {
if( stream_hash != NULL ) {
g_hash_table_destroy( stream_hash );
stream_hash = NULL;
}
if( stream_keys != NULL ) {
g_mem_chunk_destroy( stream_keys );
stream_keys = NULL;
}
if( streams != NULL ) {
g_mem_chunk_destroy( streams );
streams = NULL;
}
streams = g_mem_chunk_create(stream_t,
MEMCHUNK_STREAM_COUNT,
G_ALLOC_ONLY);
stream_keys = g_mem_chunk_create(stream_key_t,
MEMCHUNK_STREAM_COUNT,
G_ALLOC_ONLY);
stream_hash = g_hash_table_new_full(stream_hash_func,
stream_compare_func,
NULL,
stream_value_destroy_func);
}
/* lookup function, returns null if not found */
static stream_t *stream_hash_lookup_circ( const struct circuit *circuit, int p2p_dir )
{
stream_key_t key = {TRUE,{circuit}, p2p_dir};
return (stream_t *)g_hash_table_lookup(stream_hash, &key);
}
static stream_t *stream_hash_lookup_conv( const struct conversation *conv, int p2p_dir )
{
stream_key_t key = {FALSE,{NULL}, p2p_dir};
key.circ.conv = conv;
return (stream_t *)g_hash_table_lookup(stream_hash, &key);
}
static stream_t *new_stream( stream_key_t *key )
{
stream_t *val;
val = g_mem_chunk_alloc(streams);
val -> key = key;
val -> pdu_counter = 0;
val -> current_pdu = NULL;
val -> lastfrag_framenum = 0;
val -> lastfrag_offset = 0;
g_hash_table_insert(stream_hash, key, val);
return val;
}
/* insert function */
static stream_t *stream_hash_insert_circ( const struct circuit *circuit, int p2p_dir )
{
stream_key_t *key;
key = g_mem_chunk_alloc(stream_keys);
key->is_circuit = TRUE;
key->circ.circuit = circuit;
key->p2p_dir = p2p_dir;
return new_stream(key);
}
static stream_t *stream_hash_insert_conv( const struct conversation *conv, int p2p_dir )
{
stream_key_t *key;
key = g_mem_chunk_alloc(stream_keys);
key->is_circuit = FALSE;
key->circ.conv = conv;
key->p2p_dir = p2p_dir;
return new_stream(key);
}
/******************************************************************************
*
* PDU data
*/
static GMemChunk *pdus = NULL;
/* pdu counter, for generating unique pdu ids */
static guint32 pdu_counter;
static void stream_init_pdu_data(void)
{
if( pdus != NULL ) {
g_mem_chunk_destroy( pdus );
pdus = NULL;
}
pdus = g_mem_chunk_create(stream_pdu_t,
MEMCHUNK_PDU_COUNT,
G_ALLOC_ONLY);
pdu_counter = 0;
}
/* new pdu in this stream */
static stream_pdu_t *stream_new_pdu(stream_t *stream)
{
stream_pdu_t *pdu;
pdu = g_mem_chunk_alloc(pdus);
pdu -> fd_head = NULL;
pdu -> pdu_number = stream -> pdu_counter++;
pdu -> id = pdu_counter++;
return pdu;
}
/*****************************************************************************
*
* fragment hash
*/
/* key */
typedef struct fragment_key {
const stream_t *stream;
guint32 framenum;
guint32 offset;
} fragment_key_t;
/* hash func */
guint fragment_hash_func(gconstpointer k)
{
const fragment_key_t *key = (const fragment_key_t *)k;
return ((guint)key->stream) + ((guint)key -> framenum) + ((guint)key->offset);
}
/* compare func */
gboolean fragment_compare_func(gconstpointer a,
gconstpointer b)
{
const fragment_key_t *key1 = (const fragment_key_t *)a;
const fragment_key_t *key2 = (const fragment_key_t *)b;
return (key1 -> stream == key2 -> stream &&
key1 -> framenum == key2 -> framenum &&
key1 -> offset == key2 -> offset );
}
/* memory pools */
static GMemChunk *fragment_keys = NULL;
static GMemChunk *fragment_vals = NULL;
/* the hash table */
static GHashTable *fragment_hash;
/* init/reset function, call from stream_init() */
static void init_fragment_hash( void ) {
if( fragment_hash != NULL ) {
g_hash_table_destroy( fragment_hash );
fragment_hash = NULL;
}
if( fragment_vals != NULL ) {
g_mem_chunk_destroy( fragment_vals );
fragment_vals = NULL;
}
if( fragment_keys != NULL ) {
g_mem_chunk_destroy( fragment_keys );
fragment_keys = NULL;
}
fragment_keys = g_mem_chunk_create(fragment_key_t,
MEMCHUNK_FRAGMENT_COUNT,
G_ALLOC_ONLY);
fragment_vals = g_mem_chunk_create(stream_pdu_fragment_t,
MEMCHUNK_FRAGMENT_COUNT,
G_ALLOC_ONLY);
fragment_hash = g_hash_table_new(fragment_hash_func,
fragment_compare_func);
}
/* lookup function, returns null if not found */
static stream_pdu_fragment_t *fragment_hash_lookup( const stream_t *stream, guint32 framenum, guint32 offset )
{
fragment_key_t key = {stream, framenum, offset};
stream_pdu_fragment_t *val = g_hash_table_lookup(fragment_hash, &key);
return val;
}
/* insert function */
static stream_pdu_fragment_t *fragment_hash_insert( const stream_t *stream, guint32 framenum, guint32 offset,
guint32 length)
{
fragment_key_t *key;
stream_pdu_fragment_t *val;
key = g_mem_chunk_alloc(fragment_keys);
key->stream = stream;
key->framenum = framenum;
key->offset = offset;
val = g_mem_chunk_alloc(fragment_vals);
val->len = length;
val->pdu = NULL;
val->final_fragment = FALSE;
g_hash_table_insert(fragment_hash, key, val);
return val;
}
/*****************************************************************************/
/* fragmentation hash tables */
static GHashTable *stream_fragment_table = NULL;
static GHashTable *stream_reassembled_table = NULL;
/* Initialise a new stream. Call this when you first identify a distinct
* stream. */
stream_t *stream_new_circ ( const struct circuit *circuit, int p2p_dir )
{
stream_t * stream;
/* we don't want to replace the previous data if we get called twice on the
same circuit, so do a lookup first */
stream = stream_hash_lookup_circ(circuit, p2p_dir);
g_assert( stream == NULL );
stream = stream_hash_insert_circ(circuit, p2p_dir);
return stream;
}
stream_t *stream_new_conv ( const struct conversation *conv, int p2p_dir )
{
stream_t * stream;
/* we don't want to replace the previous data if we get called twice on the
same conversation, so do a lookup first */
stream = stream_hash_lookup_conv(conv, p2p_dir);
g_assert( stream == NULL );
stream = stream_hash_insert_conv(conv, p2p_dir);
return stream;
}
/* retrieve a previously-created stream.
*
* Returns null if no matching stream was found.
*/
stream_t *find_stream_circ ( const struct circuit *circuit, int p2p_dir )
{
return stream_hash_lookup_circ(circuit,p2p_dir);
}
stream_t *find_stream_conv ( const struct conversation *conv, int p2p_dir )
{
return stream_hash_lookup_conv(conv,p2p_dir);
}
/* initialise the stream routines */
void stream_init( void )
{
init_stream_hash();
init_fragment_hash();
stream_init_pdu_data();
fragment_table_init(&stream_fragment_table);
reassembled_table_init(&stream_reassembled_table);
}
/*****************************************************************************/
stream_pdu_fragment_t *stream_find_frag( stream_t *stream, guint32 framenum, guint32 offset )
{
return fragment_hash_lookup( stream, framenum, offset );
}
stream_pdu_fragment_t *stream_add_frag( stream_t *stream, guint32 framenum, guint32 offset,
tvbuff_t *tvb, packet_info *pinfo, gboolean more_frags )
{
fragment_data *fd_head;
stream_pdu_t *pdu;
stream_pdu_fragment_t *frag_data;
g_assert(stream);
/* check that this fragment is at the end of the stream */
g_assert( framenum > stream->lastfrag_framenum ||
(framenum == stream->lastfrag_framenum && offset > stream->lastfrag_offset));
pdu = stream->current_pdu;
if( pdu == NULL ) {
/* start a new pdu */
pdu = stream->current_pdu = stream_new_pdu(stream);
}
/* add it to the reassembly tables */
fd_head = fragment_add_seq_next(tvb, 0, pinfo, pdu->id,
stream_fragment_table, stream_reassembled_table,
tvb_reported_length(tvb), more_frags);
/* add it to our hash */
frag_data = fragment_hash_insert( stream, framenum, offset, tvb_reported_length(tvb));
frag_data -> pdu = pdu;
if( fd_head != NULL ) {
/* if this was the last fragment, update the pdu data.
*/
pdu -> fd_head = fd_head;
/* start a new pdu next time */
stream->current_pdu = NULL;
frag_data -> final_fragment = TRUE;
}
/* stashing the framenum and offset permit future sanity checks */
stream -> lastfrag_framenum = framenum;
stream -> lastfrag_offset = offset;
return frag_data;
}
tvbuff_t *stream_process_reassembled(
tvbuff_t *tvb, int offset, packet_info *pinfo,
char *name, const stream_pdu_fragment_t *frag,
const struct _fragment_items *fit,
gboolean *update_col_infop, proto_tree *tree)
{
stream_pdu_t *pdu;
g_assert(frag);
pdu = frag->pdu;
/* we handle non-terminal fragments ourselves, because
reassemble.c messes them up */
if(!frag->final_fragment) {
if (pdu->fd_head != NULL && fit->hf_reassembled_in != NULL) {
proto_tree_add_uint(tree,
*(fit->hf_reassembled_in), tvb,
0, 0, pdu->fd_head->reassembled_in);
}
return NULL;
}
return process_reassembled_data(tvb, offset, pinfo, name, pdu->fd_head,
fit, update_col_infop, tree);
}
guint32 stream_get_frag_length( const stream_pdu_fragment_t *frag)
{
g_assert( frag );
return frag->len;
}
fragment_data *stream_get_frag_data( const stream_pdu_fragment_t *frag)
{
g_assert( frag );
return frag->pdu->fd_head;
}
guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag)
{
g_assert( frag );
return frag->pdu->pdu_number;
}

138
epan/stream.h Normal file
View File

@ -0,0 +1,138 @@
/* stream.h
*
* Definititions for handling circuit-switched protocols
* which are handled as streams, and don't have lengths
* and IDs such as are required for reassemble.h
*
* $Id$
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef STREAM_H
#define STREAM_H
#include <epan/tvbuff.h>
extern struct _fragment_items;
/* A stream represents the concept of an arbitrary stream of data,
divided up into frames for transmission, where the frames have
little or no correspondence to the PDUs of the protocol being
streamed, and those PDUs are just delineated by a magic number.
For example, we stream H.223 over IAX2. IAX2 has no concept of
H.223 PDUs and just divides the H.223 stream into 160-byte
frames. H.223 PDUs are delineated by two-byte magic numbers (which
may, of course, straddle an IAX2 frame boundary).
Essentially we act as a wrapper to reassemble.h, by making up
PDU ids and keeping some additional data on fragments to allow the
PDUs to be defragmented again.
*/
/* A stream_t represents a stream. There might be one or two streams
in a circuit, depending on whether that circuit is mono- or bi-directional.
*/
typedef struct stream stream_t;
/* Fragments in a PDU are represented using a stream_pdu_fragment_t,
and placed in a linked-list with other fragments in the PDU.
(They're also placed in a hash so we can find them again later)
*/
typedef struct stream_pdu_fragment stream_pdu_fragment_t;
struct circuit;
struct conversation;
/* initialise a new stream. Call this when you first identify a distinct
* stream. The circit pointer is just used as a key to look up the stream. */
extern stream_t *stream_new_circ ( const struct circuit *circuit, int p2p_dir );
extern stream_t *stream_new_conv ( const struct conversation *conv, int p2p_dir );
/* retrieve a previously-created stream.
*
* Returns null if no matching stream was found.
*/
extern stream_t *find_stream_circ ( const struct circuit *circuit, int p2p_dir );
extern stream_t *find_stream_conv ( const struct conversation *conv, int p2p_dir );
/* see if we've seen this fragment before.
The framenum and offset are just hash keys, so can be any values unique
to this frame, but the idea is that you use the number of the frame being
disassembled, and the byte-offset within that frame.
*/
extern stream_pdu_fragment_t *stream_find_frag( stream_t *stream, guint32 framenum, guint32 offset );
/* add a new fragment to the fragment tables for the stream. The framenum and
* offset are keys allowing future access with stream_find_frag(), tvb is the
* fragment to be added, and pinfo is the information for the frame containing
* this fragment. more_frags should be set if this is the final fragment in the
* PDU.
*
* * the fragment must be later in the stream than any previous fragment
* (ie, framenum.offset must be greater than those passed on the previous
* call)
*
* This essentially means that you can only add fragments on the first pass
* through the stream.
*/
extern stream_pdu_fragment_t *stream_add_frag( stream_t *stream, guint32 framenum, guint32 offset,
tvbuff_t *tvb, packet_info *pinfo, gboolean more_frags );
/* Get the length of a fragment previously found by stream_find_frag().
*/
extern guint32 stream_get_frag_length( const stream_pdu_fragment_t *frag);
/* Get a handle on the top of the chain of fragment_datas underlying this PDU
* frag can be any fragment within a PDU, and it will always return the head of
* the chain
*
* Returns NULL until the last fragment is added.
*/
extern struct _fragment_data *stream_get_frag_data( const stream_pdu_fragment_t *frag);
/*
* Process reassembled data; if this is the last fragment, put the fragment
* information into the protocol tree, and construct a tvbuff with the
* reassembled data, otherwise just put a "reassembled in" item into the
* protocol tree.
*/
extern tvbuff_t *stream_process_reassembled(
tvbuff_t *tvb, int offset, packet_info *pinfo,
char *name, const stream_pdu_fragment_t *frag,
const struct _fragment_items *fit,
gboolean *update_col_infop, proto_tree *tree);
/* Get the PDU number. PDUs are numbered from zero within a stream.
* frag can be any fragment within a PDU.
*/
extern guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag);
/* initialise the stream routines */
void stream_init( void );
#endif /* STREAM_H */