]> git.samba.org - obnox/wireshark/wip.git/blobdiff - packet-x25.c
As with "file_write_error_message()", so with
[obnox/wireshark/wip.git] / packet-x25.c
index 0dad34b2ba6dc220824bcf21698595992f736e6d..9e5496bf6b620b200afc9d8b1ccd98154ff05692 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for X.25 packet disassembly
  * Olivier Abad <oabad@noos.fr>
  *
- * $Id: packet-x25.c,v 1.72 2003/01/06 02:24:57 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>
@@ -34,6 +34,7 @@
 #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"
@@ -47,6 +48,11 @@ typedef enum {
        X25_UNKNOWN             /* direction unknown */
 } x25_dir_t;
 
+/*
+ * 0 for data packets, 1 for non-data packets.
+ */
+#define        X25_NONDATA_BIT                 0x01
+
 #define        X25_CALL_REQUEST                0x0B
 #define        X25_CALL_ACCEPTED               0x0F
 #define        X25_CLEAR_REQUEST               0x13
@@ -65,6 +71,17 @@ typedef enum {
 #define        X25_REJ                         0x09
 #define        X25_DATA                        0x00
 
+#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
@@ -100,6 +117,8 @@ static int hf_x25_dbit = -1;
 static int hf_x25_mod = -1;
 static int hf_x25_lcn = -1;
 static int hf_x25_type = -1;
+static int hf_x25_type_fc_mod8 = -1;
+static int hf_x25_type_data = -1;
 static int hf_x25_p_r_mod8 = -1;
 static int hf_x25_p_r_mod128 = -1;
 static int hf_x25_mbit_mod8 = -1;
@@ -132,6 +151,16 @@ static gint ett_x25_fac_call_deflect = -1;
 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" },
@@ -155,10 +184,29 @@ static const value_string vals_x25_type[] = {
        { X25_RR, "RR" },
        { X25_RNR, "RNR" },
        { X25_REJ, "REJ" },
-       { X25_DATA, "DATA" },
+       { X25_DATA, "Data" },
        { 0,   NULL}
 };
 
+static struct true_false_string m_bit_tfs = {
+       "More data follows",
+       "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;
@@ -167,6 +215,12 @@ static dissector_handle_t data_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;
@@ -988,7 +1042,7 @@ dump_facilities(proto_tree *tree, int *offset, tvbuff_t *tvb)
                    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++) {
@@ -1021,7 +1075,7 @@ dump_facilities(proto_tree *tree, int *offset, tvbuff_t *tvb)
                    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++) {
@@ -1359,9 +1413,10 @@ get_x25_pkt_len(tvbuff_t *tvb)
        return MIN(tvb_reported_length(tvb),length);
     }
 
-    if ((byte2 & 0x01) == X25_DATA) return MIN(tvb_reported_length(tvb),3);
+    if (PACKET_IS_DATA(byte2))
+       return MIN(tvb_reported_length(tvb),3);
 
-    switch (byte2 & 0x1F)
+    switch (PACKET_TYPE_FC(byte2))
     {
     case X25_RR:
        return MIN(tvb_reported_length(tvb),3);
@@ -1391,7 +1446,7 @@ static const value_string sharing_strategy_vals[] = {
 
 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;
@@ -1399,14 +1454,18 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
     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");
@@ -1419,7 +1478,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
     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);
@@ -1434,8 +1493,8 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
     }
 
     pkt_type = tvb_get_guint8(tvb, 2);
-    if ((pkt_type & 0x01) == X25_DATA) {
-       if (bytes0_1 & 0x8000)
+    if (PACKET_IS_DATA(pkt_type)) {
+       if (bytes0_1 & X25_QBIT)
            q_bit_set = TRUE;
     }
 
@@ -1445,7 +1504,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
         ti = proto_tree_add_item(x25_tree, hf_x25_gfi, tvb, 0, 2, FALSE);
         gfi_tree = proto_item_add_subtree(ti, ett_x25_gfi);
 
-        if ((pkt_type & 0x01) == X25_DATA) {
+        if (PACKET_IS_DATA(pkt_type)) {
             proto_tree_add_boolean(gfi_tree, hf_x25_qbit, tvb, 0, 2,
                 bytes0_1);
         }
@@ -1458,7 +1517,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
         }
 
         if (pkt_type == X25_CALL_REQUEST || pkt_type == X25_CALL_ACCEPTED ||
-            (pkt_type & 0x01) == X25_DATA) {
+            PACKET_IS_DATA(pkt_type)) {
             proto_tree_add_boolean(gfi_tree, hf_x25_dbit, tvb, 0, 2,
                 bytes0_1);
         }
@@ -1490,7 +1549,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
            proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb,
                    0, 2, bytes0_1);
            proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
-                   X25_CALL_REQUEST, "%s", long_name);
+                   X25_CALL_REQUEST, "Packet Type: %s", long_name);
        }
        localoffset = 3;
        if (localoffset < x25_pkt_len) { /* calling/called addresses */
@@ -1658,11 +1717,11 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                }
                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)"));
@@ -1677,14 +1736,16 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 
                case PRT_ID_ISO_8073:
                    /* ISO 8073 COTP */
-                   x25_hash_add_proto_start(vc, pinfo->fd->num, ositp_handle);
+                   if (!pinfo->fd->flags.visited)
+                       x25_hash_add_proto_start(vc, pinfo->fd->num, ositp_handle);
                    /* XXX - dissect the rest of the user data as COTP?
                       That needs support for NCM TPDUs, etc. */
                    break;
 
                case PRT_ID_ISO_8602:
                    /* ISO 8602 CLTP */
-                   x25_hash_add_proto_start(vc, pinfo->fd->num, ositp_handle);
+                   if (!pinfo->fd->flags.visited)
+                       x25_hash_add_proto_start(vc, pinfo->fd->num, ositp_handle);
                    break;
                }
            } else if (is_x_264 == 0) {
@@ -1697,22 +1758,60 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                        "X.263 secondary protocol ID: %s",
                                        val_to_str(spi, nlpid_vals, "Unknown (0x%02x)"));
                }
-               localoffset++;
+
+               if (!pinfo->fd->flags.visited) {
+                   /*
+                    * Is there a dissector handle for this SPI?
+                    * If so, assign it to this virtual circuit.
+                    */
+                   dissect = dissector_get_port_handle(x25_subdissector_table, spi);
+                   if (dissect != NULL)
+                       x25_hash_add_proto_start(vc, pinfo->fd->num, dissect);
+               }
 
                /*
-                * Is there a dissector handle for this SPI?
-                * If so, assign it to this virtual circuit.
+                * If there's only one octet of user data, it's just
+                * an NLPID; don't try to dissect it.
                 */
-               dissect = dissector_get_port_handle(x25_subdissector_table, spi);
-               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 (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;
@@ -1739,7 +1838,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        if (x25_tree) {
            proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
            proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
-                   X25_CALL_ACCEPTED, "%s", long_name);
+                   X25_CALL_ACCEPTED, "Packet Type: %s", long_name);
        }
        localoffset = 3;
         if (localoffset < x25_pkt_len) { /* calling/called addresses */
@@ -1751,13 +1850,6 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 
        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) {
@@ -1786,7 +1878,8 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        if (x25_tree) {
            proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
            proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb,
-                   localoffset+2, 1, X25_CLEAR_REQUEST, "%s", long_name);
+                   localoffset+2, 1, X25_CLEAR_REQUEST, "Packet Type: %s",
+                   long_name);
            proto_tree_add_text(x25_tree, tvb, 3, 1,
                    "Cause : %s", clear_code(tvb_get_guint8(tvb, 3)));
            proto_tree_add_text(x25_tree, tvb, 4, 1,
@@ -1874,7 +1967,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        if (x25_tree) {
            proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
            proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
-                   X25_RESET_REQUEST, "%s", long_name);
+                   X25_RESET_REQUEST, "Packet Type: %s", long_name);
            proto_tree_add_text(x25_tree, tvb, 3, 1,
                    "Cause : %s", reset_code(tvb_get_guint8(tvb, 3)));
            proto_tree_add_text(x25_tree, tvb, 4, 1,
@@ -1918,7 +2011,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        }
        if (x25_tree) {
            proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
-                   X25_RESTART_REQUEST, "%s", long_name);
+                   X25_RESTART_REQUEST, "Packet Type: %s", long_name);
            proto_tree_add_text(x25_tree, tvb, 3, 1,
                    "Cause : %s", restart_code(tvb_get_guint8(tvb, 3)));
            proto_tree_add_text(x25_tree, tvb, 4, 1,
@@ -1985,7 +2078,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        break;
     default :
        localoffset = 2;
-       if ((pkt_type & 0x01) == X25_DATA)
+       if (PACKET_IS_DATA(pkt_type))
        {
            if(check_col(pinfo->cinfo, COL_INFO)) {
                if (modulo == 8)
@@ -1993,47 +2086,102 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                            "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,
                        2, bytes0_1);
-               proto_tree_add_uint_hidden(x25_tree, hf_x25_type, tvb,
-                       localoffset, 1, X25_DATA);
                if (modulo == 8) {
                    proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
                            localoffset, 1, pkt_type);
-                   if (pkt_type & 0x10)
-                       proto_tree_add_boolean(x25_tree, hf_x25_mbit_mod8, tvb,
+                   proto_tree_add_boolean(x25_tree, hf_x25_mbit_mod8, tvb,
                            localoffset, 1, pkt_type);
                    proto_tree_add_uint(x25_tree, hf_x25_p_s_mod8, tvb,
                            localoffset, 1, pkt_type);
-                   proto_tree_add_text(x25_tree, tvb, localoffset, 1,
-                           decode_boolean_bitfield(pkt_type, 0x01, 1*8,
-                               NULL, "DATA"));
+                   proto_tree_add_uint(x25_tree, hf_x25_type_data, tvb,
+                           localoffset, 1, pkt_type);
                }
                else {
                    proto_tree_add_uint(x25_tree, hf_x25_p_r_mod128, tvb,
                            localoffset, 1, pkt_type);
+                   proto_tree_add_uint(x25_tree, hf_x25_type_data, tvb,
+                           localoffset, 1, pkt_type);
                    proto_tree_add_uint(x25_tree, hf_x25_p_s_mod128, tvb,
                            localoffset+1, 1,
                            tvb_get_guint8(tvb, localoffset+1));
-                   if (tvb_get_guint8(tvb, localoffset+1) & 0x01)
-                       proto_tree_add_boolean(x25_tree, hf_x25_mbit_mod128, tvb,
-                               localoffset+1, 1,
-                               tvb_get_guint8(tvb, localoffset+1));
+                   proto_tree_add_boolean(x25_tree, hf_x25_mbit_mod128, tvb,
+                           localoffset+1, 1,
+                           tvb_get_guint8(tvb, localoffset+1));
+               }
+           }
+           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;
                }
            }
-           localoffset += (modulo == 8) ? 1 : 2;
            break;
        }
-       switch (pkt_type & 0x1F)
+
+       /*
+        * Non-data packets (RR, RNR, REJ).
+        */
+       switch (PACKET_TYPE_FC(pkt_type))
        {
        case X25_RR:
            if(check_col(pinfo->cinfo, COL_INFO)) {
@@ -2050,7 +2198,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                if (modulo == 8) {
                    proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
                            localoffset, 1, pkt_type);
-                   proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
+                   proto_tree_add_uint(x25_tree, hf_x25_type_fc_mod8, tvb,
                            localoffset, 1, X25_RR);
                }
                else {
@@ -2077,7 +2225,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                if (modulo == 8) {
                    proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
                            localoffset, 1, pkt_type);
-                   proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
+                   proto_tree_add_uint(x25_tree, hf_x25_type_fc_mod8, tvb,
                            localoffset, 1, X25_RNR);
                }
                else {
@@ -2104,7 +2252,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                if (modulo == 8) {
                    proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
                            localoffset, 1, pkt_type);
-                   proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
+                   proto_tree_add_uint(x25_tree, hf_x25_type_fc_mod8, tvb,
                            localoffset, 1, X25_REJ);
                }
                else {
@@ -2118,9 +2266,13 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        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;
@@ -2135,7 +2287,8 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
     /* Did the user suggest QLLC/SNA? */
     if (payload_is_qllc_sna) {
        /* Yes - dissect it as QLLC/SNA. */
-       x25_hash_add_proto_start(vc, pinfo->fd->num, qllc_handle);
+       if (!pinfo->fd->flags.visited)
+           x25_hash_add_proto_start(vc, pinfo->fd->num, qllc_handle);
        call_dissector(qllc_handle, next_tvb, pinfo, tree);
        pinfo->private_data = saved_private_data;
        return;
@@ -2147,13 +2300,15 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 
     case 0x45:
        /* Looks like an IP header */
-       x25_hash_add_proto_start(vc, pinfo->fd->num, ip_handle);
+       if (!pinfo->fd->flags.visited)
+           x25_hash_add_proto_start(vc, pinfo->fd->num, ip_handle);
        call_dissector(ip_handle, next_tvb, pinfo, tree);
        pinfo->private_data = saved_private_data;
        return;
 
     case NLPID_ISO8473_CLNP:
-       x25_hash_add_proto_start(vc, pinfo->fd->num, clnp_handle);
+       if (!pinfo->fd->flags.visited)
+           x25_hash_add_proto_start(vc, pinfo->fd->num, clnp_handle);
        call_dissector(clnp_handle, next_tvb, pinfo, tree);
        pinfo->private_data = saved_private_data;
        return;
@@ -2180,7 +2335,8 @@ dissect_x25_dir(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
     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);
 }
 
 /*
@@ -2190,10 +2346,25 @@ dissect_x25_dir(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 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
@@ -2201,16 +2372,16 @@ proto_register_x25(void)
 {
     static hf_register_info hf[] = {
        { &hf_x25_gfi,
-         { "GFI", "x.25.gfi", FT_UINT16, BASE_BIN, NULL, 0xF000,
+         { "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,
@@ -2221,24 +2392,57 @@ proto_register_x25(void)
        { &hf_x25_type,
          { "Packet Type", "x.25.type", FT_UINT8, BASE_HEX, VALS(vals_x25_type), 0x0,
                "Packet Type", HFILL }},
+       { &hf_x25_type_fc_mod8,
+         { "Packet Type", "x.25.type", FT_UINT8, BASE_HEX, VALS(vals_x25_type), 0x1F,
+               "Packet Type", HFILL }},
+       { &hf_x25_type_data,
+         { "Packet Type", "x.25.type", FT_UINT8, BASE_HEX, VALS(vals_x25_type), 0x01,
+               "Packet Type", HFILL }},
        { &hf_x25_p_r_mod8,
-         { "P(R)", "x.25.p_r", FT_UINT8, BASE_HEX, NULL, 0xE0,
+         { "P(R)", "x.25.p_r", FT_UINT8, BASE_DEC, NULL, 0xE0,
                "Packet Receive Sequence Number", HFILL }},
        { &hf_x25_p_r_mod128,
-         { "P(R)", "x.25.p_r", FT_UINT8, BASE_HEX, NULL, 0xFE,
+         { "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, NULL, 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, NULL, 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_HEX, NULL, 0x0E,
+         { "P(S)", "x.25.p_s", FT_UINT8, BASE_DEC, NULL, 0x0E,
                "Packet Send Sequence Number", HFILL }},
        { &hf_x25_p_s_mod128,
-         { "P(S)", "x.25.p_s", FT_UINT8, BASE_HEX, NULL, 0xFE,
+         { "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,
@@ -2264,7 +2468,9 @@ proto_register_x25(void)
        &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;
 
@@ -2286,6 +2492,11 @@ proto_register_x25(void)
             "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