Frame numbers are unsigned.
[obnox/wireshark/wip.git] / packet-q2931.c
index 18a8233acb3dd969165575153932eab2ba6ba249..d54762a6205fbffda1c898b5875761fe36d20b23 100644 (file)
@@ -2,23 +2,22 @@
  * Routines for Q.2931 frame disassembly
  * Guy Harris <guy@alum.mit.edu>
  *
- * $Id: packet-q2931.c,v 1.2 1999/11/25 10:01:18 guy Exp $
+ * $Id: packet-q2931.c,v 1.30 2002/11/10 20:26:24 guy 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 <string.h>
-#include "packet.h"
+#include <epan/packet.h>
+#include "oui.h"
+#include "nlpid.h"
+#include "etypes.h"
 #include "packet-q931.h"
+#include "packet-arp.h"
 
 /*
  * See
  *
  * for some information on Q.2931, although, alas, not the actual message
  * type and information element values - those I got from the FreeBSD 3.2
- * ATM code.
+ * ATM code, and from Q.2931 (and Q.931) itself.
  */
 
 static int proto_q2931 = -1;
 static int hf_q2931_discriminator = -1;
 static int hf_q2931_call_ref_len = -1;
+static int hf_q2931_call_ref_flag = -1;
 static int hf_q2931_call_ref = -1;
 static int hf_q2931_message_type = -1;
 static int hf_q2931_message_type_ext = -1;
@@ -62,6 +62,10 @@ static gint ett_q2931 = -1;
 static gint ett_q2931_ext = -1;
 static gint ett_q2931_ie = -1;
 static gint ett_q2931_ie_ext = -1;
+static gint ett_q2931_nsap = -1;
+
+static void dissect_q2931_ie(tvbuff_t *tvb, int offset, int len,
+    proto_tree *tree, guint8 info_element, guint8 info_element_ext);
 
 /*
  * Q.2931 message types.
@@ -86,6 +90,8 @@ static gint ett_q2931_ie_ext = -1;
 #define        Q2931_ADD_PARTY_REJ     0x82
 #define        Q2931_DROP_PARTY        0x83
 #define        Q2931_DROP_PARTY_ACK    0x84
+#define        Q2931_LEAF_SETUP_FAIL   0x90
+#define        Q2931_LEAF_SETUP_REQ    0x91
 
 static const value_string q2931_message_type_vals[] = {
        { Q2931_ALERTING,               "ALERTING" },
@@ -108,9 +114,16 @@ static const value_string q2931_message_type_vals[] = {
        { Q2931_ADD_PARTY_REJ,          "ADD PARTY REJECT" },
        { Q2931_DROP_PARTY,             "DROP PARTY" },
        { Q2931_DROP_PARTY_ACK,         "DROP PARTY ACKNOWLEDGE" },
+       { Q2931_LEAF_SETUP_FAIL,        "LEAF SETUP FAILURE" },
+       { Q2931_LEAF_SETUP_REQ,         "LEAF SETUP REQUEST" },
        { 0,                            NULL }
 };
 
+static const true_false_string tfs_call_ref_flag = {
+       "Message sent to originating side",
+       "Message sent from originating side"
+};
+
 /*
  * Bits in the message type extension.
  */
@@ -192,6 +205,7 @@ static const value_string ie_action_ind_vals[] = {
 #define        Q2931_IE_RESTART_INDICATOR      0x79
 #define        Q2931_IE_NBAND_LOW_LAYER_COMPAT 0x7C    /* Narrowband Low-Layer Compatibility */
 #define        Q2931_IE_NBAND_HIGH_LAYER_COMPAT 0x7D   /* Narrowband High-Layer Compatibility */
+#define        Q2931_IE_GENERIC_IDENT_TRANSPORT 0x7F   /* Generic identifier transport */
 
 static const value_string q2931_info_element_vals[] = {
        { Q2931_IE_NBAND_BEARER_CAP,            "Narrowband bearer capability" },
@@ -222,6 +236,7 @@ static const value_string q2931_info_element_vals[] = {
        { Q2931_IE_RESTART_INDICATOR,           "Restart indicator" },
        { Q2931_IE_NBAND_LOW_LAYER_COMPAT,      "Narrowband low-layer compatibility" },
        { Q2931_IE_NBAND_HIGH_LAYER_COMPAT,     "Narrowband high-layer compatibility" },
+       { Q2931_IE_GENERIC_IDENT_TRANSPORT,     "Generic identifier transport" },
        { 0,                                    NULL }
 };
 
@@ -238,15 +253,17 @@ static const value_string q2931_codeset_vals[] = {
 };
 
 static void
-dissect_q2931_shift_ie(const u_char *pd, int offset, int len,
+dissect_q2931_shift_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree, guint8 info_element)
 {
        gboolean non_locking_shift;
        guint8 codeset;
 
+       if (len == 0)
+               return;
        non_locking_shift = (info_element == Q2931_IE_BBAND_NLOCKING_SHIFT);
-       codeset = pd[offset] & 0x07;
-       proto_tree_add_text(tree, offset, 1, "%s shift to codeset %u: %s",
+       codeset = tvb_get_guint8(tvb, offset) & 0x07;
+       proto_tree_add_text(tree, tvb, offset, 1, "%s shift to codeset %u: %s",
            (non_locking_shift ? "Non-locking" : "Locking"),
            codeset,
            val_to_str(codeset, q2931_codeset_vals, "Unknown (0x%02X)"));
@@ -276,6 +293,7 @@ static const value_string q9231_aal1_subtype_vals[] = {
        { 0x00, "Null" },
        { 0x01, "64 kbit/s voice-band signal transport (G.711/G.722)" },
        { 0x02, "Circuit transport (I.363)" },
+       { 0x03, "Circuit emulation (asynchronous)" },
        { 0x04, "High-quality audio signal transport (I.363)" },
        { 0x05, "Video signal transport (I.363)" },
        { 0x00, NULL }
@@ -310,7 +328,13 @@ static const value_string q2931_aal1_src_clk_rec_meth_vals[] = {
 static const value_string q2931_aal1_err_correction_method_vals[] = {
        { 0x00, "Null" },
        { 0x01, "FEC method for less sensitive signal transport" },
-       { 0x02, "FEC method for delay-sensigive signal transport" },
+       { 0x02, "FEC method for delay-sensitive signal transport" },
+       { 0x00, NULL }
+};
+
+static const value_string q2931_aal_mode_vals[] = {
+       { 0x01, "Message" },
+       { 0x02, "Streaming" },
        { 0x00, NULL }
 };
 
@@ -323,7 +347,7 @@ static const value_string q2931_sscs_type_vals[] = {
 };
 
 static void
-dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
+dissect_q2931_aal_parameters_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 aal_type;
@@ -333,8 +357,8 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
 
        if (len == 0)
                return;
-       aal_type = pd[offset];
-       proto_tree_add_text(tree, offset, 1, "AAL type: %s",
+       aal_type = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1, "AAL type: %s",
            val_to_str(aal_type, q9231_aal_type_vals, "Unknown (0x%02X)"));
        offset += 1;
        len -= 1;
@@ -348,21 +372,21 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
                 */
                if (len > 4)
                        len = 4;
-               proto_tree_add_text(tree, offset, len,
+               proto_tree_add_text(tree, tvb, offset, len,
                    "User defined AAL information: %s",
-                   bytes_to_str(&pd[offset], len));
+                   tvb_bytes_to_str(tvb, offset, len));
                return;
        }
 
        while (len != 0) {
-               identifier = pd[offset];
+               identifier = tvb_get_guint8(tvb, offset);
                switch (identifier) {
 
                case 0x85:      /* Subtype identifier for AAL1 */
                        if (len < 2)
                                return;
-                       value = pd[offset + 1];
-                       proto_tree_add_text(tree, offset, 2,
+                       value = tvb_get_guint8(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 2,
                            "Subtype: %s",
                            val_to_str(value, q9231_aal1_subtype_vals,
                            "Unknown (0x%02X)"));
@@ -373,8 +397,8 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
                case 0x86:      /* CBR identifier for AAL1 */
                        if (len < 2)
                                return;
-                       value = pd[offset + 1];
-                       proto_tree_add_text(tree, offset, 2,
+                       value = tvb_get_guint8(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 2,
                            "CBR rate: %s",
                            val_to_str(value, q9231_aal1_cbr_rate_vals,
                            "Unknown (0x%02X)"));
@@ -385,8 +409,8 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
                case 0x87:      /* Multiplier identifier for AAL1 */
                        if (len < 3)
                                return;
-                       value = pntohs(&pd[offset + 1]);
-                       proto_tree_add_text(tree, offset, 3,
+                       value = tvb_get_ntohs(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 3,
                            "Multiplier: %u", value);
                        offset += 3;
                        len -= 3;
@@ -395,8 +419,8 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
                case 0x88:      /* Source clock frequency recovery method identifier for AAL1 */
                        if (len < 2)
                                return;
-                       value = pd[offset + 1];
-                       proto_tree_add_text(tree, offset, 2,
+                       value = tvb_get_guint8(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 2,
                            "Source clock frequency recovery method: %s",
                            val_to_str(value, q2931_aal1_src_clk_rec_meth_vals,
                            "Unknown (0x%02X)"));
@@ -407,8 +431,8 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
                case 0x89:      /* Error correction method identifier for AAL1 */
                        if (len < 2)
                                return;
-                       value = pd[offset + 1];
-                       proto_tree_add_text(tree, offset, 2,
+                       value = tvb_get_guint8(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 2,
                            "Error correction method: %s",
                            val_to_str(value, q2931_aal1_err_correction_method_vals,
                            "Unknown (0x%02X)"));
@@ -419,8 +443,8 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
                case 0x8A:      /* Structured data transfer block size identifier for AAL1 */
                        if (len < 3)
                                return;
-                       value = pntohs(&pd[offset + 1]);
-                       proto_tree_add_text(tree, offset, 3,
+                       value = tvb_get_ntohs(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 3,
                            "Structured data transfer block size: %u", value);
                        offset += 3;
                        len -= 3;
@@ -429,8 +453,8 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
                case 0x8B:      /* Partially filled cells identifier for AAL1 */
                        if (len < 2)
                                return;
-                       value = pd[offset + 1];
-                       proto_tree_add_text(tree, offset, 2,
+                       value = tvb_get_guint8(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 2,
                            "Partially filled cells method: %u octets", value);
                        offset += 2;
                        len -= 2;
@@ -439,8 +463,8 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
                case 0x8C:      /* Forward maximum CPCS-SDU size identifier for AAL3/4 and AAL5 */
                        if (len < 3)
                                return;
-                       value = pntohs(&pd[offset + 1]);
-                       proto_tree_add_text(tree, offset, 3,
+                       value = tvb_get_ntohs(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 3,
                            "Forward maximum CPCS-SDU size: %u", value);
                        offset += 3;
                        len -= 3;
@@ -449,8 +473,8 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
                case 0x81:      /* Backward maximum CPCS-SDU size identifier for AAL3/4 and AAL5 */
                        if (len < 3)
                                return;
-                       value = pntohs(&pd[offset + 1]);
-                       proto_tree_add_text(tree, offset, 3,
+                       value = tvb_get_ntohs(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 3,
                            "Backward maximum CPCS-SDU size: %u", value);
                        offset += 3;
                        len -= 3;
@@ -459,19 +483,31 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
                case 0x82:      /* MID range identifier for AAL3/4 */
                        if (len < 5)
                                return;
-                       low_mid = pntohs(&pd[offset + 1]);
-                       high_mid = pntohs(&pd[offset + 3]);
-                       proto_tree_add_text(tree, offset, 3,
+                       low_mid = tvb_get_ntohs(tvb, offset + 1);
+                       high_mid = tvb_get_ntohs(tvb, offset + 3);
+                       proto_tree_add_text(tree, tvb, offset, 3,
                            "MID range: %u - %u", low_mid, high_mid);
                        offset += 5;
                        len -= 5;
                        break;
 
+               case 0x83:      /* Mode identifier for AAL3/4 and AAL5 */
+                       if (len < 2)
+                               return;
+                       value = tvb_get_guint8(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 2,
+                           "Mode: %s",
+                           val_to_str(value, q2931_aal_mode_vals,
+                           "Unknown (0x%02X)"));
+                       offset += 2;
+                       len -= 2;
+                       break;
+
                case 0x84:      /* SSCS type identifier for AAL3/4 and AAL5 */
                        if (len < 2)
                                return;
-                       value = pd[offset + 1];
-                       proto_tree_add_text(tree, offset, 2,
+                       value = tvb_get_guint8(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 2,
                            "SSCS type: %s",
                            val_to_str(value, q2931_sscs_type_vals,
                            "Unknown (0x%02X)"));
@@ -480,7 +516,7 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
                        break;
 
                default:        /* unknown AAL parameter */
-                       proto_tree_add_text(tree, offset, 1,
+                       proto_tree_add_text(tree, tvb, offset, 1,
                            "Unknown AAL parameter (0x%02X)",
                            identifier);
                        return; /* give up */
@@ -491,205 +527,110 @@ dissect_q2931_aal_parameters_ie(const u_char *pd, int offset, int len,
 /*
  * Dissect an ATM traffic descriptor information element.
  */
+#define        Q2931_ATM_CR_FW_PEAK_CLP_0      0x82    /* Forward peak cell rate (CLP = 0) */
+#define        Q2931_ATM_CR_BW_PEAK_CLP_0      0x83    /* Backward peak cell rate (CLP = 0) */
+#define        Q2931_ATM_CR_FW_PEAK_CLP_0_1    0x84    /* Forward peak cell rate (CLP = 0 + 1) */
+#define        Q2931_ATM_CR_BW_PEAK_CLP_0_1    0x85    /* Backward peak cell rate (CLP = 0 + 1) */
+#define        Q2931_ATM_CR_FW_SUST_CLP_0      0x88    /* Forward sustainable cell rate (CLP = 0) */
+#define        Q2931_ATM_CR_BW_SUST_CLP_0      0x89    /* Backward sustainable cell rate (CLP = 0) */
+#define        Q2931_ATM_CR_FW_SUST_CLP_0_1    0x90    /* Forward sustainable cell rate (CLP = 0 + 1) */
+#define        Q2931_ATM_CR_BW_SUST_CLP_0_1    0x91    /* Backward sustainable cell rate (CLP = 0 + 1) */
+#define        Q2931_ATM_CR_FW_MAXB_CLP_0      0xA0    /* Forward maximum burst size (CLP = 0) */
+#define        Q2931_ATM_CR_BW_MAXB_CLP_0      0xA1    /* Backward maximum burst size (CLP = 0) */
+#define        Q2931_ATM_CR_FW_MAXB_CLP_0_1    0xB0    /* Forward maximum burst size (CLP = 0 + 1) */
+#define        Q2931_ATM_CR_BW_MAXB_CLP_0_1    0xB1    /* Backward maximum burst size (CLP = 0 + 1) */
+#define        Q2931_ATM_CR_BEST_EFFORT_IND    0xBE    /* Best effort indicator */
+#define        Q2931_ATM_CR_TRAFFIC_MGMT_OPT   0xBF    /* Traffic management options */
+
+static const value_string q2931_atm_td_subfield_vals[] = {
+       { Q2931_ATM_CR_FW_PEAK_CLP_0,   "Forward peak cell rate (CLP = 0)" },
+       { Q2931_ATM_CR_BW_PEAK_CLP_0,   "Backward peak cell rate (CLP = 0)" },
+       { Q2931_ATM_CR_FW_PEAK_CLP_0_1, "Forward peak cell rate (CLP = 0 + 1)" },
+       { Q2931_ATM_CR_BW_PEAK_CLP_0_1, "Backward peak cell rate (CLP = 0 + 1)" },
+       { Q2931_ATM_CR_FW_SUST_CLP_0,   "Forward sustainable cell rate (CLP = 0)" },
+       { Q2931_ATM_CR_BW_SUST_CLP_0,   "Backward sustainable cell rate (CLP = 0)" },
+       { Q2931_ATM_CR_FW_SUST_CLP_0_1, "Forward sustainable cell rate (CLP = 0 + 1)" },
+       { Q2931_ATM_CR_BW_SUST_CLP_0_1, "Backward sustainable cell rate (CLP = 0 + 1)" },
+       { Q2931_ATM_CR_FW_MAXB_CLP_0,   "Forward maximum burst size (CLP = 0)" },
+       { Q2931_ATM_CR_BW_MAXB_CLP_0,   "Backward maximum burst size (CLP = 0)" },
+       { Q2931_ATM_CR_FW_MAXB_CLP_0_1, "Forward maximum burst size (CLP = 0 + 1)" },
+       { Q2931_ATM_CR_BW_MAXB_CLP_0_1, "Backward maximum burst size (CLP = 0 + 1)" },
+       { Q2931_ATM_CR_BEST_EFFORT_IND, "Best effort indicator" },
+       { Q2931_ATM_CR_TRAFFIC_MGMT_OPT,"Traffic management options" },
+       { 0x0,                          NULL }
+};
+
 static void
-dissect_q2931_atm_cell_rate_ie(const u_char *pd, int offset, int len,
+dissect_q2931_atm_cell_rate_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 identifier;
        guint32 value;
 
        while (len != 0) {
-               identifier = pd[offset];
+               identifier = tvb_get_guint8(tvb, offset);
                switch (identifier) {
 
-               case 0x82:      /* Forward peak cell rate (CLP = 0) */
-                       if (len < 4)
-                               return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Forward peak cell rate (CLP = 0): %u cell%s/s",
-                           value, plurality(value, "", "s"));
-                       offset += 4;
-                       len -= 4;
-                       break;
-
-               case 0x83:      /* Backward peak cell rate (CLP = 0) */
-                       if (len < 4)
-                               return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Backward peak cell rate (CLP = 0): %u cell%s/s",
-                           value, plurality(value, "", "s"));
-                       offset += 4;
-                       len -= 4;
-                       break;
-
-               case 0x84:      /* Forward peak cell rate (CLP = 0 + 1) */
-                       if (len < 4)
-                               return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Forward peak cell rate (CLP = 0 + 1): %u cell%s/s",
-                           value, plurality(value, "", "s"));
-                       offset += 4;
-                       len -= 4;
-                       break;
-
-               case 0x85:      /* Backward peak cell rate (CLP = 0 + 1) */
-                       if (len < 4)
-                               return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Backward peak cell rate (CLP = 0 + 1): %u cell%s/s",
-                           value, plurality(value, "", "s"));
-                       offset += 4;
-                       len -= 4;
-                       break;
-
-               case 0x88:      /* Forward sustainable cell rate (CLP = 0) */
-                       if (len < 4)
-                               return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Forward sustainable cell rate (CLP = 0): %u cell%s/s",
-                           value, plurality(value, "", "s"));
-                       offset += 4;
-                       len -= 4;
-                       break;
-
-               case 0x89:      /* Backward sustainable cell rate (CLP = 0) */
-                       if (len < 4)
-                               return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Backward sustainable cell rate (CLP = 0): %u cell%s/s",
-                           value, plurality(value, "", "s"));
-                       offset += 4;
-                       len -= 4;
-                       break;
-
-               case 0x90:      /* Forward sustainable cell rate (CLP = 0 + 1) */
-                       if (len < 4)
-                               return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Forward sustainable cell rate (CLP = 0 + 1): %u cell%s/s",
-                           value, plurality(value, "", "s"));
-                       offset += 4;
-                       len -= 4;
-                       break;
-
-               case 0x91:      /* Backward sustainable cell rate (CLP = 0 + 1) */
-                       if (len < 4)
-                               return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Backward sustainable cell rate (CLP = 0 + 1): %u cell%s/s",
-                           value, plurality(value, "", "s"));
-                       offset += 4;
-                       len -= 4;
-                       break;
-
-               case 0xA0:      /* Forward maximum burst size (CLP = 0) */
+               case Q2931_ATM_CR_FW_PEAK_CLP_0:
+               case Q2931_ATM_CR_BW_PEAK_CLP_0:
+               case Q2931_ATM_CR_FW_PEAK_CLP_0_1:
+               case Q2931_ATM_CR_BW_PEAK_CLP_0_1:
+               case Q2931_ATM_CR_FW_SUST_CLP_0:
+               case Q2931_ATM_CR_BW_SUST_CLP_0:
+               case Q2931_ATM_CR_FW_SUST_CLP_0_1:
+               case Q2931_ATM_CR_BW_SUST_CLP_0_1:
+               case Q2931_ATM_CR_FW_MAXB_CLP_0:
+               case Q2931_ATM_CR_BW_MAXB_CLP_0:
+               case Q2931_ATM_CR_FW_MAXB_CLP_0_1:
+               case Q2931_ATM_CR_BW_MAXB_CLP_0_1:
                        if (len < 4)
                                return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Forward maximum burst size (CLP = 0): %u cell%s/s",
+                       value = tvb_get_ntoh24(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 4,
+                           "%s: %u cell%s/s",
+                           val_to_str(identifier, q2931_atm_td_subfield_vals,
+                             NULL),
                            value, plurality(value, "", "s"));
                        offset += 4;
                        len -= 4;
                        break;
 
-               case 0xA1:      /* Backward maximum burst size (CLP = 0) */
-                       if (len < 4)
-                               return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Backward maximum burst size (CLP = 0): %u cell%s/s",
-                           value, plurality(value, "", "s"));
-                       offset += 4;
-                       len -= 4;
-                       break;
-
-               case 0xB0:      /* Forward maximum burst size (CLP = 0 + 1) */
-                       if (len < 4)
-                               return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Forward maximum burst size (CLP = 0 + 1): %u cell%s/s",
-                           value, plurality(value, "", "s"));
-                       offset += 4;
-                       len -= 4;
-                       break;
-
-               case 0xB1:      /* Backward maximum burst size (CLP = 0 + 1) */
-                       if (len < 4)
-                               return;
-                       value = (pd[offset + 1] << 16)
-                             | (pd[offset + 2] << 8)
-                             | (pd[offset + 3] << 0);
-                       proto_tree_add_text(tree, offset, 4,
-                           "Backward maximum burst size (CLP = 0 + 1): %u cell%s/s",
-                           value, plurality(value, "", "s"));
-                       offset += 4;
-                       len -= 4;
-                       break;
-
-               case 0xBE:      /* best effort indicator */
+               case Q2931_ATM_CR_BEST_EFFORT_IND:
                        /* Yes, its value *IS* 0xBE.... */
-                       proto_tree_add_text(tree, offset, 1,
-                           "Best effort indicator");
+                       proto_tree_add_text(tree, tvb, offset, 1,
+                           "%s",
+                           val_to_str(identifier, q2931_atm_td_subfield_vals,
+                             NULL));
                        offset += 1;
                        len -= 1;
                        break;
 
-               case 0xBF:      /* Traffic management options */
+               case Q2931_ATM_CR_TRAFFIC_MGMT_OPT:
                        if (len < 2)
                                return;
-                       value = pd[offset + 1];
-                       proto_tree_add_text(tree, offset, 2,
-                           "Traffic management options");
-                       proto_tree_add_text(tree, offset + 1, 1,
+                       value = tvb_get_guint8(tvb, offset + 1);
+                       proto_tree_add_text(tree, tvb, offset, 2,
+                           "%s",
+                           val_to_str(identifier, q2931_atm_td_subfield_vals,
+                             NULL));
+                       proto_tree_add_text(tree, tvb, offset + 1, 1,
                            "%s allowed in forward direction",
                            (value & 0x80) ? "Frame discard" : "No frame discard");
-                       proto_tree_add_text(tree, offset + 1, 1,
+                       proto_tree_add_text(tree, tvb, offset + 1, 1,
                            "%s allowed in backward direction",
                            (value & 0x40) ? "Frame discard" : "No frame discard");
-                       proto_tree_add_text(tree, offset + 1, 1,
-                           "%s requested in forward direction",
-                           (value & 0x02) ? "Tagging" : "No tagging");
-                       proto_tree_add_text(tree, offset + 1, 1,
-                           "%s requested in backward direction",
-                           (value & 0x01) ? "Tagging" : "No tagging");
+                       proto_tree_add_text(tree, tvb, offset + 1, 1,
+                           "Tagging %srequested in backward direction",
+                           (value & 0x02) ? "" : "not ");
+                       proto_tree_add_text(tree, tvb, offset + 1, 1,
+                           "Tagging %srequested in forward direction",
+                           (value & 0x01) ? "" : "not ");
                        offset += 2;
                        len -= 2;
                        break;
 
                default:        /* unknown ATM traffic descriptor element */
-                       proto_tree_add_text(tree, offset, 1,
+                       proto_tree_add_text(tree, tvb, offset, 1,
                            "Unknown ATM traffic descriptor element (0x%02X)",
                            identifier);
                        return; /* give up */
@@ -704,20 +645,23 @@ static const value_string q2931_bearer_class_vals[] = {
        { 0x01, "BCOB-A" },
        { 0x03, "BCOB-C" },
        { 0x10, "BCOB-X" },
+       { 0x18, "Transparent VP Service" },
        { 0x00, NULL }
 };
 
-static const value_string q2931_traffic_type_vals[] = {
-       { 0x00, "No indication" },
-       { 0x04, "Constant bit rate" },
-       { 0x08, "Variable bit rate" },
-       { 0x00, NULL }
-};
-
-static const value_string q2931_timing_requirements_vals[] = {
-       { 0x00, "No indication" },
-       { 0x01, "End-to-end timing required" },
-       { 0x02, "End-to-end timing not required" },
+static const value_string q2931_transfer_capability_vals[] = {
+       { 0x00, "No bit rate indication" },
+       { 0x01, "No bit rate indication, end-to-end timing required" },
+       { 0x02, "No bit rate indication, end-to-end timing not required" },
+       { 0x04, "CBR" },
+       { 0x05, "CBR, end-to-end timing required" },
+       { 0x06, "CBR, end-to-end timing not required" },
+       { 0x07, "CBR with CLR commitment on CLP=0+1" },
+       { 0x08, "VBR, no timing requirements indication" },
+       { 0x09, "Real time VBR" },
+       { 0x0A, "Non-real time VBR" },
+       { 0x0B, "Non-real time VBR with CLR commitment on CLP=0+1" },
+       { 0x0C, "ABR" },
        { 0x00, NULL }
 };
 
@@ -733,16 +677,16 @@ static const value_string q2931_up_conn_config_vals[] = {
        { 0x00, NULL }
 };
 
-void
-dissect_q2931_bband_bearer_cap_ie(const u_char *pd, int offset, int len,
+static void
+dissect_q2931_bband_bearer_cap_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Bearer class: %s",
            val_to_str(octet & 0x1F, q2931_bearer_class_vals,
            "Unknown (0x%02X)"));
@@ -752,14 +696,10 @@ dissect_q2931_bband_bearer_cap_ie(const u_char *pd, int offset, int len,
        if (len == 0)
                return;
        if (!(octet & Q2931_IE_EXTENSION)) {
-               octet = pd[offset];
-               proto_tree_add_text(tree, offset, 1,
-                   "Traffic type: %s",
-                   val_to_str(octet & 0x1C, q2931_traffic_type_vals,
-                   "Unknown (0x%02X)"));
-               proto_tree_add_text(tree, offset, 1,
-                   "Timing requirements: %s",
-                   val_to_str(octet & 0x03, q2931_timing_requirements_vals,
+               octet = tvb_get_guint8(tvb, offset);
+               proto_tree_add_text(tree, tvb, offset, 1,
+                   "ATM Transfer Capability: %s",
+                   val_to_str(octet & 0x1F, q2931_transfer_capability_vals,
                    "Unknown (0x%02X)"));
                offset += 1;
                len -= 1;
@@ -767,12 +707,12 @@ dissect_q2931_bband_bearer_cap_ie(const u_char *pd, int offset, int len,
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Susceptibility to clipping: %s",
            val_to_str(octet & 0x60, q2931_susc_clip_vals,
            "Unknown (0x%02X)"));
-       proto_tree_add_text(tree, offset, 1,
+       proto_tree_add_text(tree, tvb, offset, 1,
            "User-plane connection configuration: %s",
            val_to_str(octet & 0x03, q2931_up_conn_config_vals,
            "Unknown (0x%02X)"));
@@ -789,16 +729,16 @@ static const value_string q2931_hi_layer_info_type_vals[] = {
        { 0x00, NULL }
 };
 
-void
-dissect_q2931_bband_hi_layer_info_ie(const u_char *pd, int offset, int len,
+static void
+dissect_q2931_bband_hi_layer_info_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "High layer information type: %s",
            val_to_str(octet & 0x7F, q2931_hi_layer_info_type_vals,
            "Unknown (0x%02X)"));
@@ -841,7 +781,6 @@ static const value_string q2931_mode_vals[] = {
 #define        Q2931_UIL3_USER_SPEC    0x10
 
 static const value_string q2931_uil3_vals[] = {
-       { 0x02,                 "Q.931/I.451" },
        { Q2931_UIL3_X25_PL,    "X.25, packet layer" },
        { Q2931_UIL3_ISO_8208,  "ISO/IEC 8208" },
        { Q2931_UIL3_X223,      "X.223/ISO 8878" },
@@ -852,25 +791,37 @@ static const value_string q2931_uil3_vals[] = {
        { 0,                    NULL }
 };
 
+static const value_string lane_pid_vals[] = {
+       { 0x0001, "LE Configuration Direct/Control Direct/Control Distribute" },
+       { 0x0002, "Ethernet/IEEE 002.3 LE Data Direct" },
+       { 0x0003, "IEEE 802.5 LE Data Direct" },
+       { 0x0004, "Ethernet/IEEE 802.3 LE Multicast Send/Multicast Forward" },
+       { 0x0005, "IEEE 802.5 LE Multicast Send/Multicast Forward" },
+       { 0,      NULL },
+};
+
 /*
  * Dissect a broadband low layer information information element.
  */
-void
-dissect_q2931_bband_low_layer_info_ie(const u_char *pd, int offset, int len,
+static void
+dissect_q2931_bband_low_layer_info_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
        guint8 uil2_protocol;
        guint8 uil3_protocol;
+       guint8 add_l3_info;
+       guint32 organization_code;
+       guint16 pid;
 
        if (len == 0)
                return;
-       octet = pd[offset];
+       octet = tvb_get_guint8(tvb, offset);
        if ((octet & 0x60) == 0x20) {
                /*
                 * Layer 1 information.
                 */
-               proto_tree_add_text(tree, offset, 1,
+               proto_tree_add_text(tree, tvb, offset, 1,
                    "User information layer 1 protocol: 0x%02X",
                    octet & 0x1F);
                offset += 1;
@@ -879,13 +830,13 @@ dissect_q2931_bband_low_layer_info_ie(const u_char *pd, int offset, int len,
 
        if (len == 0)
                return;
-       octet = pd[offset];
+       octet = tvb_get_guint8(tvb, offset);
        if ((octet & 0x60) == 0x40) {
                /*
                 * Layer 2 information.
                 */
                uil2_protocol = octet & 0x1F;
-               proto_tree_add_text(tree, offset, 1,
+               proto_tree_add_text(tree, tvb, offset, 1,
                    "User information layer 2 protocol: %s",
                    val_to_str(uil2_protocol, q2931_uil2_vals,
                      "Unknown (0x%02X)"));
@@ -896,13 +847,13 @@ dissect_q2931_bband_low_layer_info_ie(const u_char *pd, int offset, int len,
                        goto l2_done;
                if (len == 0)
                        return;
-               octet = pd[offset];
+               octet = tvb_get_guint8(tvb, offset);
                if (uil2_protocol == Q2931_UIL2_USER_SPEC) {
-                       proto_tree_add_text(tree, offset, 1,
+                       proto_tree_add_text(tree, tvb, offset, 1,
                            "User-specified layer 2 protocol information: 0x%02X",
                            octet & 0x7F);
                } else {
-                       proto_tree_add_text(tree, offset, 1,
+                       proto_tree_add_text(tree, tvb, offset, 1,
                            "Mode: %s",
                            val_to_str(octet & 0x60, q2931_mode_vals,
                              "Unknown (0x%02X)"));
@@ -914,8 +865,8 @@ dissect_q2931_bband_low_layer_info_ie(const u_char *pd, int offset, int len,
                        goto l2_done;
                if (len == 0)
                        return;
-               octet = pd[offset];
-               proto_tree_add_text(tree, offset, 1,
+               octet = tvb_get_guint8(tvb, offset);
+               proto_tree_add_text(tree, tvb, offset, 1,
                    "Window size: %u k", octet & 0x7F);
                offset += 1;
                len -= 1;
@@ -925,13 +876,13 @@ l2_done:
 
        if (len == 0)
                return;
-       octet = pd[offset];
+       octet = tvb_get_guint8(tvb, offset);
        if ((octet & 0x60) == 0x60) {
                /*
                 * Layer 3 information.
                 */
                uil3_protocol = octet & 0x1F;
-               proto_tree_add_text(tree, offset, 1,
+               proto_tree_add_text(tree, tvb, offset, 1,
                    "User information layer 3 protocol: %s",
                    val_to_str(uil3_protocol, q2931_uil3_vals,
                      "Unknown (0x%02X)"));
@@ -946,23 +897,25 @@ l2_done:
                        goto l3_done;
                if (len == 0)
                        return;
-               octet = pd[offset];
+               octet = tvb_get_guint8(tvb, offset);
                switch (uil3_protocol) {
 
                case Q2931_UIL3_X25_PL:
                case Q2931_UIL3_ISO_8208:
                case Q2931_UIL3_X223:
-                       proto_tree_add_text(tree, offset, 1,
+                       proto_tree_add_text(tree, tvb, offset, 1,
                            "Mode: %s",
                            val_to_str(octet & 0x60, q2931_mode_vals,
                              "Unknown (0x%02X)"));
+                       offset += 1;
+                       len -= 1;
 
                        if (octet & Q2931_IE_EXTENSION)
                                goto l3_done;
                        if (len == 0)
                                return;
-                       octet = pd[offset];
-                       proto_tree_add_text(tree, offset, 1,
+                       octet = tvb_get_guint8(tvb, offset);
+                       proto_tree_add_text(tree, tvb, offset, 1,
                            "Default packet size: %u", octet & 0x0F);
                        offset += 1;
                        len -= 1;
@@ -971,15 +924,15 @@ l2_done:
                                goto l3_done;
                        if (len == 0)
                                return;
-                       octet = pd[offset];
-                       proto_tree_add_text(tree, offset, 1,
+                       octet = tvb_get_guint8(tvb, offset);
+                       proto_tree_add_text(tree, tvb, offset, 1,
                            "Packet window size: %u", octet & 0x7F);
                        offset += 1;
                        len -= 1;
                        break;
 
                case Q2931_UIL3_USER_SPEC:
-                       proto_tree_add_text(tree, offset, 1,
+                       proto_tree_add_text(tree, tvb, offset, 1,
                            "Default packet size: %u octets",
                            1 << (octet & 0x0F));
                        offset += 1;
@@ -987,13 +940,57 @@ l2_done:
                        break;
 
                case Q2931_UIL3_TR_9577:
-                       if (len == 0)
+                       add_l3_info = (octet & 0x7F) << 1;
+                       if (octet & Q2931_IE_EXTENSION)
+                               goto l3_done;
+                       if (len < 2)
                                return;
-                       proto_tree_add_text(tree, offset, len,
+                       add_l3_info |= (tvb_get_guint8(tvb, offset + 1) & 0x40) >> 6;
+                       proto_tree_add_text(tree, tvb, offset, 2,
                            "Additional layer 3 protocol information: %s",
-                               bytes_to_str(&pd[offset], len));
-                       offset += len;
-                       len -= len;
+                           val_to_str(add_l3_info, nlpid_vals,
+                             "Unknown (0x%02X)"));
+                       offset += 2;
+                       len -= 2;
+                       if (add_l3_info == NLPID_SNAP) {
+                               if (len < 6)
+                                       return;
+                               offset += 1;
+                               len -= 1;
+                               organization_code = tvb_get_ntoh24(tvb, offset);
+                               proto_tree_add_text(tree, tvb, offset, 3,
+                                   "Organization Code: 0x%06X (%s)",
+                                   organization_code,
+                                   val_to_str(organization_code, oui_vals,
+                                       "Unknown"));
+                               offset += 3;
+                               len -= 3;
+
+                               if (len < 2)
+                                       return;
+                               pid = tvb_get_ntohs(tvb, offset);
+                               switch (organization_code) {
+
+                               case OUI_ENCAP_ETHER:
+                                       proto_tree_add_text(tree, tvb, offset, 2,
+                                           "Ethernet type: %s",
+                                           val_to_str(pid, etype_vals,
+                                               "Unknown (0x%04X)"));
+                                       break;
+
+                               case OUI_ATM_FORUM:
+                                       proto_tree_add_text(tree, tvb, offset, 2,
+                                           "LANE Protocol ID: %s",
+                                           val_to_str(pid, lane_pid_vals,
+                                               "Unknown (0x%04X)"));
+                                       break;
+
+                               default:
+                                       proto_tree_add_text(tree, tvb, offset, 2,
+                                           "Protocol ID: 0x%04X", pid);
+                                       break;
+                               }
+                       }
                        break;
                }
        }
@@ -1001,6 +998,284 @@ l3_done:
        ;
 }
 
+/*
+ * Dissect a Cause information element.
+ */
+static const value_string q2931_cause_coding_standard_vals[] = {
+       { 0x00, "ITU-T standardized coding" },
+       { 0x20, "ISO/IEC standard" },
+       { 0x40, "National standard" },
+       { 0x60, "Standard defined for the network" },
+       { 0,    NULL }
+};
+
+static const value_string q2931_cause_location_vals[] = {
+       { 0x00, "User (U)" },
+       { 0x01, "Private network serving the local user (LPN)" },
+       { 0x02, "Public network serving the local user (LN)" },
+       { 0x03, "Transit network (TN)" },
+       { 0x04, "Public network serving the remote user (RLN)" },
+       { 0x05, "Private network serving the remote user (RPN)" },
+       { 0x07, "International network (INTL)" },
+       { 0x0A, "Network beyond interworking point (BI)" },
+       { 0,    NULL }
+};
+
+/*
+ * Cause codes for Cause.
+ */
+#define        Q2931_CAUSE_UNALLOC_NUMBER      0x01
+#define        Q2931_CAUSE_NO_ROUTE_TO_DEST    0x03
+#define        Q2931_CAUSE_CALL_REJECTED       0x15
+#define        Q2931_CAUSE_NUMBER_CHANGED      0x16
+#define        Q2931_CAUSE_CELL_RATE_UNAVAIL   0x25
+#define        Q2931_CAUSE_ACCESS_INFO_DISC    0x2B
+#define        Q2931_CAUSE_QOS_UNAVAILABLE     0x31
+#define        Q2931_CAUSE_CHAN_NONEXISTENT    0x52
+#define        Q2931_CAUSE_INCOMPATIBLE_DEST   0x58
+#define        Q2931_CAUSE_MAND_IE_MISSING     0x60
+#define        Q2931_CAUSE_MT_NONEX_OR_UNIMPL  0x61
+#define        Q2931_CAUSE_IE_NONEX_OR_UNIMPL  0x63
+#define        Q2931_CAUSE_INVALID_IE_CONTENTS 0x64
+#define        Q2931_CAUSE_MSG_INCOMPAT_W_CS   0x65
+#define        Q2931_CAUSE_REC_TIMER_EXP       0x66
+
+static const value_string q2931_cause_code_vals[] = {
+       { Q2931_CAUSE_UNALLOC_NUMBER,   "Unallocated (unassigned) number" },
+       { 0x02,                         "No route to specified transit network" },
+       { Q2931_CAUSE_NO_ROUTE_TO_DEST, "No route to destination" },
+       { 0x10,                         "Normal call clearing" },
+       { 0x11,                         "User busy" },
+       { 0x12,                         "No user responding" },
+       { Q2931_CAUSE_CALL_REJECTED,    "Call rejected" },
+       { Q2931_CAUSE_NUMBER_CHANGED,   "Number changed" },
+       { 0x17,                         "User rejects calls with calling line identification restriction" },
+       { 0x1B,                         "Destination out of order" },
+       { 0x1C,                         "Invalid number format (incomplete number)" },
+       { 0x1E,                         "Response to STATUS ENQUIRY" },
+       { 0x1F,                         "Normal unspecified" },
+       { 0x23,                         "Requested VPCI/VCI not available" },
+       { 0x24,                         "VPCI/VCI assignment failure" },
+       { Q2931_CAUSE_CELL_RATE_UNAVAIL,"User cell rate not available" },
+       { 0x26,                         "Network out of order" },
+       { 0x29,                         "Temporary failure" },
+       { Q2931_CAUSE_ACCESS_INFO_DISC, "Access information discarded" },
+       { 0x2D,                         "No VPCI/VCI available" },
+       { 0x2F,                         "Resources unavailable, unspecified" },
+       { Q2931_CAUSE_QOS_UNAVAILABLE,  "Quality of service unavailable" },
+       { 0x39,                         "Bearer capability not authorized" },
+       { 0x3A,                         "Bearer capability not presently available" },
+       { 0x3F,                         "Service or option not available, unspecified" },
+       { 0x41,                         "Bearer capability not implemented" },
+       { 0x49,                         "Unsupported combination of traffic parameters" },
+       { 0x4E,                         "AAL parameters cannot be supported" },
+       { 0x51,                         "Invalid call reference value" },
+       { Q2931_CAUSE_CHAN_NONEXISTENT, "Identified channel does not exist" },
+       { Q2931_CAUSE_INCOMPATIBLE_DEST,"Incompatible destination" },
+       { 0x59,                         "Invalid endpoint reference" },
+       { 0x5B,                         "Invalid transit network selection" },
+       { 0x5C,                         "Too many pending ADD PARTY requests" },
+       { Q2931_CAUSE_MAND_IE_MISSING,  "Mandatory information element is missing" },
+       { Q2931_CAUSE_MT_NONEX_OR_UNIMPL,"Message type non-existent or not implemented" },
+       { Q2931_CAUSE_IE_NONEX_OR_UNIMPL,"Information element nonexistant or not implemented" },
+       { Q2931_CAUSE_INVALID_IE_CONTENTS,"Invalid information element contents" },
+       { Q2931_CAUSE_MSG_INCOMPAT_W_CS,"Message not compatible with call state" },
+       { Q2931_CAUSE_REC_TIMER_EXP,    "Recovery on timer expiry" },
+       { 0x68,                         "Incorrect message length" },
+       { 0x6F,                         "Protocol error, unspecified" },
+       { 0,                            NULL }
+};
+
+static const value_string q2931_cause_condition_vals[] = {
+       { 0x00, "Unknown" },
+       { 0x01, "Permanent" },
+       { 0x02, "Transient" },
+       { 0x00, NULL }
+};
+
+#define        Q2931_REJ_USER_SPECIFIC         0x00
+#define        Q2931_REJ_IE_MISSING            0x04
+#define        Q2931_REJ_IE_INSUFFICIENT       0x08
+
+static const value_string q2931_rejection_reason_vals[] = {
+       { 0x00, "User specific" },
+       { 0x04, "Information element missing" },
+       { 0x08, "Information element contents are not sufficient" },
+       { 0x00, NULL }
+};
+
+static void
+dissect_q2931_cause_ie(tvbuff_t *tvb, int offset, int len,
+    proto_tree *tree)
+{
+       guint8 octet;
+       guint8 cause_value;
+       guint8 rejection_reason;
+       guint8 info_element;
+       guint8 info_element_ext;
+       guint16 info_element_len;
+
+       if (len == 0)
+               return;
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
+           "Location: %s",
+           val_to_str(octet & 0x0F, q2931_cause_location_vals,
+             "Unknown (0x%X)"));
+       offset += 1;
+       len -= 1;
+
+       if (len == 0)
+               return;
+       octet = tvb_get_guint8(tvb, offset);
+       cause_value = octet & 0x7F;
+       proto_tree_add_text(tree, tvb, offset, 1,
+           "Cause value: %s",
+           val_to_str(cause_value, q2931_cause_code_vals,
+             "Unknown (0x%X)"));
+       offset += 1;
+       len -= 1;
+
+       if (len == 0)
+               return;
+       switch (cause_value) {
+
+       case Q2931_CAUSE_UNALLOC_NUMBER:
+       case Q2931_CAUSE_NO_ROUTE_TO_DEST:
+       case Q2931_CAUSE_QOS_UNAVAILABLE:
+               octet = tvb_get_guint8(tvb, offset);
+               proto_tree_add_text(tree, tvb, offset, 1,
+                   "Network service: %s",
+                   (octet & 0x80) ? "User" : "Provider");
+               proto_tree_add_text(tree, tvb, offset, 1,
+                   "%s",
+                   (octet & 0x40) ? "Abnormal" : "Normal");
+               proto_tree_add_text(tree, tvb, offset, 1,
+                   "Condition: %s",
+                   val_to_str(octet & 0x03, q2931_cause_condition_vals,
+                     "Unknown (0x%X)"));
+               break;
+
+       case Q2931_CAUSE_CALL_REJECTED:
+               rejection_reason = octet & 0x7C;
+               proto_tree_add_text(tree, tvb, offset, 1,
+                   "Rejection reason: %s",
+                   val_to_str(octet & 0x7C, q2931_cause_condition_vals,
+                     "Unknown (0x%X)"));
+               proto_tree_add_text(tree, tvb, offset, 1,
+                   "Condition: %s",
+                   val_to_str(octet & 0x03, q2931_cause_condition_vals,
+                     "Unknown (0x%X)"));
+               offset += 1;
+               len -= 1;
+
+               if (len == 0)
+                       return;
+               switch (rejection_reason) {
+
+               case Q2931_REJ_USER_SPECIFIC:
+                       proto_tree_add_text(tree, tvb, offset, len,
+                           "User specific diagnostic: %s",
+                           tvb_bytes_to_str(tvb, offset, len));
+                       break;
+
+               case Q2931_REJ_IE_MISSING:
+                       proto_tree_add_text(tree, tvb, offset, 1,
+                           "Missing information element: %s",
+                           val_to_str(tvb_get_guint8(tvb, offset), q2931_info_element_vals,
+                             "Unknown (0x%02X)"));
+                       break;
+
+               case Q2931_REJ_IE_INSUFFICIENT:
+                       proto_tree_add_text(tree, tvb, offset, 1,
+                           "Insufficient information element: %s",
+                           val_to_str(tvb_get_guint8(tvb, offset), q2931_info_element_vals,
+                             "Unknown (0x%02X)"));
+                       break;
+
+               default:
+                       proto_tree_add_text(tree, tvb, offset, len,
+                           "Diagnostic: %s",
+                           tvb_bytes_to_str(tvb, offset, len));
+                       break;
+               }
+               break;
+
+       case Q2931_CAUSE_NUMBER_CHANGED:
+               /*
+                * UNI 3.1 claims this "is formatted as the called party
+                * number information element, including information
+                * element identifier.
+                */
+               info_element = tvb_get_guint8(tvb, offset);
+               info_element_ext = tvb_get_guint8(tvb, offset + 1);
+               info_element_len = tvb_get_ntohs(tvb, offset + 2);
+               dissect_q2931_ie(tvb, offset, info_element_len, tree,
+                   info_element, info_element_ext);
+               break;
+
+       case Q2931_CAUSE_ACCESS_INFO_DISC:
+       case Q2931_CAUSE_INCOMPATIBLE_DEST:
+       case Q2931_CAUSE_MAND_IE_MISSING:
+       case Q2931_CAUSE_IE_NONEX_OR_UNIMPL:
+       case Q2931_CAUSE_INVALID_IE_CONTENTS:
+               do {
+                       proto_tree_add_text(tree, tvb, offset, 1,
+                           "Information element: %s",
+                           val_to_str(tvb_get_guint8(tvb, offset), q2931_info_element_vals,
+                             "Unknown (0x%02X)"));
+                       offset += 1;
+                       len -= 1;
+               } while (len != 0);
+               break;
+
+       case Q2931_CAUSE_CELL_RATE_UNAVAIL:
+               do {
+                       proto_tree_add_text(tree, tvb, offset, 1,
+                           "Cell rate subfield identifier: %s",
+                           val_to_str(tvb_get_guint8(tvb, offset), q2931_atm_td_subfield_vals,
+                             "Unknown (0x%02X)"));
+                       offset += 1;
+                       len -= 1;
+               } while (len != 0);
+               break;
+
+       case Q2931_CAUSE_CHAN_NONEXISTENT:
+               if (len < 2)
+                       return;
+               proto_tree_add_text(tree, tvb, offset, 2,
+                   "VPCI: %u", tvb_get_ntohs(tvb, offset));
+               offset += 2;
+               len -= 2;
+
+               if (len < 2)
+                       return;
+               proto_tree_add_text(tree, tvb, offset, 2,
+                   "VCI: %u", tvb_get_ntohs(tvb, offset));
+               break;
+
+       case Q2931_CAUSE_MT_NONEX_OR_UNIMPL:
+       case Q2931_CAUSE_MSG_INCOMPAT_W_CS:
+               proto_tree_add_text(tree, tvb, offset, 1,
+                   "Message type: %s",
+                   val_to_str(tvb_get_guint8(tvb, offset), q2931_message_type_vals,
+                     "Unknown (0x%02X)"));
+               break;
+
+       case Q2931_CAUSE_REC_TIMER_EXP:
+               if (len < 3)
+                       return;
+               proto_tree_add_text(tree, tvb, offset, 3,
+                   "Timer: %.3s", tvb_get_ptr(tvb, offset, 3));
+               break;
+
+       default:
+               proto_tree_add_text(tree, tvb, offset, len,
+                   "Diagnostics: %s",
+                   tvb_bytes_to_str(tvb, offset, len));
+       }
+}
+
 /*
  * Dissect a Call state information element.
  */
@@ -1027,15 +1302,15 @@ static const value_string q2931_call_state_vals[] = {
 };
 
 static void
-dissect_q2931_call_state_ie(const u_char *pd, int offset, int len,
+dissect_q2931_call_state_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Call state: %s",
            val_to_str(octet & 0x3F, q2931_call_state_vals,
              "Unknown (0x%02X)"));
@@ -1081,21 +1356,23 @@ static const value_string q2931_screening_indicator_vals[] = {
 };
 
 static void
-dissect_q2931_number_ie(const u_char *pd, int offset, int len,
+dissect_q2931_number_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
        guint8 numbering_plan;
+       proto_item *ti;
+       proto_tree *nsap_tree;
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Type of number: %s",
            val_to_str(octet & 0x70, q2931_number_type_vals,
              "Unknown (0x%02X)"));
        numbering_plan = octet & 0x0F;
-       proto_tree_add_text(tree, offset, 1,
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Numbering plan: %s",
            val_to_str(numbering_plan, q2931_numbering_plan_vals,
              "Unknown (0x%02X)"));
@@ -1105,12 +1382,12 @@ dissect_q2931_number_ie(const u_char *pd, int offset, int len,
        if (!(octet & Q2931_IE_EXTENSION)) {
                if (len == 0)
                        return;
-               octet = pd[offset];
-               proto_tree_add_text(tree, offset, 1,
+               octet = tvb_get_guint8(tvb, offset);
+               proto_tree_add_text(tree, tvb, offset, 1,
                    "Presentation indicator: %s",
                    val_to_str(octet & 0x60, q2931_presentation_indicator_vals,
                      "Unknown (0x%X)"));
-               proto_tree_add_text(tree, offset, 1,
+               proto_tree_add_text(tree, tvb, offset, 1,
                    "Screening indicator: %s",
                    val_to_str(octet & 0x03, q2931_screening_indicator_vals,
                      "Unknown (0x%X)"));
@@ -1120,12 +1397,29 @@ dissect_q2931_number_ie(const u_char *pd, int offset, int len,
 
        if (len == 0)
                return;
-       if (numbering_plan == Q2931_ISDN_NUMBERING) {
-               proto_tree_add_text(tree, offset, len, "Number: %.*s",
-                   len, &pd[offset]);
-       } else {
-               proto_tree_add_text(tree, offset, len, "Number: %s",
-                   bytes_to_str(&pd[offset], len));
+       switch (numbering_plan) {
+
+       case Q2931_ISDN_NUMBERING:
+               proto_tree_add_text(tree, tvb, offset, len, "Number: %.*s",
+                   len, tvb_get_ptr(tvb, offset, len));
+               break;
+
+       case Q2931_NSAP_ADDRESSING:
+               if (len < 20) {
+                       proto_tree_add_text(tree, tvb, offset, len,
+                           "Number (too short): %s",
+                           tvb_bytes_to_str(tvb, offset, len));
+                       return;
+               }
+               ti = proto_tree_add_text(tree, tvb, offset, len, "Number");
+               nsap_tree = proto_item_add_subtree(ti, ett_q2931_nsap);
+               dissect_atm_nsap(tvb, offset, len, nsap_tree);
+               break;
+
+       default:
+               proto_tree_add_text(tree, tvb, offset, len, "Number: %s",
+                   tvb_bytes_to_str(tvb, offset, len));
+               break;
        }
 }
 
@@ -1146,19 +1440,19 @@ static const value_string q2931_odd_even_indicator_vals[] = {
 };
 
 static void
-dissect_q2931_party_subaddr_ie(const u_char *pd, int offset, int len,
+dissect_q2931_party_subaddr_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Type of subaddress: %s",
            val_to_str(octet & 0x70, q2931_subaddress_type_vals,
              "Unknown (0x%02X)"));
-       proto_tree_add_text(tree, offset, 1,
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Odd/even indicator: %s",
            val_to_str(octet & 0x10, q2931_odd_even_indicator_vals,
              NULL));
@@ -1167,8 +1461,8 @@ dissect_q2931_party_subaddr_ie(const u_char *pd, int offset, int len,
 
        if (len == 0)
                return;
-       proto_tree_add_text(tree, offset, len, "Subaddress: %s",
-           bytes_to_str(&pd[offset], len));
+       proto_tree_add_text(tree, tvb, offset, len, "Subaddress: %s",
+           tvb_bytes_to_str(tvb, offset, len));
 }
 
 /*
@@ -1183,23 +1477,24 @@ static const value_string q2931_vp_associated_signalling_vals[] = {
 static const value_string q2931_preferred_exclusive_vals[] = {
        { 0x00, "Exclusive VPCI; exclusive VCI" },
        { 0x01, "Exclusive VPCI; any VCI" },
+       { 0x04, "Exclusive VPCI; no VCI" },
        { 0x00, NULL }
 };
 
 static void
-dissect_q2931_connection_identifier_ie(const u_char *pd, int offset, int len,
+dissect_q2931_connection_identifier_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "VP-associated signalling: %s",
            val_to_str(octet & 0x18, q2931_vp_associated_signalling_vals,
              "Unknown (0x%02X)"));
-       proto_tree_add_text(tree, offset, 1,
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Preferred/exclusive: %s",
            val_to_str(octet & 0x07, q2931_preferred_exclusive_vals,
              "Unknown (0x%02X)"));
@@ -1208,50 +1503,50 @@ dissect_q2931_connection_identifier_ie(const u_char *pd, int offset, int len,
 
        if (len < 2)
                return;
-       proto_tree_add_text(tree, offset, 2, "VPCI: %u",
-           pntohs(&pd[offset]));
+       proto_tree_add_text(tree, tvb, offset, 2, "VPCI: %u",
+           tvb_get_ntohs(tvb, offset));
        offset += 2;
        len -= 2;
 
        if (len < 2)
                return;
-       proto_tree_add_text(tree, offset, 2, "VCI: %u",
-           pntohs(&pd[offset]));
+       proto_tree_add_text(tree, tvb, offset, 2, "VCI: %u",
+           tvb_get_ntohs(tvb, offset));
 }
 
 /*
  * Dissect an End-to-end transit delay information element.
  */
 static void
-dissect_q2931_e2e_transit_delay_ie(const u_char *pd, int offset, int len,
+dissect_q2931_e2e_transit_delay_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 identifier;
        guint16 value;
 
        while (len >= 3) {
-               identifier = pd[offset];
-               value = pntohs(&pd[offset + 1]);
+               identifier = tvb_get_guint8(tvb, offset);
+               value = tvb_get_ntohs(tvb, offset + 1);
                switch (identifier) {
 
                case 0x01:      /* Cumulative transit delay identifier */
-                       proto_tree_add_text(tree, offset, 3,
+                       proto_tree_add_text(tree, tvb, offset, 3,
                            "Cumulative transit delay: %u ms", value);
                        break;
 
                case 0x03:      /* Maximum transit delay identifier */
                        if (value == 0xFFFF) {
-                               proto_tree_add_text(tree, offset, 3,
+                               proto_tree_add_text(tree, tvb, offset, 3,
                                    "Any end-to-end transit delay value acceptable");
                        } else {
-                               proto_tree_add_text(tree, offset, 3,
+                               proto_tree_add_text(tree, tvb, offset, 3,
                                    "Maximum end-to-end transit delay: %u ms",
                                    value);
                        }
                        break;
 
                default:        /* Unknown transit delay identifier */
-                       proto_tree_add_text(tree, offset, 1,
+                       proto_tree_add_text(tree, tvb, offset, 1,
                            "Unknown transit delay identifier (0x%02X)",
                            identifier);
                        return; /* give up */
@@ -1268,15 +1563,15 @@ static const value_string q2931_qos_parameter_vals[] = {
 };
 
 static void
-dissect_q2931_qos_parameter_ie(const u_char *pd, int offset, int len,
+dissect_q2931_qos_parameter_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "QOS class forward: %s",
            val_to_str(octet, q2931_qos_parameter_vals,
              "Unknown (0x%02X)"));
@@ -1285,8 +1580,8 @@ dissect_q2931_qos_parameter_ie(const u_char *pd, int offset, int len,
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "QOS class backward: %s",
            val_to_str(octet, q2931_qos_parameter_vals,
              "Unknown (0x%02X)"));
@@ -1301,15 +1596,15 @@ static const value_string q2931_bband_rpt_indicator_vals[] = {
 };
 
 static void
-dissect_q2931_bband_rpt_indicator(const u_char *pd, int offset, int len,
+dissect_q2931_bband_rpt_indicator(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Broadband repeat indicator: %s",
            val_to_str(octet & 0x0F, q2931_bband_rpt_indicator_vals,
              "Unknown (0x%02X)"));
@@ -1326,15 +1621,15 @@ static const value_string q2931_class_vals[] = {
 };
 
 static void
-dissect_q2931_restart_indicator(const u_char *pd, int offset, int len,
+dissect_q2931_restart_indicator(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Restart indicator: %s",
            val_to_str(octet & 0x07, q2931_class_vals,
              "Unknown (0x%02X)"));
@@ -1344,24 +1639,24 @@ dissect_q2931_restart_indicator(const u_char *pd, int offset, int len,
  * Dissect an broadband sending complete information element.
  */
 static void
-dissect_q2931_bband_sending_compl_ie(const u_char *pd, int offset, int len,
+dissect_q2931_bband_sending_compl_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 identifier;
 
        while (len != 0) {
-               identifier = pd[offset];
+               identifier = tvb_get_guint8(tvb, offset);
                switch (identifier) {
 
                case 0xA1:      /* Sending complete indication */
-                       proto_tree_add_text(tree, offset, 1,
+                       proto_tree_add_text(tree, tvb, offset, 1,
                            "Broadband sending complete indication");
                        offset += 1;
                        len -= 1;
                        break;
 
                default:        /* unknown broadband sending complete element */
-                       proto_tree_add_text(tree, offset, 1,
+                       proto_tree_add_text(tree, tvb, offset, 1,
                            "Unknown broadband sending complete element (0x%02X)",
                            identifier);
                        return; /* give up */
@@ -1387,19 +1682,19 @@ static const value_string q2931_netid_plan_vals[] = {
 };
 
 static void
-dissect_q2931_transit_network_sel_ie(const u_char *pd, int offset, int len,
+dissect_q2931_transit_network_sel_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Type of network identification: %s",
            val_to_str(octet & 0x70, q2931_netid_type_vals,
              "Unknown (0x%02X)"));
-       proto_tree_add_text(tree, offset, 1,
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Network identification plan: %s",
            val_to_str(octet & 0x0F, q2931_netid_plan_vals,
              "Unknown (0x%02X)"));
@@ -1408,8 +1703,8 @@ dissect_q2931_transit_network_sel_ie(const u_char *pd, int offset, int len,
 
        if (len == 0)
                return;
-       proto_tree_add_text(tree, offset, len,
-           "Network identification: %.*s", len, &pd[offset]);
+       proto_tree_add_text(tree, tvb, offset, len,
+           "Network identification: %.*s", len, tvb_get_ptr(tvb, offset, len));
 }
 
 /*
@@ -1442,142 +1737,268 @@ static const value_string q2931_bwd_e2e_oam_f5_flow_indicator_vals[] = {
 };
 
 static void
-dissect_q2931_oam_traffic_descriptor_ie(const u_char *pd, int offset, int len,
+dissect_q2931_oam_traffic_descriptor_ie(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree)
 {
        guint8 octet;
 
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Shaping indicator: %s",
            val_to_str(octet & 0x60, q2931_shaping_indicator_vals,
              "Unknown (0x%02X)"));
-       proto_tree_add_text(tree, offset, 1,
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Use of end-to-end OAM F5 flow is %s",
            (octet & 0x10) ? "mandatory" : "optional");
-       proto_tree_add_text(tree, offset, 1,
+       proto_tree_add_text(tree, tvb, offset, 1,
            "User-Network fault management indicator: %s",
            val_to_str(octet & 0x07, q2931_user_net_fault_mgmt_vals,
              "Unknown (0x%02X)"));
        offset += 1;
        len -= 1;
 
-
        if (len == 0)
                return;
-       octet = pd[offset];
-       proto_tree_add_text(tree, offset, 1,
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Forward end-to-end OAM F5 flow indicator: %s",
            val_to_str(octet & 0x70, q2931_fwd_e2e_oam_f5_flow_indicator_vals,
              "Unknown (0x%02X)"));
-       proto_tree_add_text(tree, offset, 1,
+       proto_tree_add_text(tree, tvb, offset, 1,
            "Backward end-to-end OAM F5 flow indicator: %s",
            val_to_str(octet & 0x07, q2931_bwd_e2e_oam_f5_flow_indicator_vals,
              "Unknown (0x%02X)"));
 }
 
+/*
+ * Dissect an Endpoint reference information element.
+ */
+static const value_string q2931_endpoint_reference_type_vals[] = {
+       { 0x00, "Locally defined integer" },
+       { 0,    NULL }
+};
+
 static void
-dissect_q2931_ie(const u_char *pd, int offset, int len,
+dissect_q2931_endpoint_reference_ie(tvbuff_t *tvb, int offset, int len,
+    proto_tree *tree)
+{
+       guint8 octet;
+       guint16 value;
+
+       if (len == 0)
+               return;
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
+           "Endpoint reference type: %s",
+           val_to_str(octet, q2931_endpoint_reference_type_vals,
+             "Unknown (0x%02X)"));
+       offset += 1;
+       len -= 1;
+
+       if (len < 2)
+               return;
+       value = tvb_get_ntohs(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 2,
+           "Endpoint reference flag: %s",
+           (value & 0x8000) ? "Message sent to side that originates the endpoint reference" :
+                              "Message sent from side that originates the endpoint reference");
+       proto_tree_add_text(tree, tvb, offset, 2,
+           "Endpoint reference identifier value: %u",
+           value & 0x7FFF);
+}
+
+/*
+ * Dissect an Endpoint state information element.
+ */
+static const value_string q2931_endpoint_reference_party_state_vals[] = {
+       { 0x00, "Null" },
+       { 0x01, "ADD PARTY initiated" },
+       { 0x06, "ADD PARTY received" },
+       { 0x0B, "DROP PARTY initiated" },
+       { 0x0C, "DROP PARTY received" },
+       { 0x0A, "Active" },
+       { 0,    NULL }
+};
+
+static void
+dissect_q2931_endpoint_state_ie(tvbuff_t *tvb, int offset, int len,
+    proto_tree *tree)
+{
+       guint8 octet;
+
+       if (len == 0)
+               return;
+       octet = tvb_get_guint8(tvb, offset);
+       proto_tree_add_text(tree, tvb, offset, 1,
+           "Endpoint reference party-state: %s",
+           val_to_str(octet & 0x3F, q2931_endpoint_reference_party_state_vals,
+             "Unknown (0x%02X)"));
+}
+
+static void
+dissect_q2931_ie_contents(tvbuff_t *tvb, int offset, int len,
     proto_tree *tree, guint8 info_element)
 {
        switch (info_element) {
 
        case Q2931_IE_BBAND_LOCKING_SHIFT:
        case Q2931_IE_BBAND_NLOCKING_SHIFT:
-               dissect_q2931_shift_ie(pd, offset, len, tree, info_element);
+               dissect_q2931_shift_ie(tvb, offset, len, tree, info_element);
                break;
 
        case Q2931_IE_NBAND_BEARER_CAP:
        case Q2931_IE_NBAND_LOW_LAYER_COMPAT:
-               dissect_q931_bearer_capability_ie(pd, offset, len, tree);
+               dissect_q931_bearer_capability_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_NBAND_HIGH_LAYER_COMPAT:
-               dissect_q931_high_layer_compat_ie(pd, offset, len, tree);
+               dissect_q931_high_layer_compat_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_PROGRESS_INDICATOR:
-               dissect_q931_progress_indicator_ie(pd, offset, len, tree);
+               dissect_q931_progress_indicator_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_AAL_PARAMETERS:
-               dissect_q2931_aal_parameters_ie(pd, offset, len, tree);
+               dissect_q2931_aal_parameters_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_ATM_USER_CELL_RATE:
-               dissect_q2931_atm_cell_rate_ie(pd, offset, len, tree);
+               dissect_q2931_atm_cell_rate_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_BBAND_BEARER_CAP:
-               dissect_q2931_bband_bearer_cap_ie(pd, offset, len, tree);
+               dissect_q2931_bband_bearer_cap_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_BBAND_HI_LAYER_INFO:
-               dissect_q2931_bband_hi_layer_info_ie(pd, offset, len, tree);
+               dissect_q2931_bband_hi_layer_info_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_BBAND_LOW_LAYER_INFO:
-               dissect_q2931_bband_low_layer_info_ie(pd, offset, len, tree);
+               dissect_q2931_bband_low_layer_info_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_CALL_STATE:
-               dissect_q2931_call_state_ie(pd, offset, len, tree);
+               dissect_q2931_call_state_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_CALLED_PARTY_NUMBER:
        case Q2931_IE_CALLING_PARTY_NUMBER:
-               dissect_q2931_number_ie(pd, offset, len, tree);
+               dissect_q2931_number_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_CALLED_PARTY_SUBADDR:
        case Q2931_IE_CALLING_PARTY_SUBADDR:
-               dissect_q2931_party_subaddr_ie(pd, offset, len, tree);
+               dissect_q2931_party_subaddr_ie(tvb, offset, len, tree);
+               break;
+
+       case Q2931_IE_CAUSE:
+               dissect_q2931_cause_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_CONNECTION_IDENTIFIER:
-               dissect_q2931_connection_identifier_ie(pd, offset, len, tree);
+               dissect_q2931_connection_identifier_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_E2E_TRANSIT_DELAY:
-               dissect_q2931_e2e_transit_delay_ie(pd, offset, len, tree);
+               dissect_q2931_e2e_transit_delay_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_QOS_PARAMETER:
-               dissect_q2931_qos_parameter_ie(pd, offset, len, tree);
+               dissect_q2931_qos_parameter_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_BBAND_RPT_INDICATOR:
-               dissect_q2931_bband_rpt_indicator(pd, offset, len, tree);
+               dissect_q2931_bband_rpt_indicator(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_RESTART_INDICATOR:
-               dissect_q2931_restart_indicator(pd, offset, len, tree);
+               dissect_q2931_restart_indicator(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_BBAND_SENDING_COMPL:
-               dissect_q2931_bband_sending_compl_ie(pd, offset, len, tree);
+               dissect_q2931_bband_sending_compl_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_TRANSIT_NETWORK_SEL:
-               dissect_q2931_transit_network_sel_ie(pd, offset, len, tree);
+               dissect_q2931_transit_network_sel_ie(tvb, offset, len, tree);
                break;
 
        case Q2931_IE_OAM_TRAFFIC_DESCRIPTOR:
-               dissect_q2931_oam_traffic_descriptor_ie(pd, offset, len, tree);
+               dissect_q2931_oam_traffic_descriptor_ie(tvb, offset, len, tree);
+               break;
+
+       case Q2931_IE_ENDPOINT_REFERENCE:
+               dissect_q2931_endpoint_reference_ie(tvb, offset, len, tree);
+               break;
+
+       case Q2931_IE_ENDPOINT_STATE:
+               dissect_q2931_endpoint_state_ie(tvb, offset, len, tree);
                break;
        }
 }
 
-void
-dissect_q2931(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
+static void
+dissect_q2931_ie(tvbuff_t *tvb, int offset, int len, proto_tree *tree,
+    guint8 info_element, guint8 info_element_ext)
 {
-       proto_tree      *q2931_tree = NULL;
        proto_item      *ti;
-       proto_tree      *ext_tree;
        proto_tree      *ie_tree;
        proto_tree      *ie_ext_tree;
+
+       ti = proto_tree_add_text(tree, tvb, offset, 1+1+2+len, "%s",
+           val_to_str(info_element, q2931_info_element_vals,
+             "Unknown information element (0x%02X)"));
+       ie_tree = proto_item_add_subtree(ti, ett_q2931_ie);
+       proto_tree_add_text(ie_tree, tvb, offset, 1, "Information element: %s",
+           val_to_str(info_element, q2931_info_element_vals,
+             "Unknown (0x%02X)"));
+       ti = proto_tree_add_text(ie_tree, tvb, offset + 1, 1,
+           "Information element extension: 0x%02x",
+           info_element_ext);
+       ie_ext_tree = proto_item_add_subtree(ti, ett_q2931_ie_ext);
+       proto_tree_add_text(ie_ext_tree, tvb, offset + 1, 1,
+           decode_enumerated_bitfield(info_element_ext,
+               Q2931_IE_COMPAT_CODING_STD, 8,
+               coding_std_vals, "Coding standard: %s"));
+       proto_tree_add_text(ie_ext_tree, tvb, offset + 1, 1,
+           decode_boolean_bitfield(info_element_ext,
+           Q2931_IE_COMPAT_FOLLOW_INST, 8,
+           "Follow explicit error handling instructions",
+           "Regular error handling procedures apply"));
+       if (info_element_ext & Q2931_IE_COMPAT_FOLLOW_INST) {
+               proto_tree_add_text(ie_ext_tree, tvb, offset + 1, 1,
+                   decode_enumerated_bitfield(info_element_ext,
+                       Q2931_IE_COMPAT_ACTION_IND, 8,
+                       ie_action_ind_vals,
+                       "Action indicator: %s"));
+       }
+       proto_tree_add_text(ie_tree, tvb, offset + 2, 2, "Length: %u", len);
+
+       if ((info_element_ext & Q2931_IE_COMPAT_CODING_STD)
+           == Q2931_ITU_STANDARDIZED_CODING) {
+               dissect_q2931_ie_contents(tvb, offset + 4,
+                   len, ie_tree, info_element);
+       } else {
+               /*
+                * We don't know how it's encoded, so just
+                * dump it as data and be done with it.
+                */
+               proto_tree_add_text(ie_tree, tvb, offset + 4,  len,
+                   "Data: %s", tvb_bytes_to_str(tvb, offset + 4, len));
+       }
+}
+
+static void
+dissect_q2931(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       int             offset = 0;
+       proto_tree      *q2931_tree = NULL;
+       proto_item      *ti;
+       proto_tree      *ext_tree;
        guint8          call_ref_len;
        guint8          call_ref[15];
        guint8          message_type;
@@ -1589,55 +2010,58 @@ dissect_q2931(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
        int             codeset;
        gboolean        non_locking_shift;
 
-       if (check_col(fd, COL_PROTOCOL))
-               col_add_str(fd, COL_PROTOCOL, "Q.2931");
+       if (check_col(pinfo->cinfo, COL_PROTOCOL))
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, "Q.2931");
 
        if (tree) {
-               ti = proto_tree_add_item(tree, proto_q2931, offset,
-                   END_OF_FRAME, NULL);
+               ti = proto_tree_add_item(tree, proto_q2931, tvb, offset, -1,
+                   FALSE);
                q2931_tree = proto_item_add_subtree(ti, ett_q2931);
 
-               proto_tree_add_item(q2931_tree, hf_q2931_discriminator, offset, 1, pd[offset]);
+               proto_tree_add_uint(q2931_tree, hf_q2931_discriminator, tvb, offset, 1, tvb_get_guint8(tvb, offset));
        }
        offset += 1;
-       call_ref_len = pd[offset] & 0xF;        /* XXX - do as a bit field? */
+       call_ref_len = tvb_get_guint8(tvb, offset) & 0xF;       /* XXX - do as a bit field? */
        if (q2931_tree != NULL)
-               proto_tree_add_item(q2931_tree, hf_q2931_call_ref_len, offset, 1, call_ref_len);
+               proto_tree_add_uint(q2931_tree, hf_q2931_call_ref_len, tvb, offset, 1, call_ref_len);
        offset += 1;
        if (call_ref_len != 0) {
-               /* XXX - split this into flag and value */
-               memcpy(call_ref, &pd[offset], call_ref_len);
-               if (q2931_tree != NULL)
-                       proto_tree_add_item(q2931_tree, hf_q2931_call_ref, offset, call_ref_len, call_ref);
+               tvb_memcpy(tvb, call_ref, offset, call_ref_len);
+               if (q2931_tree != NULL) {
+                       proto_tree_add_boolean(q2931_tree, hf_q2931_call_ref_flag,
+                           tvb, offset, 1, (call_ref[0] & 0x80) != 0);
+                       call_ref[0] &= 0x7F;
+                       proto_tree_add_bytes(q2931_tree, hf_q2931_call_ref, tvb, offset, call_ref_len, call_ref);
+               }
                offset += call_ref_len;
        }
-       message_type = pd[offset];
-       if (check_col(fd, COL_INFO)) {
-               col_add_str(fd, COL_INFO,
+       message_type = tvb_get_guint8(tvb, offset);
+       if (check_col(pinfo->cinfo, COL_INFO)) {
+               col_add_str(pinfo->cinfo, COL_INFO,
                    val_to_str(message_type, q2931_message_type_vals,
                      "Unknown message type (0x%02X)"));
        }
        if (q2931_tree != NULL)
-               proto_tree_add_item(q2931_tree, hf_q2931_message_type, offset, 1, message_type);
+               proto_tree_add_uint(q2931_tree, hf_q2931_message_type, tvb, offset, 1, message_type);
        offset += 1;
 
-       message_type_ext = pd[offset];
+       message_type_ext = tvb_get_guint8(tvb, offset);
        if (q2931_tree != NULL) {
-               ti = proto_tree_add_item(q2931_tree, hf_q2931_message_type_ext,
+               ti = proto_tree_add_uint(q2931_tree, hf_q2931_message_type_ext, tvb,
                    offset, 1, message_type_ext);
                ext_tree = proto_item_add_subtree(ti, ett_q2931_ext);
-               proto_tree_add_item(ext_tree, hf_q2931_message_flag,
+               proto_tree_add_boolean(ext_tree, hf_q2931_message_flag, tvb,
                    offset, 1, message_type_ext);
                if (message_type_ext & Q2931_MSG_TYPE_EXT_FOLLOW_INST) {
-                       proto_tree_add_item(ext_tree, hf_q2931_message_action_indicator,
+                       proto_tree_add_uint(ext_tree, hf_q2931_message_action_indicator, tvb,
                            offset, 1, message_type_ext);
                }
        }
        offset += 1;
 
-       message_len = pntohs(&pd[offset]);
+       message_len = tvb_get_ntohs(tvb, offset);
        if (q2931_tree != NULL)
-               proto_tree_add_item(q2931_tree, hf_q2931_message_len, offset, 2, message_len);
+               proto_tree_add_uint(q2931_tree, hf_q2931_message_len, tvb, offset, 2, message_len);
        offset += 2;
 
        /*
@@ -1645,64 +2069,13 @@ dissect_q2931(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
         */
        codeset = 0;    /* start out in codeset 0 */
        non_locking_shift = TRUE;
-       while (IS_DATA_IN_FRAME(offset)) {
-               info_element = pd[offset];
-               if (!BYTES_ARE_IN_FRAME(offset + 1, 1))
-                       break;  /* ran past end of frame */
-               info_element_ext = pd[offset + 1];
-               if (!BYTES_ARE_IN_FRAME(offset + 2, 2))
-                       break;  /* ran past end of frame */
-               info_element_len = pntohs(&pd[offset + 2]);
-               if (!BYTES_ARE_IN_FRAME(offset + 4, info_element_len))
-                       break;  /* ran past end of frame */
+       while (tvb_reported_length_remaining(tvb, offset) > 0) {
+               info_element = tvb_get_guint8(tvb, offset);
+               info_element_ext = tvb_get_guint8(tvb, offset + 1);
+               info_element_len = tvb_get_ntohs(tvb, offset + 2);
                if (q2931_tree != NULL) {
-                       ti = proto_tree_add_text(q2931_tree, offset,
-                           1+1+2+info_element_len, "%s",
-                           val_to_str(info_element, q2931_info_element_vals,
-                             "Unknown information element (0x%02X)"));
-                       ie_tree = proto_item_add_subtree(ti, ett_q2931_ie);
-                       proto_tree_add_text(ie_tree, offset, 1,
-                           "Information element: %s",
-                           val_to_str(info_element, q2931_info_element_vals,
-                             "Unknown (0x%02X)"));
-                       ti = proto_tree_add_text(ie_tree, offset + 1, 1,
-                           "Information element extension: 0x%02x",
-                           info_element_ext);
-                       ie_ext_tree = proto_item_add_subtree(ti, ett_q2931_ie_ext);
-                       proto_tree_add_text(ie_ext_tree, offset + 1, 1,
-                           decode_enumerated_bitfield(info_element_ext,
-                               Q2931_IE_COMPAT_CODING_STD, 8,
-                               coding_std_vals, "Coding standard: %s"));
-                       proto_tree_add_text(ie_ext_tree, offset + 1, 1,
-                           decode_boolean_bitfield(info_element_ext,
-                           Q2931_IE_COMPAT_FOLLOW_INST, 8,
-                           "Follow explicit error handling instructions",
-                           "Regular error handling procedures apply"));
-                       if (info_element_ext & Q2931_IE_COMPAT_FOLLOW_INST) {
-                               proto_tree_add_text(ie_ext_tree, offset + 1, 1,
-                                   decode_enumerated_bitfield(info_element_ext,
-                                       Q2931_IE_COMPAT_ACTION_IND, 8,
-                                       ie_action_ind_vals,
-                                       "Action indicator: %s"));
-                       }
-                       proto_tree_add_text(ie_tree, offset + 2, 2,
-                           "Length: %u", info_element_len);
-
-                       if ((info_element_ext & Q2931_IE_COMPAT_CODING_STD)
-                           == Q2931_ITU_STANDARDIZED_CODING) {
-                               dissect_q2931_ie(pd, offset + 4,
-                                   info_element_len, ie_tree, info_element);
-                       } else {
-                               /*
-                                * We don't know how it's encoded, so just
-                                * dump it as data and be done with it.
-                                */
-                               proto_tree_add_text(ie_tree, offset + 4,
-                                   info_element_len,
-                                   "Data: %s",
-                                   bytes_to_str(&pd[offset + 4],
-                                   info_element_len));
-                       }
+                       dissect_q2931_ie(tvb, offset, info_element_len,
+                           q2931_tree, info_element, info_element_ext);
                }
                if (non_locking_shift)
                        codeset = 0;
@@ -1715,14 +2088,14 @@ dissect_q2931(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
                case Q2931_IE_BBAND_LOCKING_SHIFT:
                        if (info_element_len >= 1) {
                                non_locking_shift = FALSE;
-                               codeset = pd[offset + 4] & 0x07;
+                               codeset = tvb_get_guint8(tvb, offset + 4) & 0x07;
                        }
                        break;
 
                case Q2931_IE_BBAND_NLOCKING_SHIFT:
                        if (info_element_len >= 1) {
                                non_locking_shift = TRUE;
-                               codeset = pd[offset + 4] & 0x07;
+                               codeset = tvb_get_guint8(tvb, offset + 4) & 0x07;
                        }
                        break;
                }
@@ -1733,48 +2106,55 @@ dissect_q2931(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
 void
 proto_register_q2931(void)
 {
-    static hf_register_info hf[] = {
-       { &hf_q2931_discriminator,
-         { "Protocol discriminator", "q2931.disc", FT_UINT8, BASE_HEX, NULL, 0x0, 
-               "" }},
-
-       { &hf_q2931_call_ref_len,
-         { "Call reference value length", "q2931.call_ref_len", FT_UINT8, BASE_DEC, NULL, 0x0,
-               "" }},
-
-       { &hf_q2931_call_ref,
-         { "Call reference value", "q2931.call_ref", FT_BYTES, BASE_HEX, NULL, 0x0,
-               "" }},
-
-       { &hf_q2931_message_type,
-         { "Message type", "q2931.message_type", FT_UINT8, BASE_HEX, VALS(q2931_message_type_vals), 0x0,
-               "" }},
-
-       { &hf_q2931_message_type_ext,
-         { "Message type extension", "q2931.message_type_ext", FT_UINT8, BASE_HEX, NULL, 0x0,
-               "" }},
-
-       { &hf_q2931_message_flag,
-         { "Flag", "q2931.message_flag", FT_BOOLEAN, 8, TFS(&tos_msg_flag), Q2931_MSG_TYPE_EXT_FOLLOW_INST,
-               "" }},
-
-       { &hf_q2931_message_action_indicator,
-         { "Action indicator", "q2931.message_action_indicator", FT_UINT8, BASE_DEC, VALS(msg_action_ind_vals), Q2931_MSG_TYPE_EXT_ACTION_IND,
-               "" }},
-
-       { &hf_q2931_message_len,
-         { "Message length", "q2931.message_len", FT_UINT16, BASE_DEC, NULL, 0x0,
-               "" }},
-
-    };
-    static gint *ett[] = {
-        &ett_q2931,
-        &ett_q2931_ext,
-        &ett_q2931_ie,
-        &ett_q2931_ie_ext,
-    };
-
-    proto_q2931 = proto_register_protocol ("Q.2931", "q2931");
-    proto_register_field_array (proto_q2931, hf, array_length(hf));
-    proto_register_subtree_array(ett, array_length(ett));
+       static hf_register_info hf[] = {
+               { &hf_q2931_discriminator,
+                 { "Protocol discriminator", "q2931.disc", FT_UINT8, BASE_HEX, NULL, 0x0,
+                       "", HFILL }},
+
+               { &hf_q2931_call_ref_len,
+                 { "Call reference value length", "q2931.call_ref_len", FT_UINT8, BASE_DEC, NULL, 0x0,
+                       "", HFILL }},
+
+               { &hf_q2931_call_ref_flag,
+                 { "Call reference flag", "q2931.call_ref_flag", FT_BOOLEAN, BASE_NONE, TFS(&tfs_call_ref_flag), 0x0,
+                       "", HFILL }},
+
+               { &hf_q2931_call_ref,
+                 { "Call reference value", "q2931.call_ref", FT_BYTES, BASE_HEX, NULL, 0x0,
+                       "", HFILL }},
+
+               { &hf_q2931_message_type,
+                 { "Message type", "q2931.message_type", FT_UINT8, BASE_HEX, VALS(q2931_message_type_vals), 0x0,
+                       "", HFILL }},
+
+               { &hf_q2931_message_type_ext,
+                 { "Message type extension", "q2931.message_type_ext", FT_UINT8, BASE_HEX, NULL, 0x0,
+                       "", HFILL }},
+
+               { &hf_q2931_message_flag,
+                 { "Flag", "q2931.message_flag", FT_BOOLEAN, 8, TFS(&tos_msg_flag), Q2931_MSG_TYPE_EXT_FOLLOW_INST,
+                       "", HFILL }},
+
+               { &hf_q2931_message_action_indicator,
+                 { "Action indicator", "q2931.message_action_indicator", FT_UINT8, BASE_DEC, VALS(msg_action_ind_vals), Q2931_MSG_TYPE_EXT_ACTION_IND,
+                       "", HFILL }},
+
+               { &hf_q2931_message_len,
+                 { "Message length", "q2931.message_len", FT_UINT16, BASE_DEC, NULL, 0x0,
+                       "", HFILL }},
+
+       };
+       static gint *ett[] = {
+               &ett_q2931,
+               &ett_q2931_ext,
+               &ett_q2931_ie,
+               &ett_q2931_ie_ext,
+               &ett_q2931_nsap,
+       };
+
+       proto_q2931 = proto_register_protocol("Q.2931", "Q.2931", "q2931");
+       proto_register_field_array (proto_q2931, hf, array_length(hf));
+       proto_register_subtree_array(ett, array_length(ett));
+
+       register_dissector("q2931", dissect_q2931, proto_q2931);
 }