aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn A. Thacker <johnthacker@gmail.com>2016-07-07 18:24:43 -0400
committerAnders Broman <a.broman58@gmail.com>2016-11-03 05:08:07 +0000
commitda7354a6368fd72dcb58410d8572c061884045fc (patch)
tree66092cc2d47122165f512c62e17081886ad5ed23
parent8ea8cb645326ddb111afaa2bb23c978ddec05451 (diff)
Fragmentation reassembly as in PPP MP (RFC 1990/2686)
Add support for defragmentation of fragments that use the defragmentation scheme of PPP MP (RFC 1990). Instead of getting "sequence_number, fragment_number, last" as in other protocols, PPP MP provides a single sequence number that is effectively "seqnum + fragnum", though it provides flags for both the first and last fragment of a reassembly. See Appendix A of RFC 4623 (PWE3 Fragmentation and Reassembly) for a list of protocols that use this style, including PPP MP (RFC 1990), PWE3 MPLS (RFC 4385), L2TPv2 (RFC 2661), L2TPv3 (RFC 3931), ATM, and Frame Relay. Also add support for the Multi-class Extension to Multilink PPP (RFC 2686), which uses some of the previously reserved bits as classes that distinguish otherwise identical sequence numbers. Bug: 12548 Change-Id: Ic2ce3c50e61ab2eb50e4d92fd353ca4d2a48fe18 Reviewed-on: https://code.wireshark.org/review/16327 Reviewed-by: Michael Mann <mmann78@netscape.net> Petri-Dish: Michael Mann <mmann78@netscape.net> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Anders Broman <a.broman58@gmail.com>
-rw-r--r--debian/libwireshark0.symbols2
-rw-r--r--epan/dissectors/packet-ppp.c202
-rw-r--r--epan/reassemble.c361
-rw-r--r--epan/reassemble.h36
-rw-r--r--epan/reassemble_test.c28
5 files changed, 592 insertions, 37 deletions
diff --git a/debian/libwireshark0.symbols b/debian/libwireshark0.symbols
index ad3f5c53fb..66848e3f8e 100644
--- a/debian/libwireshark0.symbols
+++ b/debian/libwireshark0.symbols
@@ -597,6 +597,8 @@ libwireshark.so.0 libwireshark0 #MINVER#
fragment_add_seq_check@Base 1.9.1
fragment_add_seq_next@Base 1.9.1
fragment_add_seq_offset@Base 1.9.1
+ fragment_add_seq_single@Base 2.3.0
+ fragment_add_seq_single_aging@Base 2.3.0
fragment_delete@Base 1.9.1
fragment_end_seq_next@Base 1.9.1
fragment_get@Base 1.9.1
diff --git a/epan/dissectors/packet-ppp.c b/epan/dissectors/packet-ppp.c
index ef421d3dbb..49270311e3 100644
--- a/epan/dissectors/packet-ppp.c
+++ b/epan/dissectors/packet-ppp.c
@@ -44,6 +44,7 @@
#include <epan/ipproto.h>
#include <epan/addr_resolv.h>
#include <epan/oui.h>
+#include <epan/reassemble.h>
#include "packet-sll.h"
#include "packet-juniper.h"
#include "packet-sflow.h"
@@ -356,17 +357,57 @@ static gint ett_pppmux_subframe_hdr = -1;
static gint ett_pppmux_subframe_flags = -1;
static gint ett_pppmux_subframe_info = -1;
+static reassembly_table mp_reassembly_table;
+
static int proto_mp = -1;
static int hf_mp_frag = -1;
+static int hf_mp_frag_short = -1;
static int hf_mp_frag_first = -1;
static int hf_mp_frag_last = -1;
-static int hf_mp_short_sequence_num_reserved = -1;
static int hf_mp_sequence_num = -1;
+static int hf_mp_sequence_num_cls = -1;
static int hf_mp_sequence_num_reserved = -1;
static int hf_mp_short_sequence_num = -1;
+static int hf_mp_short_sequence_num_cls = -1;
+static int hf_mp_payload = -1;
+static gint hf_mp_fragments = -1;
+static gint hf_mp_fragment = -1;
+static gint hf_mp_fragment_overlap = -1;
+static gint hf_mp_fragment_overlap_conflicts = -1;
+static gint hf_mp_fragment_multiple_tails = -1;
+static gint hf_mp_fragment_too_long_fragment = -1;
+static gint hf_mp_fragment_error = -1;
+static gint hf_mp_fragment_count = -1;
+static gint hf_mp_reassembled_in = -1;
+static gint hf_mp_reassembled_length = -1;
static int ett_mp = -1;
static int ett_mp_flags = -1;
+static gint ett_mp_fragment = -1;
+static gint ett_mp_fragments = -1;
+
+static const fragment_items mp_frag_items = {
+ /* Fragment subtrees */
+ &ett_mp_fragment,
+ &ett_mp_fragments,
+ /* Fragment fields */
+ &hf_mp_fragments,
+ &hf_mp_fragment,
+ &hf_mp_fragment_overlap,
+ &hf_mp_fragment_overlap_conflicts,
+ &hf_mp_fragment_multiple_tails,
+ &hf_mp_fragment_too_long_fragment,
+ &hf_mp_fragment_error,
+ &hf_mp_fragment_count,
+ /* Reassembled in field */
+ &hf_mp_reassembled_in,
+ /* Reassembled length field */
+ &hf_mp_reassembled_length,
+ /* Reassembled data field */
+ NULL,
+ /* Tag */
+ "Message fragments"
+};
static int proto_mplscp = -1;
static gint ett_mplscp = -1;
@@ -5201,67 +5242,113 @@ dissect_cdpcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U
return tvb_captured_length(tvb);
}
+/* PPP Multilink Protcol (RFC 1990) and
+ * the Multiclass Extension to Multi-Link PPP (RFC 2686)
+ */
static gboolean mp_short_seqno = FALSE; /* Default to long sequence numbers */
+static guint mp_max_fragments = 6;
+/* Maximum fragments to try to reassemble. This affects performance and
+ * memory use significantly. */
+static guint mp_fragment_aging = 4000; /* Short sequence numbers only 12 bit */
-#define MP_FRAG_MASK 0xC0
+#define MP_FRAG_MASK 0xFC
+#define MP_FRAG_MASK_SHORT 0xF0
#define MP_FRAG_FIRST 0x80
#define MP_FRAG_LAST 0x40
-#define MP_FRAG_RESERVED 0x3f
-#define MP_FRAG_RESERVED_SHORT 0x30
-
-static const value_string mp_frag_vals[] = {
- { MP_FRAG_FIRST, "First" },
- { MP_FRAG_LAST, "Last" },
- { MP_FRAG_FIRST|MP_FRAG_LAST, "First, Last" },
- { 0, NULL }
-};
+#define MP_FRAG_CLS 0x3C
+#define MP_FRAG_RESERVED 0x03
+#define MP_FRAG_CLS_SHORT 0x30
/* According to RFC 1990, the length the MP header isn't indicated anywhere
in the header itself. It starts out at four bytes and can be
negotiated down to two using LCP. We currently have a preference
to select short headers. - gcc & gh
*/
+
static int
dissect_mp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
proto_tree *mp_tree;
proto_item *ti;
+ gboolean save_fragmented;
+ guint8 flags;
+ guint32 cls; /* 32 bit since we shift it left and XOR with seqnum */
+ guint32 seqnum;
gint hdrlen;
+ fragment_head *frag_mp;
tvbuff_t *next_tvb;
static const int * mp_flags[] = {
&hf_mp_frag_first,
&hf_mp_frag_last,
+ &hf_mp_sequence_num_cls,
&hf_mp_sequence_num_reserved,
NULL
};
static const int * mp_short_flags[] = {
&hf_mp_frag_first,
&hf_mp_frag_last,
- &hf_mp_short_sequence_num_reserved,
+ &hf_mp_short_sequence_num_cls,
NULL
};
col_set_str(pinfo->cinfo, COL_PROTOCOL, "PPP MP");
col_set_str(pinfo->cinfo, COL_INFO, "PPP Multilink");
+ save_fragmented = pinfo->fragmented;
+ flags = tvb_get_guint8(tvb, 0);
+
ti = proto_tree_add_item(tree, proto_mp, tvb, 0,
mp_short_seqno ? 2 : 4, ENC_NA);
mp_tree = proto_item_add_subtree(ti, ett_mp);
if (mp_short_seqno) {
- proto_tree_add_bitmask(mp_tree, tvb, 0, hf_mp_frag, ett_mp_flags, mp_short_flags, ENC_NA);
- proto_tree_add_item(mp_tree, hf_mp_short_sequence_num, tvb, 0, 2, ENC_BIG_ENDIAN);
+ proto_tree_add_bitmask(mp_tree, tvb, 0, hf_mp_frag_short, ett_mp_flags, mp_short_flags, ENC_NA);
+ proto_tree_add_item_ret_uint(mp_tree, hf_mp_short_sequence_num, tvb, 0, 2, ENC_BIG_ENDIAN, &seqnum);
} else {
proto_tree_add_bitmask(mp_tree, tvb, 0, hf_mp_frag, ett_mp_flags, mp_flags, ENC_NA);
- proto_tree_add_item(mp_tree, hf_mp_sequence_num, tvb, 1, 3,
- ENC_BIG_ENDIAN);
+ proto_tree_add_item_ret_uint(mp_tree, hf_mp_sequence_num, tvb, 1, 3, ENC_BIG_ENDIAN, &seqnum);
}
hdrlen = mp_short_seqno ? 2 : 4;
+ if (mp_short_seqno) {
+ cls = (flags & MP_FRAG_CLS_SHORT) >> 4;
+ } else {
+ cls = (flags & MP_FRAG_CLS) >> 2;
+ }
if (tvb_reported_length_remaining(tvb, hdrlen) > 0) {
- next_tvb = tvb_new_subset_remaining(tvb, hdrlen);
- dissect_ppp(next_tvb, pinfo, tree, NULL);
+ pinfo->fragmented = TRUE;
+ frag_mp = NULL;
+ if (!pinfo->fd->flags.visited) {
+ frag_mp = fragment_add_seq_single_aging(&mp_reassembly_table,
+ tvb, hdrlen, pinfo, seqnum ^ (cls << 24), NULL,
+ tvb_captured_length_remaining(tvb, hdrlen),
+ flags & MP_FRAG_FIRST, flags & MP_FRAG_LAST,
+ mp_max_fragments, mp_fragment_aging);
+ } else {
+ frag_mp = fragment_get_reassembled_id(&mp_reassembly_table, pinfo, seqnum ^ (cls << 24));
+ }
+ next_tvb = process_reassembled_data(tvb, hdrlen, pinfo,
+ "Reassembled PPP MP payload", frag_mp, &mp_frag_items,
+ NULL, mp_tree);
+
+ if (frag_mp) {
+ if (pinfo->num == frag_mp->reassembled_in) {
+ dissect_ppp(next_tvb, pinfo, tree, NULL);
+ } else {
+ col_append_fstr(pinfo->cinfo, COL_INFO,
+ " (PPP MP reassembled in packet %u)",
+ frag_mp->reassembled_in);
+ proto_tree_add_item(mp_tree, hf_mp_payload, tvb, hdrlen, -1, ENC_NA);
+ }
+ } else {
+ col_append_fstr(pinfo->cinfo, COL_INFO,
+ " (PPP MP Unreassembled fragment %u)",
+ seqnum);
+ proto_tree_add_item(mp_tree, hf_mp_payload, tvb, hdrlen, -1, ENC_NA);
+ }
}
+
+ pinfo->fragmented = save_fragmented;
return tvb_captured_length(tvb);
}
@@ -5974,13 +6061,29 @@ proto_reg_handoff_ppp(void)
dissector_add_uint("l2tp.pw_type", L2TPv3_PROTOCOL_PPP, ppp_hdlc_handle);
}
+static void
+mp_reassemble_init(void)
+{
+ reassembly_table_init(&mp_reassembly_table,
+ &addresses_reassembly_table_functions);
+}
+
+static void
+mp_reassemble_cleanup(void)
+{
+ reassembly_table_destroy(&mp_reassembly_table);
+}
+
void
proto_register_mp(void)
{
static hf_register_info hf[] = {
{ &hf_mp_frag,
{ "Fragment", "mp.frag", FT_UINT8, BASE_HEX,
- VALS(mp_frag_vals), MP_FRAG_MASK, NULL, HFILL }},
+ NULL, MP_FRAG_MASK, NULL, HFILL }},
+ { &hf_mp_frag_short,
+ { "Fragment", "mp.frag", FT_UINT8, BASE_HEX,
+ NULL, MP_FRAG_MASK_SHORT, NULL, HFILL }},
{ &hf_mp_frag_first,
{ "First fragment", "mp.first", FT_BOOLEAN, 8,
TFS(&tfs_yes_no), MP_FRAG_FIRST, NULL, HFILL }},
@@ -5990,25 +6093,70 @@ proto_register_mp(void)
{ &hf_mp_sequence_num,
{ "Sequence number", "mp.seq", FT_UINT24, BASE_DEC,
NULL, 0x0, NULL, HFILL }},
+ { &hf_mp_sequence_num_cls,
+ { "Class", "mp.sequence_num_cls", FT_UINT8, BASE_DEC,
+ NULL, MP_FRAG_CLS, NULL, HFILL }},
{ &hf_mp_sequence_num_reserved,
{ "Reserved", "mp.sequence_num_reserved", FT_BOOLEAN, 8,
NULL, MP_FRAG_RESERVED, NULL, HFILL }},
{ &hf_mp_short_sequence_num,
{ "Short Sequence number", "mp.sseq", FT_UINT16, BASE_DEC,
NULL, 0x0FFF, NULL, HFILL }},
- { &hf_mp_short_sequence_num_reserved,
- { "Reserved", "mp.short_sequence_num_reserved", FT_BOOLEAN, 8,
- NULL, MP_FRAG_RESERVED_SHORT, NULL, HFILL }},
+ { &hf_mp_short_sequence_num_cls,
+ { "Class", "mp.short_sequence_num_cls", FT_UINT8, BASE_DEC,
+ NULL, MP_FRAG_CLS_SHORT, NULL, HFILL }},
+ { &hf_mp_payload,
+ {"Payload", "mp.payload", FT_BYTES, BASE_NONE,
+ NULL, 0x00, NULL, HFILL }},
+ { &hf_mp_fragments,
+ {"Message fragments", "mp.fragments", FT_NONE, BASE_NONE,
+ NULL, 0x00, NULL, HFILL }},
+ { &hf_mp_fragment,
+ {"Message fragment", "mp.fragment", FT_FRAMENUM, BASE_NONE,
+ NULL, 0x00, NULL, HFILL }},
+ { &hf_mp_fragment_overlap,
+ {"Message fragment overlap", "mp.fragment.overlap",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x00, NULL, HFILL }},
+ { &hf_mp_fragment_overlap_conflicts,
+ {"Message fragment overlapping with conflicting data", "mp.fragment.overlap.conflicts",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x00, NULL, HFILL }},
+ { &hf_mp_fragment_multiple_tails,
+ {"Message has multiple tail fragments", "mp.fragment.multiple_tails",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x00, NULL, HFILL }},
+ { &hf_mp_fragment_too_long_fragment,
+ {"Message fragment too long", "mp.fragment.too_long_fragment",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x00, NULL, HFILL }},
+ { &hf_mp_fragment_error,
+ {"Message defragmentation error", "mp.fragment.error",
+ FT_FRAMENUM, BASE_NONE,
+ NULL, 0x00, NULL, HFILL }},
+ { &hf_mp_fragment_count,
+ {"Message fragment count", "mp.fragment.count", FT_UINT32, BASE_DEC,
+ NULL, 0x00, NULL, HFILL }},
+ { &hf_mp_reassembled_in,
+ {"Reassembled in", "mp.reassembled.in", FT_FRAMENUM, BASE_NONE,
+ NULL, 0x00, NULL, HFILL }},
+ { &hf_mp_reassembled_length,
+ {"Reassembled length", "mp.reassembled.length", FT_UINT32, BASE_DEC,
+ NULL, 0x00, NULL, HFILL }}
};
static gint *ett[] = {
&ett_mp,
- &ett_mp_flags
+ &ett_mp_flags,
+ &ett_mp_fragment,
+ &ett_mp_fragments
};
module_t *mp_module;
proto_mp = proto_register_protocol("PPP Multilink Protocol", "PPP MP",
"mp");
+ register_init_routine(&mp_reassemble_init);
+ register_cleanup_routine(&mp_reassemble_cleanup);
proto_register_field_array(proto_mp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
@@ -6019,6 +6167,14 @@ proto_register_mp(void)
"Short sequence numbers",
"Whether PPP Multilink frames use 12-bit sequence numbers",
&mp_short_seqno);
+ prefs_register_uint_preference(mp_module, "max_fragments",
+ "Maximum fragments",
+ "Maximum number of PPP Multilink fragments to try to reassemble into one frame",
+ 10, &mp_max_fragments);
+ prefs_register_uint_preference(mp_module, "fragment_aging",
+ "Max unreassembled fragment age",
+ "Age off unreassmbled fragments after this many packets",
+ 10, &mp_fragment_aging);
}
void
diff --git a/epan/reassemble.c b/epan/reassemble.c
index 399f8ad686..e18da27431 100644
--- a/epan/reassemble.c
+++ b/epan/reassemble.c
@@ -828,6 +828,44 @@ fragment_reassembled(reassembly_table *table, fragment_head *fd_head,
fd_head->reas_in_layer_num = pinfo->curr_layer_num;
}
+/*
+ * This function is a variant of the above for the single sequence
+ * case, using id+offset (i.e., the original sequence number) for the id
+ * in the key.
+ */
+static void
+fragment_reassembled_single(reassembly_table *table, fragment_head *fd_head,
+ const packet_info *pinfo, const guint32 id)
+{
+ reassembled_key *new_key;
+ fragment_item *fd;
+
+ if (fd_head->next == NULL) {
+ /*
+ * This was not fragmented, so there's no fragment
+ * table; just hash it using the current frame number.
+ */
+ new_key = g_slice_new(reassembled_key);
+ new_key->frame = pinfo->num;
+ new_key->id = id;
+ g_hash_table_insert(table->reassembled_table, new_key, fd_head);
+ } else {
+ /*
+ * Hash it with the frame numbers for all the frames.
+ */
+ for (fd = fd_head->next; fd != NULL; fd = fd->next){
+ new_key = g_slice_new(reassembled_key);
+ new_key->frame = fd->frame;
+ new_key->id = id + fd->offset;
+ g_hash_table_insert(table->reassembled_table, new_key,
+ fd_head);
+ }
+ }
+ fd_head->flags |= FD_DEFRAGMENTED;
+ fd_head->reassembled_in = pinfo->num;
+ fd_head->reas_in_layer_num = pinfo->curr_layer_num;
+}
+
static void
LINK_FRAG(fragment_head *fd_head,fragment_item *fd)
{
@@ -842,6 +880,22 @@ LINK_FRAG(fragment_head *fd_head,fragment_item *fd)
fd_i->next=fd;
}
+static void
+MERGE_FRAG(fragment_head *fd_head, fragment_item *fd)
+{
+ fragment_item *fd_i, *tmp;
+
+ if (fd == NULL) return;
+
+ for(fd_i = fd_head; fd_i->next; fd_i=fd_i->next) {
+ if (fd->offset < fd_i->next->offset) {
+ tmp = fd_i->next;
+ fd_i->next = fd;
+ fd = tmp;
+ }
+ }
+ fd_i->next = fd;
+}
/*
* This function adds a new fragment to the fragment hash table.
* If this is the first fragment seen for this datagram, a new entry
@@ -1627,6 +1681,11 @@ fragment_add_seq_work(fragment_head *fd_head, tvbuff_t *tvb, const int offset,
fd->tvb_data = NULL;
fd->error = NULL;
+ /* fd_head->frame is the maximum of the frame numbers of all the
+ * fragments added to the reassembly. */
+ if (fd->frame > fd_head->frame)
+ fd_head->frame = fd->frame;
+
if (!more_frags) {
/*
* This is the tail fragment in the sequence.
@@ -2043,6 +2102,308 @@ fragment_add_seq_next(reassembly_table *table, tvbuff_t *tvb, const int offset,
REASSEMBLE_FLAGS_NO_FRAG_NUMBER);
}
+static void
+fragment_add_seq_single_move(reassembly_table *table, const packet_info *pinfo,
+ const guint32 id, const void *data,
+ const guint32 offset)
+{
+ fragment_head *fh, *new_fh;
+ fragment_item *fd, *prev_fd;
+ tvbuff_t *old_tvb_data;
+ if (offset == 0) {
+ return;
+ }
+ fh = lookup_fd_head(table, pinfo, id, data, NULL);
+ if (fh == NULL) {
+ /* Shouldn't be called this way.
+ * Probably wouldn't hurt to just create fh in this case. */
+ g_assert_not_reached();
+ return;
+ }
+ if (fh->flags & FD_DATALEN_SET && fh->datalen <= offset) {
+ /* Don't take from past the end. <= because we don't
+ * want to take a First fragment from the next one
+ * either */
+ return;
+ }
+ new_fh = lookup_fd_head(table, pinfo, id+offset, data, NULL);
+ if (new_fh != NULL) {
+ /* Attach to the end of the sorted list. */
+ for(prev_fd = fh; prev_fd->next != NULL; prev_fd=prev_fd->next) {}
+ /* Don't take a reassembly starting with a First fragment. */
+ fd = new_fh->next;
+ if (fd && fd->offset != 0) {
+ prev_fd->next = fd;
+ for (; fd; fd=fd->next) {
+ fd->offset += offset;
+ if (fh->frame < fd->frame) {
+ fh->frame = fd->frame;
+ }
+ }
+ /* If previously found a Last fragment,
+ * transfer that info to the new one. */
+ if (new_fh->flags & FD_DATALEN_SET) {
+ fh->flags |= FD_DATALEN_SET;
+ fh->datalen = new_fh->datalen + offset;
+ }
+ /* Now remove and delete */
+ new_fh->next = NULL;
+ old_tvb_data = fragment_delete(table, pinfo, id+offset, data);
+ if (old_tvb_data)
+ tvb_free(old_tvb_data);
+ }
+ }
+}
+
+static fragment_head *
+fragment_add_seq_single_work(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo,
+ const guint32 id, const void* data,
+ const guint32 frag_data_len,
+ const gboolean first, const gboolean last,
+ const guint32 max_frags, const guint32 max_age,
+ const guint32 flags)
+{
+ reassembled_key reass_key;
+ tvbuff_t *old_tvb_data;
+ gpointer orig_key;
+ fragment_head *fh, *new_fh;
+ fragment_item *fd, *prev_fd;
+ guint32 frag_number, tmp_offset;
+ /* Have we already seen this frame?
+ * If so, look for it in the table of reassembled packets.
+ * Note here we store in the reassembly table by the single sequence
+ * number rather than the sequence number of the First fragment. */
+ if (pinfo->fd->flags.visited) {
+ reass_key.frame = pinfo->num;
+ reass_key.id = id;
+ fh = (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_key);
+ return fh;
+ }
+ /* First let's figure out where we want to add our new fragment */
+ fh = NULL;
+ if (first) {
+ frag_number = 0;
+ fh = lookup_fd_head(table, pinfo, id-frag_number, data, NULL);
+ if ((flags & REASSEMBLE_FLAGS_AGING) &&
+ fh && ((fh->frame + max_age) < pinfo->num)) {
+ old_tvb_data = fragment_delete(table, pinfo, id-frag_number, data);
+ if (old_tvb_data)
+ tvb_free(old_tvb_data);
+ fh = NULL;
+ }
+ if (fh == NULL) {
+ /* Not found. Create list-head. */
+ fh = new_head(FD_BLOCKSEQUENCE);
+ insert_fd_head(table, fh, pinfo, id-frag_number, data);
+ }
+ /* As this is the first fragment, we might have added segments
+ * for this reassembly to the previous one in-progress. */
+ fd = NULL;
+ for (frag_number=1; frag_number < max_frags; frag_number++) {
+ new_fh = lookup_fd_head(table, pinfo, id-frag_number, data, NULL);
+ if (new_fh != NULL) {
+ prev_fd = new_fh;
+ new_fh->frame = 0;
+ for (fd=new_fh->next; fd && fd->offset < frag_number; fd=fd->next) {
+ prev_fd = fd;
+ if (new_fh->frame < fd->frame) {
+ new_fh->frame = fd->frame;
+ }
+ }
+ prev_fd->next = NULL;
+ if (new_fh->next == NULL) {
+ old_tvb_data = fragment_delete(table, pinfo, id-frag_number, data);
+ if (old_tvb_data)
+ tvb_free(old_tvb_data);
+ }
+ break;
+ }
+ }
+ if (fd != NULL) {
+ tmp_offset = 0;
+ for (prev_fd = fd; prev_fd; prev_fd = prev_fd->next) {
+ prev_fd->offset -= frag_number;
+ tmp_offset = prev_fd->offset;
+ if (fh->frame < prev_fd->frame) {
+ fh->frame = prev_fd->frame;
+ }
+ }
+ MERGE_FRAG(fh, fd);
+ /* If we've moved a Last packet, change the datalen.
+ * Second part of this test should be unnecessary. */
+ if (new_fh->flags & FD_DATALEN_SET &&
+ new_fh->datalen >= frag_number) {
+ fh->flags |= FD_DATALEN_SET;
+ fh->datalen = new_fh->datalen - frag_number;
+ new_fh->flags &= ~FD_DATALEN_SET;
+ new_fh->datalen = 0;
+ } else {
+ /* Look forward and take off the next (this is
+ * necessary in some edge cases where max_frags
+ * prevented some fragments from going on the
+ * previous First, but they can go on this one. */
+ fragment_add_seq_single_move(table, pinfo, id,
+ data, tmp_offset);
+ }
+ }
+ frag_number = 0; /* For the rest of the function */
+ } else {
+ for (frag_number=1; frag_number < max_frags; frag_number++) {
+ fh = lookup_fd_head(table, pinfo, id-frag_number, data, NULL);
+ if ((flags & REASSEMBLE_FLAGS_AGING) &&
+ fh && ((fh->frame + max_age) < pinfo->num)) {
+ old_tvb_data = fragment_delete(table, pinfo, id-frag_number, data);
+ if (old_tvb_data)
+ tvb_free(old_tvb_data);
+ fh = NULL;
+ }
+ if (fh != NULL) {
+ if (fh->flags & FD_DATALEN_SET &&
+ fh->datalen < frag_number) {
+ /* This fragment is after the Last
+ * fragment, so must go after here. */
+ fh = NULL;
+ }
+ break;
+ }
+ }
+ if (fh == NULL) { /* Didn't find location, use default */
+ frag_number = 1;
+ /* Already looked for frag_number 1, so just create */
+ fh = new_head(FD_BLOCKSEQUENCE);
+ insert_fd_head(table, fh, pinfo, id-frag_number, data);
+ }
+ }
+ if (last) {
+ /* Look for fragments past the end set by this Last fragment. */
+ prev_fd = fh;
+ for (fd=fh->next; fd && fd->offset <= frag_number; fd=fd->next) {
+ prev_fd = fd;
+ }
+ /* fd is now all fragments offset > frag_number (the Last).
+ * It shouldn't have a fragment with offset frag_number+1,
+ * as that would be a First fragment not marked as such.
+ * However, this can happen if we had unreassembled fragments
+ * (missing, or at the start of the capture) and we've also
+ * looped around on the sequence numbers. It can also happen
+ * if bit errors mess up Last or First. */
+ if (fd != NULL) {
+ prev_fd->next = NULL;
+ fh->frame = 0;
+ for (prev_fd=fh->next; prev_fd; prev_fd=prev_fd->next) {
+ if (fh->frame < prev_fd->frame) {
+ fh->frame = prev_fd->frame;
+ }
+ }
+ while (fd && fd->offset == frag_number+1) {
+ /* Definitely have bad data here. Best to
+ * delete these and leave unreassembled. */
+ fragment_item *tmp_fd;
+ tmp_fd=fd->next;
+
+ if (fd->tvb_data && !(fd->flags & FD_SUBSET_TVB))
+ tvb_free(fd->tvb_data);
+ g_slice_free(fragment_item, fd);
+ fd=tmp_fd;
+ }
+ }
+ if (fd != NULL) {
+ /* Move these onto the next frame. */
+ new_fh = lookup_fd_head(table, pinfo, id+1, data, NULL);
+ if (new_fh==NULL) {
+ /* Not found. Create list-head. */
+ new_fh = new_head(FD_BLOCKSEQUENCE);
+ insert_fd_head(table, new_fh, pinfo, id+1, data);
+ }
+ tmp_offset = 0;
+ for (prev_fd = fd; prev_fd; prev_fd = prev_fd->next) {
+ prev_fd->offset -= (frag_number+1);
+ tmp_offset = prev_fd->offset;
+ if (new_fh->frame < fd->frame) {
+ new_fh->frame = fd->frame;
+ }
+ }
+ MERGE_FRAG(new_fh, fd);
+ /* If we previously found a different Last fragment,
+ * transfer that information to the new reassembly. */
+ if (fh->flags & FD_DATALEN_SET &&
+ fh->datalen > frag_number) {
+ new_fh->flags |= FD_DATALEN_SET;
+ new_fh->datalen = fh->datalen - (frag_number+1);
+ fh->flags &= ~FD_DATALEN_SET;
+ fh->datalen = 0;
+ } else {
+ /* Look forward and take off the next (this is
+ * necessary in some edge cases where max_frags
+ * prevented some fragments from going on the
+ * previous First, but they can go on this one. */
+ fragment_add_seq_single_move(table, pinfo, id+1,
+ data, tmp_offset);
+ }
+ }
+ } else {
+ fragment_add_seq_single_move(table, pinfo, id-frag_number, data,
+ frag_number+1);
+ }
+ /* Having cleaned up everything, finally ready to add our new
+ * fragment. Note that only this will ever complete a reassembly. */
+ fh = fragment_add_seq_common(table, tvb, offset, pinfo,
+ id-frag_number, data,
+ frag_number, frag_data_len,
+ !last, 0, &orig_key);
+ if (fh) {
+ /*
+ * Reassembly is complete.
+ *
+ * If this is in the table of in-progress reassemblies,
+ * remove it from that table. (It could be that this
+ * was the first and last fragment, so that no
+ * reassembly was done.)
+ */
+ if (orig_key != NULL)
+ fragment_unhash(table, orig_key);
+
+ /*
+ * Add this item to the table of reassembled packets.
+ */
+ fragment_reassembled_single(table, fh, pinfo, id-frag_number);
+ return fh;
+ } else {
+ /*
+ * Reassembly isn't complete.
+ */
+ return NULL;
+ }
+}
+
+fragment_head *
+fragment_add_seq_single(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo,
+ const guint32 id, const void* data,
+ const guint32 frag_data_len,
+ const gboolean first, const gboolean last,
+ const guint32 max_frags)
+{
+ return fragment_add_seq_single_work(table, tvb, offset, pinfo,
+ id, data, frag_data_len,
+ first, last, max_frags, 0, 0);
+}
+
+fragment_head *
+fragment_add_seq_single_aging(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo,
+ const guint32 id, const void* data,
+ const guint32 frag_data_len,
+ const gboolean first, const gboolean last,
+ const guint32 max_frags, const guint32 max_age)
+{
+ return fragment_add_seq_single_work(table, tvb, offset, pinfo,
+ id, data, frag_data_len,
+ first, last, max_frags, max_age,
+ REASSEMBLE_FLAGS_AGING);
+}
+
void
fragment_start_seq_check(reassembly_table *table, const packet_info *pinfo,
const guint32 id, const void *data,
diff --git a/epan/reassemble.h b/epan/reassemble.h
index d70c526264..a99cff7bd9 100644
--- a/epan/reassemble.h
+++ b/epan/reassemble.h
@@ -111,6 +111,13 @@ typedef struct _fragment_item {
#define REASSEMBLE_FLAGS_802_11_HACK 0x0002
/*
+ * Flags for fragment_add_seq_single_*
+ */
+
+/* we want to age off old packets */
+#define REASSEMBLE_FLAGS_AGING 0x0001
+
+/*
* Generates a fragment identifier based on the given parameters. "data" is an
* opaque type whose interpretation is up to the caller of fragment_add*
* functions and the fragment key function (possibly NULL if you do not care).
@@ -295,6 +302,35 @@ fragment_add_seq_next(reassembly_table *table, tvbuff_t *tvb, const int offset,
const gboolean more_frags);
/*
+ * Like fragment_add_seq_check, but for protocols like PPP MP with a single
+ * sequence number that increments for each fragment, thus acting like the sum
+ * of the PDU sequence number and explicit fragment number in other protocols.
+ * See Appendix A of RFC 4623 (PWE3 Fragmentation and Reassembly) for a list
+ * of protocols that use this style, including PPP MP (RFC 1990), PWE3 MPLS
+ * (RFC 4385), L2TPv2 (RFC 2661), L2TPv3 (RFC 3931), ATM, and Frame Relay.
+ * It is guaranteed to reassemble a packet split up to "max_frags" in size,
+ * but may manage to reassemble more in certain cases.
+ */
+WS_DLL_PUBLIC fragment_head *
+fragment_add_seq_single(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo, const guint32 id,
+ const void* data, const guint32 frag_data_len,
+ const gboolean first, const gboolean last,
+ const guint32 max_frags);
+
+/*
+ * A variation on the above that ages off fragments that have not been
+ * reassembled. Useful if the sequence number loops to deal with leftover
+ * fragments from the beginning of the capture or missing fragments.
+ */
+WS_DLL_PUBLIC fragment_head *
+fragment_add_seq_single_aging(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo, const guint32 id,
+ const void* data, const guint32 frag_data_len,
+ const gboolean first, const gboolean last,
+ const guint32 max_frags, const guint32 max_age);
+
+/*
* Start a reassembly, expecting "tot_len" as the number of given fragments (not
* the number of bytes). Data can be added later using fragment_add_seq_check.
*/
diff --git a/epan/reassemble_test.c b/epan/reassemble_test.c
index 73df4339af..289e42da30 100644
--- a/epan/reassemble_test.c
+++ b/epan/reassemble_test.c
@@ -252,7 +252,7 @@ test_simple_fragment_add_seq(void)
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(4,fd_head->frame); /* max frame number of fragment in assembly */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(170,fd_head->len); /* the length of data we have */
ASSERT_EQ(2,fd_head->datalen); /* seqno of the last fragment we have */
@@ -352,7 +352,7 @@ test_fragment_add_seq_partial_reassembly(void)
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(1,fd_head->frame); /* max frame in reassembly */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(50,fd_head->len); /* the length of data we have */
ASSERT_EQ(0,fd_head->datalen); /* seqno of the last fragment we have */
@@ -389,7 +389,7 @@ test_fragment_add_seq_partial_reassembly(void)
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(2,fd_head->frame); /* max frame in reassembly */
ASSERT_EQ(0,fd_head->offset); /* unused */
/* ASSERT_EQ(50,fd_head->len); the length of data we have */
ASSERT_EQ(0,fd_head->datalen); /* seqno of the last fragment we have */
@@ -424,7 +424,7 @@ test_fragment_add_seq_partial_reassembly(void)
ASSERT_EQ_POINTER(NULL,fd_head);
fd_head=fragment_get(&test_reassembly_table, &pinfo, 12, NULL);
ASSERT_NE_POINTER(NULL,fd_head);
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(3,fd_head->frame); /* max frame we have */
ASSERT_EQ(0,fd_head->offset); /* unused */
/* ASSERT_EQ(50,fd_head->len); the length of data we have */
ASSERT_EQ(0,fd_head->datalen); /* seqno of the last fragment we have */
@@ -468,7 +468,7 @@ test_fragment_add_seq_partial_reassembly(void)
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(4,fd_head->frame); /* max frame we have */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(190,fd_head->len); /* the length of data we have */
ASSERT_EQ(2,fd_head->datalen); /* seqno of the last fragment we have */
@@ -526,7 +526,7 @@ test_fragment_add_seq_partial_reassembly(void)
fd_head=fragment_get(&test_reassembly_table, &pinfo, 12, NULL);
ASSERT_NE_POINTER(NULL,fd_head);
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(5,fd_head->frame); /* max frame we have */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(230,fd_head->len); /* the length of data we have */
ASSERT_EQ(3,fd_head->datalen); /* seqno of the last fragment we have */
@@ -633,7 +633,7 @@ test_fragment_add_seq_duplicate_first(void)
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(4,fd_head->frame); /* max frame we have */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(150,fd_head->len); /* the length of data we have */
ASSERT_EQ(2,fd_head->datalen); /* seqno of the last fragment we have */
@@ -732,7 +732,7 @@ test_fragment_add_seq_duplicate_middle(void)
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(4,fd_head->frame); /* max frame we have */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(150,fd_head->len); /* the length of data we have */
ASSERT_EQ(2,fd_head->datalen); /* seqno of the last fragment we have */
@@ -830,7 +830,7 @@ test_fragment_add_seq_duplicate_last(void)
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(4,fd_head->frame); /* max frame we have */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(150,fd_head->len); /* the length of data we have */
ASSERT_EQ(2,fd_head->datalen); /* seqno of the last fragment we have */
@@ -932,7 +932,7 @@ test_fragment_add_seq_duplicate_conflict(void)
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(4,fd_head->frame); /* max frame we have */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(150,fd_head->len); /* the length of data we have */
ASSERT_EQ(2,fd_head->datalen); /* seqno of the last fragment we have */
@@ -1044,7 +1044,7 @@ test_fragment_add_seq_check_work(fragment_head *(*fn)(reassembly_table *,
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(4,fd_head->frame); /* max frame we have */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(170,fd_head->len); /* the length of data we have */
ASSERT_EQ(2,fd_head->datalen); /* seqno of the last fragment we have */
@@ -1123,7 +1123,7 @@ test_fragment_add_seq_check_1(void)
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(2,fd_head->frame); /* max frame of fragment in structure */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(110,fd_head->len); /* the length of data we have */
ASSERT_EQ(1,fd_head->datalen); /* seqno of the last fragment we have */
@@ -1316,7 +1316,7 @@ test_simple_fragment_add_seq_next(void)
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(3,fd_head->frame); /* max frame we have */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(110,fd_head->len); /* the length of data we have */
ASSERT_EQ(1,fd_head->datalen); /* seqno of the last fragment we have */
@@ -1492,7 +1492,7 @@ test_missing_data_fragment_add_seq_next_3(void)
ASSERT_NE_POINTER(NULL,fd_head);
/* check the contents of the structure. */
- ASSERT_EQ(0,fd_head->frame); /* unused */
+ ASSERT_EQ(0,fd_head->frame); /* max frame we have */
ASSERT_EQ(0,fd_head->offset); /* unused */
ASSERT_EQ(0,fd_head->len); /* the length of data we have */
ASSERT_EQ(0,fd_head->datalen); /* seqno of the last fragment we have */