Guy suggested that the dcerpc opnum value_string code could be simplified
[obnox/wireshark/wip.git] / packet-atm.c
index f3630e8100b1b4509046829496f5884bc91b19f7..f3bf5c1148be53e16d68296fa645c20380b7dff1 100644 (file)
@@ -1,23 +1,22 @@
 /* packet-atm.c
  * Routines for ATM packet disassembly
  *
- * $Id: packet-atm.c,v 1.29 2001/01/03 06:55:27 guy Exp $
+ * $Id: packet-atm.c,v 1.64 2003/03/07 03:19:50 guy Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
+ * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1998 Gerald Combs
  *
- * 
  * 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 "packet.h"
+#include <epan/packet.h>
 #include "oui.h"
-#include "resolv.h"
+#include <epan/resolv.h>
 
-#include "packet-eth.h"
-#include "packet-llc.h"
+#include "packet-atm.h"
 #include "packet-snmp.h"
-#include "packet-sscop.h"
+#include "packet-eth.h"
 #include "packet-tr.h"
+#include "packet-llc.h"
 
 static int proto_atm = -1;
+static int hf_atm_aal = -1;
 static int hf_atm_vpi = -1;
 static int hf_atm_vci = -1;
 static int proto_atm_lane = -1;
 static int proto_ilmi = -1;
+static int proto_aal1 = -1;
+static int proto_aal3_4 = -1;
+static int proto_oamaal = -1;
 
 static gint ett_atm = -1;
 static gint ett_atm_lane = -1;
@@ -56,6 +55,17 @@ static gint ett_atm_lane_lc_lan_dest_rd = -1;
 static gint ett_atm_lane_lc_flags = -1;
 static gint ett_atm_lane_lc_tlv = -1;
 static gint ett_ilmi = -1;
+static gint ett_aal1 = -1;
+static gint ett_aal3_4 = -1;
+static gint ett_oamaal = -1;
+
+static dissector_handle_t eth_handle;
+static dissector_handle_t tr_handle;
+static dissector_handle_t llc_handle;
+static dissector_handle_t sscop_handle;
+static dissector_handle_t lane_handle;
+static dissector_handle_t ilmi_handle;
+static dissector_handle_t data_handle;
 
 /*
  * See
@@ -83,6 +93,8 @@ static gint ett_ilmi = -1;
 #define LE_FLUSH_RESPONSE      0x0107
 #define LE_NARP_REQUEST                0x0008
 #define LE_TOPOLOGY_REQUEST    0x0009
+#define LE_VERIFY_REQUEST      0x000A
+#define LE_VERIFY_RESPONSE     0x010A
 
 static const value_string le_control_opcode_vals[] = {
        { LE_CONFIGURE_REQUEST,   "LE_CONFIGURE_REQUEST" },
@@ -101,6 +113,8 @@ static const value_string le_control_opcode_vals[] = {
        { LE_FLUSH_RESPONSE,      "LE_FLUSH_RESPONSE" },
        { LE_NARP_REQUEST,        "LE_NARP_REQUEST" },
        { LE_TOPOLOGY_REQUEST,    "LE_TOPOLOGY_REQUEST" },
+       { LE_VERIFY_REQUEST,      "LE_VERIFY_REQUEST" },
+       { LE_VERIFY_RESPONSE,     "LE_VERIFY_RESPONSE" },
        { 0,                      NULL }
 };
 
@@ -119,6 +133,7 @@ static const value_string le_control_status_vals[] = {
        { 20, "No configuraton" },
        { 21, "LE_CONFIGURE error" },
        { 22, "Insufficient information" },
+       { 24, "TLV not found" },
        { 0,  NULL }
 };
 
@@ -146,8 +161,17 @@ static const value_string le_control_lan_type_vals[] = {
        { 0,           NULL }
 };
 
+static const value_string le_control_frame_size_vals[] = {
+       { 0x00, "Unspecified" },
+       { 0x01, "1516/1528/1580/1592" },
+       { 0x02, "4544/4556/1580/1592" },
+       { 0x03, "9234/9246" },
+       { 0x04, "18190/18202" },
+       { 0,    NULL }
+};
+
 static void
-dissect_le_client(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+dissect_le_client(tvbuff_t *tvb, proto_tree *tree)
 {
   proto_item *ti;
   proto_tree *lane_tree;
@@ -162,7 +186,7 @@ dissect_le_client(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 }
 
 static void
-dissect_lan_destination(tvbuff_t *tvb, int offset, const char *type, proto_tree *tree) 
+dissect_lan_destination(tvbuff_t *tvb, int offset, const char *type, proto_tree *tree)
 {
   proto_item *td;
   proto_tree *dest_tree;
@@ -223,6 +247,13 @@ dissect_lan_destination(tvbuff_t *tvb, int offset, const char *type, proto_tree
 #define        LE_MCAST_SEND_VCC_AVGRATE       TLV_TYPE(OUI_ATM_FORUM, 0x0D)
 #define        LE_MCAST_SEND_VCC_PEAKRATE      TLV_TYPE(OUI_ATM_FORUM, 0x0E)
 #define        LE_CONN_COMPLETION_TIMER        TLV_TYPE(OUI_ATM_FORUM, 0x0F)
+#define        LE_CONFIG_FRAG_INFO             TLV_TYPE(OUI_ATM_FORUM, 0x10)
+#define        LE_LAYER_3_ADDRESS              TLV_TYPE(OUI_ATM_FORUM, 0x11)
+#define        LE_ELAN_ID                      TLV_TYPE(OUI_ATM_FORUM, 0x12)
+#define        LE_SERVICE_CATEGORY             TLV_TYPE(OUI_ATM_FORUM, 0x13)
+#define        LE_LLC_MUXED_ATM_ADDRESS        TLV_TYPE(OUI_ATM_FORUM, 0x2B)
+#define        LE_X5_ADJUSTMENT                TLV_TYPE(OUI_ATM_FORUM, 0x2C)
+#define        LE_PREFERRED_LES                TLV_TYPE(OUI_ATM_FORUM, 0x2D)
 
 static const value_string le_tlv_type_vals[] = {
        { LE_CONTROL_TIMEOUT,           "Control Time-out" },
@@ -240,27 +271,213 @@ static const value_string le_tlv_type_vals[] = {
        { LE_MCAST_SEND_VCC_AVGRATE,    "Mcast Send VCC AvgRate" },
        { LE_MCAST_SEND_VCC_PEAKRATE,   "Mcast Send VCC PeakRate" },
        { LE_CONN_COMPLETION_TIMER,     "Connection Completion Timer" },
+       { LE_CONFIG_FRAG_INFO,          "Config Frag Info" },
+       { LE_LAYER_3_ADDRESS,           "Layer 3 Address" },
+       { LE_ELAN_ID,                   "ELAN ID" },
+       { LE_SERVICE_CATEGORY,          "Service Category" },
+       { LE_LLC_MUXED_ATM_ADDRESS,     "LLC-muxed ATM Address" },
+       { LE_X5_ADJUSTMENT,             "X5 Adjustment" },
+       { LE_PREFERRED_LES,             "Preferred LES" },
        { 0,                            NULL },
 };
 
+static void
+dissect_le_control_tlvs(tvbuff_t *tvb, int offset, guint num_tlvs,
+                       proto_tree *tree)
+{
+  guint32 tlv_type;
+  guint8 tlv_length;
+  proto_item *ttlv;
+  proto_tree *tlv_tree;
+
+  while (num_tlvs != 0) {
+    tlv_type = tvb_get_ntohl(tvb, offset);
+    tlv_length = tvb_get_guint8(tvb, offset+4);
+    ttlv = proto_tree_add_text(tree, tvb, offset, 5+tlv_length, "TLV type: %s",
+       val_to_str(tlv_type, le_tlv_type_vals, "Unknown (0x%08x)"));
+    tlv_tree = proto_item_add_subtree(ttlv, ett_atm_lane_lc_tlv);
+    proto_tree_add_text(tlv_tree, tvb, offset, 4, "TLV Type: %s",
+       val_to_str(tlv_type, le_tlv_type_vals, "Unknown (0x%08x)"));
+    proto_tree_add_text(tlv_tree, tvb, offset+4, 1, "TLV Length: %u", tlv_length);
+    offset += 5+tlv_length;
+    num_tlvs--;
+  }
+}
+
+static void
+dissect_le_configure_join_frame(tvbuff_t *tvb, int offset, proto_tree *tree)
+{
+  guint8 num_tlvs;
+  guint8 name_size;
+
+  dissect_lan_destination(tvb, offset, "Source", tree);
+  offset += 8;
+
+  dissect_lan_destination(tvb, offset, "Target", tree);
+  offset += 8;
+
+  proto_tree_add_text(tree, tvb, offset, 20, "Source ATM Address: %s",
+                     tvb_bytes_to_str(tvb, offset, 20));
+  offset += 20;
+
+  proto_tree_add_text(tree, tvb, offset, 1, "LAN type: %s",
+       val_to_str(tvb_get_guint8(tvb, offset), le_control_lan_type_vals,
+                               "Unknown (0x%02X)"));
+  offset += 1;
+
+  proto_tree_add_text(tree, tvb, offset, 1, "Maximum frame size: %s",
+       val_to_str(tvb_get_guint8(tvb, offset), le_control_frame_size_vals,
+                               "Unknown (0x%02X)"));
+  offset += 1;
+
+  num_tlvs = tvb_get_guint8(tvb, offset);
+  proto_tree_add_text(tree, tvb, offset, 1, "Number of TLVs: %u", num_tlvs);
+  offset += 1;
+
+  name_size = tvb_get_guint8(tvb, offset);
+  proto_tree_add_text(tree, tvb, offset, 1, "ELAN name size: %u", name_size);
+  offset += 1;
+
+  proto_tree_add_text(tree, tvb, offset, 20, "Target ATM Address: %s",
+                     tvb_bytes_to_str(tvb, offset, 20));
+  offset += 20;
+
+  if (name_size > 32)
+    name_size = 32;
+  if (name_size != 0) {
+    proto_tree_add_text(tree, tvb, offset, name_size, "ELAN name: %s",
+                       tvb_bytes_to_str(tvb, offset, name_size));
+  }
+  offset += 32;
+
+  dissect_le_control_tlvs(tvb, offset, num_tlvs, tree);
+}
+
+static void
+dissect_le_registration_frame(tvbuff_t *tvb, int offset, proto_tree *tree)
+{
+  guint8 num_tlvs;
+
+  dissect_lan_destination(tvb, offset, "Source", tree);
+  offset += 8;
+
+  dissect_lan_destination(tvb, offset, "Target", tree);
+  offset += 8;
+
+  proto_tree_add_text(tree, tvb, offset, 20, "Source ATM Address: %s",
+                     tvb_bytes_to_str(tvb, offset, 20));
+  offset += 20;
+
+  /* Reserved */
+  offset += 2;
+
+  num_tlvs = tvb_get_guint8(tvb, offset);
+  proto_tree_add_text(tree, tvb, offset, 1, "Number of TLVs: %u", num_tlvs);
+  offset += 1;
+
+  /* Reserved */
+  offset += 53;
+
+  dissect_le_control_tlvs(tvb, offset, num_tlvs, tree);
+}
+
+static void
+dissect_le_arp_frame(tvbuff_t *tvb, int offset, proto_tree *tree)
+{
+  guint8 num_tlvs;
+
+  dissect_lan_destination(tvb, offset, "Source", tree);
+  offset += 8;
+
+  dissect_lan_destination(tvb, offset, "Target", tree);
+  offset += 8;
+
+  proto_tree_add_text(tree, tvb, offset, 20, "Source ATM Address: %s",
+                     tvb_bytes_to_str(tvb, offset, 20));
+  offset += 20;
+
+  /* Reserved */
+  offset += 2;
+
+  num_tlvs = tvb_get_guint8(tvb, offset);
+  proto_tree_add_text(tree, tvb, offset, 1, "Number of TLVs: %u", num_tlvs);
+  offset += 1;
+
+  /* Reserved */
+  offset += 1;
+
+  proto_tree_add_text(tree, tvb, offset, 20, "Target ATM Address: %s",
+                     tvb_bytes_to_str(tvb, offset, 20));
+  offset += 20;
+
+  /* Reserved */
+  offset += 32;
+
+  dissect_le_control_tlvs(tvb, offset, num_tlvs, tree);
+}
+
+static void
+dissect_le_verify_frame(tvbuff_t *tvb, int offset, proto_tree *tree)
+{
+  guint8 num_tlvs;
+
+  /* Reserved */
+  offset += 38;
+
+  num_tlvs = tvb_get_guint8(tvb, offset);
+  proto_tree_add_text(tree, tvb, offset, 1, "Number of TLVs: %u", num_tlvs);
+  offset += 1;
+
+  /* Reserved */
+  offset += 1;
+
+  proto_tree_add_text(tree, tvb, offset, 20, "Target ATM Address: %s",
+                     tvb_bytes_to_str(tvb, offset, 20));
+  offset += 20;
+
+  /* Reserved */
+  offset += 32;
+
+  dissect_le_control_tlvs(tvb, offset, num_tlvs, tree);
+}
+
+static void
+dissect_le_flush_frame(tvbuff_t *tvb, int offset, proto_tree *tree)
+{
+  dissect_lan_destination(tvb, offset, "Source", tree);
+  offset += 8;
+
+  dissect_lan_destination(tvb, offset, "Target", tree);
+  offset += 8;
+
+  proto_tree_add_text(tree, tvb, offset, 20, "Source ATM Address: %s",
+                     tvb_bytes_to_str(tvb, offset, 20));
+  offset += 20;
+
+  /* Reserved */
+  offset += 4;
+
+  proto_tree_add_text(tree, tvb, offset, 20, "Target ATM Address: %s",
+                     tvb_bytes_to_str(tvb, offset, 20));
+  offset += 20;
+
+  /* Reserved */
+  offset += 32;
+}
+
 static void
 dissect_le_control(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
   proto_item *ti;
-  proto_tree *lane_tree;
+  proto_tree *lane_tree = NULL;
   int offset = 0;
   proto_item *tf;
   proto_tree *flags_tree;
-  proto_item *ttlv;
-  proto_tree *tlv_tree;
   guint16 opcode;
   guint16 flags;
-  guint8 num_tlvs;
-  guint32 tlv_type;
-  guint8 tlv_length;
 
-  if (check_col(pinfo->fd, COL_INFO))
-    col_set_str(pinfo->fd, COL_INFO, "LE Control");
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_set_str(pinfo->cinfo, COL_INFO, "LE Control");
 
   if (tree) {
     ti = proto_tree_add_protocol_format(tree, proto_atm_lane, tvb, offset, 108, "ATM LANE");
@@ -268,27 +485,40 @@ dissect_le_control(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
     proto_tree_add_text(lane_tree, tvb, offset, 2, "Marker: 0x%04X",
                        tvb_get_ntohs(tvb, offset));
-    offset += 2;
+  }
+  offset += 2;
 
+  if (tree) {
     proto_tree_add_text(lane_tree, tvb, offset, 1, "Protocol: 0x%02X",
                        tvb_get_guint8(tvb, offset));
-    offset += 1;
+  }
+  offset += 1;
 
+  if (tree) {
     proto_tree_add_text(lane_tree, tvb, offset, 1, "Version: 0x%02X",
                        tvb_get_guint8(tvb, offset));
-    offset += 1;
+  }
+  offset += 1;
 
-    opcode = tvb_get_ntohs(tvb, offset);
+  opcode = tvb_get_ntohs(tvb, offset);
+  if (check_col(pinfo->cinfo, COL_INFO)) {
+    col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
+       val_to_str(opcode, le_control_opcode_vals,
+                               "Unknown opcode (0x%04X)"));
+  }
+  if (tree) {
     proto_tree_add_text(lane_tree, tvb, offset, 2, "Opcode: %s",
        val_to_str(opcode, le_control_opcode_vals,
                                "Unknown (0x%04X)"));
-    offset += 2;
+  }
+  offset += 2;
 
-    if (opcode == READY_QUERY || opcode == READY_IND) {
-      /* There's nothing more in this packet. */
-      return;
-    }
+  if (opcode == READY_QUERY || opcode == READY_IND) {
+    /* There's nothing more in this packet. */
+    return;
+  }
 
+  if (tree) {
     if (opcode & 0x0100) {
       /* Response; decode status. */
       proto_tree_add_text(lane_tree, tvb, offset, 2, "Status: %s",
@@ -309,474 +539,1057 @@ dissect_le_control(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     tf = proto_tree_add_text(lane_tree, tvb, offset, 2, "Flags: 0x%04X",
                        flags);
     flags_tree = proto_item_add_subtree(tf, ett_atm_lane_lc_flags);
-    proto_tree_add_text(flags_tree, tvb, offset, 2, "%s",
-       decode_boolean_bitfield(flags, 0x0001, 8*2,
-                               "Remote address", "Local address"));
-    proto_tree_add_text(flags_tree, tvb, offset, 2, "%s",
+
+    switch (opcode) {
+
+    case LE_CONFIGURE_REQUEST:
+    case LE_CONFIGURE_RESPONSE:
+      proto_tree_add_text(flags_tree, tvb, offset, 2, "%s",
+       decode_boolean_bitfield(flags, 0x0002, 8*2,
+                               "V2 capable", "Not V2 capable"));
+      offset += 2;
+      dissect_le_configure_join_frame(tvb, offset, lane_tree);
+      break;
+
+    case LE_JOIN_REQUEST:
+    case LE_JOIN_RESPONSE:
+      proto_tree_add_text(flags_tree, tvb, offset, 2, "%s",
+       decode_boolean_bitfield(flags, 0x0002, 8*2,
+                               "V2 capable", "Not V2 capable"));
+      if (opcode == LE_JOIN_REQUEST) {
+        proto_tree_add_text(flags_tree, tvb, offset, 2, "%s",
+               decode_boolean_bitfield(flags, 0x0004, 8*2,
+                               "Selective multicast", "No selective multicast"));
+      } else {
+        proto_tree_add_text(flags_tree, tvb, offset, 2, "%s",
+               decode_boolean_bitfield(flags, 0x0008, 8*2,
+                               "V2 required", "V2 not required"));
+      }
+      proto_tree_add_text(flags_tree, tvb, offset, 2, "%s",
        decode_boolean_bitfield(flags, 0x0080, 8*2,
                                "Proxy", "Not proxy"));
-    proto_tree_add_text(flags_tree, tvb, offset, 2, "%s",
-       decode_boolean_bitfield(flags, 0x0100, 8*2,
-                               "Topology change", "No topology change"));
-    offset += 2;
-
-    dissect_lan_destination(tvb, offset, "Source", lane_tree);
-    offset += 8;
+      proto_tree_add_text(flags_tree, tvb, offset, 2, "%s",
+       decode_boolean_bitfield(flags, 0x0200, 8*2,
+                               "Exclude explorer frames",
+                               "Don't exclude explorer frames"));
+      offset += 2;
+      dissect_le_configure_join_frame(tvb, offset, lane_tree);
+      break;
 
-    dissect_lan_destination(tvb, offset, "Target", lane_tree);
-    offset += 8;
+    case LE_REGISTER_REQUEST:
+    case LE_REGISTER_RESPONSE:
+    case LE_UNREGISTER_REQUEST:
+    case LE_UNREGISTER_RESPONSE:
+      offset += 2;
+      dissect_le_registration_frame(tvb, offset, lane_tree);
+      break;
 
-    proto_tree_add_text(lane_tree, tvb, offset, 20, "Source ATM Address: %s",
-                       tvb_bytes_to_str(tvb, offset, 20));
-    offset += 20;
+    case LE_ARP_REQUEST:
+    case LE_ARP_RESPONSE:
+    case LE_NARP_REQUEST:
+      if (opcode != LE_NARP_REQUEST) {
+        proto_tree_add_text(flags_tree, tvb, offset, 2, "%s",
+               decode_boolean_bitfield(flags, 0x0001, 8*2,
+                               "Remote address", "Local address"));
+      }
+      offset += 2;
+      dissect_le_arp_frame(tvb, offset, lane_tree);
+      break;
 
-    proto_tree_add_text(lane_tree, tvb, offset, 1, "LAN type: %s",
-       val_to_str(tvb_get_guint8(tvb, offset), le_control_lan_type_vals,
-                               "Unknown (0x%02X)"));
-    offset += 1;
+    case LE_TOPOLOGY_REQUEST:
+      proto_tree_add_text(flags_tree, tvb, offset, 2, "%s",
+       decode_boolean_bitfield(flags, 0x0100, 8*2,
+                               "Topology change", "No topology change"));
+      offset += 2;
+      /* 92 reserved bytes */
+      break;
 
-    proto_tree_add_text(lane_tree, tvb, offset, 1, "Maximum frame size: %u",
-                       tvb_get_guint8(tvb, offset));
-    offset += 1;
+    case LE_VERIFY_REQUEST:
+    case LE_VERIFY_RESPONSE:
+      offset += 2;
+      dissect_le_verify_frame(tvb, offset, lane_tree);
+      break;
 
-    num_tlvs = tvb_get_guint8(tvb, offset);
-    proto_tree_add_text(lane_tree, tvb, offset, 1, "Number of TLVs: %u",
-                       num_tlvs);
-    offset += 1;
+    case LE_FLUSH_REQUEST:
+    case LE_FLUSH_RESPONSE:
+      offset += 2;
+      dissect_le_flush_frame(tvb, offset, lane_tree);
+      break;
+    }
+  }
+}
 
-    proto_tree_add_text(lane_tree, tvb, offset, 1, "ELAN name size: %u",
-                       tvb_get_guint8(tvb, offset));
-    offset += 1;
+static void
+capture_lane(const union wtap_pseudo_header *pseudo_header, const guchar *pd,
+    int len, packet_counts *ld)
+{
+  /* Is it LE Control, 802.3, 802.5, or "none of the above"? */
+  switch (pseudo_header->atm.subtype) {
 
-    proto_tree_add_text(lane_tree, tvb, offset, 20, "Target ATM Address: %s",
-                       tvb_bytes_to_str(tvb, offset, 20));
-    offset += 20;
+  case TRAF_ST_LANE_802_3:
+  case TRAF_ST_LANE_802_3_MC:
+    /* Dissect as Ethernet */
+    capture_eth(pd, 2, len, ld);
+    break;
 
-    proto_tree_add_text(lane_tree, tvb, offset, 32, "ELAN name: %s",
-                       tvb_bytes_to_str(tvb, offset, 32));
-    offset += 32;
+  case TRAF_ST_LANE_802_5:
+  case TRAF_ST_LANE_802_5_MC:
+    /* Dissect as Token-Ring */
+    capture_tr(pd, 2, len, ld);
+    break;
 
-    while (num_tlvs != 0) {
-      tlv_type = tvb_get_ntohl(tvb, offset);
-      tlv_length = tvb_get_guint8(tvb, offset+4);
-      ttlv = proto_tree_add_text(lane_tree, tvb, offset, 5+tlv_length, "TLV type: %s",
-       val_to_str(tlv_type, le_tlv_type_vals, "Unknown (0x%08x)"));
-      tlv_tree = proto_item_add_subtree(ttlv, ett_atm_lane_lc_tlv);
-      proto_tree_add_text(tlv_tree, tvb, offset, 4, "TLV Type: %s",
-       val_to_str(tlv_type, le_tlv_type_vals, "Unknown (0x%08x)"));
-      proto_tree_add_text(tlv_tree, tvb, offset+4, 1, "TLV Length: %u", tlv_length);
-      offset += 5+tlv_length;
-      num_tlvs--;
-    }
+  default:
+    ld->other++;
+    break;
   }
 }
 
 static void
-dissect_lane(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) 
+dissect_lane(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
   tvbuff_t     *next_tvb;
   tvbuff_t     *next_tvb_le_client;
 
-  CHECK_DISPLAY_AS_DATA(proto_atm_lane, tvb, pinfo, tree);
-
-  pinfo->current_proto = "ATM LANE";
-
-  if (check_col(pinfo->fd, COL_PROTOCOL))
-    col_set_str(pinfo->fd, COL_PROTOCOL, "ATM LANE");
-  if (check_col(pinfo->fd, COL_INFO))
-    col_set_str(pinfo->fd, COL_INFO, "ATM LANE");
+  if (check_col(pinfo->cinfo, COL_PROTOCOL))
+    col_set_str(pinfo->cinfo, COL_PROTOCOL, "ATM LANE");
 
   /* Is it LE Control, 802.3, 802.5, or "none of the above"? */
-  switch (pinfo->pseudo_header->ngsniffer_atm.AppHLType) {
+  switch (pinfo->pseudo_header->atm.subtype) {
 
-  case AHLT_LANE_LE_CTRL:
+  case TRAF_ST_LANE_LE_CTRL:
     dissect_le_control(tvb, pinfo, tree);
     break;
 
-  case AHLT_LANE_802_3:
-  case AHLT_LANE_802_3_MC:
-    dissect_le_client(tvb, pinfo, tree);
+  case TRAF_ST_LANE_802_3:
+  case TRAF_ST_LANE_802_3_MC:
+    if (check_col(pinfo->cinfo, COL_INFO))
+      col_set_str(pinfo->cinfo, COL_INFO, "LE Client - Ethernet/802.3");
+    dissect_le_client(tvb, tree);
 
     /* Dissect as Ethernet */
     next_tvb_le_client = tvb_new_subset(tvb, 2, -1, -1);
-    dissect_eth(next_tvb_le_client, pinfo, tree);
+    call_dissector(eth_handle, next_tvb_le_client, pinfo, tree);
     break;
 
-  case AHLT_LANE_802_5:
-  case AHLT_LANE_802_5_MC:
-    dissect_le_client(tvb, pinfo, tree);
+  case TRAF_ST_LANE_802_5:
+  case TRAF_ST_LANE_802_5_MC:
+    if (check_col(pinfo->cinfo, COL_INFO))
+      col_set_str(pinfo->cinfo, COL_INFO, "LE Client - 802.5");
+    dissect_le_client(tvb, tree);
 
     /* Dissect as Token-Ring */
     next_tvb_le_client = tvb_new_subset(tvb, 2, -1, -1);
-    dissect_tr(next_tvb_le_client, pinfo, tree);
+    call_dissector(tr_handle, next_tvb_le_client, pinfo, tree);
     break;
 
   default:
     /* Dump it as raw data. */
+    if (check_col(pinfo->cinfo, COL_INFO))
+      col_set_str(pinfo->cinfo, COL_INFO, "Unknown LANE traffic type");
     next_tvb           = tvb_new_subset(tvb, 0, -1, -1);
-    dissect_data(next_tvb, 0, pinfo, tree);
+    call_dissector(data_handle,next_tvb, pinfo, tree);
     break;
   }
 }
 
+static void
+dissect_ilmi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+  dissect_snmp_pdu(tvb, 0, pinfo, tree, "ILMI", proto_ilmi, ett_ilmi);
+}
+
 /* AAL types */
 static const value_string aal_vals[] = {
-       { ATT_AAL_UNKNOWN,    "Unknown AAL" },
-       { ATT_AAL1,           "AAL1" },
-       { ATT_AAL3_4,         "AAL3/4" },
-       { ATT_AAL5,           "AAL5" },
-       { ATT_AAL_USER,       "User AAL" },
-       { ATT_AAL_SIGNALLING, "Signalling AAL" },
-       { ATT_OAMCELL,        "OAM cell" },
-       { 0,                  NULL }
+       { AAL_UNKNOWN,    "Unknown AAL" },
+       { AAL_1,          "AAL1" },
+       { AAL_2,          "AAL2" },
+       { AAL_3_4,        "AAL3/4" },
+       { AAL_5,          "AAL5" },
+       { AAL_USER,       "User AAL" },
+       { AAL_SIGNALLING, "Signalling AAL" },
+       { AAL_OAMCELL,    "OAM cell" },
+       { 0,              NULL }
 };
 
 /* AAL5 higher-level traffic types */
 static const value_string aal5_hltype_vals[] = {
-       { ATT_HL_UNKNOWN, "Unknown traffic type" },
-       { ATT_HL_LLCMX,   "LLC multiplexed" },
-       { ATT_HL_VCMX,    "VC multiplexed" },
-       { ATT_HL_LANE,    "LANE" },
-       { ATT_HL_ILMI,    "ILMI" },
-       { ATT_HL_FRMR,    "Frame Relay" },
-       { ATT_HL_SPANS,   "FORE SPANS" },
-       { ATT_HL_IPSILON, "Ipsilon" },
+       { TRAF_UNKNOWN, "Unknown traffic type" },
+       { TRAF_LLCMX,   "LLC multiplexed" },
+       { TRAF_VCMX,    "VC multiplexed" },
+       { TRAF_LANE,    "LANE" },
+       { TRAF_ILMI,    "ILMI" },
+       { TRAF_FR,      "Frame Relay" },
+       { TRAF_SPANS,   "FORE SPANS" },
+       { TRAF_IPSILON, "Ipsilon" },
        { 0,              NULL }
 };
 
 /* Traffic subtypes for VC multiplexed traffic */
 static const value_string vcmx_type_vals[] = {
-       { AHLT_UNKNOWN,        "Unknown VC multiplexed traffic type" },
-       { AHLT_VCMX_802_3_FCS, "802.3 FCS" },
-       { AHLT_VCMX_802_4_FCS, "802.4 FCS" },
-       { AHLT_VCMX_802_5_FCS, "802.5 FCS" },
-       { AHLT_VCMX_FDDI_FCS,  "FDDI FCS" },
-       { AHLT_VCMX_802_6_FCS, "802.6 FCS" },
-       { AHLT_VCMX_802_3,     "802.3" },
-       { AHLT_VCMX_802_4,     "802.4" },
-       { AHLT_VCMX_802_5,     "802.5" },
-       { AHLT_VCMX_FDDI,      "FDDI" },
-       { AHLT_VCMX_802_6,     "802.6" },
-       { AHLT_VCMX_FRAGMENTS, "Fragments" },
-       { AHLT_VCMX_BPDU,      "BPDU" },
+       { TRAF_ST_UNKNOWN,        "Unknown VC multiplexed traffic type" },
+       { TRAF_ST_VCMX_802_3_FCS, "802.3 FCS" },
+       { TRAF_ST_VCMX_802_4_FCS, "802.4 FCS" },
+       { TRAF_ST_VCMX_802_5_FCS, "802.5 FCS" },
+       { TRAF_ST_VCMX_FDDI_FCS,  "FDDI FCS" },
+       { TRAF_ST_VCMX_802_6_FCS, "802.6 FCS" },
+       { TRAF_ST_VCMX_802_3,     "802.3" },
+       { TRAF_ST_VCMX_802_4,     "802.4" },
+       { TRAF_ST_VCMX_802_5,     "802.5" },
+       { TRAF_ST_VCMX_FDDI,      "FDDI" },
+       { TRAF_ST_VCMX_802_6,     "802.6" },
+       { TRAF_ST_VCMX_FRAGMENTS, "Fragments" },
+       { TRAF_ST_VCMX_BPDU,      "BPDU" },
        { 0,                   NULL }
 };
 
 /* Traffic subtypes for LANE traffic */
 static const value_string lane_type_vals[] = {
-       { AHLT_UNKNOWN,       "Unknown LANE traffic type" },
-       { AHLT_LANE_LE_CTRL,  "LE Control" },
-       { AHLT_LANE_802_3,    "802.3" },
-       { AHLT_LANE_802_5,    "802.5" },
-       { AHLT_LANE_802_3_MC, "802.3 multicast" },
-       { AHLT_LANE_802_5_MC, "802.5 multicast" },
-       { 0,                  NULL }
+       { TRAF_ST_UNKNOWN,       "Unknown LANE traffic type" },
+       { TRAF_ST_LANE_LE_CTRL,  "LE Control" },
+       { TRAF_ST_LANE_802_3,    "802.3" },
+       { TRAF_ST_LANE_802_5,    "802.5" },
+       { TRAF_ST_LANE_802_3_MC, "802.3 multicast" },
+       { TRAF_ST_LANE_802_5_MC, "802.5 multicast" },
+        { 0,                     NULL }
 };
 
 /* Traffic subtypes for Ipsilon traffic */
 static const value_string ipsilon_type_vals[] = {
-       { AHLT_UNKNOWN,     "Unknown Ipsilon traffic type" },
-       { AHLT_IPSILON_FT0, "Flow type 0" },
-       { AHLT_IPSILON_FT1, "Flow type 1" },
-       { AHLT_IPSILON_FT2, "Flow type 2" },
+       { TRAF_ST_UNKNOWN,     "Unknown Ipsilon traffic type" },
+       { TRAF_ST_IPSILON_FT0, "Flow type 0" },
+       { TRAF_ST_IPSILON_FT1, "Flow type 1" },
+       { TRAF_ST_IPSILON_FT2, "Flow type 2" },
        { 0,                NULL }
 };
 
+void
+capture_atm(const union wtap_pseudo_header *pseudo_header, const guchar *pd,
+    int len, packet_counts *ld)
+{
+  if (pseudo_header->atm.aal == AAL_5) {
+    switch (pseudo_header->atm.type) {
+
+    case TRAF_LLCMX:
+      /* Dissect as WTAP_ENCAP_ATM_RFC1483 */
+      /* The ATM iptrace capture that we have shows LLC at this point,
+       * so that's what I'm calling */
+      capture_llc(pd, 0, len, ld);
+      break;
+
+    case TRAF_LANE:
+      capture_lane(pseudo_header, pd, len, ld);
+      break;
+
+    default:
+      ld->other++;
+      break;
+    }
+  } else
+    ld->other++;
+}
+
 /*
- * We don't know what kind of traffic this is; try to guess.
- * We at least know it's AAL5....
+ * Charles Michael Heard's CRC-32 code, from
+ *
+ *     http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/32bitCRC.c.html
+ *
+ * with the CRC table initialized with values computed by
+ * his "gen_crc_table()" routine, rather than by calling that routine
+ * at run time, and with various data type cleanups.
  */
-static void
-atm_guess_content(tvbuff_t *tvb, packet_info *pinfo)
+
+/* crc32h.c -- package to compute 32-bit CRC one byte at a time using   */
+/*             the high-bit first (Big-Endian) bit ordering convention  */
+/*                                                                      */
+/* Synopsis:                                                            */
+/*  gen_crc_table() -- generates a 256-word table containing all CRC    */
+/*                     remainders for every possible 8-bit byte.  It    */
+/*                     must be executed (once) before any CRC updates.  */
+/*                                                                      */
+/*  unsigned update_crc(crc_accum, data_blk_ptr, data_blk_size)         */
+/*           unsigned crc_accum; char *data_blk_ptr; int data_blk_size; */
+/*           Returns the updated value of the CRC accumulator after     */
+/*           processing each byte in the addressed block of data.       */
+/*                                                                      */
+/*  It is assumed that an unsigned long is at least 32 bits wide and    */
+/*  that the predefined type char occupies one 8-bit byte of storage.   */
+/*                                                                      */
+/*  The generator polynomial used for this version of the package is    */
+/*  x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 */
+/*  as specified in the Autodin/Ethernet/ADCCP protocol standards.      */
+/*  Other degree 32 polynomials may be substituted by re-defining the   */
+/*  symbol POLYNOMIAL below.  Lower degree polynomials must first be    */
+/*  multiplied by an appropriate power of x.  The representation used   */
+/*  is that the coefficient of x^0 is stored in the LSB of the 32-bit   */
+/*  word and the coefficient of x^31 is stored in the most significant  */
+/*  bit.  The CRC is to be appended to the data most significant byte   */
+/*  first.  For those protocols in which bytes are transmitted MSB      */
+/*  first and in the same order as they are encountered in the block    */
+/*  this convention results in the CRC remainder being transmitted with */
+/*  the coefficient of x^31 first and with that of x^0 last (just as    */
+/*  would be done by a hardware shift register mechanization).          */
+/*                                                                      */
+/*  The table lookup technique was adapted from the algorithm described */
+/*  by Avram Perez, Byte-wise CRC Calculations, IEEE Micro 3, 40 (1983).*/
+
+static const guint32 crc_table[256] = {
+       0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
+       0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
+       0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+       0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+       0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
+       0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+       0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
+       0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
+       0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+       0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
+       0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
+       0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+       0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
+       0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+       0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+       0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
+       0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
+       0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+       0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+       0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
+       0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+       0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
+       0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
+       0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+       0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
+       0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
+       0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+       0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
+       0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+       0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+       0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
+       0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
+       0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+       0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+       0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
+       0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+       0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
+       0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
+       0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+       0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
+       0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
+       0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+       0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
+       0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+       0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+       0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
+       0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
+       0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+       0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+       0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
+       0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+       0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
+       0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
+       0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+       0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
+       0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
+       0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+       0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
+       0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+       0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+       0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
+       0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
+       0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+       0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4,
+};
+
+static guint32
+update_crc(guint32 crc_accum, const guint8 *data_blk_ptr, int data_blk_size)
 {
-       guint8 byte0, byte1, byte2;
-
-       if (pinfo->pseudo_header->ngsniffer_atm.Vpi == 0) {
-               /*
-                * Traffic on some PVCs with a VPI of 0 and certain
-                * VCIs is of particular types.
-                */
-               switch (pinfo->pseudo_header->ngsniffer_atm.Vci) {
-
-               case 5:
-                       /*
-                        * Signalling AAL.
-                        */
-                       pinfo->pseudo_header->ngsniffer_atm.AppTrafType =
-                           ATT_AAL_SIGNALLING;
-                       return;
-
-               case 16:
-                       /*
-                        * ILMI.
-                        */
-                       pinfo->pseudo_header->ngsniffer_atm.AppTrafType |=
-                           ATT_HL_ILMI;
-                       return;
-               }
-       }
+  register int i, j;
 
-       /*
-        * OK, we can't tell what it is based on the VPI/VCI; try
-        * guessing based on the contents.
-        */
-       byte0 = tvb_get_guint8(tvb, 0);
-       byte1 = tvb_get_guint8(tvb, 1);
-       byte2 = tvb_get_guint8(tvb, 2);
-       if (byte0 == 0xaa && byte1 == 0xaa && byte2 == 0x03) {
-               /*
-                * Looks like a SNAP header; assume it's LLC multiplexed
-                * RFC 1483 traffic.
-                */
-               pinfo->pseudo_header->ngsniffer_atm.AppTrafType |= ATT_HL_LLCMX;
-       } else {
-               /*
-                * Assume it's LANE.
-                */
-               pinfo->pseudo_header->ngsniffer_atm.AppTrafType |= ATT_HL_LANE;
-               if (byte0 == 0xff && byte1 == 0x00) {
-                       /*
-                        * Looks like LE Control traffic.
-                        */
-                       pinfo->pseudo_header->ngsniffer_atm.AppHLType =
-                           AHLT_LANE_LE_CTRL;
-               } else {
-                       /*
-                        * XXX - Ethernet, or Token Ring?
-                        * Assume Ethernet for now; if we see earlier
-                        * LANE traffic, we may be able to figure out
-                        * the traffic type from that, but there may
-                        * still be situations where the user has to
-                        * tell us.
-                        */
-                       pinfo->pseudo_header->ngsniffer_atm.AppHLType =
-                           AHLT_LANE_802_3;
-               }
-       }
+  /* update the CRC on the data block one byte at a time */
+  for (j = 0; j < data_blk_size;  j++) {
+    i = ( (int) ( crc_accum >> 24) ^ *data_blk_ptr++ ) & 0xff;
+    crc_accum = ( crc_accum << 8 ) ^ crc_table[i];
+  }
+  return crc_accum;
 }
 
 static void
-dissect_atm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+dissect_reassembled_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    proto_tree *atm_tree, gboolean truncated)
 {
-  proto_tree   *atm_tree;
-  proto_item   *ti;
-  guint         aal_type;
-  guint         hl_type;
-  const guint8 *pd;
-  int           offset;
-
-  CHECK_DISPLAY_AS_DATA(proto_atm, tvb, pinfo, tree);
+  guint        length, reported_length;
+  guint16      aal5_length;
+  int          pad_length;
+  tvbuff_t     *next_tvb;
+  guint32      crc;
+  guint32      calc_crc;
+
+  /*
+   * This is reassembled traffic, so the cell headers are missing;
+   * show the traffic type for AAL5 traffic, and the VPI and VCI,
+   * from the pseudo-header.
+   */
+  if (pinfo->pseudo_header->atm.aal == AAL_5) {
+    proto_tree_add_text(atm_tree, tvb, 0, 0, "Traffic type: %s",
+               val_to_str(pinfo->pseudo_header->atm.type, aal5_hltype_vals,
+               "Unknown AAL5 traffic type (%u)"));
+    switch (pinfo->pseudo_header->atm.type) {
+
+    case TRAF_VCMX:
+      proto_tree_add_text(atm_tree, tvb, 0, 0, "VC multiplexed traffic type: %s",
+               val_to_str(pinfo->pseudo_header->atm.subtype,
+                       vcmx_type_vals, "Unknown VCMX traffic type (%u)"));
+      break;
 
-  pinfo->current_proto = "ATM";
+    case TRAF_LANE:
+      proto_tree_add_text(atm_tree, tvb, 0, 0, "LANE traffic type: %s",
+               val_to_str(pinfo->pseudo_header->atm.subtype,
+                       lane_type_vals, "Unknown LANE traffic type (%u)"));
+      break;
 
-  aal_type = pinfo->pseudo_header->ngsniffer_atm.AppTrafType & ATT_AALTYPE;
-  hl_type = pinfo->pseudo_header->ngsniffer_atm.AppTrafType & ATT_HLTYPE;
-  if (aal_type == ATT_AAL5) {
-    if (hl_type == ATT_HL_UNKNOWN ||
-       pinfo->pseudo_header->ngsniffer_atm.AppHLType == AHLT_UNKNOWN) {
+    case TRAF_IPSILON:
+      proto_tree_add_text(atm_tree, tvb, 0, 0, "Ipsilon traffic type: %s",
+               val_to_str(pinfo->pseudo_header->atm.subtype,
+                       ipsilon_type_vals, "Unknown Ipsilon traffic type (%u)"));
+      break;
+    }
+  }
+  proto_tree_add_uint(atm_tree, hf_atm_vpi, tvb, 0, 0,
+               pinfo->pseudo_header->atm.vpi);
+  proto_tree_add_uint(atm_tree, hf_atm_vci, tvb, 0, 0,
+               pinfo->pseudo_header->atm.vci);
+
+  next_tvb = tvb;
+  if (truncated) {
+    /*
+     * The packet data does not include stuff such as the AAL5
+     * trailer.
+     */
+    if (pinfo->pseudo_header->atm.cells != 0) {
       /*
-       * The joys of a connection-oriented link layer; the type of
-       * traffic may be implied by the connection on which it's
-       * traveling, rather than being specified in the packet itself.
-       *
-       * For this packet, the program that captured the packet didn't
-       * save the type of traffic, presumably because it didn't know
-       * the traffic type (either it didn't see the connection setup
-       * and wasn't running on one of the endpoints, and wasn't later
-       * told, e.g. by the human running it, what type of traffic was
-       * on that circuit, or was running on one of the endpoints but
-       * was using, to capture the packets, a mechanism that either
-       * doesn't have access to data saying what's going over the
-       * connection or doesn't bother providing that information).
+       * If the cell count is 0, assume it means we don't know how
+       * many cells it was.
        *
-       * For now, we try to guess the traffic type based on the VPI/VCI
-       * or the packet header; later, we should provide a mechanism
-       * by which the user can specify what sort of traffic is on a
-       * particular circuit.
+       * XXX - also assume it means we don't know what was in the AAL5
+       * trailer.  We may, however, find some capture program that can
+       * give us the AAL5 trailer information but not the cell count,
+       * in which case we need some other way of indicating whether we
+       * have the AAL5 trailer information.
+       */
+      if (tree) {
+        proto_tree_add_text(atm_tree, tvb, 0, 0, "Cells: %u",
+               pinfo->pseudo_header->atm.cells);
+        proto_tree_add_text(atm_tree, tvb, 0, 0, "AAL5 UU: 0x%02x",
+               pinfo->pseudo_header->atm.aal5t_u2u >> 8);
+        proto_tree_add_text(atm_tree, tvb, 0, 0, "AAL5 CPI: 0x%02x",
+               pinfo->pseudo_header->atm.aal5t_u2u & 0xFF);
+        proto_tree_add_text(atm_tree, tvb, 0, 0, "AAL5 len: %u",
+               pinfo->pseudo_header->atm.aal5t_len);
+        proto_tree_add_text(atm_tree, tvb, 0, 0, "AAL5 CRC: 0x%08X",
+               pinfo->pseudo_header->atm.aal5t_chksum);
+      }
+    }
+  } else {
+    /*
+     * The packet data includes stuff such as the AAL5 trailer, if
+     * it wasn't cut off by the snapshot length.
+     * Decode the trailer, if present, and then chop it off.
+     */
+    length = tvb_length(tvb);
+    reported_length = tvb_reported_length(tvb);
+    if ((reported_length % 48) == 0) {
+      /*
+       * Reported length is a multiple of 48, so we can presumably
+       * divide it by 48 to get the number of cells.
+       */
+      proto_tree_add_text(atm_tree, tvb, 0, 0, "Cells: %u",
+               reported_length/48);
+    }
+    if (length >= reported_length) {
+      /*
+       * XXX - what if the packet is truncated?  Can that happen?
+       * What if you capture with Windows Sniffer on an ATM link
+       * and tell it not to save the entire packet?  What happens
+       * to the trailer?
        */
-      atm_guess_content(tvb, pinfo);
+      aal5_length = tvb_get_ntohs(tvb, length - 6);
 
       /*
-       * OK, now get the AAL type and high-layer type again.
+       * Check for sanity in the AAL5 length.  It must be > 0
+       * and must be less than the amount of space left after
+       * we remove the trailer.
+       *
+       * If it's not sane, assume we don't have a trailer.
        */
-      aal_type = pinfo->pseudo_header->ngsniffer_atm.AppTrafType & ATT_AALTYPE;
-      hl_type = pinfo->pseudo_header->ngsniffer_atm.AppTrafType & ATT_HLTYPE;
+      if (aal5_length > 0 && aal5_length <= length - 8) {
+        /*
+         * How much padding is there?
+         */
+        pad_length = length - aal5_length - 8;
+
+        /*
+         * There is no reason for more than 47 bytes of padding.
+         * The most padding you can have would be 7 bytes at the
+         * end of the next-to-last cell (8 bytes after the end of
+         * the data means you can fit the trailer in that cell),
+         * plus 40 bytes in the last cell (with the last 8 bytes
+         * being padding).
+         *
+         * If there's more than 47 bytes of padding, assume we don't
+         * have a trailer.
+         */
+        if (pad_length <= 47) {
+          if (tree) {
+            if (pad_length > 0) {
+              proto_tree_add_text(atm_tree, tvb, aal5_length, pad_length,
+               "Padding");
+            }
+            proto_tree_add_text(atm_tree, tvb, length - 8, 1, "AAL5 UU: 0x%02x",
+               tvb_get_guint8(tvb, length - 8));
+            proto_tree_add_text(atm_tree, tvb, length - 7, 1, "AAL5 CPI: 0x%02x",
+               tvb_get_guint8(tvb, length - 7));
+            proto_tree_add_text(atm_tree, tvb, length - 6, 2, "AAL5 len: %u",
+               aal5_length);
+            crc = tvb_get_ntohl(tvb, length - 4);
+            calc_crc = update_crc(0xFFFFFFFF, tvb_get_ptr(tvb, 0, length),
+               length);
+            proto_tree_add_text(atm_tree, tvb, length - 4, 4,
+               "AAL5 CRC: 0x%08X (%s)", crc,
+               (calc_crc == 0xC704DD7B) ? "correct" : "incorrect");
+         }
+          next_tvb = tvb_new_subset(tvb, 0, aal5_length, aal5_length);
+       }
+      }
     }
   }
 
-  if (check_col(pinfo->fd, COL_PROTOCOL))
-    col_set_str(pinfo->fd, COL_PROTOCOL, "ATM");
+  switch (pinfo->pseudo_header->atm.aal) {
+
+  case AAL_SIGNALLING:
+    call_dissector(sscop_handle, next_tvb, pinfo, tree);
+    break;
 
-  switch (pinfo->pseudo_header->ngsniffer_atm.channel) {
+  case AAL_5:
+    switch (pinfo->pseudo_header->atm.type) {
 
-  case 0:
-    /* Traffic from DCE to DTE. */
-    if (check_col(pinfo->fd, COL_RES_DL_DST))
-      col_set_str(pinfo->fd, COL_RES_DL_DST, "DTE");
-    if (check_col(pinfo->fd, COL_RES_DL_SRC))
-      col_set_str(pinfo->fd, COL_RES_DL_SRC, "DCE");
+    case TRAF_LLCMX:
+      call_dissector(llc_handle, next_tvb, pinfo, tree);
+      break;
+
+    case TRAF_LANE:
+      call_dissector(lane_handle, next_tvb, pinfo, tree);
+      break;
+
+    case TRAF_ILMI:
+      call_dissector(ilmi_handle, next_tvb, pinfo, tree);
+      break;
+
+    default:
+      if (tree) {
+        /* Dump it as raw data. */
+        call_dissector(data_handle, next_tvb, pinfo, tree);
+        break;
+      }
+    }
     break;
 
-  case 1:
-    /* Traffic from DTE to DCE. */
-    if (check_col(pinfo->fd, COL_RES_DL_DST))
-      col_set_str(pinfo->fd, COL_RES_DL_DST, "DCE");
-    if (check_col(pinfo->fd, COL_RES_DL_SRC))
-      col_set_str(pinfo->fd, COL_RES_DL_SRC, "DTE");
+  default:
+    if (tree) {
+      /* Dump it as raw data. */
+      call_dissector(data_handle, next_tvb, pinfo, tree);
+    }
     break;
   }
+}
 
-  if (check_col(pinfo->fd, COL_INFO)) {
-    if (aal_type == ATT_AAL5) {
-      col_add_fstr(pinfo->fd, COL_INFO, "AAL5 %s",
-               val_to_str(hl_type, aal5_hltype_vals,
-                               "Unknown traffic type (%x)"));
-    } else {
-      col_add_str(pinfo->fd, COL_INFO,
-               val_to_str(aal_type, aal_vals, "Unknown AAL (%x)"));
-    }
+/*
+ * Charles Michael Heard's HEC code, from
+ *
+ *     http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/32bitCRC.tutorial.html
+ *
+ * with the syndrome and error position tables initialized with values
+ * computed by his "gen_syndrome_table()" and "gen_err_posn_table()" routines,
+ * rather than by calling those routines at run time, and with various data
+ * type cleanups and changes not to correct the header if a correctible
+ * error was detected.
+ */
+#define COSET_LEADER    0x055               /* x^6 + x^4 + x^2 + 1  */
+
+static const guint8 syndrome_table[256] = {
+       0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15,
+       0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d,
+       0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
+       0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d,
+       0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5,
+       0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
+       0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85,
+       0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd,
+       0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
+       0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea,
+       0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2,
+       0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
+       0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32,
+       0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a,
+       0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
+       0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a,
+       0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c,
+       0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
+       0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,
+       0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4,
+       0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
+       0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44,
+       0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c,
+       0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
+       0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b,
+       0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63,
+       0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
+       0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13,
+       0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb,
+       0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83,
+       0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb,
+       0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3,
+};
+
+#define NO_ERROR_DETECTED   -128
+#define UNCORRECTIBLE_ERROR  128
+
+static const int err_posn_table[256] = {
+       NO_ERROR_DETECTED,      37,
+       36,                     UNCORRECTIBLE_ERROR,
+       35,                     UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    29,
+       34,                     UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    6,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       28,                     UNCORRECTIBLE_ERROR,
+       33,                     UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    21,
+       5,                      UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       27,                     UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       32,                     UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       20,                     UNCORRECTIBLE_ERROR,
+       4,                      UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       26,                     UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+       UNCORRECTIBLE_ERROR,    UNCORRECTIBLE_ERROR,
+};
+
+/*
+ * Return an indication of whether there was an error in the cell header
+ * and, if so, where the error was, if it was correctable.
+ */
+static int
+get_header_err(const guint8 *cell_header)
+{
+  register guint8 syndrome;
+  register int i, err_posn;
+
+  syndrome = 0;
+  for (i = 0;  i < 4;  i++)
+    syndrome = syndrome_table[syndrome ^ cell_header[i]];
+  syndrome ^= cell_header[4] ^ COSET_LEADER;
+
+  err_posn = err_posn_table [syndrome];
+
+  if (err_posn < 0)
+    return NO_ERROR_DETECTED;
+  else if (err_posn < 40)
+    return err_posn;
+  else
+    return UNCORRECTIBLE_ERROR;
+}
+
+static const value_string pt_vals[] = {
+       { 0, "User data cell, congestion not experienced, SDU-type = 0" },
+       { 1, "User data cell, congestion not experienced, SDU-type = 1" },
+       { 2, "User data cell, congestion experienced, SDU-type = 0" },
+       { 3, "User data cell, congestion experienced, SDU-type = 1" },
+       { 4, "Segment OAM F5 flow related cell" },
+       { 5, "End-to-end OAM F5 flow related cell" },
+       { 0, NULL }
+};
+
+/*
+ * Charles Michael Heard's CRC-10 code, from
+ *
+ *     http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/crc10.html
+ *
+ * with the CRC table initialized with values computed by
+ * his "gen_byte_crc10_table()" routine, rather than by calling that
+ * routine at run time, and with various data type cleanups.
+ */
+static const guint16 byte_crc10_table[256] = {
+       0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
+       0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
+       0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
+       0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
+       0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
+       0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
+       0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
+       0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
+       0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
+       0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
+       0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
+       0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
+       0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
+       0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
+       0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
+       0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
+       0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
+       0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
+       0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
+       0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
+       0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
+       0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
+       0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
+       0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
+       0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
+       0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
+       0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
+       0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
+       0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
+       0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
+       0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
+       0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1,
+};
+
+/* update the data block's CRC-10 remainder one byte at a time */
+static guint16
+update_crc10_by_bytes(guint16 crc10_accum, const guint8 *data_blk_ptr,
+    int data_blk_size)
+{
+  register int i;
+
+  for (i = 0;  i < data_blk_size; i++) {
+      crc10_accum = ((crc10_accum << 8) & 0x3ff)
+                     ^ byte_crc10_table[( crc10_accum >> 2) & 0xff]
+                                                    ^ *data_blk_ptr++;
   }
+  return crc10_accum;
+}
 
-  if (tree) {
-    ti = proto_tree_add_protocol_format(tree, proto_atm, tvb, 0, 0, "ATM");
-    atm_tree = proto_item_add_subtree(ti, ett_atm);
+static const value_string st_vals[] = {
+       { 2, "BOM" },
+       { 0, "COM" },
+       { 1, "EOM" },
+       { 3, "SSM" },
+       { 0, NULL }
+};
 
-    proto_tree_add_text(atm_tree, tvb, 0, 0, "AAL: %s",
-       val_to_str(aal_type, aal_vals, "Unknown AAL (%x)"));
-    if (aal_type == ATT_AAL5) {
-      proto_tree_add_text(atm_tree, tvb, 0, 0, "Traffic type: %s",
-       val_to_str(hl_type, aal5_hltype_vals, "Unknown AAL5 traffic type (%x)"));
-      switch (hl_type) {
+#define OAM_TYPE_FM    1       /* Fault Management */
+#define OAM_TYPE_PM    2       /* Performance Management */
+#define OAM_TYPE_AD    8       /* Activation/Deactivation */
 
-      case ATT_HL_LLCMX:
-        proto_tree_add_text(atm_tree, tvb, 0, 0, "LLC multiplexed traffic");
-        break;
+static const value_string oam_type_vals[] = {
+       { OAM_TYPE_FM, "Fault Management" },
+       { OAM_TYPE_PM, "Performance Management" },
+       { OAM_TYPE_AD, "Activation/Deactivation" },
+       { 0,           NULL }
+};
 
-      case ATT_HL_VCMX:
-        proto_tree_add_text(atm_tree, tvb, 0, 0, "VC multiplexed traffic type: %s",
-               val_to_str(pinfo->pseudo_header->ngsniffer_atm.AppHLType,
-                       vcmx_type_vals, "Unknown VCMX traffic type (%x)"));
-        break;
+static const value_string ft_fm_vals[] = {
+       { 0, "Alarm Indication Signal" },
+       { 1, "Far End Receive Failure" },
+       { 8, "OAM Cell Loopback" },
+       { 4, "Continuity Check" },
+       { 0, NULL }
+};
 
-      case ATT_HL_LANE:
-        proto_tree_add_text(atm_tree, tvb, 0, 0, "LANE traffic type: %s",
-               val_to_str(pinfo->pseudo_header->ngsniffer_atm.AppHLType,
-                       lane_type_vals, "Unknown LANE traffic type (%x)"));
-        break;
+static const value_string ft_pm_vals[] = {
+       { 0, "Forward Monitoring" },
+       { 1, "Backward Reporting" },
+       { 2, "Monitoring and Reporting" },
+       { 0, NULL }
+};
 
-      case ATT_HL_IPSILON:
-        proto_tree_add_text(atm_tree, tvb, 0, 0, "Ipsilon traffic type: %s",
-               val_to_str(pinfo->pseudo_header->ngsniffer_atm.AppHLType,
-                       ipsilon_type_vals, "Unknown Ipsilon traffic type (%x)"));
-        break;
-      }
+static const value_string ft_ad_vals[] = {
+       { 0, "Performance Monitoring" },
+       { 1, "Continuity Check" },
+       { 0, NULL }
+};
+
+static void
+dissect_atm_cell(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    proto_tree *atm_tree)
+{
+  int          offset;
+  proto_tree   *aal_tree;
+  proto_item   *ti;
+  guint8       octet;
+  int          err;
+  guint8       vpi;
+  guint16      vci;
+  guint16      aal3_4_hdr, aal3_4_trlr;
+  guint16      oam_crc;
+  gint         length;
+  guint16      crc10;
+  tvbuff_t     *next_tvb;
+
+  octet = tvb_get_guint8(tvb, 0);
+  proto_tree_add_text(atm_tree, tvb, 0, 1, "GFC: 0x%x", octet >> 4);
+  vpi = (octet & 0xF0) << 4;
+  octet = tvb_get_guint8(tvb, 1);
+  vpi |= octet >> 4;
+  proto_tree_add_uint(atm_tree, hf_atm_vpi, tvb, 0, 2, vpi);
+  vci = (octet & 0x0F) << 12;
+  octet = tvb_get_guint8(tvb, 2);
+  vci |= octet << 4;
+  octet = tvb_get_guint8(tvb, 3);
+  vci |= octet >> 4;
+  proto_tree_add_uint(atm_tree, hf_atm_vci, tvb, 1, 3, vci);
+  proto_tree_add_text(atm_tree, tvb, 3, 1, "Payload Type: %s",
+        val_to_str((octet >> 1) & 0x7, pt_vals, "Unknown (%u)"));
+  proto_tree_add_text(atm_tree, tvb, 3, 1, "Cell Loss Priority: %s",
+       (octet & 0x01) ? "Low priority" : "High priority");
+  ti = proto_tree_add_text(atm_tree, tvb, 4, 1, "Header Error Check: 0x%02x",
+         tvb_get_guint8(tvb, 4));
+  err = get_header_err(tvb_get_ptr(tvb, 0, 5));
+  if (err == NO_ERROR_DETECTED)
+    proto_item_append_text(ti, " (correct)");
+  else if (err == UNCORRECTIBLE_ERROR)
+    proto_item_append_text(ti, " (uncorrectable error)");
+  else
+    proto_item_append_text(ti, " (error in bit %d)", err);
+  offset = 5;
+
+  switch (pinfo->pseudo_header->atm.aal) {
+
+  case AAL_1:
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+      col_set_str(pinfo->cinfo, COL_PROTOCOL, "AAL1");
+    ti = proto_tree_add_item(tree, proto_aal1, tvb, offset, -1, FALSE);
+    aal_tree = proto_item_add_subtree(ti, ett_aal1);
+    octet = tvb_get_guint8(tvb, offset);
+    proto_tree_add_text(aal_tree, tvb, offset, 1, "CSI: %u", octet >> 7);
+    proto_tree_add_text(aal_tree, tvb, offset, 1, "Sequence Count: %u",
+               (octet >> 4) & 0x7);
+    if (check_col(pinfo->cinfo, COL_INFO)) {
+      col_add_fstr(pinfo->cinfo, COL_INFO, "Sequence count = %u",
+               (octet >> 4) & 0x7);
     }
-    proto_tree_add_uint(atm_tree, hf_atm_vpi, tvb, 0, 0,
-               pinfo->pseudo_header->ngsniffer_atm.Vpi);
-    proto_tree_add_uint(atm_tree, hf_atm_vci, tvb, 0, 0,
-               pinfo->pseudo_header->ngsniffer_atm.Vci);
-    switch (pinfo->pseudo_header->ngsniffer_atm.channel) {
+    proto_tree_add_text(aal_tree, tvb, offset, 1, "CRC: 0x%x",
+               (octet >> 1) & 0x7);
+    proto_tree_add_text(aal_tree, tvb, offset, 1, "Parity: %u",
+               octet & 0x1);
+    offset++;
 
-    case 0:
-      /* Traffic from DCE to DTE. */
-      proto_tree_add_text(atm_tree, tvb, 0, 0, "Channel: DCE->DTE");
+    proto_tree_add_text(aal_tree, tvb, offset, 47, "Payload");
+    break;
+
+  case AAL_3_4:
+    /*
+     * XXX - or should this be the CS PDU?
+     */
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+      col_set_str(pinfo->cinfo, COL_PROTOCOL, "AAL3/4");
+    ti = proto_tree_add_item(tree, proto_aal3_4, tvb, offset, -1, FALSE);
+    aal_tree = proto_item_add_subtree(ti, ett_aal3_4);
+    aal3_4_hdr = tvb_get_ntohs(tvb, offset);
+    if (check_col(pinfo->cinfo, COL_INFO)) {
+      col_add_fstr(pinfo->cinfo, COL_INFO, "%s, sequence number = %u",
+               val_to_str(aal3_4_hdr >> 14, st_vals, "Unknown (%u)"),
+               (aal3_4_hdr >> 10) & 0xF);
+    }
+    proto_tree_add_text(aal_tree, tvb, offset, 2, "Segment Type: %s",
+               val_to_str(aal3_4_hdr >> 14, st_vals, "Unknown (%u)"));
+    proto_tree_add_text(aal_tree, tvb, offset, 2, "Sequence Number: %u",
+               (aal3_4_hdr >> 10) & 0xF);
+    proto_tree_add_text(aal_tree, tvb, offset, 2, "Multiplex ID: %u",
+               aal3_4_hdr & 0x3FF);
+    offset += 2;
+
+    proto_tree_add_text(aal_tree, tvb, offset, 44, "Information");
+    offset += 44;
+
+    aal3_4_trlr = tvb_get_ntohs(tvb, offset);
+    proto_tree_add_text(aal_tree, tvb, offset, 2, "Length Indicator: %u",
+               (aal3_4_trlr >> 10) & 0x3F);
+    length = tvb_length_remaining(tvb, 5);
+    crc10 = update_crc10_by_bytes(0, tvb_get_ptr(tvb, 5, length),
+       length);
+    proto_tree_add_text(aal_tree, tvb, offset, 2, "CRC: 0x%03x (%s)",
+               aal3_4_trlr & 0x3FF,
+               (crc10 == 0) ? "correct" : "incorrect");
+    break;
+
+  case AAL_OAMCELL:
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+      col_set_str(pinfo->cinfo, COL_PROTOCOL, "OAM AAL");
+    ti = proto_tree_add_item(tree, proto_oamaal, tvb, offset, -1, FALSE);
+    aal_tree = proto_item_add_subtree(ti, ett_oamaal);
+    octet = tvb_get_guint8(tvb, offset);
+    proto_tree_add_text(aal_tree, tvb, offset, 1, "OAM Type: %s",
+               val_to_str(octet >> 4, oam_type_vals, "Unknown (%u)"));
+    switch (octet >> 4) {
+
+    case OAM_TYPE_FM:
+      proto_tree_add_text(aal_tree, tvb, offset, 1, "Function Type: %s",
+               val_to_str(octet & 0x0F, ft_fm_vals, "Unknown (%u)"));
       break;
 
-    case 1:
-      /* Traffic from DTE to DCE. */
-      proto_tree_add_text(atm_tree, tvb, 0, 0, "Channel: DTE->DCE");
+    case OAM_TYPE_PM:
+      proto_tree_add_text(aal_tree, tvb, offset, 1, "Function Type: %s",
+               val_to_str(octet & 0x0F, ft_pm_vals, "Unknown (%u)"));
+      break;
+
+    case OAM_TYPE_AD:
+      proto_tree_add_text(aal_tree, tvb, offset, 1, "Function Type: %s",
+               val_to_str(octet & 0x0F, ft_ad_vals, "Unknown (%u)"));
       break;
 
     default:
-      /* Sniffers shouldn't provide anything other than 0 or 1. */
-      proto_tree_add_text(atm_tree, tvb, 0, 0, "Channel: %u",
-               pinfo->pseudo_header->ngsniffer_atm.channel);
+      proto_tree_add_text(aal_tree, tvb, offset, 1, "Function Type: %u",
+               octet & 0x0F);
       break;
     }
-    if (pinfo->pseudo_header->ngsniffer_atm.cells != 0) {
-      /*
-       * If the cell count is 0, assume it means we don't know how
-       * many cells it was.
-       *
-       * XXX - also, if this is AAL5 traffic, assume it means we don't
-       * know what was in the AAL5 trailer.  We may, however, find
-       * some capture program that can give us the AAL5 trailer
-       * information but not the cell count, in which case we need
-       * some other way of indicating whether we have the AAL5 trailer
-       * information.
-       */
-      proto_tree_add_text(atm_tree, tvb, 0, 0, "Cells: %u",
-               pinfo->pseudo_header->ngsniffer_atm.cells);
-      if (aal_type == ATT_AAL5) {
-        proto_tree_add_text(atm_tree, tvb, 0, 0, "AAL5 U2U: %u",
-               pinfo->pseudo_header->ngsniffer_atm.aal5t_u2u);
-        proto_tree_add_text(atm_tree, tvb, 0, 0, "AAL5 len: %u",
-               pinfo->pseudo_header->ngsniffer_atm.aal5t_len);
-        proto_tree_add_text(atm_tree, tvb, 0, 0, "AAL5 checksum: 0x%08X",
-               pinfo->pseudo_header->ngsniffer_atm.aal5t_chksum);
-      }
-    }
+    offset += 1;
+
+    proto_tree_add_text(aal_tree, tvb, offset, 45, "Function-specific information");
+    offset += 45;
+
+    length = tvb_length_remaining(tvb, 5);
+    crc10 = update_crc10_by_bytes(0, tvb_get_ptr(tvb, 5, length),
+       length);
+    oam_crc = tvb_get_ntohs(tvb, offset);
+    proto_tree_add_text(aal_tree, tvb, offset, 2, "CRC-10: 0x%03x (%s)",
+               oam_crc & 0x3FF,
+               (crc10 == 0) ? "correct" : "incorrect");
+    break;
+
+  default:
+    next_tvb = tvb_new_subset(tvb, offset, -1, -1);
+    call_dissector(data_handle, next_tvb, pinfo, tree);
+    break;
   }
+}
+
+static void
+dissect_atm_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    gboolean truncated)
+{
+  proto_tree   *atm_tree = NULL;
+  proto_item   *ti = NULL;
 
-  switch (aal_type) {
+  if (check_col(pinfo->cinfo, COL_PROTOCOL))
+    col_set_str(pinfo->cinfo, COL_PROTOCOL, "ATM");
 
-  case ATT_AAL_SIGNALLING:
-    dissect_sscop(tvb, pinfo, tree);
+  switch (pinfo->pseudo_header->atm.channel) {
+
+  case 0:
+    /* Traffic from DTE to DCE. */
+    if (check_col(pinfo->cinfo, COL_RES_DL_DST))
+      col_set_str(pinfo->cinfo, COL_RES_DL_DST, "DCE");
+    if (check_col(pinfo->cinfo, COL_RES_DL_SRC))
+      col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "DTE");
     break;
 
-  case ATT_AAL5:
-    switch (hl_type) {
+  case 1:
+    /* Traffic from DCE to DTE. */
+    if (check_col(pinfo->cinfo, COL_RES_DL_DST))
+      col_set_str(pinfo->cinfo, COL_RES_DL_DST, "DTE");
+    if (check_col(pinfo->cinfo, COL_RES_DL_SRC))
+      col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "DCE");
+    break;
+  }
 
-    case ATT_HL_LLCMX:
-      /* Dissect as WTAP_ENCAP_ATM_RFC1483 */
-      /* The ATM iptrace capture that we have shows LLC at this point,
-       * so that's what I'm calling */
-      dissect_llc(tvb, &pi, tree);
-      break;
+  if (check_col(pinfo->cinfo, COL_INFO)) {
+    if (pinfo->pseudo_header->atm.aal == AAL_5) {
+      col_add_fstr(pinfo->cinfo, COL_INFO, "AAL5 %s",
+               val_to_str(pinfo->pseudo_header->atm.type, aal5_hltype_vals,
+                               "Unknown traffic type (%u)"));
+    } else {
+      col_add_str(pinfo->cinfo, COL_INFO,
+               val_to_str(pinfo->pseudo_header->atm.aal, aal_vals,
+                       "Unknown AAL (%u)"));
+    }
+  }
+
+  if (tree) {
+    ti = proto_tree_add_protocol_format(tree, proto_atm, tvb, 0, 0, "ATM");
+    atm_tree = proto_item_add_subtree(ti, ett_atm);
+
+    switch (pinfo->pseudo_header->atm.channel) {
 
-    case ATT_HL_LANE:
-      dissect_lane(tvb, pinfo, tree);
+    case 0:
+      /* Traffic from DTE to DCE. */
+      proto_tree_add_text(atm_tree, tvb, 0, 0, "Channel: DTE->DCE");
       break;
 
-    case ATT_HL_ILMI:
-      CHECK_DISPLAY_AS_DATA(proto_ilmi, tvb, pinfo, tree);
-      tvb_compat(tvb, &pd, &offset);
-      dissect_snmp_pdu(pd, offset, pinfo->fd, tree, "ILMI", proto_ilmi, ett_ilmi);
+    case 1:
+      /* Traffic from DCE to DTE. */
+      proto_tree_add_text(atm_tree, tvb, 0, 0, "Channel: DCE->DTE");
       break;
 
     default:
-      if (tree) {
-        /* Dump it as raw data. */
-        dissect_data(tvb, 0, pinfo, tree);
-        break;
-      }
+      /* Sniffers shouldn't provide anything other than 0 or 1. */
+      proto_tree_add_text(atm_tree, tvb, 0, 0, "Channel: %u",
+               pinfo->pseudo_header->atm.channel);
+      break;
     }
-    break;
 
-  default:
-    if (tree) {
-      /* Dump it as raw data.  (Is this a single cell?) */
-      dissect_data(tvb, 0, pinfo, tree);
-    }
-    break;
+    proto_tree_add_uint_format(atm_tree, hf_atm_aal, tvb, 0, 0,
+       pinfo->pseudo_header->atm.aal,
+       "AAL: %s",
+       val_to_str(pinfo->pseudo_header->atm.aal, aal_vals,
+         "Unknown AAL (%u)"));
   }
+  if (pinfo->pseudo_header->atm.flags & ATM_RAW_CELL) {
+    /* This is a single cell, with the cell header at the beginning. */
+    proto_item_set_len(ti, 5);
+    dissect_atm_cell(tvb, pinfo, tree, atm_tree);
+  } else {
+    /* This is a reassembled PDU. */
+    dissect_reassembled_pdu(tvb, pinfo, tree, atm_tree, truncated);
+  }
+}
+
+static void
+dissect_atm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+  dissect_atm_common(tvb, pinfo, tree, TRUE);
+}
+
+static void
+dissect_atm_untruncated(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+  dissect_atm_common(tvb, pinfo, tree, FALSE);
 }
 
 void
 proto_register_atm(void)
 {
        static hf_register_info hf[] = {
+               { &hf_atm_aal,
+               { "AAL",                "atm.aal", FT_UINT8, BASE_DEC, VALS(aal_vals), 0x0,
+                       "", HFILL }},
+
                { &hf_atm_vpi,
                { "VPI",                "atm.vpi", FT_UINT8, BASE_DEC, NULL, 0x0,
-                       "" }},
+                       "", HFILL }},
 
                { &hf_atm_vci,
                { "VCI",                "atm.vci", FT_UINT16, BASE_DEC, NULL, 0x0,
-                       "" }},
+                       "", HFILL }},
        };
        static gint *ett[] = {
                &ett_atm,
                &ett_ilmi,
+               &ett_aal1,
+               &ett_aal3_4,
+               &ett_oamaal,
                &ett_atm_lane,
                &ett_atm_lane_lc_lan_dest,
                &ett_atm_lane_lc_lan_dest_rd,
@@ -784,15 +1597,44 @@ proto_register_atm(void)
                &ett_atm_lane_lc_tlv,
        };
        proto_atm = proto_register_protocol("ATM", "ATM", "atm");
+       proto_aal1 = proto_register_protocol("ATM AAL1", "AAL1", "aal1");
+       proto_aal3_4 = proto_register_protocol("ATM AAL3/4", "AAL3_4", "aal3_4");
+       proto_oamaal = proto_register_protocol("ATM OAM AAL", "OAM AAL", "oamaal");
        proto_register_field_array(proto_atm, hf, array_length(hf));
+       proto_register_subtree_array(ett, array_length(ett));
+
        proto_ilmi = proto_register_protocol("ILMI", "ILMI", "ilmi");
+
+       register_dissector("ilmi", dissect_ilmi, proto_ilmi);
+
        proto_atm_lane = proto_register_protocol("ATM LAN Emulation",
            "ATM LANE", "lane");
-       proto_register_subtree_array(ett, array_length(ett));
+
+       register_dissector("lane", dissect_lane, proto_atm_lane);
 }
 
 void
 proto_reg_handoff_atm(void)
 {
-       dissector_add("wtap_encap", WTAP_ENCAP_ATM_SNIFFER, dissect_atm);
+       dissector_handle_t atm_handle, atm_untruncated_handle;
+
+       /*
+        * Get handles for the Ethernet, Token Ring, LLC, SSCOP, LANE,
+        * and ILMI dissectors.
+        */
+       eth_handle = find_dissector("eth");
+       tr_handle = find_dissector("tr");
+       llc_handle = find_dissector("llc");
+       sscop_handle = find_dissector("sscop");
+       lane_handle = find_dissector("lane");
+       ilmi_handle = find_dissector("ilmi");
+       data_handle = find_dissector("data");
+
+       atm_handle = create_dissector_handle(dissect_atm, proto_atm);
+       dissector_add("wtap_encap", WTAP_ENCAP_ATM_PDUS, atm_handle);
+
+       atm_untruncated_handle = create_dissector_handle(dissect_atm_untruncated,
+           proto_atm);
+       dissector_add("wtap_encap", WTAP_ENCAP_ATM_PDUS_UNTRUNCATED,
+           atm_untruncated_handle);
 }