* Routines for X.25 packet disassembly
* Olivier Abad <oabad@noos.fr>
*
- * $Id: packet-x25.c,v 1.76 2003/01/31 03:17:47 guy Exp $
+ * $Id: packet-x25.c,v 1.85 2003/10/07 18:19:59 oabad Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
#include "llcsaps.h"
#include <epan/packet.h>
#include <epan/circuit.h>
+#include "reassemble.h"
#include "prefs.h"
#include "nlpid.h"
#include "x264_prt_id.h"
#define PACKET_IS_DATA(type) (!(type & X25_NONDATA_BIT))
#define PACKET_TYPE_FC(type) (type & 0x1F)
+#define X25_MBIT_MOD8 0x10
+#define X25_MBIT_MOD128 0x01
+
+#define X25_ABIT 0x8000
+
+#define X25_QBIT 0x8000
+#define X25_DBIT 0x4000
+
#define X25_FAC_CLASS_MASK 0xC0
#define X25_FAC_CLASS_A 0x00
static gint ett_x25_fac_priority = -1;
static gint ett_x25_user_data = -1;
+static gint ett_x25_segment = -1;
+static gint ett_x25_segments = -1;
+static gint hf_x25_segments = -1;
+static gint hf_x25_segment = -1;
+static gint hf_x25_segment_overlap = -1;
+static gint hf_x25_segment_overlap_conflict = -1;
+static gint hf_x25_segment_multiple_tails = -1;
+static gint hf_x25_segment_too_long_segment = -1;
+static gint hf_x25_segment_error = -1;
+
static const value_string vals_modulo[] = {
{ 1, "8" },
{ 2, "128" },
"End of data"
};
+static const fragment_items x25_frag_items = {
+ &ett_x25_segment,
+ &ett_x25_segments,
+ &hf_x25_segments,
+ &hf_x25_segment,
+ &hf_x25_segment_overlap,
+ &hf_x25_segment_overlap_conflict,
+ &hf_x25_segment_multiple_tails,
+ &hf_x25_segment_too_long_segment,
+ &hf_x25_segment_error,
+ NULL,
+ "segments"
+};
+
static dissector_handle_t ip_handle;
static dissector_handle_t clnp_handle;
static dissector_handle_t ositp_handle;
/* Preferences */
static gboolean payload_is_qllc_sna = FALSE;
+static gboolean reassemble_x25 = FALSE;
+
+/* Reassembly of X.25 */
+
+static GHashTable *x25_segment_table = NULL;
+static GHashTable *x25_reassembled_table = NULL;
static dissector_table_t x25_subdissector_table;
static heur_dissector_list_t x25_heur_subdissector_list;
byte1 = tvb_get_guint8(tvb, *offset+1);
proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
"Length : %u", byte1);
- byte2 = tvb_get_guint8(tvb, *offset+2);
+ byte2 = tvb_get_guint8(tvb, *offset+2) & 0x3F;
proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
"Number of semi-octets in DTE address : %u", byte2);
for (i = 0; i < byte2; i++) {
byte1 = tvb_get_guint8(tvb, *offset+1);
proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
"Length : %u", byte1);
- byte2 = tvb_get_guint8(tvb, *offset+2);
+ byte2 = tvb_get_guint8(tvb, *offset+2) & 0x3F;
proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
"Number of semi-octets in DTE address : %u", byte2);
for (i = 0; i < byte2; i++) {
static void
dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
- x25_dir_t dir)
+ x25_dir_t dir, gboolean side)
{
proto_tree *x25_tree=0, *gfi_tree=0, *userdata_tree=0;
proto_item *ti;
guint x25_pkt_len;
int modulo;
guint16 vc;
- dissector_handle_t dissect;
+ dissector_handle_t dissect = NULL;
gboolean toa; /* TOA/NPI address format */
guint16 bytes0_1;
guint8 pkt_type;
char *short_name = NULL, *long_name = NULL;
- tvbuff_t *next_tvb;
+ tvbuff_t *next_tvb = NULL;
gboolean q_bit_set = FALSE;
+ gboolean m_bit_set;
+ gint payload_len;
+ guint32 frag_key;
void *saved_private_data;
+ fragment_data *fd_head;
if (check_col(pinfo->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, "X.25");
pinfo->ctype = CT_X25;
pinfo->circuit_id = vc;
- if (bytes0_1 & 0x8000) toa = TRUE;
+ if (bytes0_1 & X25_ABIT) toa = TRUE;
else toa = FALSE;
x25_pkt_len = get_x25_pkt_len(tvb);
pkt_type = tvb_get_guint8(tvb, 2);
if (PACKET_IS_DATA(pkt_type)) {
- if (bytes0_1 & 0x8000)
+ if (bytes0_1 & X25_QBIT)
q_bit_set = TRUE;
}
}
prt_id = tvb_get_guint8(tvb, localoffset+2);
if (userdata_tree) {
- proto_tree_add_text(x25_tree, tvb, localoffset+2, 1,
+ proto_tree_add_text(userdata_tree, tvb, localoffset+2, 1,
"X.264 protocol identifier: %s",
val_to_str(prt_id, prt_id_vals,
"Unknown (0x%02X)"));
- proto_tree_add_text(x25_tree, tvb, localoffset+3, 1,
+ proto_tree_add_text(userdata_tree, tvb, localoffset+3, 1,
"X.264 sharing strategy: %s",
val_to_str(tvb_get_guint8(tvb, localoffset+3),
sharing_strategy_vals, "Unknown (0x%02X)"));
"X.263 secondary protocol ID: %s",
val_to_str(spi, nlpid_vals, "Unknown (0x%02x)"));
}
- localoffset++;
if (!pinfo->fd->flags.visited) {
/*
if (dissect != NULL)
x25_hash_add_proto_start(vc, pinfo->fd->num, dissect);
}
- }
- if (localoffset < tvb_length(tvb)) {
- if (userdata_tree) {
- proto_tree_add_text(userdata_tree, tvb, localoffset, -1,
- "Data");
+
+ /*
+ * If there's only one octet of user data, it's just
+ * an NLPID; don't try to dissect it.
+ */
+ if (localoffset + 1 == tvb_reported_length(tvb))
+ return;
+
+ /*
+ * There's more than one octet of user data, so we'll
+ * dissect it; for some protocols, the NLPID is considered
+ * to be part of the PDU, so, for those cases, we don't
+ * skip past it. For other protocols, we skip the NLPID.
+ */
+ switch (spi) {
+
+ case NLPID_ISO8473_CLNP:
+ case NLPID_ISO9542_ESIS:
+ case NLPID_ISO10589_ISIS:
+ case NLPID_ISO10747_IDRP:
+ case NLPID_SNDCF:
+ /*
+ * The NLPID is part of the PDU. Don't skip it.
+ * But if it's all there is to the PDU, don't
+ * bother dissecting it.
+ */
+ break;
+
+ case NLPID_SPI_X_29:
+ /*
+ * The first 4 bytes of the call user data are
+ * the SPI plus 3 reserved bytes; they are not
+ * part of the data to be dissected as X.29 data.
+ */
+ localoffset += 4;
+ break;
+
+ default:
+ /*
+ * The NLPID isn't part of the PDU - skip it.
+ * If that means there's nothing to dissect
+ */
+ localoffset++;
}
- localoffset = tvb_length(tvb);
}
}
break;
if (localoffset < x25_pkt_len) /* facilities */
dump_facilities(x25_tree, &localoffset, tvb);
-
- if (localoffset < tvb_reported_length(tvb)) { /* user data */
- if (x25_tree)
- proto_tree_add_text(x25_tree, tvb, localoffset,
- tvb_reported_length(tvb)-localoffset, "Data");
- localoffset=tvb_reported_length(tvb);
- }
break;
case X25_CLEAR_REQUEST:
switch (dir) {
"Data VC:%d P(S):%d P(R):%d %s", vc,
(pkt_type >> 1) & 0x07,
(pkt_type >> 5) & 0x07,
- ((pkt_type >> 4) & 0x01) ? " M" : "");
+ (pkt_type & X25_MBIT_MOD8) ? " M" : "");
else
col_add_fstr(pinfo->cinfo, COL_INFO,
"Data VC:%d P(S):%d P(R):%d %s", vc,
tvb_get_guint8(tvb, localoffset+1) >> 1,
pkt_type >> 1,
- (tvb_get_guint8(tvb, localoffset+1) & 0x01) ? " M" : "");
+ (tvb_get_guint8(tvb, localoffset+1) & X25_MBIT_MOD128) ? " M" : "");
}
if (x25_tree) {
proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
tvb_get_guint8(tvb, localoffset+1));
}
}
- localoffset += (modulo == 8) ? 1 : 2;
+ if (modulo == 8) {
+ m_bit_set = pkt_type & X25_MBIT_MOD8;
+ localoffset += 1;
+ } else {
+ m_bit_set = tvb_get_guint8(tvb, localoffset+1) & X25_MBIT_MOD128;
+ localoffset += 2;
+ }
+ payload_len = tvb_reported_length_remaining(tvb, localoffset);
+ if (reassemble_x25) {
+ /*
+ * Reassemble received and sent traffic separately.
+ * We don't reassemble traffic with an unknown direction
+ * at all.
+ */
+ frag_key = vc;
+ if (side) {
+ /*
+ * OR in an extra bit to distinguish from traffic
+ * in the other direction.
+ */
+ frag_key |= 0x10000;
+ }
+ fd_head = fragment_add_seq_next(tvb, localoffset,
+ pinfo, frag_key,
+ x25_segment_table,
+ x25_reassembled_table,
+ payload_len, m_bit_set);
+ pinfo->fragmented = m_bit_set;
+
+ if (fd_head) {
+ if (fd_head->next) {
+ /* This is the last packet */
+ next_tvb = tvb_new_real_data(fd_head->data,
+ fd_head->len,
+ fd_head->len);
+ tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+ add_new_data_source(pinfo, next_tvb, "Reassembled X.25");
+ show_fragment_seq_tree(fd_head,
+ &x25_frag_items,
+ x25_tree,
+ pinfo, next_tvb);
+ }
+ }
+
+ if (m_bit_set && next_tvb == NULL) {
+ /*
+ * This isn't the last packet, so just
+ * show it as X.25 user data.
+ */
+ proto_tree_add_text(x25_tree, tvb, localoffset, -1,
+ "User data (%u byte%s)", payload_len,
+ plurality(payload_len, "", "s"));
+ return;
+ }
+ }
break;
}
+
+ /*
+ * Non-data packets (RR, RNR, REJ).
+ */
switch (PACKET_TYPE_FC(pkt_type))
{
case X25_RR:
localoffset += (modulo == 8) ? 1 : 2;
}
- if (localoffset >= tvb_reported_length(tvb)) return;
+ if (localoffset >= tvb_reported_length(tvb))
+ return;
+ if (pinfo->fragmented)
+ return;
- next_tvb = tvb_new_subset(tvb, localoffset, -1, -1);
+ if (!next_tvb)
+ next_tvb = tvb_new_subset(tvb, localoffset, -1, -1);
saved_private_data = pinfo->private_data;
pinfo->private_data = &q_bit_set;
{
dissect_x25_common(tvb, pinfo, tree,
(pinfo->pseudo_header->x25.flags & FROM_DCE) ? X25_FROM_DCE :
- X25_FROM_DTE);
+ X25_FROM_DTE,
+ pinfo->pseudo_header->x25.flags & FROM_DCE);
}
/*
static void
dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
+ int direction;
+
/*
* We don't know if this packet is DTE->DCE or DCE->DCE.
+ * However, we can, at least, distinguish between the two
+ * sides of the conversation, based on the addresses and
+ * ports.
*/
- dissect_x25_common(tvb, pinfo, tree, X25_UNKNOWN);
+ direction = CMP_ADDRESS(&pinfo->src, &pinfo->dst);
+ if (direction == 0)
+ direction = (pinfo->srcport > pinfo->destport)*2 - 1;
+ dissect_x25_common(tvb, pinfo, tree, X25_UNKNOWN, direction > 0);
+}
+
+static void
+x25_reassemble_init(void)
+{
+ fragment_table_init(&x25_segment_table);
+ reassembled_table_init(&x25_reassembled_table);
}
void
{ "GFI", "x.25.gfi", FT_UINT16, BASE_DEC, NULL, 0xF000,
"General format identifier", HFILL }},
{ &hf_x25_abit,
- { "A Bit", "x.25.a", FT_BOOLEAN, 16, NULL, 0x8000,
+ { "A Bit", "x.25.a", FT_BOOLEAN, 16, NULL, X25_ABIT,
"Address Bit", HFILL }},
{ &hf_x25_qbit,
- { "Q Bit", "x.25.q", FT_BOOLEAN, 16, NULL, 0x8000,
+ { "Q Bit", "x.25.q", FT_BOOLEAN, 16, NULL, X25_QBIT,
"Qualifier Bit", HFILL }},
{ &hf_x25_dbit,
- { "D Bit", "x.25.d", FT_BOOLEAN, 16, NULL, 0x4000,
+ { "D Bit", "x.25.d", FT_BOOLEAN, 16, NULL, X25_DBIT,
"Delivery Confirmation Bit", HFILL }},
{ &hf_x25_mod,
{ "Modulo", "x.25.mod", FT_UINT16, BASE_DEC, VALS(vals_modulo), 0x3000,
{ "P(R)", "x.25.p_r", FT_UINT8, BASE_DEC, NULL, 0xFE,
"Packet Receive Sequence Number", HFILL }},
{ &hf_x25_mbit_mod8,
- { "M Bit", "x.25.m", FT_BOOLEAN, 8, TFS(&m_bit_tfs), 0x10,
+ { "M Bit", "x.25.m", FT_BOOLEAN, 8, TFS(&m_bit_tfs), X25_MBIT_MOD8,
"More Bit", HFILL }},
{ &hf_x25_mbit_mod128,
- { "M Bit", "x.25.m", FT_BOOLEAN, 8, TFS(&m_bit_tfs), 0x01,
+ { "M Bit", "x.25.m", FT_BOOLEAN, 8, TFS(&m_bit_tfs), X25_MBIT_MOD128,
"More Bit", HFILL }},
{ &hf_x25_p_s_mod8,
{ "P(S)", "x.25.p_s", FT_UINT8, BASE_DEC, NULL, 0x0E,
{ &hf_x25_p_s_mod128,
{ "P(S)", "x.25.p_s", FT_UINT8, BASE_DEC, NULL, 0xFE,
"Packet Send Sequence Number", HFILL }},
+ { &hf_x25_segment_overlap,
+ { "Fragment overlap", "x25.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Fragment overlaps with other fragments", HFILL }},
+
+ { &hf_x25_segment_overlap_conflict,
+ { "Conflicting data in fragment overlap", "x25.fragment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Overlapping fragments contained conflicting data", HFILL }},
+
+ { &hf_x25_segment_multiple_tails,
+ { "Multiple tail fragments found", "x25.fragment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Several tails were found when defragmenting the packet", HFILL }},
+
+ { &hf_x25_segment_too_long_segment,
+ { "Fragment too long", "x25.fragment.toolongfragment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Fragment contained data past end of packet", HFILL }},
+
+ { &hf_x25_segment_error,
+ { "Defragmentation error", "x25.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "Defragmentation error due to illegal fragments", HFILL }},
+
+ { &hf_x25_segment,
+ { "X.25 Fragment", "x25.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "X25 Fragment", HFILL }},
+
+ { &hf_x25_segments,
+ { "X.25 Fragments", "x25.fragments", FT_NONE, BASE_NONE, NULL, 0x0,
+ "X.25 Fragments", HFILL }},
};
static gint *ett[] = {
&ett_x25,
&ett_x25_fac_calling_addr_ext,
&ett_x25_fac_call_deflect,
&ett_x25_fac_priority,
- &ett_x25_user_data
+ &ett_x25_user_data,
+ &ett_x25_segment,
+ &ett_x25_segments
};
module_t *x25_module;
"Default to QLLC/SNA",
"If CALL REQUEST not seen or didn't specify protocol, dissect as QLLC/SNA",
&payload_is_qllc_sna);
+ prefs_register_bool_preference(x25_module, "reassemble",
+ "Reassemble fragmented X.25 packets",
+ "Reassemble fragmented X.25 packets",
+ &reassemble_x25);
+ register_init_routine(&x25_reassemble_init);
}
void