Put the value(s) of a parameter into the top-level item for that
[obnox/wireshark/wip.git] / packet-x25.c
index 56d3d3d23d2956057ae5693252abc16bbeb68a6a..9e5496bf6b620b200afc9d8b1ccd98154ff05692 100644 (file)
@@ -1,22 +1,23 @@
 /* packet-x25.c
- * Routines for x25 packet disassembly
- * Olivier Abad <abad@daba.dhis.org>
+ * Routines for X.25 packet disassembly
+ * Olivier Abad <oabad@noos.fr>
+ *
+ * $Id: packet-x25.c,v 1.85 2003/10/07 18:19:59 oabad Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
+ * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1998
  *
- * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 # include "config.h"
 #endif
 
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
 #include <stdio.h>
 #include <glib.h>
 #include <stdlib.h>
 #include <string.h>
-#include "packet.h"
+#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 FROM_DCE                       0x80
+/*
+ * Direction of packet.
+ */
+typedef enum {
+       X25_FROM_DCE,           /* DCE->DTE */
+       X25_FROM_DTE,           /* DTE->DCE */
+       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_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
 #define X25_FAC_CALL_TRANSFER          0xC3
 #define X25_FAC_CALLED_ADDR_EXT                0xC9
 #define X25_FAC_ETE_TRANSIT_DELAY      0xCA
-#define X25_FAC_ADDR_EXT               0xCB
+#define X25_FAC_CALLING_ADDR_EXT       0xCB
 #define X25_FAC_CALL_DEFLECT           0xD1
-
-int proto_x25 = -1;
-int hf_x25_lcn = -1;
-int hf_x25_type = -1;
-
-/*
- * each vc_info node contains :
- *   the time of the first frame using this dissector (secs and usecs)
- *   the time of the last frame using this dissector (0 if it is unknown)
- *   a pointer to the dissector
- *
- * the "time of first frame" is initialized when a Call Req. is received
- * the "time of last frame" is initialized when a Clear, Reset, or Restart
- * is received
- */
-typedef struct _vc_info {
-       guint32 first_frame_secs, first_frame_usecs;
-       guint32 last_frame_secs, last_frame_usecs;
-       void (*dissect)(const u_char *, int, frame_data *, proto_tree *);
-       struct _vc_info *next;
-} vc_info;
-
-/*
- * the hash table will contain linked lists of global_vc_info
- * each global_vc_info struct contains :
- *   the VC number (the hash table is indexed with VC % 64)
- *   a linked list of vc_info
- */
-typedef struct _global_vc_info {
-       int vc_num;
-       vc_info *info;
-       struct _global_vc_info *next;
-} global_vc_info;
-
-global_vc_info *hash_table[64];
-
-void
-free_vc_info(vc_info *pt)
+#define X25_FAC_PRIORITY               0xD2
+
+static int proto_x25 = -1;
+static int hf_x25_gfi = -1;
+static int hf_x25_abit = -1;
+static int hf_x25_qbit = -1;
+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;
+static int hf_x25_mbit_mod128 = -1;
+static int hf_x25_p_s_mod8 = -1;
+static int hf_x25_p_s_mod128 = -1;
+
+static gint ett_x25 = -1;
+static gint ett_x25_gfi = -1;
+static gint ett_x25_fac = -1;
+static gint ett_x25_fac_unknown = -1;
+static gint ett_x25_fac_mark = -1;
+static gint ett_x25_fac_reverse = -1;
+static gint ett_x25_fac_throughput = -1;
+static gint ett_x25_fac_cug = -1;
+static gint ett_x25_fac_called_modif = -1;
+static gint ett_x25_fac_cug_outgoing_acc = -1;
+static gint ett_x25_fac_throughput_min = -1;
+static gint ett_x25_fac_express_data = -1;
+static gint ett_x25_fac_bilateral_cug = -1;
+static gint ett_x25_fac_packet_size = -1;
+static gint ett_x25_fac_window_size = -1;
+static gint ett_x25_fac_rpoa_selection = -1;
+static gint ett_x25_fac_transit_delay = -1;
+static gint ett_x25_fac_call_transfer = -1;
+static gint ett_x25_fac_called_addr_ext = -1;
+static gint ett_x25_fac_ete_transit_delay = -1;
+static gint ett_x25_fac_calling_addr_ext = -1;
+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" },
+       { 0, NULL}
+};
+
+static const value_string vals_x25_type[] = {
+       { X25_CALL_REQUEST, "Call" },
+       { X25_CALL_ACCEPTED, "Call Accepted" },
+       { X25_CLEAR_REQUEST, "Clear" },
+       { X25_CLEAR_CONFIRMATION, "Clear Confirmation" },
+       { X25_INTERRUPT, "Interrupt" },
+       { X25_INTERRUPT_CONFIRMATION, "Interrupt Confirmation" },
+       { X25_RESET_REQUEST, "Reset" },
+       { X25_RESET_CONFIRMATION, "Reset Confirmation" },
+       { X25_RESTART_REQUEST, "Restart" },
+       { X25_RESTART_CONFIRMATION, "Restart Confirmation" },
+       { X25_REGISTRATION_REQUEST, "Registration" },
+       { X25_REGISTRATION_CONFIRMATION, "Registration Confirmation" },
+       { X25_DIAGNOSTIC, "Diagnostic" },
+       { X25_RR, "RR" },
+       { X25_RNR, "RNR" },
+       { X25_REJ, "REJ" },
+       { 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;
+static dissector_handle_t qllc_handle;
+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;
+
+static void
+x25_hash_add_proto_start(guint16 vc, guint32 frame, dissector_handle_t dissect)
 {
-  vc_info *vci = pt;
-
-  while (pt) {
-    vci = pt;
-    pt = pt->next;
-    g_free(vci);
+  circuit_t *circuit;
+
+  /*
+   * Is there already a circuit with this VC number?
+   */
+  circuit = find_circuit(CT_X25, vc, frame);
+  if (circuit != NULL) {
+    /*
+     * Yes - close it, as we're creating a new one.
+     */
+    close_circuit(circuit, frame - 1);
   }
-}
 
-void
-init_dissect_x25()
-{
-  int i;
+  /*
+   * Set up a new circuit.
+   */
+  circuit = circuit_new(CT_X25, vc, frame);
 
-  for (i=0; i<64; i++) {
-    if (hash_table[i]) /* not NULL ==> free */
-    {
-      global_vc_info *hash_ent, *hash_ent2;
-      hash_ent2 = hash_ent = hash_table[i];
-      while (hash_ent)
-      {
-        hash_ent2 = hash_ent;
-       hash_ent = hash_ent->next;
-       free_vc_info(hash_ent2->info);
-       g_free(hash_ent2);
-      }
-      hash_table[i]=0;
-    }
-  }
+  /*
+   * Set its dissector.
+   */
+  circuit_set_dissector(circuit, dissect);
 }
 
-void
-x25_hash_add_proto_start(guint16 vc, guint32 frame_secs, guint32 frame_usecs,
-                        void (*dissect)(const u_char *, int, frame_data *,
-                                      proto_tree *))
+static void
+x25_hash_add_proto_end(guint16 vc, guint32 frame)
 {
-  int idx = vc % 64;
-  global_vc_info *hash_ent;
-  global_vc_info *hash_ent2;
-
-  if (hash_table[idx] == 0)
-  {
-    hash_ent = (global_vc_info *)g_malloc(sizeof(global_vc_info));
-    if (!hash_ent) {
-      fprintf(stderr, "Could not allocate space for hash structure in dissect_x25\n");
-      exit(1);
-    }
-    hash_ent->vc_num = vc;
-    hash_ent->next=0;
-    hash_ent->info = (vc_info *)g_malloc(sizeof(vc_info));
-    if (!hash_ent->info) {
-      fprintf(stderr, "Could not allocate space for hash structure in dissect_x25\n");
-      exit(1);
-    }
-    hash_ent->info->first_frame_secs = frame_secs;
-    hash_ent->info->first_frame_usecs = frame_usecs;
-    hash_ent->info->last_frame_secs = 0;
-    hash_ent->info->last_frame_usecs = 0;
-    hash_ent->info->dissect = dissect;
-    hash_ent->info->next = 0;
-    hash_table[idx] = hash_ent;
-  }
-  else
-  {
-    hash_ent2 = hash_ent = hash_table[idx];
-    /* search an entry with the same VC number */
-    while (hash_ent != NULL && hash_ent->vc_num != vc) {
-      hash_ent2 = hash_ent;
-      hash_ent = hash_ent->next;
-    }
-    if (hash_ent != NULL) /* hash_ent->vc_num == vc */
-    {
-      vc_info *vci = hash_ent->info;
-      while (vci->next) vci = vci->next; /* last element */
-      if (vci->dissect == dissect) {
-       vci->last_frame_secs = 0;
-       vci->last_frame_usecs = 0;
-      }
-      else {
-        vci->next = (vc_info *)g_malloc(sizeof(vc_info));
-       if (vci->next == 0) {
-         fprintf(stderr, "Could not allocate space for hash structure in dissect_x25\n");
-         exit(1);
-       }
-       vci->next->first_frame_secs = frame_secs;
-       vci->next->first_frame_usecs = frame_usecs;
-       vci->next->last_frame_secs = 0;
-       vci->next->last_frame_usecs = 0;
-       vci->next->dissect = dissect;
-       vci->next->next = 0;
-      }
-    }
-    else /* new vc number */
-    {
-      hash_ent2->next = (global_vc_info *)g_malloc(sizeof(global_vc_info));
-      if (!hash_ent2->next) {
-        fprintf(stderr, "Could not allocate space for hash structure in dissect_x25\n");
-        exit(1);
-      }
-      hash_ent2->next->info = (vc_info *)g_malloc(sizeof(vc_info));
-      if (!hash_ent2->next->info) {
-        fprintf(stderr, "Could not allocate space for hash structure in dissect_x25\n");
-        exit(1);
-      }
-      hash_ent2->next->info->first_frame_secs = frame_secs;
-      hash_ent2->next->info->first_frame_usecs = frame_usecs;
-      hash_ent2->next->info->last_frame_secs = 0;
-      hash_ent2->next->info->last_frame_usecs = 0;
-      hash_ent2->next->info->dissect = dissect;
-      hash_ent2->next->info->next = 0;
-    }
-  }
-}
-
-void
-x25_hash_add_proto_end(guint16 vc, guint32 frame_secs, guint32 frame_usecs)
-{
-  global_vc_info *hash_ent = hash_table[vc%64];
-  vc_info *vci;
-
-  if (!hash_ent) return;
-  while(hash_ent->vc_num != vc) hash_ent = hash_ent->next;
-  if (!hash_ent) return;
-
-  vci = hash_ent->info;
-  while (vci->next) vci = vci->next;
-  vci->last_frame_secs = frame_secs;
-  vci->last_frame_usecs = frame_usecs;
-}
-
-void (*x25_hash_get_dissect(guint32 frame_secs, guint32 frame_usecs, guint16 vc))(const u_char *, int, frame_data *, proto_tree *)
-{
-  global_vc_info *hash_ent = hash_table[vc%64];
-  vc_info *vci;
-  vc_info *vci2;
-
-  if (!hash_ent) return 0;
-
-  while(hash_ent && hash_ent->vc_num != vc) hash_ent = hash_ent->next;
-  if (!hash_ent) return 0;
-
-  /* a hash_ent was found for this VC number */
-  vci2 = vci = hash_ent->info;
-
-  /* looking for an entry matching our frame time */
-  while (vci && (vci->last_frame_secs < frame_secs ||
-                (vci->last_frame_secs == frame_secs &&
-                 vci->last_frame_usecs < frame_usecs))) {
-    vci2 = vci;
-    vci = vci->next;
-  }
-  /* we reached last record, and previous record has a non zero
-   * last frame time ==> no dissector */
-  if (!vci && (vci2->last_frame_secs || vci2->last_frame_usecs)) return 0;
-
-  /* we reached last record, and previous record has a zero last frame time
-   * ==> dissector for previous frame has not been "stopped" by a Clear, etc */
-  if (!vci) {
-    /* if the start time for vci2 is greater than our frame time
-     * ==> no dissector */
-    if (frame_secs < vci2->first_frame_secs ||
-        (frame_secs == vci2->first_frame_secs &&
-         frame_usecs < vci2->first_frame_usecs))
-      return 0;
-    else
-      return vci2->dissect;
-  }
-
-  /* our frame time is before vci's end. Check if it is adter vci's start */
-  if (frame_secs < vci->first_frame_secs ||
-      (frame_secs == vci->first_frame_secs &&
-       frame_usecs < vci->first_frame_usecs))
-    return 0;
-  else
-    return vci->dissect;
+  circuit_t *circuit;
+
+  /*
+   * Try to find the circuit.
+   */
+  circuit = find_circuit(CT_X25, vc, frame);
+
+  /*
+   * If we succeeded, close it.
+   */
+  if (circuit != NULL)
+    close_circuit(circuit, frame);
 }
 
 static char *clear_code(unsigned char code)
@@ -305,10 +277,22 @@ static char *clear_code(unsigned char code)
        return "DTE Originated";
     if (code == 0x01)
        return "Number Busy";
+    if (code == 0x03)
+       return "Invalid Facility Requested";
+    if (code == 0x05)
+       return "Network Congestion";
     if (code == 0x09)
        return "Out Of Order";
+    if (code == 0x0B)
+       return "Access Barred";
+    if (code == 0x0D)
+       return "Not Obtainable";
     if (code == 0x11)
        return "Remote Procedure Error";
+    if (code == 0x13)
+       return "Local Procedure Error";
+    if (code == 0x15)
+       return "RPOA Out Of Order";
     if (code == 0x19)
        return "Reverse Charging Acceptance Not Subscribed";
     if (code == 0x21)
@@ -317,18 +301,6 @@ static char *clear_code(unsigned char code)
        return "Fast Select Acceptance Not Subscribed";
     if (code == 0x39)
        return "Destination Absent";
-    if (code == 0x03)
-       return "Invalid Facility Requested";
-    if (code == 0x0B)
-       return "Access Barred";
-    if (code == 0x13)
-       return "Local Procedure Error";
-    if (code == 0x05)
-       return "Network Congestion";
-    if (code == 0x0D)
-       return "Not Obtainable";
-    if (code == 0x15)
-       return "RPOA Out Of Order";
 
     sprintf(buffer, "Unknown %02X", code);
 
@@ -475,6 +447,87 @@ static char *clear_diag(unsigned char code)
        return "Unknown called DNIC";
     if (code == 122)
        return "Maintenance action";
+    if (code == 144)
+       return "Timer expired or retransmission count surpassed";
+    if (code == 145)
+       return "Timer expired or retransmission count surpassed for INTERRUPT";
+    if (code == 146)
+       return "Timer expired or retransmission count surpassed for DATA "
+              "packet transmission";
+    if (code == 147)
+       return "Timer expired or retransmission count surpassed for REJECT";
+    if (code == 160)
+       return "DTE-specific signals";
+    if (code == 161)
+       return "DTE operational";
+    if (code == 162)
+       return "DTE not operational";
+    if (code == 163)
+       return "DTE resource constraint";
+    if (code == 164)
+       return "Fast select not subscribed";
+    if (code == 165)
+       return "Invalid partially full DATA packet";
+    if (code == 166)
+       return "D-bit procedure not supported";
+    if (code == 167)
+       return "Registration/Cancellation confirmed";
+    if (code == 224)
+       return "OSI network service problem";
+    if (code == 225)
+       return "Disconnection (transient condition)";
+    if (code == 226)
+       return "Disconnection (permanent condition)";
+    if (code == 227)
+       return "Connection rejection - reason unspecified (transient "
+              "condition)";
+    if (code == 228)
+       return "Connection rejection - reason unspecified (permanent "
+              "condition)";
+    if (code == 229)
+       return "Connection rejection - quality of service not available "
+               "transient condition)";
+    if (code == 230)
+       return "Connection rejection - quality of service not available "
+               "permanent condition)";
+    if (code == 231)
+       return "Connection rejection - NSAP unreachable (transient condition)";
+    if (code == 232)
+       return "Connection rejection - NSAP unreachable (permanent condition)";
+    if (code == 233)
+       return "reset - reason unspecified";
+    if (code == 234)
+       return "reset - congestion";
+    if (code == 235)
+       return "Connection rejection - NSAP address unknown (permanent "
+               "condition)";
+    if (code == 240)
+       return "Higher layer initiated";
+    if (code == 241)
+       return "Disconnection - normal";
+    if (code == 242)
+       return "Disconnection - abnormal";
+    if (code == 243)
+       return "Disconnection - incompatible information in user data";
+    if (code == 244)
+       return "Connection rejection - reason unspecified (transient "
+               "condition)";
+    if (code == 245)
+       return "Connection rejection - reason unspecified (permanent "
+               "condition)";
+    if (code == 246)
+       return "Connection rejection - quality of service not available "
+               "(transient condition)";
+    if (code == 247)
+       return "Connection rejection - quality of service not available "
+               "(permanent condition)";
+    if (code == 248)
+       return "Connection rejection - incompatible information in user data";
+    if (code == 249)
+       return "Connection rejection - unrecognizable protocol indentifier "
+               "in user data";
+    if (code == 250)
+       return "Reset - user resynchronization";
 
     sprintf(buffer, "Unknown %d", code);
 
@@ -547,332 +600,721 @@ static char *registration_code(unsigned char code)
     return buffer;
 }
 
-void
-dump_facilities(proto_tree *tree, int *offset, const guint8 *p)
+static void
+dump_facilities(proto_tree *tree, int *offset, tvbuff_t *tvb)
 {
-    const guint8 *ptr = p;
+    guint8 fac, byte1, byte2, byte3;
     guint32 len;      /* facilities length */
-
-    len = *ptr++;
-    if (len && tree)
-       proto_tree_add_text(tree, *offset, 1,
+    proto_item *ti=0;
+    proto_tree *fac_tree = 0;
+    proto_tree *fac_subtree;
+
+    len = tvb_get_guint8(tvb, *offset);
+    if (len && tree) {
+       ti = proto_tree_add_text(tree, tvb, *offset, len + 1,
+                                "Facilities");
+       fac_tree = proto_item_add_subtree(ti, ett_x25_fac);
+       proto_tree_add_text(fac_tree, tvb, *offset, 1,
                            "Facilities length: %d", len);
+    }
     (*offset)++;
 
     while (len > 0) {
-       switch(*ptr & X25_FAC_CLASS_MASK) {
+       fac = tvb_get_guint8(tvb, *offset);
+       switch(fac & X25_FAC_CLASS_MASK) {
        case X25_FAC_CLASS_A:
-           switch (*ptr) {
+           switch (fac) {
            case X25_FAC_COMP_MARK:
-               switch (ptr[1]) {
+               if (fac_tree)
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+                           "Code : 00 (Marker)");
+               switch (tvb_get_guint8(tvb, *offset + 1)) {
                case 0x00:
-                   if (tree)
-                       proto_tree_add_text(tree, *offset, 2,
-                                           "Network complementary services - calling DTE");
+                   if (fac_tree) {
+                       fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_mark);
+                       proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                                           "Parameter : 00 (Network complementary "
+                                           "services - calling DTE)");
+                   }
                    break;
                case 0xFF:
-                   if (tree)
-                       proto_tree_add_text(tree, *offset, 2,
-                                           "Network complementary services - called DTE");
+                   if (fac_tree) {
+                       fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_mark);
+                       proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                                           "Parameter : FF (Network complementary "
+                                           "services - called DTE)");
+                   }
                    break;
                case 0x0F:
-                   if (tree)
-                       proto_tree_add_text(tree, *offset, 2,
-                                           "DTE complementary services");
+                   if (fac_tree) {
+                       fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_mark);
+                       proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                                           "Parameter : 0F (DTE complementary "
+                                           "services)");
+                   }
                    break;
                default:
-                   if (tree)
-                       proto_tree_add_text(tree, *offset, 2,
-                                           "Unknown marker");
+                   if (fac_tree) {
+                       fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_mark);
+                       proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                                           "Parameter : %02X (Unknown marker)",
+                                           tvb_get_guint8(tvb, *offset+1));
+                   }
                    break;
                }
                break;
            case X25_FAC_REVERSE:
-               if (tree) {
-                   if (ptr[1] & 0x01)
-                       proto_tree_add_text(tree, *offset, 2,
-                                           "Reverse Charging");
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Reverse charging / Fast select)", fac);
+                   fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_reverse);
+                   byte1 = tvb_get_guint8(tvb, *offset + 1);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           "Parameter : %02X", byte1);
+                   if (byte1 & 0xC0)
+                       proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                               "11.. .... = Fast select with restriction");
+                   else if (byte1 & 0x80)
+                       proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                               "10.. .... = Fast select - no restriction");
                    else
-                       proto_tree_add_text(tree, *offset, 2,
-                                           "No Reverse Charging");
-                   if (ptr[1] & 0xC0)
-                       proto_tree_add_text(tree, *offset, 2,
-                                           "Fast select with restriction");
-                   else if (ptr[1] & 0x80)
-                       proto_tree_add_text(tree, *offset, 2,
-                                           "Fast select - no restriction");
-                   else
-                       proto_tree_add_text(tree, *offset, 2,
-                                           "No Fast select");
+                       proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                               "00.. .... = Fast select not requested");
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           decode_boolean_bitfield(byte1, 0x01, 1*8,
+                               "Reverse charging requested",
+                               "Reverse charging not requested"));
                }
                break;
            case X25_FAC_THROUGHPUT:
-               if (tree) {
-                   int called_dte_throughput=0;
-                   int calling_dte_throughput=0;
-
-                   if ( (ptr[1] >> 4) >= 3 && (ptr[1] >> 4) <= 13 )
-                       called_dte_throughput = 75*2^((ptr[1] >> 4)-3);
-                   if ( (ptr[1] & 0x0F) >= 3 && (ptr[1] & 0x0F) <= 13 )
-                       calling_dte_throughput = 75*2^((ptr[1] & 0x0F)-3);
-                   proto_tree_add_text(tree, *offset, 2,
-                           "Throughput: called DTE: %d - calling DTE: %d",
-                           called_dte_throughput, calling_dte_throughput);
+               if (fac_tree) {
+                   char tmpbuf[80];
+
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Throughput class negociation)", fac);
+                   fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_throughput);
+                   byte1 = tvb_get_guint8(tvb, *offset + 1);
+                   switch (byte1 >> 4)
+                   {
+                   case 3:
+                   case 4:
+                   case 5:
+                   case 6:
+                   case 7:
+                   case 8:
+                   case 9:
+                   case 10:
+                   case 11:
+                       sprintf(tmpbuf, "From the called DTE : %%u (%d bps)",
+                               75*(1<<((byte1 >> 4)-3)));
+                       break;
+                   case 12:
+                       sprintf(tmpbuf, "From the called DTE : %%u (48000 bps)");
+                       break;
+                   case 13:
+                       sprintf(tmpbuf, "From the called DTE : %%u (64000 bps)");
+                       break;
+                   default:
+                       sprintf(tmpbuf, "From the called DTE : %%u (Reserved)");
+                   }
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           decode_numeric_bitfield(byte1, 0xF0, 1*8, tmpbuf));
+                   switch (byte1 & 0x0F)
+                   {
+                   case 3:
+                   case 4:
+                   case 5:
+                   case 6:
+                   case 7:
+                   case 8:
+                   case 9:
+                   case 10:
+                   case 11:
+                       sprintf(tmpbuf, "From the calling DTE : %%u (%d bps)",
+                               75*(1<<((byte1 & 0x0F)-3)));
+                       break;
+                   case 12:
+                       sprintf(tmpbuf, "From the calling DTE : %%u (48000 bps)");
+                       break;
+                   case 13:
+                       sprintf(tmpbuf, "From the calling DTE : %%u (64000 bps)");
+                       break;
+                   default:
+                       sprintf(tmpbuf, "From the calling DTE : %%u (Reserved)");
+                   }
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           decode_numeric_bitfield(byte1, 0x0F, 1*8, tmpbuf));
                }
                break;
            case X25_FAC_CUG:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2,
-                                       "Closed user group: %d%d",
-                                       ptr[1] >> 4, ptr[1] & 0x0F);
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Closed user group selection)", fac);
+                   fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_cug);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           "Closed user group: %02X", tvb_get_guint8(tvb, *offset+1));
+               }
                break;
            case X25_FAC_CALLED_MODIF:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2,
-                                       "Called address modified: %02X",
-                                       ptr[1]);
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Called address modified)", fac);
+                   fac_subtree = proto_item_add_subtree(ti,
+                           ett_x25_fac_called_modif);
+                   proto_tree_add_text(fac_tree, tvb, *offset+1, 1,
+                           "Parameter %02X", tvb_get_guint8(tvb, *offset+1));
+               }
                break;
            case X25_FAC_CUG_OUTGOING_ACC:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2,
-                                       "CUG with outgoing access: %d%d",
-                                       ptr[1]>>4, ptr[1] & 0x0F);
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Closed user group with outgoing access selection)",
+                           fac);
+                   fac_subtree = proto_item_add_subtree(ti,
+                           ett_x25_fac_cug_outgoing_acc);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           "Closed user group: %02X", tvb_get_guint8(tvb, *offset+1));
+               }
                break;
            case X25_FAC_THROUGHPUT_MIN:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2,
-                                       "Minimum throughput class");
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Minimum throughput class)", fac);
+                   fac_subtree = proto_item_add_subtree(ti,
+                           ett_x25_fac_throughput_min);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           "Parameter %02X", tvb_get_guint8(tvb, *offset+1));
+               }
                break;
            case X25_FAC_EXPRESS_DATA:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2,
-                                       "Negociation of express data");
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Negociation of express data)", fac);
+                   fac_subtree = proto_item_add_subtree(ti,
+                           ett_x25_fac_express_data);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           "Parameter %02X", tvb_get_guint8(tvb, *offset+1));
+               }
                break;
            default:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2,
-                                       "Unknown facility %02X, value %02X",
-                                       ptr[0], ptr[1]);
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+                           "Code : %02X (Unknown class A)", fac);
+                   fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_unknown);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           "Parameter %02X", tvb_get_guint8(tvb, *offset+1));
+               }
                break;
            }
            (*offset) += 2;
            len -= 2;
-           ptr += 2;
            break;
        case X25_FAC_CLASS_B:
-           switch (*ptr) {
+           switch (fac) {
            case X25_FAC_BILATERAL_CUG:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 3,
-                                       "Bilateral CUG: %d%d%d%d",
-                                       ptr[1] >> 4,
-                                       ptr[1] & 0x0F,
-                                       ptr[2] >> 4,
-                                       ptr[2] & 0x0F);
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Bilateral closed user group selection)", fac);
+                   fac_subtree = proto_item_add_subtree(ti,
+                           ett_x25_fac_bilateral_cug);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 2,
+                                       "Bilateral CUG: %04X",
+                                       tvb_get_ntohs(tvb, *offset+1));
+               }
                break;
            case X25_FAC_PACKET_SIZE:
-               if (tree)
+               if (fac_tree)
                {
-                   int called_dte_size, calling_dte_size;
+                   char tmpbuf[80];
 
-                   switch (ptr[1])
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Packet size)", fac);
+                   fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_packet_size);
+                   byte1 = tvb_get_guint8(tvb, *offset + 1);
+                   switch (byte1)
                    {
                    case 0x04:
-                       called_dte_size = 16;
+                       sprintf(tmpbuf, "From the called DTE : %%u (16)");
                        break;
                    case 0x05:
-                       called_dte_size = 32;
+                       sprintf(tmpbuf, "From the called DTE : %%u (32)");
                        break;
                    case 0x06:
-                       called_dte_size = 64;
+                       sprintf(tmpbuf, "From the called DTE : %%u (64)");
                        break;
                    case 0x07:
-                       called_dte_size = 128;
+                       sprintf(tmpbuf, "From the called DTE : %%u (128)");
                        break;
                    case 0x08:
-                       called_dte_size = 256;
+                       sprintf(tmpbuf, "From the called DTE : %%u (256)");
                        break;
                    case 0x0D:
-                       called_dte_size = 512;
+                       sprintf(tmpbuf, "From the called DTE : %%u (512)");
                        break;
                    case 0x0C:
-                       called_dte_size = 1024;
+                       sprintf(tmpbuf, "From the called DTE : %%u (1024)");
                        break;
                    case 0x0E:
-                       called_dte_size = 2048;
+                       sprintf(tmpbuf, "From the called DTE : %%u (2048)");
                        break;
                    case 0x0F:
-                       called_dte_size = 4096;
+                       sprintf(tmpbuf, "From the called DTE : %%u (4096)");
                        break;
                    default:
-                       called_dte_size = 0;
+                       sprintf(tmpbuf, "From the called DTE : %%u (Unknown)");
                        break;
                    }
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           decode_numeric_bitfield(byte1, 0x0F, 1*8, tmpbuf));
 
-                   switch (ptr[2])
+                   byte2 = tvb_get_guint8(tvb, *offset + 1);
+                   switch (byte2)
                    {
                    case 0x04:
-                       calling_dte_size = 16;
+                       sprintf(tmpbuf, "From the calling DTE : %%u (16)");
                        break;
                    case 0x05:
-                       calling_dte_size = 32;
+                       sprintf(tmpbuf, "From the calling DTE : %%u (32)");
                        break;
                    case 0x06:
-                       calling_dte_size = 64;
+                       sprintf(tmpbuf, "From the calling DTE : %%u (64)");
                        break;
                    case 0x07:
-                       calling_dte_size = 128;
+                       sprintf(tmpbuf, "From the calling DTE : %%u (128)");
                        break;
                    case 0x08:
-                       calling_dte_size = 256;
+                       sprintf(tmpbuf, "From the calling DTE : %%u (256)");
                        break;
                    case 0x0D:
-                       calling_dte_size = 512;
+                       sprintf(tmpbuf, "From the calling DTE : %%u (512)");
                        break;
                    case 0x0C:
-                       calling_dte_size = 1024;
+                       sprintf(tmpbuf, "From the calling DTE : %%u (1024)");
                        break;
                    case 0x0E:
-                       calling_dte_size = 2048;
+                       sprintf(tmpbuf, "From the calling DTE : %%u (2048)");
                        break;
                    case 0x0F:
-                       calling_dte_size = 4096;
+                       sprintf(tmpbuf, "From the calling DTE : %%u (4096)");
                        break;
                    default:
-                       calling_dte_size = 0;
+                       sprintf(tmpbuf, "From the calling DTE : %%u (Unknown)");
                        break;
                    }
-                   proto_tree_add_text(tree, *offset, 3,
-                           "Packet Size: called DTE: %d - calling DTE: %d",
-                           called_dte_size,
-                           calling_dte_size);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+                           decode_numeric_bitfield(byte2, 0x0F, 1*8, tmpbuf));
                }
                break;
            case X25_FAC_WINDOW_SIZE:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 3,
-                           "Window Size: called DTE: %d - calling DTE: %d",
-                           ptr[1], ptr[2]);
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Window size)", fac);
+                   fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_window_size);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           decode_numeric_bitfield(tvb_get_guint8(tvb, *offset+1),
+                               0x7F, 1*8, "From the called DTE: %u"));
+                   proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+                           decode_numeric_bitfield(tvb_get_guint8(tvb, *offset+2),
+                               0x7F, 1*8, "From the calling DTE: %u"));
+               }
                break;
            case X25_FAC_RPOA_SELECTION:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 3,
-                                       "RPOA: %d%d%d%d",
-                                       ptr[1] >> 4,
-                                       ptr[1] & 0x0F,
-                                       ptr[2] >> 4,
-                                       ptr[2] & 0x0F);
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(RPOA selection)", fac);
+                   fac_subtree = proto_item_add_subtree(ti,
+                           ett_x25_fac_rpoa_selection);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 2,
+                                       "Data network identification code : %04X",
+                                       tvb_get_ntohs(tvb, *offset+1));
+               }
                break;
            case X25_FAC_TRANSIT_DELAY:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 3,
-                                       "Transit delay: %d",
-                                       (ptr[1]<<8) + ptr[2]);
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Transit delay selection and indication)", fac);
+                   fac_subtree = proto_item_add_subtree(ti,
+                           ett_x25_fac_transit_delay);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 2,
+                                       "Transit delay: %d ms",
+                                       tvb_get_ntohs(tvb, *offset+1));
+               }
                break;
            default:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 3,
-                                       "Unknown facility %02X, values %02X%02X",
-                                       ptr[0], ptr[1], ptr[2]);
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+                           "Code : %02X (Unknown class B)", fac);
+                   fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_unknown);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 2,
+                           "Parameter %04X", tvb_get_ntohs(tvb, *offset+1));
+               }
                break;
            }
            (*offset) += 3;
            len -= 3;
-           ptr += 3;
            break;
        case X25_FAC_CLASS_C:
-           if (tree)
-               proto_tree_add_text(tree, *offset, 4,
-                                   "Unknown facility %02X, values %02X%02X%02X",
-                                   ptr[0], ptr[1], ptr[2], ptr[3]);
+           if (fac_tree) {
+               ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+                       "Code : %02X (Unknown class C)", fac);
+               fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_unknown);
+               proto_tree_add_text(fac_subtree, tvb, *offset+1, 3,
+                       "Parameter %06X",
+                       tvb_get_ntoh24(tvb, *offset+1));
+           }
            (*offset) += 4;
            len -= 4;
-           ptr += 4;
            break;
        case X25_FAC_CLASS_D:
-           switch (*ptr) {
+           switch (fac) {
            case X25_FAC_CALL_TRANSFER:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2+ptr[1],
-                                       "Call Transfer: reason = %02X",
-                                       ptr[2]);
+               if (fac_tree) {
+                   int i;
+                   char tmpbuf[256];
+
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Call redirection or deflection notification)", fac);
+                   fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_call_transfer);
+                   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);
+                   if ((byte2 & 0xC0) == 0xC0) {
+                       proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+                               "Reason : call deflection by the originally "
+                               "called DTE address");
+                   }
+                   else {
+                       switch (byte2) {
+                       case 0x01:
+                           proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+                                   "Reason : originally called DTE busy");
+                           break;
+                       case 0x07:
+                           proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+                                   "Reason : call dist. within a hunt group");
+                           break;
+                       case 0x09:
+                           proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+                                   "Reason : originally called DTE out of order");
+                           break;
+                       case 0x0F:
+                           proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+                                   "Reason : systematic call redirection");
+                           break;
+                       default:
+                           proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+                                   "Reason : unknown");
+                           break;
+                       }
+                   }
+                   byte3 = tvb_get_guint8(tvb, *offset+3);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+3, 1,
+                           "Number of semi-octets in DTE address : %u",
+                           byte3);
+                   for (i = 0; i < byte3; i++) {
+                       if (i % 2 == 0) {
+                           tmpbuf[i] = ((tvb_get_guint8(tvb, *offset+4+i/2) >> 4)
+                                   & 0x0F) + '0';
+                           /* if > 9, convert to the right hexadecimal letter */
+                           if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+                       } else {
+                           tmpbuf[i] = (tvb_get_guint8(tvb, *offset+4+i/2)
+                                   & 0x0F) + '0';
+                           /* if > 9, convert to the right hexadecimal letter */
+                           if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+                       }
+                   }
+                   tmpbuf[i] = 0;
+                   proto_tree_add_text(fac_subtree, tvb, *offset+4, byte1 - 2,
+                           "DTE address : %s", tmpbuf);
+               }
                break;
-           case X25_FAC_ADDR_EXT:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2+ptr[1],
-                                       "Address extension");
+           case X25_FAC_CALLING_ADDR_EXT:
+               if (fac_tree) {
+                   int i;
+                   char tmpbuf[256];
+
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Calling address extension)", fac);
+                   fac_subtree = proto_item_add_subtree(ti,
+                           ett_x25_fac_calling_addr_ext);
+                   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) & 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++) {
+                       if (i % 2 == 0) {
+                           tmpbuf[i] = ((tvb_get_guint8(tvb, *offset+3+i/2) >> 4)
+                                   & 0x0F) + '0';
+                           /* if > 9, convert to the right hexadecimal letter */
+                           if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+                       } else {
+                           tmpbuf[i] = (tvb_get_guint8(tvb, *offset+3+i/2)
+                                   & 0x0F) + '0';
+                           /* if > 9, convert to the right hexadecimal letter */
+                           if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+                       }
+                   }
+                   tmpbuf[i] = 0;
+                   proto_tree_add_text(fac_subtree, tvb, *offset+3, byte1 - 1,
+                           "DTE address : %s", tmpbuf);
+               }
                break;
            case X25_FAC_CALLED_ADDR_EXT:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2+ptr[1],
-                                       "Called address extension");
+               if (fac_tree) {
+                   int i;
+                   char tmpbuf[256];
+
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Called address extension)", fac);
+                   fac_subtree = proto_item_add_subtree(ti,
+                           ett_x25_fac_called_addr_ext);
+                   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) & 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++) {
+                       if (i % 2 == 0) {
+                           tmpbuf[i] = ((tvb_get_guint8(tvb, *offset+3+i/2) >> 4)
+                                   & 0x0F) + '0';
+                           /* if > 9, convert to the right hexadecimal letter */
+                           if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+                       } else {
+                           tmpbuf[i] = (tvb_get_guint8(tvb, *offset+3+i/2)
+                                   & 0x0F) + '0';
+                           /* if > 9, convert to the right hexadecimal letter */
+                           if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+                       }
+                   }
+                   tmpbuf[i] = 0;
+                   proto_tree_add_text(fac_subtree, tvb, *offset+3, byte1 - 1,
+                           "DTE address : %s", tmpbuf);
+               }
                break;
            case X25_FAC_ETE_TRANSIT_DELAY:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2+ptr[1],
-                                       "End to end transit delay");
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(End to end transit delay)", fac);
+                   fac_subtree = proto_item_add_subtree(ti,
+                           ett_x25_fac_ete_transit_delay);
+                   byte1 = tvb_get_guint8(tvb, *offset+1);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           "Length : %u", byte1);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+2, byte1, "Value");
+               }
                break;
            case X25_FAC_CALL_DEFLECT:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2+ptr[1],
-                                       "Call deflection: reason = %02X",
-                                       ptr[2]);
+               if (fac_tree) {
+                   int i;
+                   char tmpbuf[256];
+
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+                           "(Call deflection selection)", fac);
+                   fac_subtree = proto_item_add_subtree(ti,
+                           ett_x25_fac_call_deflect);
+                   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);
+                   if ((byte2 & 0xC0) == 0xC0)
+                       proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+                               "Reason : call DTE originated");
+                   else
+                       proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+                               "Reason : unknown");
+                   byte3 = tvb_get_guint8(tvb, *offset+3);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+3, 1,
+                           "Number of semi-octets in the alternative DTE address : %u",
+                           byte3);
+                   for (i = 0; i < byte3; i++) {
+                       if (i % 2 == 0) {
+                           tmpbuf[i] = ((tvb_get_guint8(tvb, *offset+4+i/2) >> 4)
+                                   & 0x0F) + '0';
+                           /* if > 9, convert to the right hexadecimal letter */
+                           if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+                       } else {
+                           tmpbuf[i] = (tvb_get_guint8(tvb, *offset+4+i/2)
+                                   & 0x0F) + '0';
+                           /* if > 9, convert to the right hexadecimal letter */
+                           if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+                       }
+                   }
+                   tmpbuf[i] = 0;
+                   proto_tree_add_text(fac_subtree, tvb, *offset+4, byte1 - 2,
+                           "Alternative DTE address : %s", tmpbuf);
+               }
+               break;
+           case X25_FAC_PRIORITY:
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+                           "Code : %02X (Priority)", fac);
+                   fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_priority);
+                   byte1 = tvb_get_guint8(tvb, *offset+1);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           "Length : %u", byte1);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+2, byte1, "Value");
+               }
                break;
            default:
-               if (tree)
-                   proto_tree_add_text(tree, *offset, 2+ptr[1],
-                                       "Unknown facility %02X, length %02X",
-                                       ptr[0], ptr[1]);
+               if (fac_tree) {
+                   ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+                           "Code : %02X (Unknown class D)", fac);
+                   fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_unknown);
+                   byte1 = tvb_get_guint8(tvb, *offset+1);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+                           "Length : %u", byte1);
+                   proto_tree_add_text(fac_subtree, tvb, *offset+2, byte1, "Value");
+               }
            }
-           (*offset) += ptr[1]+2;
-           len -= ptr[1]+2;
-           ptr += ptr[1]+2;
+           byte1 = tvb_get_guint8(tvb, *offset+1);
+           (*offset) += byte1+2;
+           len -= byte1+2;
            break;
        }
     }
 }
 
-void
-x25_ntoa(proto_tree *tree, int *offset, const guint8 *p,
-        frame_data *fd, gboolean toa)
+static void
+x25_ntoa(proto_tree *tree, int *offset, tvbuff_t *tvb,
+        packet_info *pinfo, gboolean is_registration)
 {
     int len1, len2;
     int i;
     char addr1[16], addr2[16];
     char *first, *second;
+    guint8 byte;
+    int localoffset;
+
+    byte = tvb_get_guint8(tvb, *offset);
+    len1 = (byte >> 0) & 0x0F;
+    len2 = (byte >> 4) & 0x0F;
+
+    if (tree) {
+       proto_tree_add_text(tree, tvb, *offset, 1,
+               decode_numeric_bitfield(byte, 0xF0, 1*8,
+                       is_registration ?
+                         "DTE address length : %u" :
+                         "Calling address length : %u"));
+       proto_tree_add_text(tree, tvb, *offset, 1,
+               decode_numeric_bitfield(byte, 0x0F, 1*8,
+                       is_registration ?
+                         "DCE address length : %u" :
+                         "Called address length : %u"));
+    }
+    (*offset)++;
+
+    localoffset = *offset;
+    byte = tvb_get_guint8(tvb, localoffset);
+
+    first=addr1;
+    second=addr2;
+    for (i = 0; i < (len1 + len2); i++) {
+       if (i < len1) {
+           if (i % 2 != 0) {
+               *first++ = ((byte >> 0) & 0x0F) + '0';
+               localoffset++;
+               byte = tvb_get_guint8(tvb, localoffset);
+           } else {
+               *first++ = ((byte >> 4) & 0x0F) + '0';
+           }
+       } else {
+           if (i % 2 != 0) {
+               *second++ = ((byte >> 0) & 0x0F) + '0';
+               localoffset++;
+               byte = tvb_get_guint8(tvb, localoffset);
+           } else {
+               *second++ = ((byte >> 4) & 0x0F) + '0';
+           }
+       }
+    }
+
+    *first  = '\0';
+    *second = '\0';
+
+    if (len1) {
+       if (check_col(pinfo->cinfo, COL_RES_DL_DST))
+           col_add_str(pinfo->cinfo, COL_RES_DL_DST, addr1);
+       if (tree)
+           proto_tree_add_text(tree, tvb, *offset,
+                               (len1 + 1) / 2,
+                               is_registration ?
+                                 "DCE address : %s" :
+                                 "Called address : %s",
+                               addr1);
+    }
+    if (len2) {
+       if (check_col(pinfo->cinfo, COL_RES_DL_SRC))
+           col_add_str(pinfo->cinfo, COL_RES_DL_SRC, addr2);
+       if (tree)
+           proto_tree_add_text(tree, tvb, *offset + len1/2,
+                               (len2+1)/2+(len1%2+(len2+1)%2)/2,
+                               is_registration ?
+                                 "DTE address : %s" :
+                                 "Calling address : %s",
+                               addr2);
+    }
+    (*offset) += ((len1 + len2 + 1) / 2);
+}
+
+static void
+x25_toa(proto_tree *tree, int *offset, tvbuff_t *tvb,
+       packet_info *pinfo)
+{
+    int len1, len2;
+    int i;
+    char addr1[256], addr2[256];
+    char *first, *second;
+    guint8 byte;
+    int localoffset;
 
-    len1  = (*p >> 0) & 0x0F;
-    len2 = (*p >> 4) & 0x0F;
+    len1 = tvb_get_guint8(tvb, *offset);
     if (tree) {
-       proto_tree_add_text(tree, *offset, 1,
-               "%s address length : %d",
-               toa ? "Called" : "Calling",
-               len1);
-       proto_tree_add_text(tree, *offset, 1,
-               "%s address length : %d",
-               toa ? "Calling" : "Called",
-               len2);
+       proto_tree_add_text(tree, tvb, *offset, 1,
+                   "Called address length : %u",
+                   len1);
     }
     (*offset)++;
 
-    p++;
+    len2 = tvb_get_guint8(tvb, *offset);
+    if (tree) {
+       proto_tree_add_text(tree, tvb, *offset, 1,
+                   "Calling address length : %u",
+                   len2);
+    }
+    (*offset)++;
+
+    localoffset = *offset;
+    byte = tvb_get_guint8(tvb, localoffset);
 
+    /*
+     * XXX - the first two half-octets of the address are the TOA and
+     * NPI; process them as such and, if the TOA says an address is
+     * an alternative address, process it correctly (i.e., not as a
+     * sequence of half-octets containing digit values).
+     */
     first=addr1;
     second=addr2;
     for (i = 0; i < (len1 + len2); i++) {
        if (i < len1) {
            if (i % 2 != 0) {
-               *first++ = ((*p >> 0) & 0x0F) + '0';
-               p++;
+               *first++ = ((byte >> 0) & 0x0F) + '0';
+               localoffset++;
+               byte = tvb_get_guint8(tvb, localoffset);
            } else {
-               *first++ = ((*p >> 4) & 0x0F) + '0';
+               *first++ = ((byte >> 4) & 0x0F) + '0';
            }
        } else {
            if (i % 2 != 0) {
-               *second++ = ((*p >> 0) & 0x0F) + '0';
-               p++;
+               *second++ = ((byte >> 0) & 0x0F) + '0';
+               localoffset++;
+               byte = tvb_get_guint8(tvb, localoffset);
            } else {
-               *second++ = ((*p >> 4) & 0x0F) + '0';
+               *second++ = ((byte >> 4) & 0x0F) + '0';
            }
        }
     }
@@ -881,537 +1323,1196 @@ x25_ntoa(proto_tree *tree, int *offset, const guint8 *p,
     *second = '\0';
 
     if (len1) {
-       if(check_col(fd, COL_RES_DL_DST))
-           col_add_str(fd, COL_RES_DL_DST, addr1);
+       if (check_col(pinfo->cinfo, COL_RES_DL_DST))
+           col_add_str(pinfo->cinfo, COL_RES_DL_DST, addr1);
        if (tree)
-           proto_tree_add_text(tree, *offset,
+           proto_tree_add_text(tree, tvb, *offset,
                                (len1 + 1) / 2,
-                               "%s address : %s",
-                               toa ? "Called" : "Calling",
+                               "Called address : %s",
                                addr1);
     }
     if (len2) {
-       if(check_col(fd, COL_RES_DL_SRC))
-           col_add_str(fd, COL_RES_DL_SRC, addr2);
+       if (check_col(pinfo->cinfo, COL_RES_DL_SRC))
+           col_add_str(pinfo->cinfo, COL_RES_DL_SRC, addr2);
        if (tree)
-           proto_tree_add_text(tree, *offset + len1/2,
+           proto_tree_add_text(tree, tvb, *offset + len1/2,
                                (len2+1)/2+(len1%2+(len2+1)%2)/2,
-                               "%s address : %s",
-                               toa ? "Calling" : "Called",
+                               "Calling address : %s",
                                addr2);
     }
     (*offset) += ((len1 + len2 + 1) / 2);
 }
 
-int
-get_x25_pkt_len(const char *data)
+static int
+get_x25_pkt_len(tvbuff_t *tvb)
 {
-    int length, called_len, calling_len, dte_len, dce_len;
+    guint length, called_len, calling_len, dte_len, dce_len;
+    guint8 byte2, bytex;
 
-    switch ((guint8)data[2])
+    byte2 = tvb_get_guint8(tvb, 2);
+    switch (byte2)
     {
     case X25_CALL_REQUEST:
-       called_len  = (data[3] >> 0) & 0x0F;
-       calling_len = (data[3] >> 4) & 0x0F;
+       bytex = tvb_get_guint8(tvb, 3);
+       called_len  = (bytex >> 0) & 0x0F;
+       calling_len = (bytex >> 4) & 0x0F;
        length = 4 + (called_len + calling_len + 1) / 2; /* addr */
-       length += (1 + data[length]); /* facilities */
-       return length;
+       if (length < tvb_reported_length(tvb))
+           length += (1 + tvb_get_guint8(tvb, length)); /* facilities */
+
+       return MIN(tvb_reported_length(tvb),length);
 
     case X25_CALL_ACCEPTED:
-       called_len  = (data[3] >> 0) & 0x0F;
-       calling_len = (data[3] >> 4) & 0x0F;
+       /* The calling/called address length byte (following the packet type)
+        * is not mandatory, so we must check the packet length before trying
+        * to read it */
+       if (tvb_reported_length(tvb) == 3)
+           return(3);
+       bytex = tvb_get_guint8(tvb, 3);
+       called_len  = (bytex >> 0) & 0x0F;
+       calling_len = (bytex >> 4) & 0x0F;
        length = 4 + (called_len + calling_len + 1) / 2; /* addr */
-       length += (1 + data[length]); /* facilities */
-       return length;
+       if (length < tvb_reported_length(tvb))
+           length += (1 + tvb_get_guint8(tvb, length)); /* facilities */
 
-    case X25_CLEAR_REQUEST:
-       return 5;
+       return MIN(tvb_reported_length(tvb),length);
 
-    case X25_CLEAR_CONFIRMATION:
-       return 3;
+    case X25_CLEAR_REQUEST:
+    case X25_RESET_REQUEST:
+    case X25_RESTART_REQUEST:
+       return MIN(tvb_reported_length(tvb),5);
 
     case X25_DIAGNOSTIC:
-       return 4;
+       return MIN(tvb_reported_length(tvb),4);
 
+    case X25_CLEAR_CONFIRMATION:
     case X25_INTERRUPT:
-       return 3;
-
     case X25_INTERRUPT_CONFIRMATION:
-       return 3;
-
-    case X25_RESET_REQUEST:
-       return 5;
-
     case X25_RESET_CONFIRMATION:
-       return 3;
-               
-    case X25_RESTART_REQUEST:
-       return 5;
-               
     case X25_RESTART_CONFIRMATION:
-       return 3;
+       return MIN(tvb_reported_length(tvb),3);
 
     case X25_REGISTRATION_REQUEST:
-       dce_len  = (data[3] >> 0) & 0x0F;
-       dte_len = (data[3] >> 4) & 0x0F;
+       bytex = tvb_get_guint8(tvb, 3);
+       dce_len = (bytex >> 0) & 0x0F;
+       dte_len = (bytex >> 4) & 0x0F;
        length = 4 + (dte_len + dce_len + 1) / 2; /* addr */
-       length += (1 + data[length]); /* registration */
-       return length;
-               
+       if (length < tvb_reported_length(tvb))
+           length += (1 + tvb_get_guint8(tvb, length)); /* registration */
+
+       return MIN(tvb_reported_length(tvb),length);
+
     case X25_REGISTRATION_CONFIRMATION:
-       dce_len  = (data[5] >> 0) & 0x0F;
-       dte_len = (data[5] >> 4) & 0x0F;
+       bytex = tvb_get_guint8(tvb, 5);
+       dce_len = (bytex >> 0) & 0x0F;
+       dte_len = (bytex >> 4) & 0x0F;
        length = 6 + (dte_len + dce_len + 1) / 2; /* addr */
-       length += (1 + data[length]); /* registration */
-       return length;
+       if (length < tvb_reported_length(tvb))
+           length += (1 + tvb_get_guint8(tvb, length)); /* registration */
+
+       return MIN(tvb_reported_length(tvb),length);
     }
-           
-    if ((data[2] & 0x01) == X25_DATA) return 3;
 
-    switch (data[2])
+    if (PACKET_IS_DATA(byte2))
+       return MIN(tvb_reported_length(tvb),3);
+
+    switch (PACKET_TYPE_FC(byte2))
     {
     case X25_RR:
-       return 3;
+       return MIN(tvb_reported_length(tvb),3);
 
     case X25_RNR:
-       return 3;
+       return MIN(tvb_reported_length(tvb),3);
 
     case X25_REJ:
-       return 3;
+       return MIN(tvb_reported_length(tvb),3);
     }
 
     return 0;
 }
 
-void
-dissect_x25(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
+static const value_string prt_id_vals[] = {
+        {PRT_ID_ISO_8073,           "ISO 8073 COTP"},
+        {PRT_ID_ISO_8602,           "ISO 8602 CLTP"},
+        {PRT_ID_ISO_10736_ISO_8073, "ISO 10736 in conjunction with ISO 8073 COTP"},
+        {PRT_ID_ISO_10736_ISO_8602, "ISO 10736 in conjunction with ISO 8602 CLTP"},
+        {0x00,                      NULL}
+};
+
+static const value_string sharing_strategy_vals[] = {
+        {0x00,            "No sharing"},
+        {0x00,            NULL}
+};
+
+static void
+dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    x25_dir_t dir, gboolean side)
 {
-    proto_tree *x25_tree=0, *ti;
-    int localoffset=offset;
-    int x25_pkt_len;
+    proto_tree *x25_tree=0, *gfi_tree=0, *userdata_tree=0;
+    proto_item *ti;
+    guint localoffset=0;
+    guint x25_pkt_len;
     int modulo;
     guint16 vc;
-    void (*dissect)(const u_char *, int, frame_data *, proto_tree *);
-    gboolean toa=FALSE;
+    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 = 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");
+
+    bytes0_1 = tvb_get_ntohs(tvb, 0);
+
+    modulo = ((bytes0_1 & 0x2000) ? 128 : 8);
+    vc     = (int)(bytes0_1 & 0x0FFF);
+
+    pinfo->ctype = CT_X25;
+    pinfo->circuit_id = vc;
+
+    if (bytes0_1 & X25_ABIT) toa = TRUE;
+    else toa = FALSE;
+
+    x25_pkt_len = get_x25_pkt_len(tvb);
+    if (x25_pkt_len < 3) /* packet too short */
+    {
+       if (check_col(pinfo->cinfo, COL_INFO))
+           col_set_str(pinfo->cinfo, COL_INFO, "Invalid/short X.25 packet");
+       if (tree)
+           proto_tree_add_protocol_format(tree, proto_x25, tvb, 0, -1,
+                   "Invalid/short X.25 packet");
+       return;
+    }
 
-    if (check_col(fd, COL_PROTOCOL))
-       col_add_str(fd, COL_PROTOCOL, "X.25");
+    pkt_type = tvb_get_guint8(tvb, 2);
+    if (PACKET_IS_DATA(pkt_type)) {
+       if (bytes0_1 & X25_QBIT)
+           q_bit_set = TRUE;
+    }
 
-    modulo = ((pd[localoffset] & 0x20) ? 128 : 8);
-    vc = (int)(pd[localoffset] & 0x0F) + (int)pd[localoffset+1];
-    x25_pkt_len = get_x25_pkt_len(&pd[localoffset]);
     if (tree) {
-       ti = proto_tree_add_item(tree, proto_x25, localoffset, x25_pkt_len,
-                                NULL);
-       x25_tree = proto_item_add_subtree(ti, ETT_X25);
-       proto_tree_add_text(x25_tree, localoffset, 1,
-                           "GFI : Q: %d, D: %d, Mod: %d",
-                           (pd[localoffset] & 0x80) ? 1 : 0,
-                           (pd[localoffset] & 0x40) ? 1 : 0,
-                           modulo);
-       proto_tree_add_item_format(x25_tree, hf_x25_lcn, localoffset, 2,
-                                  (int)(pd[localoffset] & 0x0F) +
-                                  (int)pd[localoffset+1],
-                                  "Logical channel : %3.3X",
-                                  vc);
+        ti = proto_tree_add_item(tree, proto_x25, tvb, 0, x25_pkt_len, FALSE);
+        x25_tree = proto_item_add_subtree(ti, ett_x25);
+        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 (PACKET_IS_DATA(pkt_type)) {
+            proto_tree_add_boolean(gfi_tree, hf_x25_qbit, tvb, 0, 2,
+                bytes0_1);
+        }
+        else if (pkt_type == X25_CALL_REQUEST ||
+            pkt_type == X25_CALL_ACCEPTED ||
+            pkt_type == X25_CLEAR_REQUEST ||
+            pkt_type == X25_CLEAR_CONFIRMATION) {
+            proto_tree_add_boolean(gfi_tree, hf_x25_abit, tvb, 0, 2,
+                bytes0_1);
+        }
+
+        if (pkt_type == X25_CALL_REQUEST || pkt_type == X25_CALL_ACCEPTED ||
+            PACKET_IS_DATA(pkt_type)) {
+            proto_tree_add_boolean(gfi_tree, hf_x25_dbit, tvb, 0, 2,
+                bytes0_1);
+        }
+        proto_tree_add_uint(gfi_tree, hf_x25_mod, tvb, 0, 2, bytes0_1);
     }
-    switch (pd[localoffset+2]) {
+
+    switch (pkt_type) {
     case X25_CALL_REQUEST:
-       if (pd[localoffset+2] & 0x80) /* TOA/NPI address format */
-           toa = TRUE;
-
-       if(check_col(fd, COL_INFO))
-           col_add_fstr(fd, COL_INFO, "%s VC:%d",
-                   (fd->flags & FROM_DCE) ? "Inc. call"
-                                          : "Call req." ,
-                    vc);
-       if (x25_tree)
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                   "CALL",
-                   (fd->flags & FROM_DCE) ? "Incoming call"
-                                          : "Call request");
-       localoffset += 3;
-       x25_ntoa(x25_tree, &localoffset, &pd[localoffset], fd, toa);
+        switch (dir) {
+
+       case X25_FROM_DCE:
+           short_name = "Inc. call";
+           long_name = "Incoming call";
+           break;
+
+       case X25_FROM_DTE:
+           short_name = "Call req.";
+           long_name = "Call request";
+           break;
 
-       if (localoffset < x25_pkt_len+2) /* facilities */
-           dump_facilities(x25_tree, &localoffset, &pd[localoffset]);
+       case X25_UNKNOWN:
+           short_name = "Inc. call/Call req.";
+           long_name = "Incoming call/Call request";
+           break;
+       }
+       if (check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d", short_name, vc);
+       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_REQUEST, "Packet Type: %s", long_name);
+       }
+       localoffset = 3;
+       if (localoffset < x25_pkt_len) { /* calling/called addresses */
+           if (toa)
+               x25_toa(x25_tree, &localoffset, tvb, pinfo);
+           else
+               x25_ntoa(x25_tree, &localoffset, tvb, pinfo, FALSE);
+       }
 
-       if (localoffset < fd->cap_len) /* user data */
+       if (localoffset < x25_pkt_len) /* facilities */
+           dump_facilities(x25_tree, &localoffset, tvb);
+
+       if (localoffset < tvb_reported_length(tvb)) /* user data */
        {
-           if (pd[localoffset] == 0xCC)
-           {
-               x25_hash_add_proto_start(vc, fd->abs_secs,
-                                        fd->abs_usecs, dissect_ip);
-               if (x25_tree)
-                   proto_tree_add_text(x25_tree, localoffset, 1,
-                                       "pid = IP");
-               localoffset++;
+           guint8 spi;
+           int is_x_264;
+           guint8 prt_id;
+
+           if (x25_tree) {
+               ti = proto_tree_add_text(x25_tree, tvb, localoffset, -1,
+                       "User data");
+               userdata_tree = proto_item_add_subtree(ti, ett_x25_user_data);
            }
-           else if (pd[localoffset] == 0x03 &&
-                    pd[localoffset+1] == 0x01 &&
-                    pd[localoffset+2] == 0x01 &&
-                    pd[localoffset+3] == 0x00)
-           {
-               x25_hash_add_proto_start(vc, fd->abs_secs,
-                                        fd->abs_usecs, dissect_cotp);
-               if (x25_tree)
-                   proto_tree_add_text(x25_tree, localoffset, 4,
-                                       "pid = COTP");
-               localoffset += 4;
+
+           /* X.263/ISO 9577 says that:
+
+                   When CLNP or ESIS are run over X.25, the SPI
+                   is 0x81 or 0x82, respectively; those are the
+                   NLPIDs for those protocol.
+
+                   When X.224/ISO 8073 COTP is run over X.25, and
+                   when ISO 11570 explicit identification is being
+                   used, the first octet of the user data field is
+                   a TPDU length field, and the rest is "as defined
+                   in ITU-T Rec. X.225 | ISO/IEC 8073, Annex B,
+                   or ITU-T Rec. X.264 and ISO/IEC 11570".
+
+                   When X.264/ISO 11570 default identification is
+                   being used, there is no user data field in the
+                   CALL REQUEST packet.  This is for X.225/ISO 8073
+                   COTP.
+
+              It also says that SPI values from 0x03 through 0x3f are
+              reserved and are in use by X.224/ISO 8073 Annex B and
+              X.264/ISO 11570.  The note says that those values are
+              not NLPIDs, they're "used by the respective higher layer
+              protocol" and "not used for higher layer protocol
+              identification".  I infer from this and from what
+              X.264/ISO 11570 says that this means that values in those
+              range are valid values for the first octet of an
+              X.224/ISO 8073 packet or for X.264/ISO 11570.
+
+              Annex B of X.225/ISO 8073 mentions some additional TPDU
+              types that can be put in what I presume is the user
+              data of connect requests.  It says that:
+
+                   The sending transport entity shall:
+
+                       a) either not transmit any TPDU in the NS-user data
+                          parameter of the N-CONNECT request primitive; or
+
+                       b) transmit the UN-TPDU (see ITU-T Rec. X.264 and
+                          ISO/IEC 11570) followed by the NCM-TPDU in the
+                          NS-user data parameter of the N-CONNECT request
+                          primitive.
+
+              I don't know if this means that the user data field
+              will contain a UN TPDU followed by an NCM TPDU or not.
+
+              X.264/ISO 11570 says that:
+
+                   When default identification is being used,
+                   X.225/ISO 8073 COTP is identified.  No user data
+                   is sent in the network-layer connection request.
+
+                   When explicit identification is being used,
+                   the user data is a UN TPDU ("Use of network
+                   connection TPDU"), which specifies the transport
+                   protocol to use over this network connection.
+                   It also says that the length of a UN TPDU shall
+                   not exceed 32 octets, i.e. shall not exceed 0x20;
+                   it says this is "due to the desire not to conflict
+                   with the protocol identifier field carried by X.25
+                   CALL REQUEST/INCOMING CALL packets", and says that
+                   field has values specified in X.244.  X.244 has been
+                   superseded by X.263/ISO 9577, so that presumably
+                   means the goal is to allow a UN TPDU's length
+                   field to be distinguished from an NLPID, allowing
+                   you to tell whether X.264/ISO 11570 explicit
+                   identification is being used or an NLPID is
+                   being used as the SPI.
+
+              I read this as meaning that, if the ISO mechanisms are
+              used to identify the protocol being carried over X.25:
+
+                   if there's no user data in the CALL REQUEST/
+                   INCOMING CALL packet, it's COTP;
+
+                   if there is user data, then:
+
+                       if the first octet is less than or equal to
+                       32, it might be a UN TPDU, and that identifies
+                       the transport protocol being used, and
+                       it may be followed by more data, such
+                       as a COTP NCM TPDU if it's COTP;
+
+                       if the first octet is greater than 32, it's
+                       an NLPID, *not* a TPDU length, and the
+                       stuff following it is *not* a TPDU.
+
+              Figure A.2 of X.263/ISO 9577 seems to say that the
+              first octet of the user data is a TPDU length field,
+              in the range 0x03 through 0x82, and says they are
+              for X.225/ISO 8073 Annex B or X.264/ISO 11570.
+
+              However, X.264/ISO 11570 seems to imply that the length
+              field would be that of a UN TPDU, which must be less
+              than or equal to 0x20, and X.225/ISO 8073 Annex B seems
+              to indicate that the user data must begin with
+              an X.264/ISO 11570 UN TPDU, so I'd say that A.2 should
+              have said "in the range 0x03 through 0x20", instead
+              (the length value doesn't include the length field,
+              and the minimum UN TPDU has length, type, PRT-ID,
+              and SHARE, so that's 3 bytes without the length). */
+           spi = tvb_get_guint8(tvb, localoffset);
+           if (spi > 32 || spi < 3) {
+               /* First octet is > 32, or < 3, so the user data isn't an
+                  X.264/ISO 11570 UN TPDU */
+               is_x_264 = FALSE;
+           } else {
+               /* First octet is >= 3 and <= 32, so the user data *might*
+                  be an X.264/ISO 11570 UN TPDU.  Check whether we have
+                  enough data to see if it is. */
+               if (tvb_bytes_exist(tvb, localoffset+1, 1)) {
+                   /* We do; check whether the second octet is 1. */
+                   if (tvb_get_guint8(tvb, localoffset+1) == 0x01) {
+                       /* Yes, the second byte is 1, so it looks like
+                          a UN TPDU. */
+                       is_x_264 = TRUE;
+                   } else {
+                       /* No, the second byte is not 1, so it's not a
+                          UN TPDU. */
+                       is_x_264 = FALSE;
+                   }
+               } else {
+                   /* We can't see the second byte of the putative UN
+                      TPDU, so we don't know if that's what it is. */
+                   is_x_264 = -1;
+               }
            }
-           else {
-               if (x25_tree)
-                   proto_tree_add_text(x25_tree, localoffset,
-                                       fd->cap_len-localoffset, "Data");
-               localoffset = fd->cap_len;
+           if (is_x_264 == -1) {
+               /*
+                * We don't know what it is; just skip it.
+                */
+               localoffset = tvb_length(tvb);
+           } else if (is_x_264) {
+               /* It looks like an X.264 UN TPDU, so show it as such. */
+               if (userdata_tree) {
+                   proto_tree_add_text(userdata_tree, tvb, localoffset, 1,
+                                       "X.264 length indicator: %u",
+                                       spi);
+                   proto_tree_add_text(userdata_tree, tvb, localoffset+1, 1,
+                                       "X.264 UN TPDU identifier: 0x%02X",
+                                       tvb_get_guint8(tvb, localoffset+1));
+               }
+               prt_id = tvb_get_guint8(tvb, localoffset+2);
+               if (userdata_tree) {
+                   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(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)"));
+               }
+
+               /* XXX - dissect the variable part? */
+
+               /* The length doesn't include the length octet itself. */
+               localoffset += spi + 1;
+
+               switch (prt_id) {
+
+               case PRT_ID_ISO_8073:
+                   /* ISO 8073 COTP */
+                   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 */
+                   if (!pinfo->fd->flags.visited)
+                       x25_hash_add_proto_start(vc, pinfo->fd->num, ositp_handle);
+                   break;
+               }
+           } else if (is_x_264 == 0) {
+               /* It doesn't look like a UN TPDU, so compare the first
+                  octet of the CALL REQUEST packet with various X.263/
+                  ISO 9577 NLPIDs, as per Annex A of X.263/ISO 9577. */
+
+               if (userdata_tree) {
+                   proto_tree_add_text(userdata_tree, tvb, localoffset, 1,
+                                       "X.263 secondary protocol ID: %s",
+                                       val_to_str(spi, nlpid_vals, "Unknown (0x%02x)"));
+               }
+
+               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);
+               }
+
+               /*
+                * 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++;
+               }
            }
        }
        break;
     case X25_CALL_ACCEPTED:
-       if (pd[localoffset+2] & 0x80) /* TOA/NPI address format */
-           toa = TRUE;
-
-       if(check_col(fd, COL_INFO))
-           col_add_fstr(fd, COL_INFO, "%s VC:%d",
-                   (fd->flags & FROM_DCE) ? "Call conn."
-                                          : "Call acc." ,
-                   vc);
-       if (x25_tree)
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                   "CALL ACC",
-                   (fd->flags & FROM_DCE) ? "Call connected"
-                                          : "Call accepted");
-       localoffset += 3;
-       x25_ntoa(x25_tree, &localoffset, &pd[localoffset], fd, toa);
-
-       if (localoffset < x25_pkt_len+2) /* facilities */
-           dump_facilities(x25_tree, &localoffset, &pd[localoffset]);
-
-       if (localoffset < fd->cap_len) { /* user data */
-           if (x25_tree)
-               proto_tree_add_text(x25_tree, localoffset,
-                                   fd->cap_len-localoffset, "Data");
-           localoffset=fd->cap_len;
+        switch (dir) {
+
+       case X25_FROM_DCE:
+           short_name = "Call conn.";
+           long_name = "Call connected";
+           break;
+
+       case X25_FROM_DTE:
+           short_name = "Call acc.";
+           long_name = "Call accepted";
+           break;
+
+       case X25_UNKNOWN:
+           short_name = "Call conn./Call acc.";
+           long_name = "Call connected/Call accepted";
+           break;
+       }
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d", short_name, vc);
+       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, "Packet Type: %s", long_name);
        }
+       localoffset = 3;
+        if (localoffset < x25_pkt_len) { /* calling/called addresses */
+           if (toa)
+               x25_toa(x25_tree, &localoffset, tvb, pinfo);
+           else
+               x25_ntoa(x25_tree, &localoffset, tvb, pinfo, FALSE);
+       }
+
+       if (localoffset < x25_pkt_len) /* facilities */
+           dump_facilities(x25_tree, &localoffset, tvb);
        break;
     case X25_CLEAR_REQUEST:
-       if(check_col(fd, COL_INFO)) {
-           col_add_fstr(fd, COL_INFO, "%s VC:%d %s - %s",
-                   (fd->flags & FROM_DCE) ? "Clear ind."
-                                          : "Clear req." ,
-                   vc, clear_code(pd[localoffset+3]),
-                   clear_diag(pd[localoffset+4]));
+        switch (dir) {
+
+       case X25_FROM_DCE:
+           short_name = "Clear ind.";
+           long_name = "Clear indication";
+           break;
+
+       case X25_FROM_DTE:
+           short_name = "Clear req.";
+           long_name = "Clear request";
+           break;
+
+       case X25_UNKNOWN:
+           short_name = "Clear ind./Clear req.";
+           long_name = "Clear indication/Clear request";
+           break;
        }
-       x25_hash_add_proto_end(vc, fd->abs_secs, fd->abs_usecs);
+       if(check_col(pinfo->cinfo, COL_INFO)) {
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d %s - %s", short_name,
+                   vc, clear_code(tvb_get_guint8(tvb, 3)),
+                   clear_diag(tvb_get_guint8(tvb, 4)));
+       }
+       x25_hash_add_proto_end(vc, pinfo->fd->num);
        if (x25_tree) {
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                   "CLEAR",
-                   (fd->flags & FROM_DCE) ? "Clear indication"
-                                          : "Clear request");
-           proto_tree_add_text(x25_tree, localoffset+3, 1,
-                               "Cause : %s", clear_code(pd[localoffset+3]));
-           proto_tree_add_text(x25_tree, localoffset+4, 1,
-                               "Diagnostic : %s",
-                               clear_diag(pd[localoffset+4]));
+           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, "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,
+                   "Diagnostic : %s", clear_diag(tvb_get_guint8(tvb, 4)));
        }
-       localoffset += x25_pkt_len;
+       localoffset = x25_pkt_len;
        break;
     case X25_CLEAR_CONFIRMATION:
-       if (pd[localoffset+2] & 0x80) /* TOA/NPI address format */
-           toa = TRUE;
-
-       if(check_col(fd, COL_INFO))
-           col_add_fstr(fd, COL_INFO, "Clear Conf. VC:%d", vc);
-       if (x25_tree)
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                                      "CLEAR CONF", "Clear confirmation");
-       localoffset += x25_pkt_len;
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "Clear Conf. VC:%d", vc);
+       if (x25_tree) {
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_CLEAR_CONFIRMATION);
+       }
+       localoffset = x25_pkt_len;
 
-       if (localoffset < fd->cap_len) /* extended clear conf format */
-           x25_ntoa(x25_tree, &localoffset, &pd[localoffset], fd, toa);
+       if (localoffset < tvb_reported_length(tvb)) { /* extended clear conf format */
+           if (toa)
+               x25_toa(x25_tree, &localoffset, tvb, pinfo);
+           else
+               x25_ntoa(x25_tree, &localoffset, tvb, pinfo, FALSE);
+       }
 
-       if (localoffset < fd->cap_len) /* facilities */
-           dump_facilities(x25_tree, &localoffset, &pd[localoffset]);
+       if (localoffset < tvb_reported_length(tvb)) /* facilities */
+           dump_facilities(x25_tree, &localoffset, tvb);
        break;
     case X25_DIAGNOSTIC:
-       if(check_col(fd, COL_INFO)) {
-           col_add_fstr(fd, COL_INFO, "Diag. VC:%d %d",
-                   vc, (int)pd[localoffset+3]);
+       if(check_col(pinfo->cinfo, COL_INFO)) {
+           col_add_fstr(pinfo->cinfo, COL_INFO, "Diag. %d",
+                   (int)tvb_get_guint8(tvb, 3));
        }
        if (x25_tree) {
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                                      "DIAG", "Diagnostic");
-           proto_tree_add_text(x25_tree, localoffset+3, 1,
-                               "Diagnostic : %d", (int)pd[localoffset+3]);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_DIAGNOSTIC);
+           proto_tree_add_text(x25_tree, tvb, 3, 1,
+                   "Diagnostic : %d", (int)tvb_get_guint8(tvb, 3));
        }
-       localoffset += x25_pkt_len;
+       localoffset = x25_pkt_len;
        break;
     case X25_INTERRUPT:
-       if(check_col(fd, COL_INFO))
-           col_add_fstr(fd, COL_INFO, "Interrupt VC:%d", vc);
-       if (x25_tree)
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                                      "INTR", "Interrupt");
-       localoffset += x25_pkt_len;
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "Interrupt VC:%d", vc);
+       if (x25_tree) {
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_INTERRUPT);
+       }
+       localoffset = x25_pkt_len;
        break;
     case X25_INTERRUPT_CONFIRMATION:
-       if(check_col(fd, COL_INFO))
-           col_add_fstr(fd, COL_INFO, "Interrupt Conf. VC:%d", vc);
-       if (x25_tree)
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                                      "INTR CONF", "Interrupt confirmation");
-       localoffset += x25_pkt_len;
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "Interrupt Conf. VC:%d", vc);
+       if (x25_tree) {
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_INTERRUPT_CONFIRMATION);
+       }
+       localoffset = x25_pkt_len;
        break;
     case X25_RESET_REQUEST:
-       if(check_col(fd, COL_INFO)) {
-           col_add_fstr(fd, COL_INFO, "%s VC:%d %s - Diag.:%d",
-                   (fd->flags & FROM_DCE) ? "Reset ind."
-                                          : "Reset req.",
-                   vc, reset_code(pd[localoffset+3]),
-                   (int)pd[localoffset+4]);
+        switch (dir) {
+
+       case X25_FROM_DCE:
+           short_name = "Reset ind.";
+           long_name = "Reset indication";
+           break;
+
+       case X25_FROM_DTE:
+           short_name = "Reset req.";
+           long_name = "Reset request";
+           break;
+
+       case X25_UNKNOWN:
+           short_name = "Reset ind./Reset req.";
+           long_name = "Reset indication/Reset request";
+           break;
+       }
+       if(check_col(pinfo->cinfo, COL_INFO)) {
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d %s - Diag.:%d",
+                   short_name, vc, reset_code(tvb_get_guint8(tvb, 3)),
+                   (int)tvb_get_guint8(tvb, 4));
        }
-       x25_hash_add_proto_end(vc, fd->abs_secs, fd->abs_usecs);
+       x25_hash_add_proto_end(vc, pinfo->fd->num);
        if (x25_tree) {
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                   "RESET",
-                   (fd->flags & FROM_DCE) ? "Reset indication"
-                                           : "Reset request");
-           proto_tree_add_text(x25_tree, localoffset+3, 1,
-                               "Cause : %s", reset_code(pd[localoffset+3]));
-           proto_tree_add_text(x25_tree, localoffset+4, 1,
-                               "Diagnostic : %d", (int)pd[localoffset+4]);
+           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, "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,
+                   "Diagnostic : %d", (int)tvb_get_guint8(tvb, 4));
        }
-       localoffset += x25_pkt_len;
+       localoffset = x25_pkt_len;
        break;
     case X25_RESET_CONFIRMATION:
-       if(check_col(fd, COL_INFO))
-           col_add_fstr(fd, COL_INFO, "Reset conf. VC:%d", vc);
-       if (x25_tree)
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                                      "RESET CONF", "Reset confirmation");
-       localoffset += x25_pkt_len;
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "Reset conf. VC:%d", vc);
+       if (x25_tree) {
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_RESET_CONFIRMATION);
+       }
+       localoffset = x25_pkt_len;
        break;
     case X25_RESTART_REQUEST:
-       if(check_col(fd, COL_INFO)) {
-           col_add_fstr(fd, COL_INFO, "%s VC:%d %s - Diag.:%d",
-                   (fd->flags & FROM_DCE) ? "Restart ind."
-                                          : "Restart req.",
-                   vc, restart_code(pd[localoffset+3]),
-                   (int)pd[localoffset+4]);
+        switch (dir) {
+
+       case X25_FROM_DCE:
+           short_name = "Restart ind.";
+           long_name = "Restart indication";
+           break;
+
+       case X25_FROM_DTE:
+           short_name = "Restart req.";
+           long_name = "Restart request";
+           break;
+
+       case X25_UNKNOWN:
+           short_name = "Restart ind./Restart req.";
+           long_name = "Restart indication/Restart request";
+           break;
+       }
+       if(check_col(pinfo->cinfo, COL_INFO)) {
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s - Diag.:%d",
+                   short_name,
+                   restart_code(tvb_get_guint8(tvb, 3)),
+                   (int)tvb_get_guint8(tvb, 3));
        }
-       x25_hash_add_proto_end(vc, fd->abs_secs, fd->abs_usecs);
        if (x25_tree) {
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                   "RESTART",
-                   (fd->flags & FROM_DCE) ? "Restart indication"
-                                          : "Restart request");
-           proto_tree_add_text(x25_tree, localoffset+3, 1,
-                               "Cause : %s", restart_code(pd[localoffset+3]));
-           proto_tree_add_text(x25_tree, localoffset+4, 1,
-                               "Diagnostic : %d", (int)pd[localoffset+4]);
+           proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
+                   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,
+                   "Diagnostic : %d", (int)tvb_get_guint8(tvb, 4));
        }
-       localoffset += x25_pkt_len;
+       localoffset = x25_pkt_len;
        break;
     case X25_RESTART_CONFIRMATION:
-       if(check_col(fd, COL_INFO))
-           col_add_fstr(fd, COL_INFO, "Restart conf. VC:%d", vc);
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_set_str(pinfo->cinfo, COL_INFO, "Restart conf.");
        if (x25_tree)
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                                      "RESTART CONF", "Restart confirmation");
-       localoffset += x25_pkt_len;
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_RESTART_CONFIRMATION);
+       localoffset = x25_pkt_len;
        break;
     case X25_REGISTRATION_REQUEST:
-       if(check_col(fd, COL_INFO))
-           col_add_fstr(fd, COL_INFO, "Registration req. VC:%d", vc);
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_set_str(pinfo->cinfo, COL_INFO, "Registration req.");
        if (x25_tree)
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                                      "REG REQ", "Registration request");
-       localoffset += 3;
-       x25_ntoa(x25_tree, &localoffset, &pd[localoffset], fd, FALSE);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_REGISTRATION_REQUEST);
+       localoffset = 3;
+       if (localoffset < x25_pkt_len)
+           x25_ntoa(x25_tree, &localoffset, tvb, pinfo, TRUE);
 
        if (x25_tree) {
-           proto_tree_add_text(x25_tree, localoffset, 1,
-                   "Registration length: %d", pd[localoffset] & 0x7F);
-           proto_tree_add_text(x25_tree, localoffset+1,
-                   pd[localoffset] & 0x7F, "Registration");
+           if (localoffset < x25_pkt_len)
+               proto_tree_add_text(x25_tree, tvb, localoffset, 1,
+                       "Registration length: %d",
+                       tvb_get_guint8(tvb, localoffset) & 0x7F);
+           if (localoffset+1 < x25_pkt_len)
+               proto_tree_add_text(x25_tree, tvb, localoffset+1,
+                       tvb_get_guint8(tvb, localoffset) & 0x7F,
+                       "Registration");
        }
+       localoffset = tvb_reported_length(tvb);
        break;
     case X25_REGISTRATION_CONFIRMATION:
-       if(check_col(fd, COL_INFO))
-           col_add_fstr(fd, COL_INFO, "Registration conf. VC:%d", vc);
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_set_str(pinfo->cinfo, COL_INFO, "Registration conf.");
        if (x25_tree) {
-           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
-                                      "REG CONF", "Registration confirmation");
-           proto_tree_add_text(x25_tree, localoffset+3, 1,
-                   "Cause: %s", registration_code(pd[localoffset+3]));
-           proto_tree_add_text(x25_tree, localoffset+4, 1,
-                   "Diagnostic: %s", registration_code(pd[localoffset+4]));
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_REGISTRATION_CONFIRMATION);
+           proto_tree_add_text(x25_tree, tvb, 3, 1,
+                   "Cause: %s", registration_code(tvb_get_guint8(tvb, 3)));
+           proto_tree_add_text(x25_tree, tvb, 4, 1,
+                   "Diagnostic: %s", registration_code(tvb_get_guint8(tvb, 4)));
        }
-       localoffset += 5;
-       x25_ntoa(x25_tree, &localoffset, &pd[localoffset], fd, TRUE);
+       localoffset = 5;
+       if (localoffset < x25_pkt_len)
+           x25_ntoa(x25_tree, &localoffset, tvb, pinfo, TRUE);
 
        if (x25_tree) {
-           proto_tree_add_text(x25_tree, localoffset, 1,
-                   "Registration length: %d", pd[localoffset] & 0x7F);
-           proto_tree_add_text(x25_tree, localoffset+1,
-                   pd[localoffset] & 0x7F, "Registration");
+           if (localoffset < x25_pkt_len)
+               proto_tree_add_text(x25_tree, tvb, localoffset, 1,
+                       "Registration length: %d",
+                       tvb_get_guint8(tvb, localoffset) & 0x7F);
+           if (localoffset+1 < x25_pkt_len)
+               proto_tree_add_text(x25_tree, tvb, localoffset+1,
+                       tvb_get_guint8(tvb, localoffset) & 0x7F,
+                       "Registration");
        }
+       localoffset = tvb_reported_length(tvb);
        break;
     default :
-       localoffset += 2;
-       if ((pd[localoffset] & 0x01) == X25_DATA)
+       localoffset = 2;
+       if (PACKET_IS_DATA(pkt_type))
        {
-           if(check_col(fd, COL_INFO)) {
+           if(check_col(pinfo->cinfo, COL_INFO)) {
                if (modulo == 8)
-                   col_add_fstr(fd, COL_INFO,
+                   col_add_fstr(pinfo->cinfo, COL_INFO,
                            "Data VC:%d P(S):%d P(R):%d %s", vc,
-                           (pd[localoffset] >> 1) & 0x07,
-                           (pd[localoffset] >> 5) & 0x07,
-                           ((pd[localoffset]>>4) & 0x01) ? " M" : "");
+                           (pkt_type >> 1) & 0x07,
+                           (pkt_type >> 5) & 0x07,
+                           (pkt_type & X25_MBIT_MOD8) ? " M" : "");
                else
-                   col_add_fstr(fd, COL_INFO,
+                   col_add_fstr(pinfo->cinfo, COL_INFO,
                            "Data VC:%d P(S):%d P(R):%d %s", vc,
-                           pd[localoffset+1] >> 1,
-                           pd[localoffset] >> 1,
-                           (pd[localoffset+1] & 0x01) ? " M" : "");
+                           tvb_get_guint8(tvb, localoffset+1) >> 1,
+                           pkt_type >> 1,
+                           (tvb_get_guint8(tvb, localoffset+1) & X25_MBIT_MOD128) ? " M" : "");
            }
            if (x25_tree) {
-               proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset,
-                                          1, "DATA", "Data");
+               proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+                       2, bytes0_1);
                if (modulo == 8) {
-                   proto_tree_add_text(x25_tree, localoffset, 1,
-                           "              %d%d%d..... : P(R) = %d",
-                           (pd[localoffset] >> 7) & 0x01,
-                           (pd[localoffset] >> 6) & 0x01,
-                           (pd[localoffset] >> 5) & 0x01,
-                           (pd[localoffset] >> 5) & 0x07);
-                   proto_tree_add_text(x25_tree, localoffset, 1,
-                           "              ...%d.... : More bit",
-                           (pd[localoffset] >> 4) & 0x01);
-                   proto_tree_add_text(x25_tree, localoffset, 1,
-                           "              ....%d%d%d. : P(S) = %d",
-                           (pd[localoffset] >> 3) & 0x01,
-                           (pd[localoffset] >> 2) & 0x01,
-                           (pd[localoffset] >> 1) & 0x01,
-                           (pd[localoffset] >> 1) & 0x07);
-                   proto_tree_add_text(x25_tree, localoffset, 1,
-                           "              .......0 : Packet type id = DATA");
+                   proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
+                           localoffset, 1, pkt_type);
+                   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_uint(x25_tree, hf_x25_type_data, tvb,
+                           localoffset, 1, pkt_type);
                }
                else {
-                   proto_tree_add_text(x25_tree, localoffset+1, 1,
-                           "              %d%d%d%d%d%d%d. : P(S) = %d",
-                           (pd[localoffset+1] >> 7) & 0x01,
-                           (pd[localoffset+1] >> 6) & 0x01,
-                           (pd[localoffset+1] >> 5) & 0x01,
-                           (pd[localoffset+1] >> 4) & 0x01,
-                           (pd[localoffset+1] >> 3) & 0x01,
-                           (pd[localoffset+1] >> 2) & 0x01,
-                           (pd[localoffset+1] >> 1) & 0x01,
-                           pd[localoffset+1] >> 1);
-                   proto_tree_add_text(x25_tree, localoffset, 1,
-                           "              .......%d : More bit",
-                           pd[localoffset+1] & 0x01);
+                   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));
+                   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 (pd[localoffset] & 0x1F)
+
+       /*
+        * Non-data packets (RR, RNR, REJ).
+        */
+       switch (PACKET_TYPE_FC(pkt_type))
        {
        case X25_RR:
-           if(check_col(fd, COL_INFO)) {
+           if(check_col(pinfo->cinfo, COL_INFO)) {
                if (modulo == 8)
-                   col_add_fstr(fd, COL_INFO, "RR VC:%d P(R):%d",
-                           vc, (pd[localoffset] >> 5) & 0x07);
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "RR VC:%d P(R):%d",
+                           vc, (pkt_type >> 5) & 0x07);
                else
-                   col_add_fstr(fd, COL_INFO, "RR VC:%d P(R):%d",
-                           vc, pd[localoffset+1] >> 1);
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "RR VC:%d P(R):%d",
+                           vc, tvb_get_guint8(tvb, localoffset+1) >> 1);
            }
            if (x25_tree) {
-               if (modulo == 8)
-                   proto_tree_add_item_format(x25_tree, hf_x25_type,
-                           localoffset, 1, "RR", "RR P(R):%d",
-                           (pd[localoffset] >> 5) & 0x07);
-               else
-                   proto_tree_add_item_format(x25_tree, hf_x25_type,
-                           localoffset, 2, "RR", "RR P(R):%d",
-                           pd[localoffset+1] >> 1);
+               proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+                       2, bytes0_1);
+               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_fc_mod8, tvb,
+                           localoffset, 1, X25_RR);
+               }
+               else {
+                   proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
+                           localoffset, 1, X25_RR);
+                   proto_tree_add_item(x25_tree, hf_x25_p_r_mod128, tvb,
+                           localoffset+1, 1, FALSE);
+               }
            }
            break;
 
        case X25_RNR:
-           if(check_col(fd, COL_INFO)) {
+           if(check_col(pinfo->cinfo, COL_INFO)) {
                if (modulo == 8)
-                   col_add_fstr(fd, COL_INFO, "RNR VC:%d P(R):%d",
-                           vc, (pd[localoffset] >> 5) & 0x07);
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "RNR VC:%d P(R):%d",
+                           vc, (pkt_type >> 5) & 0x07);
                else
-                   col_add_fstr(fd, COL_INFO, "RNR VC:%d P(R):%d",
-                           vc, pd[localoffset+1] >> 1);
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "RNR VC:%d P(R):%d",
+                           vc, tvb_get_guint8(tvb, localoffset+1) >> 1);
            }
            if (x25_tree) {
-               if (modulo == 8)
-                   proto_tree_add_item_format(x25_tree, hf_x25_type,
-                           localoffset, 1, "RNR", "RNR P(R):%d",
-                           (pd[localoffset] >> 5) & 0x07);
-               else
-                   proto_tree_add_item_format(x25_tree, hf_x25_type,
-                           localoffset, 2, "RNR", "RNR P(R):%d",
-                           pd[localoffset+1] >> 1);
+               proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+                       2, bytes0_1);
+               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_fc_mod8, tvb,
+                           localoffset, 1, X25_RNR);
+               }
+               else {
+                   proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
+                           localoffset, 1, X25_RNR);
+                   proto_tree_add_item(x25_tree, hf_x25_p_r_mod128, tvb,
+                           localoffset+1, 1, FALSE);
+               }
            }
            break;
 
        case X25_REJ:
-           if(check_col(fd, COL_INFO)) {
+           if(check_col(pinfo->cinfo, COL_INFO)) {
                if (modulo == 8)
-                   col_add_fstr(fd, COL_INFO, "REJ VC:%d P(R):%d",
-                           vc, (pd[localoffset] >> 5) & 0x07);
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "REJ VC:%d P(R):%d",
+                           vc, (pkt_type >> 5) & 0x07);
                else
-                   col_add_fstr(fd, COL_INFO, "REJ VC:%d P(R):%d",
-                           vc, pd[localoffset+1] >> 1);
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "REJ VC:%d P(R):%d",
+                           vc, tvb_get_guint8(tvb, localoffset+1) >> 1);
            }
            if (x25_tree) {
-               if (modulo == 8)
-                   proto_tree_add_item_format(x25_tree, hf_x25_type,
-                           localoffset, 1, "REJ", "REJ P(R):%d",
-                           (pd[localoffset] >> 5) & 0x07);
-               else
-                   proto_tree_add_item_format(x25_tree, hf_x25_type,
-                           localoffset, 2, "REJ", "REJ P(R):%d",
-                           pd[localoffset+1] >> 1);
+               proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+                       2, bytes0_1);
+               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_fc_mod8, tvb,
+                           localoffset, 1, X25_REJ);
+               }
+               else {
+                   proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
+                           localoffset, 1, X25_REJ);
+                   proto_tree_add_item(x25_tree, hf_x25_p_r_mod128, tvb,
+                           localoffset+1, 1, FALSE);
+               }
            }
        }
        localoffset += (modulo == 8) ? 1 : 2;
     }
 
-    if (localoffset >= fd->cap_len) return;
-
-    /* search the dissector in the hash table */
-    if ((dissect = x25_hash_get_dissect(fd->abs_secs, fd->abs_usecs, vc)))
-      (*dissect)(pd, localoffset, fd, tree);
-    else {
-      if (pd[localoffset] == 0x45) /* If the Call Req. has not been captured,
-                                   * assume these packets carry IP */
-      {
-         x25_hash_add_proto_start(vc, fd->abs_secs,
-                                  fd->abs_usecs, dissect_ip);
-         dissect_ip(pd, localoffset, fd, tree);
-      }
-      else {
-         dissect_data(pd, localoffset, fd, tree);
-      }
+    if (localoffset >= tvb_reported_length(tvb))
+      return;
+    if (pinfo->fragmented)
+      return;
+
+    if (!next_tvb)
+      next_tvb = tvb_new_subset(tvb, localoffset, -1, -1);
+
+    saved_private_data = pinfo->private_data;
+    pinfo->private_data = &q_bit_set;
+
+    /* See if there's already a dissector for this circuit. */
+    if (try_circuit_dissector(CT_X25, vc, pinfo->fd->num, next_tvb, pinfo,
+                             tree)) {
+       pinfo->private_data = saved_private_data;
+       return; /* found it and dissected it */
+    }
+
+    /* Did the user suggest QLLC/SNA? */
+    if (payload_is_qllc_sna) {
+       /* Yes - dissect it as QLLC/SNA. */
+       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;
+    }
+
+    /* If the Call Req. has not been captured, let's look at the first
+       byte of the payload to see if this looks like IP or CLNP. */
+    switch (tvb_get_guint8(tvb, localoffset)) {
+
+    case 0x45:
+       /* Looks like an IP header */
+       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:
+       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;
     }
+
+    /* Try the heuristic dissectors. */
+    if (dissector_try_heuristic(x25_heur_subdissector_list, next_tvb, pinfo,
+                               tree)) {
+       pinfo->private_data = saved_private_data;
+       return;
+    }
+
+    /* All else failed; dissect it as raw data */
+    call_dissector(data_handle, next_tvb, pinfo, tree);
+    pinfo->private_data = saved_private_data;
+}
+
+/*
+ * X.25 dissector for use when "pinfo->pseudo_header" points to a
+ * "struct x25_phdr".
+ */
+static void
+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,
+       pinfo->pseudo_header->x25.flags & FROM_DCE);
+}
+
+/*
+ * X.25 dissector for use when "pinfo->pseudo_header" doesn't point to a
+ * "struct x25_phdr".
+ */
+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.
+     */
+    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
 proto_register_x25(void)
 {
     static hf_register_info hf[] = {
+       { &hf_x25_gfi,
+         { "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, X25_ABIT,
+               "Address Bit", HFILL }},
+       { &hf_x25_qbit,
+         { "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, X25_DBIT,
+               "Delivery Confirmation Bit", HFILL }},
+       { &hf_x25_mod,
+         { "Modulo", "x.25.mod", FT_UINT16, BASE_DEC, VALS(vals_modulo), 0x3000,
+               "Specifies whether the frame is modulo 8 or 128", HFILL }},
        { &hf_x25_lcn,
-         { "Logical Channel", "x25.lcn", FT_UINT16, NULL} },
+         { "Logical Channel", "x.25.lcn", FT_UINT16, BASE_DEC, NULL, 0x0FFF,
+               "Logical Channel Number", HFILL }},
        { &hf_x25_type,
-         { "Packet Type", "x25.type", FT_STRING, NULL} },
+         { "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_DEC, NULL, 0xE0,
+               "Packet Receive Sequence Number", HFILL }},
+       { &hf_x25_p_r_mod128,
+         { "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), X25_MBIT_MOD8,
+               "More Bit", HFILL }},
+       { &hf_x25_mbit_mod128,
+         { "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,
+               "Packet Send Sequence Number", HFILL }},
+       { &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_gfi,
+       &ett_x25_fac,
+       &ett_x25_fac_unknown,
+       &ett_x25_fac_mark,
+       &ett_x25_fac_reverse,
+       &ett_x25_fac_throughput,
+       &ett_x25_fac_cug,
+       &ett_x25_fac_called_modif,
+       &ett_x25_fac_cug_outgoing_acc,
+       &ett_x25_fac_throughput_min,
+       &ett_x25_fac_express_data,
+       &ett_x25_fac_bilateral_cug,
+       &ett_x25_fac_packet_size,
+       &ett_x25_fac_window_size,
+       &ett_x25_fac_rpoa_selection,
+       &ett_x25_fac_transit_delay,
+       &ett_x25_fac_call_transfer,
+       &ett_x25_fac_called_addr_ext,
+       &ett_x25_fac_ete_transit_delay,
+       &ett_x25_fac_calling_addr_ext,
+       &ett_x25_fac_call_deflect,
+       &ett_x25_fac_priority,
+       &ett_x25_user_data,
+       &ett_x25_segment,
+       &ett_x25_segments
+    };
+    module_t *x25_module;
 
-    proto_x25 = proto_register_protocol ("X.25", "x25");
+    proto_x25 = proto_register_protocol ("X.25", "X.25", "x.25");
     proto_register_field_array (proto_x25, hf, array_length(hf));
+    proto_register_subtree_array(ett, array_length(ett));
+
+    x25_subdissector_table = register_dissector_table("x.25.spi",
+       "X.25 secondary protocol identifier", FT_UINT8, BASE_HEX);
+    register_heur_dissector_list("x.25", &x25_heur_subdissector_list);
+
+    register_dissector("x.25_dir", dissect_x25_dir, proto_x25);
+    register_dissector("x.25", dissect_x25, proto_x25);
+
+    /* Preferences */
+    x25_module = prefs_register_protocol(proto_x25, NULL);
+    prefs_register_obsolete_preference(x25_module, "non_q_bit_is_sna");
+    prefs_register_bool_preference(x25_module, "payload_is_qllc_sna",
+            "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
+proto_reg_handoff_x25(void)
+{
+    dissector_handle_t x25_handle;
+
+    /*
+     * Get handles for various dissectors.
+     */
+    ip_handle = find_dissector("ip");
+    clnp_handle = find_dissector("clnp");
+    ositp_handle = find_dissector("ositp");
+    qllc_handle = find_dissector("qllc");
+    data_handle = find_dissector("data");
+
+    x25_handle = find_dissector("x.25");
+    dissector_add("llc.dsap", SAP_X25, x25_handle);
 }