From Jeff Snyder:
authorAnders Broman <anders.broman@ericsson.com>
Mon, 3 Oct 2005 18:34:21 +0000 (18:34 -0000)
committerAnders Broman <anders.broman@ericsson.com>
Mon, 3 Oct 2005 18:34:21 +0000 (18:34 -0000)
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

epan/Makefile.common
epan/packet.c
epan/stream.c [new file with mode: 0644]
epan/stream.h [new file with mode: 0644]

index fb1adb9dfa88374425b0922fc7299728f4f33344..760e544105e935879d4ea037082eb1f315a529cf 100644 (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                   \
index 45b073f1fb5d6b579186dc0db897d9b02fe567e6..e3c62828bdae5fe56585b698e8b2c1d0b6adbc2d 100644 (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
diff --git a/epan/stream.c b/epan/stream.c
new file mode 100644 (file)
index 0000000..3e72efe
--- /dev/null
@@ -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;
+}
diff --git a/epan/stream.h b/epan/stream.h
new file mode 100644 (file)
index 0000000..ee868e1
--- /dev/null
@@ -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 */