diff options
author | Michael Tüxen <tuexen@fh-muenster.de> | 2007-02-09 16:38:57 +0000 |
---|---|---|
committer | Michael Tüxen <tuexen@fh-muenster.de> | 2007-02-09 16:38:57 +0000 |
commit | f20299ba4bb7805f600c544226112a38f030034e (patch) | |
tree | b0382add700c9a7ff3307c14bca92c1ebcc5102f | |
parent | 2532bc2a1a2130b1960084b74046fc15968770a5 (diff) |
Add support for SCTP reassembly from Robin Seggelmann.
You must enable this feature via Edit/Preferences/Protocols/SCTP
svn path=/trunk/; revision=20756
-rw-r--r-- | AUTHORS | 4 | ||||
-rw-r--r-- | epan/dissectors/packet-sctp.c | 655 | ||||
-rw-r--r-- | epan/dissectors/packet-sctp.h | 32 |
3 files changed, 682 insertions, 9 deletions
@@ -2580,6 +2580,10 @@ Abhik Sarkar <sarkar.abhik [AT] gmail.com> { (as generated by the Cisco ITP packet logging facility) } +Robin Seggelmann <seggelmann [AT] fh-muenster.de> { + Support for SCTP reassembly. +} + and by: Pavel Roskin <proski [AT] gnu.org> diff --git a/epan/dissectors/packet-sctp.c b/epan/dissectors/packet-sctp.c index 0e90b692dd..5f7f1caa8e 100644 --- a/epan/dissectors/packet-sctp.c +++ b/epan/dissectors/packet-sctp.c @@ -15,13 +15,16 @@ * * Copyright 2000-2005 Michael Tuexen <tuexen [AT] fh-muenster.de> * Still to do (so stay tuned) - * - support for reassembly * - error checking mode * * padding errors * * length errors * * bundling errors * * value errors * + * + * Reassembly added 2006 by Robin Seggelmann + * + * * $Id$ * * Wireshark - Network traffic analyzer @@ -55,6 +58,9 @@ #include <epan/addr_resolv.h> #include "packet-sctp.h" #include <epan/sctpppids.h> +#include <epan/emem.h> + +#define LT(x, y) ((gint32)((x) - (y)) < 0) #define NETWORK_BYTE_ORDER FALSE #define ADD_PADDING(x) ((((x) + 3) >> 2) << 2) @@ -179,6 +185,11 @@ static int hf_pktdrop_chunk_truncated_length = -1; static int hf_pktdrop_chunk_reserved = -1; static int hf_pktdrop_chunk_data_field = -1; +static int hf_sctp_reassembled_in = -1; +static int hf_sctp_duplicate = -1; +static int hf_sctp_fragments = -1; +static int hf_sctp_fragment = -1; + static dissector_table_t sctp_port_dissector_table; static dissector_table_t sctp_ppi_dissector_table; static heur_dissector_list_t sctp_heur_subdissector_list; @@ -200,6 +211,9 @@ static gint ett_sctp_parameter_type= -1; static gint ett_sctp_sack_chunk_gap_block = -1; static gint ett_sctp_unrecognized_parameter_parameter = -1; +static gint ett_sctp_fragments = -1; +static gint ett_sctp_fragment = -1; + static dissector_handle_t data_handle; #define SCTP_DATA_CHUNK_ID 0 @@ -317,6 +331,8 @@ static gboolean show_chunk_types = TRUE; static gboolean show_always_control_chunks = TRUE; static gint sctp_checksum = SCTP_CHECKSUM_CRC32C; +static gboolean use_reassembly = FALSE; + static struct _sctp_info sctp_info; /* adler32.c -- compute the Adler-32 checksum of a data stream @@ -1568,6 +1584,595 @@ dissect_payload(tvbuff_t *payload_tvb, packet_info *pinfo, proto_tree *tree, gui #define SCTP_DATA_CHUNK_B_BIT 0x02 #define SCTP_DATA_CHUNK_U_BIT 0x04 + +/* table to hold fragmented SCTP messages */ +static GHashTable *frag_table = NULL; + + +typedef struct _frag_key { + guint16 sport; + guint16 dport; + guint32 verification_tag; + guint16 stream_id; + guint16 stream_seq_num; +} frag_key; + + +static gint +frag_equal(gconstpointer k1, gconstpointer k2) +{ + const frag_key* key1 = (const frag_key*) k1; + const frag_key* key2 = (const frag_key*) k2; + + return ( (key1->sport == key2->sport) && + (key1->dport == key1->dport) && + (key1->verification_tag == key1->verification_tag) && + (key1->stream_id == key1->stream_id) && + (key1->stream_seq_num == key1->stream_seq_num) + ? TRUE : FALSE); +} + + +static guint +frag_hash(gconstpointer k) +{ + const frag_key* key = (const frag_key*) k; + + return key->sport ^ key->dport ^ key->verification_tag ^ + key->stream_id ^ key->stream_seq_num; +} + + + +static void +frag_free_msgs(sctp_frag_msg *msg) +{ + sctp_frag_be *beginend; + sctp_fragment *fragment; + + /* free all begins */ + while (msg->begins) { + beginend = msg->begins; + msg->begins = msg->begins->next; + g_free(beginend); + } + + /* free all ends */ + while (msg->ends) { + beginend = msg->ends; + msg->ends = msg->ends->next; + g_free(beginend); + } + + /* free all fragments */ + while (msg->fragments) { + fragment = msg->fragments; + msg->fragments = msg->fragments->next; + g_free(fragment->data); + g_free(fragment); + } + + g_free(msg); +} + + +static void +frag_table_init(void) +{ + /* destroy an existing hast table and create a new one */ + if (frag_table) { + g_hash_table_destroy(frag_table); + } + + frag_table = g_hash_table_new_full(frag_hash, frag_equal, (GDestroyNotify) g_free, + (GDestroyNotify) frag_free_msgs); +} + + +static sctp_frag_msg* +find_message(guint16 stream_id, guint16 stream_seq_num) +{ + frag_key key; + + key.sport = sctp_info.sport; + key.dport = sctp_info.dport; + key.verification_tag = sctp_info.verification_tag; + key.stream_id = stream_id; + key.stream_seq_num = stream_seq_num; + + return g_hash_table_lookup(frag_table, &key); +} + + +static sctp_fragment* +find_fragment(guint32 tsn, guint16 stream_id, guint16 stream_seq_num) +{ + sctp_frag_msg *msg; + sctp_fragment *next_fragment; + + msg = find_message(stream_id, stream_seq_num); + + if (msg) { + next_fragment = msg->fragments; + while (next_fragment) { + if (next_fragment->tsn == tsn) return next_fragment; + next_fragment = next_fragment->next; + } + } + + return NULL; +} + + +static sctp_fragment* +add_fragment(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 tsn, + guint16 stream_id, guint16 stream_seq_num, guint8 b_bit, guint8 e_bit) +{ + sctp_frag_msg *msg; + sctp_fragment *fragment, *last_fragment; + sctp_frag_be *beginend, *last_beginend; + frag_key *key; + + /* lookup message. if not found, create it */ + msg = find_message(stream_id, stream_seq_num); + + if (!msg) { + msg = g_malloc (sizeof (sctp_frag_msg)); + msg->begins = NULL; + msg->ends = NULL; + msg->fragments = NULL; + msg->messages = NULL; + msg->next = NULL; + + key = g_malloc(sizeof (frag_key)); + key->sport = sctp_info.sport; + key->dport = sctp_info.dport; + key->verification_tag = sctp_info.verification_tag; + key->stream_id = stream_id; + key->stream_seq_num = stream_seq_num; + + g_hash_table_insert(frag_table, key, msg); + } + + /* lookup segment. if not found, create it */ + fragment = find_fragment(tsn, stream_id, stream_seq_num); + + if (fragment) { + /* this fragment is already known. + * compare frame number to check if it's a duplicate + */ + if (fragment->frame_num == pinfo->fd->num) { + return fragment; + } else { + /* there already is a fragment having the same ports, v_tag, + * stream id, stream_seq_num and tsn but it appeared in a different + * frame, so it must be a duplicate fragment. maybe a retransmission? + * Mark it as duplicate and return NULL + */ + if (check_col(pinfo->cinfo, COL_INFO)) + col_append_str(pinfo->cinfo, COL_INFO, " (Duplicate Message Fragment)"); + + proto_tree_add_uint(tree, hf_sctp_duplicate, tvb, 0, 0, fragment->frame_num); + return NULL; + } + } + + /* create new fragment */ + fragment = g_malloc (sizeof (sctp_fragment)); + fragment->frame_num = pinfo->fd->num; + fragment->tsn = tsn; + fragment->len = tvb_length(tvb); + fragment->next = NULL; + if (fragment->len) { + fragment->data = g_malloc (fragment->len); + tvb_memcpy(tvb, fragment->data, 0, fragment->len); + } + + /* add new fragment to linked list. sort ascending by tsn */ + if (!msg->fragments) msg->fragments = fragment; + else { + if (msg->fragments->tsn > fragment->tsn) { + fragment->next = msg->fragments; + msg->fragments = fragment; + } else { + last_fragment = msg->fragments; + while (last_fragment->next && + last_fragment->next->tsn < fragment->tsn) + last_fragment = last_fragment->next; + + fragment->next = last_fragment->next; + last_fragment->next = fragment; + } + } + + + /* save begin or end if neccessary */ + if (b_bit && !e_bit) { + beginend = g_malloc (sizeof (sctp_frag_be)); + beginend->fragment = fragment; + beginend->next = NULL; + + /* add begin to linked list. sort descending by tsn */ + if (!msg->begins) msg->begins = beginend; + else { + if (msg->begins->fragment->tsn < beginend->fragment->tsn) { + beginend->next = msg->begins; + msg->begins = beginend; + } else { + last_beginend = msg->begins; + while (last_beginend->next && + last_beginend->next->fragment->tsn > beginend->fragment->tsn) + last_beginend = last_beginend->next; + + beginend->next = last_beginend->next; + last_beginend->next = beginend; + } + } + + } + + if (!b_bit && e_bit) { + beginend = g_malloc (sizeof (sctp_frag_be)); + beginend->fragment = fragment; + beginend->next = NULL; + + /* add end to linked list. sort ascending by tsn */ + if (!msg->ends) msg->ends = beginend; + else { + if (msg->begins->fragment->tsn > beginend->fragment->tsn) { + beginend->next = msg->ends; + msg->ends = beginend; + } else { + last_beginend = msg->ends; + while (last_beginend->next && + last_beginend->next->fragment->tsn < beginend->fragment->tsn) + last_beginend = last_beginend->next; + + beginend->next = last_beginend->next; + last_beginend->next = beginend; + } + } + + } + + return fragment; +} + +static tvbuff_t* +fragment_reassembly(tvbuff_t *tvb, sctp_fragment* fragment, + packet_info *pinfo, proto_tree *tree, guint16 stream_id, + guint16 stream_seq_num) +{ + sctp_frag_msg *msg; + sctp_complete_msg *message, *last_message; + sctp_fragment *frag_i, *last_frag, *first_frag; + sctp_frag_be *begin, *end, *beginend; + guint32 len, offset = 0; + tvbuff_t* new_tvb = NULL; + proto_item *item; + proto_tree *ptree; + + msg = find_message(stream_id, stream_seq_num); + + if (!msg) { + /* no message, we can't do anything */ + return NULL; + } + + /* check if fragment is part of an already reassembled message */ + for (message = msg->messages; + message && + !(message->begin <= fragment->tsn && message->end >= fragment->tsn) && + !(message->begin > message->end && + (message->begin <= fragment->tsn || message->end >= fragment->tsn)); + message = message->next); + + if (message) { + /* we found the reassembled message this fragment belongs to */ + if (fragment == message->reassembled_in) { + + /* this is the last fragment, create data source */ + new_tvb = tvb_new_real_data(message->data, message->len, message->len); + tvb_set_child_real_data_tvbuff(tvb, new_tvb); + add_new_data_source(pinfo, new_tvb, "Reassembled SCTP Message"); + + /* display reassembly info */ + item = proto_tree_add_item(tree, hf_sctp_fragments, tvb, 0, -1, FALSE); + ptree = proto_item_add_subtree(item, ett_sctp_fragments); + proto_item_append_text(item, " (%u bytes, %u fragments): ", + message->len, message->end - message->begin + 1); + + if (message->begin > message->end) { + for (frag_i = find_fragment(message->begin, stream_id, stream_seq_num); + frag_i; + frag_i = frag_i->next) { + + proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len, + frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)", + frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len); + offset += frag_i->len; + } + + for (frag_i = msg->fragments; + frag_i && frag_i->tsn <= message->end; + frag_i = frag_i->next) { + + proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len, + frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)", + frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len); + offset += frag_i->len; + } + } else { + for (frag_i = find_fragment(message->begin, stream_id, stream_seq_num); + frag_i && frag_i->tsn <= message->end; + frag_i = frag_i->next) { + + proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len, + frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)", + frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len); + offset += frag_i->len; + } + } + + return new_tvb; + } + + /* this is not the last fragment, + * so let the user know the frame where the reassembly is + */ + if (check_col(pinfo->cinfo, COL_INFO)) + col_append_str(pinfo->cinfo, COL_INFO, " (Message Fragment) "); + + proto_tree_add_uint(tree, hf_sctp_reassembled_in, tvb, 0, 0, message->reassembled_in->frame_num); + return NULL; + } + + /* this fragment has not been reassembled, yet + * check now if we can reassemble it + * at first look for the first and last tsn of the msg + */ + for (begin = msg->begins; + begin && begin->fragment->tsn > fragment->tsn; + begin = begin->next); + + /* in case begin still is null, set it to first (highest) begin + * maybe the message tsn restart at 0 in between + */ + if (!begin) begin = msg->begins; + + for (end = msg->ends; + end && end->fragment->tsn < fragment->tsn; + end = end->next); + + /* in case end still is null, set it to first (lowest) end + * maybe the message tsn restart at 0 in between + */ + if (!end) end = msg->ends; + + if (!begin || !end || !msg->fragments || + (begin->fragment->tsn > end->fragment->tsn && msg->fragments->tsn)) { + /* begin and end have not been collected, yet + * or there might be a tsn restart but the first fragment hasn't a tsn of 0 + * just mark as fragment + */ + + if (check_col(pinfo->cinfo, COL_INFO)) + col_append_str(pinfo->cinfo, COL_INFO, " (Message Fragment) "); + + return NULL; + } + + /* we found possible begin and end + * look for the first fragment and then try to get to the end + */ + first_frag = begin->fragment; + + /* while looking if all fragments are there + * we can calculate the overall length that + * we need in case of success + */ + len = first_frag->len; + + /* check if begin is past end + * this can happen if there has been a tsn restart + * or we just got the wrong begin and end + * so give it a try + */ + if (begin->fragment->tsn > end->fragment->tsn) { + for (last_frag = first_frag, frag_i = first_frag->next; + frag_i && frag_i->tsn == (last_frag->tsn + 1); + last_frag = frag_i, frag_i = frag_i->next) len += frag_i->len; + + /* check if we reached the last possible tsn + * if yes, restart and continue + */ + if ((last_frag->tsn + 1)) { + /* there are just fragments missing */ + if (check_col(pinfo->cinfo, COL_INFO)) + col_append_str(pinfo->cinfo, COL_INFO, " (Message Fragment) "); + + return NULL; + } + + /* we got all fragments until the last possible tsn + * and the first is 0 if we got here + */ + + len += msg->fragments->len; + for (last_frag = msg->fragments, frag_i = last_frag->next; + frag_i && frag_i->tsn < end->fragment->tsn && frag_i->tsn == (last_frag->tsn + 1); + last_frag = frag_i, frag_i = frag_i->next) len += frag_i->len; + + } else { + for (last_frag = first_frag, frag_i = first_frag->next; + frag_i && frag_i->tsn < end->fragment->tsn && frag_i->tsn == (last_frag->tsn + 1); + last_frag = frag_i, frag_i = frag_i->next) len += frag_i->len; + } + + if (!frag_i || frag_i != end->fragment || frag_i->tsn != (last_frag->tsn + 1)) { + /* we need more fragments. just mark as fragment */ + if (check_col(pinfo->cinfo, COL_INFO)) + col_append_str(pinfo->cinfo, COL_INFO, " (Message Fragment) "); + + return NULL; + } + + /* ok, this message is complete, we can reassemble it + * but at first don't forget to add the length of the last fragment + */ + len += frag_i->len; + + message = se_alloc (sizeof (sctp_complete_msg)); + message->begin = begin->fragment->tsn; + message->end = end->fragment->tsn; + message->reassembled_in = fragment; + message->len = len; + message->data = se_alloc(len); + + /* now copy all fragments */ + if (begin->fragment->tsn > end->fragment->tsn) { + /* a tsn restart has occured */ + for (frag_i = first_frag; + frag_i; + frag_i = frag_i->next) { + + if (frag_i->len && frag_i->data) memcpy(message->data + offset, frag_i->data, frag_i->len); + offset += frag_i->len; + + /* release fragment data */ + g_free(frag_i->data); + frag_i->data = NULL; + } + + for (frag_i = msg->fragments; + frag_i && frag_i->tsn <= end->fragment->tsn; + frag_i = frag_i->next) { + + if (frag_i->len && frag_i->data) memcpy(message->data + offset, frag_i->data, frag_i->len); + offset += frag_i->len; + + /* release fragment data */ + g_free(frag_i->data); + frag_i->data = NULL; + } + + } else { + for (frag_i = first_frag; + frag_i && frag_i->tsn <= end->fragment->tsn; + frag_i = frag_i->next) { + + if (frag_i->len && frag_i->data) memcpy(message->data + offset, frag_i->data, frag_i->len); + offset += frag_i->len; + + /* release fragment data */ + g_free(frag_i->data); + frag_i->data = NULL; + } + } + + /* save message */ + if (!msg->messages) { + msg->messages = message; + } else { + + for (last_message = msg->messages; + last_message->next; + last_message = last_message->next); + + last_message->next = message; + } + + /* remove begin and end from list */ + if (msg->begins == begin) { + msg->begins = begin->next; + } else { + for (beginend = msg->begins; + beginend && beginend->next != begin; + beginend = beginend->next); + if (beginend->next == begin) beginend->next = begin->next; + } + g_free(begin); + + if (msg->ends == end) { + msg->ends = end->next; + } else { + for (beginend = msg->ends; + beginend && beginend->next != end; + beginend = beginend->next); + if (beginend->next == end) beginend->next = end->next; + } + g_free(end); + + /* create data source */ + new_tvb = tvb_new_real_data(message->data, len, len); + tvb_set_child_real_data_tvbuff(tvb, new_tvb); + add_new_data_source(pinfo, new_tvb, "Reassembled SCTP Message"); + + /* display reassembly info */ + item = proto_tree_add_item(tree, hf_sctp_fragments, tvb, 0, -1, FALSE); + ptree = proto_item_add_subtree(item, ett_sctp_fragments); + proto_item_append_text(item, " (%u bytes, %u fragments): ", + message->len, message->end - message->begin + 1); + + if (message->begin > message->end) { + for (frag_i = find_fragment(message->begin, stream_id, stream_seq_num); + frag_i; + frag_i = frag_i->next) { + + proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len, + frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)", + frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len); + offset += frag_i->len; + } + + for (frag_i = msg->fragments; + frag_i && frag_i->tsn <= message->end; + frag_i = frag_i->next) { + + proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len, + frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)", + frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len); + offset += frag_i->len; + } + } else { + for (frag_i = find_fragment(message->begin, stream_id, stream_seq_num); + frag_i && frag_i->tsn <= message->end; + frag_i = frag_i->next) { + + proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len, + frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)", + frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len); + offset += frag_i->len; + } + } + + /* it's not fragmented anymore */ + pinfo->fragmented = FALSE; + + return new_tvb; +} + + +static gboolean +dissect_fragmented_payload(tvbuff_t *payload_tvb, packet_info *pinfo, proto_tree *tree, + proto_tree *chunk_tree, guint32 tsn, guint32 ppi, guint16 stream_id, + guint16 stream_seq_num, guint8 b_bit, guint8 e_bit) +{ + sctp_fragment* fragment; + tvbuff_t* new_tvb = NULL; + + /* add fragement to list of known fragments. returns NULL if segment is a duplicate */ + fragment = add_fragment(payload_tvb, pinfo, chunk_tree, tsn, stream_id, stream_seq_num, b_bit, e_bit); + + if (fragment) new_tvb = fragment_reassembly(payload_tvb, fragment, pinfo, chunk_tree, stream_id, stream_seq_num); + + /* pass reassembled data to next dissector, if possible */ + if (new_tvb) return dissect_payload(new_tvb, pinfo, tree, ppi); + + /* no reasemmbly done, do nothing */ + return TRUE; +} + static const true_false_string sctp_data_chunk_e_bit_value = { "Last segment", "Not the last segment" @@ -1591,6 +2196,8 @@ dissect_data_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo tvbuff_t *payload_tvb; proto_tree *flags_tree; guint8 e_bit, b_bit, u_bit; + guint16 stream_id, stream_seq_num = 0; + guint32 tsn; if (chunk_length <= DATA_CHUNK_HEADER_LENGTH) { proto_item_append_text(chunk_item, ", bogus chunk length %u < %u)", @@ -1608,6 +2215,13 @@ dissect_data_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo pinfo->ppid[number_of_ppid] = payload_proto_id; } + e_bit = tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET) & SCTP_DATA_CHUNK_E_BIT; + b_bit = tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET) & SCTP_DATA_CHUNK_B_BIT; + u_bit = tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET) & SCTP_DATA_CHUNK_U_BIT; + stream_id = tvb_get_ntohs(chunk_tvb, DATA_CHUNK_STREAM_ID_OFFSET); + stream_seq_num = tvb_get_ntohs(chunk_tvb, DATA_CHUNK_STREAM_SEQ_NUMBER_OFFSET); + tsn = tvb_get_ntohl(chunk_tvb, DATA_CHUNK_TSN_OFFSET); + if (chunk_tree) { proto_item_set_len(chunk_item, DATA_CHUNK_HEADER_LENGTH); flags_tree = proto_item_add_subtree(flags_item, ett_sctp_data_chunk_flags); @@ -1619,10 +2233,6 @@ dissect_data_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo proto_tree_add_item(chunk_tree, hf_data_chunk_stream_seq_number, chunk_tvb, DATA_CHUNK_STREAM_SEQ_NUMBER_OFFSET, DATA_CHUNK_STREAM_SEQ_NUMBER_LENGTH, NETWORK_BYTE_ORDER); proto_tree_add_item(chunk_tree, hf_data_chunk_payload_proto_id, chunk_tvb, DATA_CHUNK_PAYLOAD_PROTOCOL_ID_OFFSET, DATA_CHUNK_PAYLOAD_PROTOCOL_ID_LENGTH, NETWORK_BYTE_ORDER); - e_bit = tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET) & SCTP_DATA_CHUNK_E_BIT; - b_bit = tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET) & SCTP_DATA_CHUNK_B_BIT; - u_bit = tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET) & SCTP_DATA_CHUNK_U_BIT; - proto_item_append_text(chunk_item, "(%s, ", (u_bit) ? "unordered" : "ordered"); if (b_bit) { if (e_bit) @@ -1644,8 +2254,25 @@ dissect_data_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo chunk_length - DATA_CHUNK_HEADER_LENGTH, plurality(chunk_length - DATA_CHUNK_HEADER_LENGTH, "", "s")); } - payload_tvb = tvb_new_subset(chunk_tvb, DATA_CHUNK_PAYLOAD_OFFSET, chunk_length - DATA_CHUNK_HEADER_LENGTH, chunk_length - DATA_CHUNK_HEADER_LENGTH); - return dissect_payload(payload_tvb, pinfo, tree, payload_proto_id); + payload_tvb = tvb_new_subset(chunk_tvb, DATA_CHUNK_PAYLOAD_OFFSET, chunk_length - DATA_CHUNK_HEADER_LENGTH, chunk_length - DATA_CHUNK_HEADER_LENGTH); + + /* Is this a fragment? */ + if (b_bit && e_bit) { + /* No - just call the subdissector. */ + return dissect_payload(payload_tvb, pinfo, tree, payload_proto_id); + } else { + /* Yes. */ + pinfo->fragmented = TRUE; + + /* if reassembly off just mark as fragment for next dissector and proceed */ + if (!use_reassembly) return dissect_payload(payload_tvb, pinfo, tree, payload_proto_id); + + /* if unordered set stream_seq_num to 0 for easier handling */ + if (u_bit) stream_seq_num = 0; + + /* start reassembly */ + return dissect_fragmented_payload(payload_tvb, pinfo, tree, chunk_tree, tsn, payload_proto_id, stream_id, stream_seq_num, b_bit, e_bit); + } } #define INIT_CHUNK_INITIATE_TAG_LENGTH 4 @@ -2479,7 +3106,6 @@ dissect_sctp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) memset(&sctp_info, 0, sizeof(struct _sctp_info)); sctp_info.verification_tag = tvb_get_ntohl(tvb, VERIFICATION_TAG_OFFSET); - /* FIXME: Do we need to put this into _sctp_info? */ sctp_info.sport = pinfo->srcport; sctp_info.dport = pinfo->destport; SET_ADDRESS(&sctp_info.ip_src, pinfo->src.type, pinfo->src.len, pinfo->src.data); @@ -2594,6 +3220,11 @@ proto_register_sctp(void) { &hf_pktdrop_chunk_truncated_length, { "Truncated length", "sctp.pktdrop_truncated_length", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_pktdrop_chunk_reserved, { "Reserved", "sctp.pktdrop_reserved", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_pktdrop_chunk_data_field, { "Data field", "sctp.pktdrop_datafield", FT_BYTES, BASE_NONE, NULL, 0x0, "", HFILL } }, + + { &hf_sctp_fragment, { "SCTP Fragment", "sctp.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,NULL, HFILL }}, + { &hf_sctp_fragments, { "Reassembled SCTP Fragments", "sctp.fragments", FT_NONE, BASE_NONE, NULL, 0x0,NULL, HFILL }}, + { &hf_sctp_reassembled_in, { "Reassembled Message in frame", "sctp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_sctp_duplicate, { "Fragment already seen in frame", "sctp.duplicate", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL }}, }; /* Setup protocol subtree array */ @@ -2611,6 +3242,8 @@ proto_register_sctp(void) &ett_sctp_parameter_type, &ett_sctp_sack_chunk_gap_block, &ett_sctp_unrecognized_parameter_parameter, + &ett_sctp_fragments, + &ett_sctp_fragment }; static enum_val_t sctp_checksum_options[] = { @@ -2645,6 +3278,10 @@ proto_register_sctp(void) "Try heuristic sub-dissectors first", "Try to decode a packet using an heuristic sub-dissector before using a sub-dissector registered to a specific port or PPI", &try_heuristic_first); + prefs_register_bool_preference(sctp_module, "reassembly", + "Reassemble fragmented SCTP user messages", + "Whether fragmented SCTP user messages should be reassembled", + &use_reassembly); /* Required function calls to register the header fields and subtrees used */ proto_register_field_array(proto_sctp, hf, array_length(hf)); @@ -2654,6 +3291,8 @@ proto_register_sctp(void) sctp_port_dissector_table = register_dissector_table("sctp.port", "SCTP port", FT_UINT16, BASE_DEC); sctp_ppi_dissector_table = register_dissector_table("sctp.ppi", "SCTP payload protocol identifier", FT_UINT32, BASE_HEX); register_heur_dissector_list("sctp", &sctp_heur_subdissector_list); + + register_init_routine(frag_table_init); } void diff --git a/epan/dissectors/packet-sctp.h b/epan/dissectors/packet-sctp.h index 29927801a8..9cb08f1c8f 100644 --- a/epan/dissectors/packet-sctp.h +++ b/epan/dissectors/packet-sctp.h @@ -37,7 +37,6 @@ struct _sctp_info { gboolean crc32c_correct; gboolean checksum_zero; gboolean vtag_reflected; - /* FIXME: do we need the ports and addresses to be here? */ guint16 sport; guint16 dport; address ip_src; @@ -47,4 +46,35 @@ struct _sctp_info { tvbuff_t *tvb[MAXIMUM_NUMBER_OF_TVBS]; }; +typedef struct _sctp_fragment { + guint32 frame_num; + guint32 tsn; + guint32 len; + unsigned char *data; + struct _sctp_fragment *next; +} sctp_fragment; + +typedef struct _sctp_frag_be { + sctp_fragment* fragment; + struct _sctp_frag_be *next; +} sctp_frag_be; + +typedef struct _sctp_complete_msg { + guint32 begin; + guint32 end; + sctp_fragment* reassembled_in; + guint32 len; + unsigned char *data; + struct _sctp_complete_msg *next; +} sctp_complete_msg; + +typedef struct _sctp_frag_msg { + sctp_frag_be* begins; + sctp_frag_be* ends; + sctp_fragment* fragments; + sctp_complete_msg* messages; + struct _sctp_frag_msg* next; +} sctp_frag_msg; + + #endif |