Add a Mac OS X icon file, for possible future use.
[obnox/wireshark/wip.git] / packet-q931.c
index ab5cc80957469987549a62cfde6002967bd9eb0d..de6da1818916873220abdf3d8c909120575cd69a 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for Q.931 frame disassembly
  * Guy Harris <guy@alum.mit.edu>
  *
- * $Id: packet-q931.c,v 1.56 2003/07/08 07:56:27 guy Exp $
+ * $Id: packet-q931.c,v 1.65 2004/01/16 18:28:09 guy Exp $
  *
  * Modified by Andreas Sikkema for possible use with H.323
  *
  * http://www.tulatelecom.ru/staff/german/DSSHelp/MessList/InfEl/InfElList.html
  */
 
-static int proto_q931 = -1;
-static int hf_q931_discriminator = -1;
-static int hf_q931_call_ref_len = -1;
-static int hf_q931_call_ref_flag = -1;
-static int hf_q931_call_ref = -1;
-static int hf_q931_message_type = -1;
-static int hf_q931_cause_value = -1;
-static int hf_q931_calling_party_number = -1;
-static int hf_q931_called_party_number = -1;
-static int hf_q931_connected_number = -1;
-static int hf_q931_redirecting_number = -1;
-
-static gint ett_q931 = -1;
-static gint ett_q931_ie = -1;
+static int proto_q931                                  = -1;
+static int hf_q931_discriminator                       = -1;
+static int hf_q931_coding_standard                     = -1;
+static int hf_q931_information_transfer_capability     = -1;
+static int hf_q931_transfer_mode                       = -1;
+static int hf_q931_information_transfer_rate           = -1;
+static int hf_q931_uil1                                        = -1;
+static int hf_q931_call_ref_len                        = -1;
+static int hf_q931_call_ref_flag                       = -1;
+static int hf_q931_call_ref                            = -1;
+static int hf_q931_message_type                        = -1;
+static int hf_q931_cause_value                                 = -1;
+static int hf_q931_number_type                         = -1;
+static int hf_q931_numbering_plan                      = -1;
+static int hf_q931_extension_ind                       = -1;
+static int hf_q931_calling_party_number                = -1;
+static int hf_q931_called_party_number                         = -1;
+static int hf_q931_connected_number                    = -1;
+static int hf_q931_redirecting_number                  = -1;
+
+static gint ett_q931                                   = -1;
+static gint ett_q931_ie                                = -1;
+
+static dissector_table_t codeset_dissector_table;
+static dissector_table_t ie_dissector_table;
 
 /* desegmentation of Q.931 over TPKT over TCP */
 static gboolean q931_desegment = TRUE;
@@ -158,6 +169,16 @@ static const true_false_string tfs_call_ref_flag = {
  * Information elements.
  */
 
+/* Shifted codeset values */
+#define CS0 0x000
+#define CS1 0x100
+#define CS2 0x200
+#define CS3 0x300
+#define CS4 0x400
+#define CS5 0x500
+#define CS6 0x600
+#define CS7 0x700
+
 #define        Q931_IE_SO_MASK 0x80    /* single-octet/variable-length mask */
 /*
  * Single-octet IEs.
@@ -168,7 +189,7 @@ static const true_false_string tfs_call_ref_flag = {
 
 #define        Q931_IE_SHIFT                   0x90
 #define        Q931_IE_SHIFT_NON_LOCKING       0x08    /* non-locking shift */
-#define        Q931_IE_SHIFT_CODESET           0x0F    /* codeset */
+#define        Q931_IE_SHIFT_CODESET           0x07    /* codeset */
 
 #define        Q931_IE_MORE_DATA_OR_SEND_COMP  0xA0    /* More Data or Sending Complete */
 #define        Q931_IE_MORE_DATA               0xA0
@@ -181,6 +202,15 @@ static const true_false_string tfs_call_ref_flag = {
  * Variable-length IEs.
  */
 #define        Q931_IE_VL_EXTENSION            0x80    /* Extension flag */
+/*     extension bit. The bit value "0" indicates that the octet continues through the         */
+/*     next octet. The bit value "1" indicates that this octet is the last octet               */
+
+static const true_false_string q931_extension_ind_value = {
+  "last octet",
+  "information continues through the next octet",
+
+};
+
 
 /*
  * Codeset 0 (default).
@@ -261,7 +291,8 @@ static const true_false_string tfs_call_ref_flag = {
 /* 0x76 is Redirection Number, but that's also Codeset 0 */
 #define        Q931_IE_CALL_APPEARANCE         0x7B
 
-static const value_string q931_info_element_vals[] = {
+/* Codeset 0 */
+static const value_string q931_info_element_vals0[] = {
        { Q931_IE_SEGMENTED_MESSAGE,            "Segmented message" },
        { Q931_IE_BEARER_CAPABILITY,            "Bearer capability" },
        { Q931_IE_CAUSE,                        "Cause" },
@@ -305,13 +336,54 @@ static const value_string q931_info_element_vals[] = {
        { Q931_IE_ESCAPE,                       "Escape" },
        { Q931_IE_CONNECTED_NUMBER,             "Connected number" },
        { Q931_IE_CONNECTED_SUBADDR,            "Connected subaddress" },
+       { 0,                                    NULL }
+};
+/* Codeset 1 */
+static const value_string q931_info_element_vals1[] = {
+       { 0,                                    NULL }
+};
+/* Codeset 2 */
+static const value_string q931_info_element_vals2[] = {
+       { 0,                                    NULL }
+};
+/* Codeset 3 */
+static const value_string q931_info_element_vals3[] = {
+       { 0,                                    NULL }
+};
+/* Codeset 4 */
+static const value_string q931_info_element_vals4[] = {
+       { 0,                                    NULL }
+};
+/* Codeset 5 */
+static const value_string q931_info_element_vals5[] = {
        { Q931_IE_CHARGING_ADVICE,              "Charging advice" },
        { Q931_IE_OPERATOR_SYSTEM_ACCESS,       "Operator system access" },
+       { 0,                                    NULL }
+};
+/* Codeset 6 */
+static const value_string q931_info_element_vals6[] = {
        { Q931_IE_REDIRECTING_NUMBER,           "Redirecting number" },
        { Q931_IE_REDIRECTING_SUBADDR,          "Redirecting subaddress" },
        { Q931_IE_CALL_APPEARANCE,              "Call appearance" },
        { 0,                                    NULL }
 };
+/* Codeset 7 */
+static const value_string q931_info_element_vals7[] = {
+       { 0,                                    NULL }
+};
+
+/* Codeset array */
+#define NUM_INFO_ELEMENT_VALS  (Q931_IE_SHIFT_CODESET+1)
+static const value_string *q931_info_element_vals[NUM_INFO_ELEMENT_VALS] = {
+  q931_info_element_vals0,
+  q931_info_element_vals1,
+  q931_info_element_vals2,
+  q931_info_element_vals3,
+  q931_info_element_vals4,
+  q931_info_element_vals5,
+  q931_info_element_vals6,
+  q931_info_element_vals7,
+};
 
 static const value_string q931_congestion_level_vals[] = {
        { 0x0, "Receiver ready" },
@@ -357,11 +429,11 @@ dissect_q931_segmented_message_ie(tvbuff_t *tvb, int offset, int len,
 /*
  * Dissect a Bearer capability or Low-layer compatibility information element.
  */
-static const value_string q931_bc_coding_standard_vals[] = {
-       { 0x00, "ITU-T standardized coding" },
-       { 0x20, "ISO/IEC standard" },
-       { 0x40, "National standard" },
-       { 0x60, "Standard defined for this particular network" },
+static const value_string q931_coding_standard_vals[] = {
+       { 0x0, "ITU-T standardized coding" },
+       { 0x1, "ISO/IEC standard" },
+       { 0x2, "National standard" },
+       { 0x3, "Standard defined for this particular network" },
        { 0,    NULL }
 };
 
@@ -377,7 +449,7 @@ static const value_string q931_information_transfer_capability_vals[] = {
 
 static const value_string q931_transfer_mode_vals[] = {
        { 0x00, "Circuit mode" },
-       { 0x40, "Packet mode" },
+       { 0x02, "Packet mode" },
        { 0,    NULL }
 };
 
@@ -577,9 +649,6 @@ dissect_q931_bearer_capability_ie(tvbuff_t *tvb, int offset, int len,
                return;
        octet = tvb_get_guint8(tvb, offset);
        coding_standard = octet & 0x60;
-       proto_tree_add_text(tree, tvb, offset, 1,
-           "Coding standard: %s",
-           val_to_str(coding_standard, q931_bc_coding_standard_vals, NULL));
        if (coding_standard != Q931_ITU_STANDARDIZED_CODING) {
                /*
                 * We don't know how the bearer capability is encoded,
@@ -588,12 +657,13 @@ dissect_q931_bearer_capability_ie(tvbuff_t *tvb, int offset, int len,
                proto_tree_add_text(tree, tvb, offset,
                    len, "Data: %s",
                    tvb_bytes_to_str(tvb, offset, len));
+               proto_tree_add_uint(tree, hf_q931_coding_standard, tvb, offset, 1, octet);
+               proto_tree_add_boolean(tree, hf_q931_extension_ind, tvb, offset, 1, octet);
                return;
        }
-       proto_tree_add_text(tree, tvb, offset, 1,
-           "Information transfer capability: %s",
-           val_to_str(octet & 0x1F, q931_information_transfer_capability_vals,
-             "Unknown (0x%02X)"));
+       proto_tree_add_uint(tree, hf_q931_information_transfer_capability, tvb, offset, 1, octet);
+       proto_tree_add_uint(tree, hf_q931_coding_standard, tvb, offset, 1, octet);
+       proto_tree_add_boolean(tree, hf_q931_extension_ind, tvb, offset, 1, octet);
        offset += 1;
        len -= 1;
 
@@ -614,15 +684,10 @@ dissect_q931_bearer_capability_ie(tvbuff_t *tvb, int offset, int len,
        if (len == 0)
                return;
        octet = tvb_get_guint8(tvb, offset);
-       proto_tree_add_text(tree, tvb, offset, 1,
-           "Transfer mode: %s",
-           val_to_str(octet & 0x60, q931_transfer_mode_vals,
-             "Unknown (0x%02X)"));
+       proto_tree_add_uint(tree, hf_q931_information_transfer_rate, tvb, offset, 1, octet);
+       proto_tree_add_uint(tree, hf_q931_transfer_mode, tvb, offset, 1, octet);
+       proto_tree_add_boolean(tree, hf_q931_extension_ind, tvb, offset, 1, octet);
        it_rate = octet & 0x1F;
-       proto_tree_add_text(tree, tvb, offset, 1,
-           "Information transfer rate: %s",
-           val_to_str(it_rate, q931_information_transfer_rate_vals,
-             "Unknown (0x%02X)"));
        offset += 1;
        len -= 1;
 
@@ -641,10 +706,8 @@ dissect_q931_bearer_capability_ie(tvbuff_t *tvb, int offset, int len,
                /*
                 * Layer 1 information.
                 */
-               proto_tree_add_text(tree, tvb, offset, 1,
-                   "User information layer 1 protocol: %s",
-                   val_to_str(octet & 0x1F, q931_uil1_vals,
-                     "Unknown (0x%02X)"));
+               proto_tree_add_uint(tree, hf_q931_uil1, tvb, offset, 1, octet);
+               proto_tree_add_boolean(tree, hf_q931_extension_ind, tvb, offset, 1, octet);
                offset += 1;
                len -= 1;
 
@@ -731,7 +794,7 @@ dissect_q931_bearer_capability_ie(tvbuff_t *tvb, int offset, int len,
                       "Unknown (0x%X)"));
                proto_tree_add_text(tree, tvb, offset, 1,
                    "Parity: %s",
-                     val_to_str(octet & 0x08, q931_l1_parity_vals,
+                     val_to_str(octet & 0x07, q931_l1_parity_vals,
                       "Unknown (0x%X)"));
 
                if (octet & Q931_IE_VL_EXTENSION)
@@ -900,13 +963,7 @@ l3_done:
 /*
  * Dissect a Cause information element.
  */
-static const value_string q931_cause_coding_standard_vals[] = {
-       { 0x00, "ITU-T standardized coding" },
-       { 0x20, "ISO/IEC standard" },
-       { 0x40, "National standard" },
-       { 0x60, "Standard specific to identified location" },
-       { 0,    NULL }
-};
+
 
 const value_string q931_cause_location_vals[] = {
        { 0x00, "User (U)" },
@@ -1076,9 +1133,7 @@ dissect_q931_cause_ie(tvbuff_t *tvb, int offset, int len,
                return;
        octet = tvb_get_guint8(tvb, offset);
        coding_standard = octet & 0x60;
-       proto_tree_add_text(tree, tvb, offset, 1,
-           "Coding standard: %s",
-           val_to_str(coding_standard, q931_cause_coding_standard_vals, NULL));
+       proto_tree_add_uint(tree, hf_q931_coding_standard, tvb, offset, 1, octet);
        if (coding_standard != Q931_ITU_STANDARDIZED_CODING) {
                /*
                 * We don't know how the cause is encoded,
@@ -1093,6 +1148,7 @@ dissect_q931_cause_ie(tvbuff_t *tvb, int offset, int len,
            "Location: %s",
            val_to_str(octet & 0x0F, q931_cause_location_vals,
              "Unknown (0x%X)"));
+       proto_tree_add_boolean(tree, hf_q931_extension_ind, tvb, offset, 1, octet);
        offset += 1;
        len -= 1;
 
@@ -1112,7 +1168,7 @@ dissect_q931_cause_ie(tvbuff_t *tvb, int offset, int len,
                return;
        octet = tvb_get_guint8(tvb, offset);
        cause_value = octet & 0x7F;
-       proto_tree_add_uint(tree, hf_cause_value, tvb, 0, 1, cause_value);
+       proto_tree_add_uint(tree, hf_cause_value, tvb, offset, 1, cause_value);
        offset += 1;
        len -= 1;
 
@@ -1162,14 +1218,14 @@ dissect_q931_cause_ie(tvbuff_t *tvb, int offset, int len,
                case Q931_REJ_IE_MISSING:
                        proto_tree_add_text(tree, tvb, offset, 1,
                            "Missing information element: %s",
-                           val_to_str(tvb_get_guint8(tvb, offset), q931_info_element_vals,
+                           val_to_str(tvb_get_guint8(tvb, offset), q931_info_element_vals0,
                              "Unknown (0x%02X)"));
                        break;
 
                case Q931_REJ_IE_INSUFFICIENT:
                        proto_tree_add_text(tree, tvb, offset, 1,
                            "Insufficient information element: %s",
-                           val_to_str(tvb_get_guint8(tvb, offset), q931_info_element_vals,
+                           val_to_str(tvb_get_guint8(tvb, offset), q931_info_element_vals0,
                              "Unknown (0x%02X)"));
                        break;
 
@@ -1189,7 +1245,7 @@ dissect_q931_cause_ie(tvbuff_t *tvb, int offset, int len,
                do {
                        proto_tree_add_text(tree, tvb, offset, 1,
                            "Information element: %s",
-                           val_to_str(tvb_get_guint8(tvb, offset), q931_info_element_vals,
+                           val_to_str(tvb_get_guint8(tvb, offset), q931_info_element_vals0,
                              "Unknown (0x%02X)"));
                        offset += 1;
                        len -= 1;
@@ -1221,14 +1277,6 @@ dissect_q931_cause_ie(tvbuff_t *tvb, int offset, int len,
 /*
  * Dissect a Call state information element.
  */
-static const value_string q931_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 q931_call_state_vals[] = {
        { 0x00, "Null" },
        { 0x01, "Call initiated" },
@@ -1263,9 +1311,7 @@ dissect_q931_call_state_ie(tvbuff_t *tvb, int offset, int len,
                return;
        octet = tvb_get_guint8(tvb, offset);
        coding_standard = octet & 0x60;
-       proto_tree_add_text(tree, tvb, offset, 1,
-           "Coding standard: %s",
-           val_to_str(coding_standard, q931_coding_standard_vals, NULL));
+       proto_tree_add_uint(tree, hf_q931_coding_standard, tvb, offset, 1, octet);
        if (coding_standard != Q931_ITU_STANDARDIZED_CODING) {
                /*
                 * We don't know how the call state is encoded,
@@ -1381,10 +1427,7 @@ dissect_q931_channel_identification_ie(tvbuff_t *tvb, int offset, int len,
                        return;
                octet = tvb_get_guint8(tvb, offset);
                coding_standard = octet & 0x60;
-               proto_tree_add_text(tree, tvb, offset, 1,
-                   "Coding standard: %s",
-                   val_to_str(coding_standard, q931_coding_standard_vals,
-                     NULL));
+               proto_tree_add_uint(tree, hf_q931_coding_standard, tvb, offset, 1, octet);
                if (coding_standard != Q931_ITU_STANDARDIZED_CODING) {
                        /*
                         * We don't know how the channel identifier is
@@ -1435,9 +1478,7 @@ dissect_q931_progress_indicator_ie(tvbuff_t *tvb, int offset, int len,
                return;
        octet = tvb_get_guint8(tvb, offset);
        coding_standard = octet & 0x60;
-       proto_tree_add_text(tree, tvb, offset, 1,
-           "Coding standard: %s",
-           val_to_str(coding_standard, q931_cause_coding_standard_vals, NULL));
+       proto_tree_add_uint(tree, hf_q931_coding_standard, tvb, offset, 1, octet);
        if (coding_standard != Q931_ITU_STANDARDIZED_CODING) {
                /*
                 * We don't know how the progress indicator is encoded,
@@ -1917,12 +1958,12 @@ dissect_q931_reverse_charge_ind_ie(tvbuff_t *tvb, int offset, int len,
  * Dissect a (phone) number information element.
  */
 static const value_string q931_number_type_vals[] = {
-       { 0x00, "Unknown" },
-       { 0x10, "International number" },
-       { 0x20, "National number" },
-       { 0x30, "Network specific number" },
-       { 0x40, "Subscriber number" },
-       { 0x60, "Abbreviated number" },
+       { 0x0, "Unknown" },
+       { 0x1, "International number" },
+       { 0x2, "National number" },
+       { 0x3, "Network specific number" },
+       { 0x4, "Subscriber number" },
+       { 0x6, "Abbreviated number" },
        { 0,    NULL }
 };
 
@@ -1971,14 +2012,10 @@ dissect_q931_number_ie(tvbuff_t *tvb, int offset, int len,
        if (len == 0)
                return;
        octet = tvb_get_guint8(tvb, offset);
-       proto_tree_add_text(tree, tvb, offset, 1,
-           "Type of number: %s",
-           val_to_str(octet & 0x70, q931_number_type_vals,
-             "Unknown (0x%02X)"));
-       proto_tree_add_text(tree, tvb, offset, 1,
-           "Numbering plan: %s",
-           val_to_str(octet & 0x0F, q931_numbering_plan_vals,
-             "Unknown (0x%02X)"));
+       proto_tree_add_uint(tree, hf_q931_numbering_plan, tvb, offset, 1, octet);
+       proto_tree_add_uint(tree, hf_q931_number_type, tvb, offset, 1, octet);
+       proto_tree_add_boolean(tree, hf_q931_extension_ind, tvb, offset, 1, octet);
+       
        offset += 1;
        len -= 1;
 
@@ -2128,9 +2165,7 @@ dissect_q931_high_layer_compat_ie(tvbuff_t *tvb, int offset, int len,
                return;
        octet = tvb_get_guint8(tvb, offset);
        coding_standard = octet & 0x60;
-       proto_tree_add_text(tree, tvb, offset, 1,
-           "Coding standard: %s",
-           val_to_str(coding_standard, q931_coding_standard_vals, NULL));
+       proto_tree_add_uint(tree, hf_q931_coding_standard, tvb, offset, 1, octet);
        offset += 1;
        len -= 1;
        if (coding_standard != Q931_ITU_STANDARDIZED_CODING) {
@@ -2238,15 +2273,6 @@ dissect_q931_ia5_ie(tvbuff_t *tvb, int offset, int len, proto_tree *tree,
        }
 }
 
-static const value_string q931_codeset_vals[] = {
-       { 0x00, "Q.931 information elements" },
-       { 0x04, "Information elements for ISO/IEC use" },
-       { 0x05, "Information elements for national use" },
-       { 0x06, "Information elements specific to the local network" },
-       { 0x07, "User-specific information elements" },
-       { 0x00, NULL },
-};
-
 static void
 dissect_q931_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
     gboolean is_tpkt)
@@ -2254,15 +2280,9 @@ dissect_q931_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        int             offset = 0;
        proto_tree      *q931_tree = NULL;
        proto_item      *ti;
-       proto_tree      *ie_tree = NULL;
        guint8          call_ref_len;
        guint8          call_ref[15];
        guint8          message_type;
-       guint8          info_element;
-       guint16         info_element_len;
-       int             codeset, locked_codeset;
-       gboolean        non_locking_shift;
-       tvbuff_t        *h225_tvb;
 
        if (check_col(pinfo->cinfo, COL_PROTOCOL))
                col_set_str(pinfo->cinfo, COL_PROTOCOL, "Q.931");
@@ -2303,34 +2323,76 @@ dissect_q931_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        /*
         * And now for the information elements....
         */
+       dissect_q931_IEs(tvb, pinfo, tree, q931_tree,is_tpkt, offset);
+}
+
+static const value_string q931_codeset_vals[] = {
+       { 0x00, "Q.931 information elements" },
+       { 0x04, "Information elements for ISO/IEC use" },
+       { 0x05, "Information elements for national use" },
+       { 0x06, "Information elements specific to the local network" },
+       { 0x07, "User-specific information elements" },
+       { 0x00, NULL },
+};
+
+void
+dissect_q931_IEs(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    proto_tree *q931_tree, gboolean is_tpkt, int offset)
+{
+       proto_item      *ti;
+       proto_tree      *ie_tree = NULL;
+       guint8          info_element;
+       guint16         info_element_len;
+       int             codeset, locked_codeset;
+       gboolean        non_locking_shift;
+       tvbuff_t        *h225_tvb, *next_tvb;
+
        codeset = locked_codeset = 0;   /* start out in codeset 0 */
        non_locking_shift = TRUE;
        while (tvb_reported_length_remaining(tvb, offset) > 0) {
                info_element = tvb_get_guint8(tvb, offset);
 
+               /* Check for the codeset shift */
+               if ((info_element & Q931_IE_SO_MASK) &&
+                   ((info_element & Q931_IE_SO_IDENTIFIER_MASK) == Q931_IE_SHIFT)) {
+                       non_locking_shift = info_element & Q931_IE_SHIFT_NON_LOCKING;
+                       codeset = info_element & Q931_IE_SHIFT_CODESET;
+                       if (!non_locking_shift)
+                               locked_codeset = codeset;
+                       if (q931_tree != NULL) {
+                               proto_tree_add_text(q931_tree, tvb, offset, 1,
+                                   "%s shift to codeset %u: %s",
+                                   (non_locking_shift ? "Non-locking" : "Locking"),
+                                   codeset,
+                                   val_to_str(codeset, q931_codeset_vals,
+                                     "Unknown (0x%02X)"));
+                       }
+                       offset += 1;
+                       continue;
+               }
+
                /*
                 * Check for the single-octet IEs.
                 */
                if (info_element & Q931_IE_SO_MASK) {
-                       switch (info_element & Q931_IE_SO_IDENTIFIER_MASK) {
-
-                       case Q931_IE_SHIFT:
-                               non_locking_shift = info_element & Q931_IE_SHIFT_NON_LOCKING;
-                               codeset = info_element & Q931_IE_SHIFT_CODESET;
-                               if (!non_locking_shift)
-                                       locked_codeset = codeset;
-                               if (q931_tree != NULL) {
-                                       proto_tree_add_text(q931_tree, tvb, offset, 1,
-                                           "%s shift to codeset %u: %s",
-                                           (non_locking_shift ? "Non-locking" : "Locking"),
-                                           codeset,
-                                       val_to_str(codeset, q931_codeset_vals,
-                                             "Unknown (0x%02X)"));
+                       /*
+                        * Check for subdissectors for this IE or
+                        * for all IEs in this codeset.
+                        */
+                       if (dissector_get_port_handle(codeset_dissector_table, codeset) ||
+                           dissector_get_port_handle(ie_dissector_table, (codeset << 8) | (info_element & Q931_IE_SO_IDENTIFIER_MASK))) {
+                               next_tvb = tvb_new_subset (tvb, offset, 1, 1);
+                               if (dissector_try_port(ie_dissector_table, (codeset << 8) | (info_element & Q931_IE_SO_IDENTIFIER_MASK), next_tvb, pinfo, q931_tree) ||
+                                   dissector_try_port(codeset_dissector_table, codeset, next_tvb, pinfo, q931_tree)) {
+                                       offset += 1;
+                                       codeset = locked_codeset;
+                                       continue;
                                }
-                               offset += 1;
-                               continue;
+                       }
+
+                       switch ((codeset << 8) | (info_element & Q931_IE_SO_IDENTIFIER_MASK)) {
 
-                       case Q931_IE_MORE_DATA_OR_SEND_COMP:
+                       case CS0 | Q931_IE_MORE_DATA_OR_SEND_COMP:
                                switch (info_element) { 
 
                                case Q931_IE_MORE_DATA:
@@ -2357,7 +2419,7 @@ dissect_q931_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                }
                                break;
 
-                       case Q931_IE_CONGESTION_LEVEL:
+                       case CS0 | Q931_IE_CONGESTION_LEVEL:
                                if (q931_tree != NULL) {
                                        proto_tree_add_text(q931_tree, tvb, offset, 1,
                                            "Congestion level: %s",
@@ -2367,7 +2429,7 @@ dissect_q931_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                }
                                break;
 
-                       case Q931_IE_REPEAT_INDICATOR:
+                       case CS0 | Q931_IE_REPEAT_INDICATOR:
                                if (q931_tree != NULL) {
                                        proto_tree_add_text(q931_tree, tvb, offset, 1,
                                            "Repeat indicator: %s",
@@ -2402,21 +2464,21 @@ dissect_q931_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                 * IE with ASN.1 encoding of the user information.
                 */
                if (is_tpkt && tvb_bytes_exist(tvb, offset, 4) &&
-                   tvb_get_guint8(tvb, offset) == Q931_IE_USER_USER &&
+                   codeset == 0 && tvb_get_guint8(tvb, offset) == Q931_IE_USER_USER &&
                    tvb_get_guint8(tvb, offset + 3) == Q931_PROTOCOL_DISCRIMINATOR_ASN1)  {
                        info_element_len = tvb_get_ntohs(tvb, offset + 1);
                        if (q931_tree != NULL) {
                                ti = proto_tree_add_text(q931_tree, tvb, offset,
                                    1+2+info_element_len, "%s",
                                    val_to_str(info_element,
-                                     q931_info_element_vals,
+                                     q931_info_element_vals[codeset],
                                      "Unknown information element (0x%02X)"));
                                ie_tree = proto_item_add_subtree(ti,
                                    ett_q931_ie);
                                proto_tree_add_text(ie_tree, tvb, offset, 1,
                                    "Information element: %s",
                                    val_to_str(info_element,
-                                     q931_info_element_vals, "Unknown (0x%02X)"));
+                                     q931_info_element_vals[codeset], "Unknown (0x%02X)"));
                                proto_tree_add_text(ie_tree, tvb, offset + 1,
                                    2, "Length: %u", info_element_len);
                                proto_tree_add_text(ie_tree, tvb, offset + 3,
@@ -2458,192 +2520,208 @@ dissect_q931_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                        offset += 1 + 2 + info_element_len;
                } else {
                        info_element_len = tvb_get_guint8(tvb, offset + 1);
+
+                       /*
+                        * Check for subdissectors for this IE or
+                        * for all IEs in this codeset.
+                        */
+                       if (dissector_get_port_handle(codeset_dissector_table, codeset) ||
+                           dissector_get_port_handle(ie_dissector_table, (codeset << 8) | info_element)) {
+                               next_tvb = tvb_new_subset (tvb, offset, info_element_len + 2, info_element_len + 2);
+                               if (dissector_try_port(ie_dissector_table, (codeset << 8) | info_element, next_tvb, pinfo, q931_tree) ||
+                                   dissector_try_port(codeset_dissector_table, codeset, next_tvb, pinfo, q931_tree)) {
+                                       offset += 2 + info_element_len;
+                                       codeset = locked_codeset;
+                                       continue;
+                               }
+                       }
+
                        if (q931_tree != NULL) {
                                ti = proto_tree_add_text(q931_tree, tvb, offset,
                                    1+1+info_element_len, "%s",
-                                   val_to_str(info_element, q931_info_element_vals,
+                                   val_to_str(info_element, q931_info_element_vals[codeset],
                                      "Unknown information element (0x%02X)"));
                                        ie_tree = proto_item_add_subtree(ti, ett_q931_ie);
                                proto_tree_add_text(ie_tree, tvb, offset, 1,
                                    "Information element: %s",
-                                   val_to_str(info_element, q931_info_element_vals,
+                                   val_to_str(info_element, q931_info_element_vals[codeset],
                                      "Unknown (0x%02X)"));
                                proto_tree_add_text(ie_tree, tvb, offset + 1, 1,
                                    "Length: %u", info_element_len);
 
-                               switch (info_element) {
+                               switch ((codeset << 8) | info_element) {
 
-                               case Q931_IE_SEGMENTED_MESSAGE:
+                               case CS0 | Q931_IE_SEGMENTED_MESSAGE:
                                        dissect_q931_segmented_message_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_BEARER_CAPABILITY:
-                               case Q931_IE_LOW_LAYER_COMPAT:
+                               case CS0 | Q931_IE_BEARER_CAPABILITY:
+                               case CS0 | Q931_IE_LOW_LAYER_COMPAT:
                                        dissect_q931_bearer_capability_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_CAUSE:
+                               case CS0 | Q931_IE_CAUSE:
                                        dissect_q931_cause_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree,
                                            hf_q931_cause_value);
                                        break;
 
-                               case Q931_IE_CALL_STATE:
+                               case CS0 | Q931_IE_CALL_STATE:
                                        dissect_q931_call_state_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_CHANNEL_IDENTIFICATION:
+                               case CS0 | Q931_IE_CHANNEL_IDENTIFICATION:
                                        dissect_q931_channel_identification_ie(
                                            tvb, offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_PROGRESS_INDICATOR:
+                               case CS0 | Q931_IE_PROGRESS_INDICATOR:
                                        dissect_q931_progress_indicator_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_NETWORK_SPECIFIC_FACIL:
-                               case Q931_IE_TRANSIT_NETWORK_SEL:
+                               case CS0 | Q931_IE_NETWORK_SPECIFIC_FACIL:
+                               case CS0 | Q931_IE_TRANSIT_NETWORK_SEL:
                                        dissect_q931_ns_facilities_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_NOTIFICATION_INDICATOR:
+                               case CS0 | Q931_IE_NOTIFICATION_INDICATOR:
                                        dissect_q931_notification_indicator_ie(
                                            tvb, offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_DISPLAY:
+                               case CS0 | Q931_IE_DISPLAY:
                                        dissect_q931_ia5_ie(tvb, offset + 2,
                                            info_element_len, ie_tree,
                                            "Display information");
                                        break;
 
-                               case Q931_IE_DATE_TIME:
+                               case CS0 | Q931_IE_DATE_TIME:
                                        dissect_q931_date_time_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_KEYPAD_FACILITY:
+                               case CS0 | Q931_IE_KEYPAD_FACILITY:
                                        dissect_q931_ia5_ie(tvb, offset + 2,
                                            info_element_len, ie_tree,
                                            "Keypad facility");
                                        break;
 
-                               case Q931_IE_SIGNAL:
+                               case CS0 | Q931_IE_SIGNAL:
                                        dissect_q931_signal_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_INFORMATION_RATE:
+                               case CS0 | Q931_IE_INFORMATION_RATE:
                                        dissect_q931_information_rate_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_E2E_TRANSIT_DELAY:
+                               case CS0 | Q931_IE_E2E_TRANSIT_DELAY:
                                        dissect_q931_e2e_transit_delay_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_TD_SELECTION_AND_INT:
+                               case CS0 | Q931_IE_TD_SELECTION_AND_INT:
                                        dissect_q931_td_selection_and_int_ie(
                                            tvb, offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_PL_BINARY_PARAMETERS:
+                               case CS0 | Q931_IE_PL_BINARY_PARAMETERS:
                                        dissect_q931_pl_binary_parameters_ie(
                                            tvb, offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_PL_WINDOW_SIZE:
+                               case CS0 | Q931_IE_PL_WINDOW_SIZE:
                                        dissect_q931_pl_window_size_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_PACKET_SIZE:
+                               case CS0 | Q931_IE_PACKET_SIZE:
                                        dissect_q931_packet_size_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_CUG:
+                               case CS0 | Q931_IE_CUG:
                                        dissect_q931_cug_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_REVERSE_CHARGE_IND:
+                               case CS0 | Q931_IE_REVERSE_CHARGE_IND:
                                        dissect_q931_reverse_charge_ind_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_CALLING_PARTY_NUMBER:
+                               case CS0 | Q931_IE_CALLING_PARTY_NUMBER:
                                        dissect_q931_number_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree,
                                            hf_q931_calling_party_number);
                                        break;
 
-                               case Q931_IE_CONNECTED_NUMBER_DEFAULT:
+                               case CS0 | Q931_IE_CONNECTED_NUMBER_DEFAULT:
                                        dissect_q931_number_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree,
                                            hf_q931_connected_number);
                                        break;
 
-                               case Q931_IE_CALLED_PARTY_NUMBER:
+                               case CS0 | Q931_IE_CALLED_PARTY_NUMBER:
                                        dissect_q931_number_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree,
                                            hf_q931_called_party_number);
                                        break;
 
-                               case Q931_IE_REDIRECTING_NUMBER:
+                               case CS0 | Q931_IE_REDIRECTING_NUMBER:
                                        dissect_q931_number_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree,
                                            hf_q931_redirecting_number);
                                        break;
 
-                               case Q931_IE_CALLING_PARTY_SUBADDR:
-                               case Q931_IE_CALLED_PARTY_SUBADDR:
+                               case CS0 | Q931_IE_CALLING_PARTY_SUBADDR:
+                               case CS0 | Q931_IE_CALLED_PARTY_SUBADDR:
                                        dissect_q931_party_subaddr_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_RESTART_INDICATOR:
+                               case CS0 | Q931_IE_RESTART_INDICATOR:
                                        dissect_q931_restart_indicator_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_HIGH_LAYER_COMPAT:
+                               case CS0 | Q931_IE_HIGH_LAYER_COMPAT:
                                        dissect_q931_high_layer_compat_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
                                        break;
 
-                               case Q931_IE_USER_USER:
+                               case CS0 | Q931_IE_USER_USER:
                                        dissect_q931_user_user_ie(tvb,
                                            offset + 2, info_element_len,
                                            ie_tree);
@@ -2759,10 +2837,6 @@ proto_register_q931(void)
                  { "Protocol discriminator", "q931.disc", FT_UINT8, BASE_HEX, NULL, 0x0,
                        "", HFILL }},
 
-               { &hf_q931_call_ref_len,
-                 { "Call reference value length", "q931.call_ref_len", FT_UINT8, BASE_DEC, NULL, 0x0,
-                       "", HFILL }},
-
                { &hf_q931_call_ref_flag,
                  { "Call reference flag", "q931.call_ref_flag", FT_BOOLEAN, BASE_NONE, TFS(&tfs_call_ref_flag), 0x0,
                        "", HFILL }},
@@ -2771,6 +2845,31 @@ proto_register_q931(void)
                  { "Call reference value", "q931.call_ref", FT_BYTES, BASE_HEX, NULL, 0x0,
                        "", HFILL }},
 
+
+               { &hf_q931_coding_standard,
+                 { "Coding standard", "q931.coding_standard", FT_UINT8, BASE_HEX,
+                        VALS(q931_coding_standard_vals), 0x60,"", HFILL }},
+
+               { &hf_q931_information_transfer_capability,
+                 { "Information transfer capability", "q931.information_transfer_capability", FT_UINT8, BASE_HEX,
+                        VALS(q931_information_transfer_capability_vals), 0x1f,"", HFILL }},
+
+               { &hf_q931_transfer_mode,
+                 { "Transfer mode", "q931.transfer_mode", FT_UINT8, BASE_HEX,
+                        VALS(q931_transfer_mode_vals), 0x60,"", HFILL }},
+
+               { &hf_q931_information_transfer_rate,
+                 { "Information transfer rate", "q931.information_transfer_rate", FT_UINT8, BASE_HEX,
+                        VALS(q931_information_transfer_rate_vals), 0x1f,"", HFILL }},
+
+               { &hf_q931_uil1,
+                 { "User information layer 1 protocol", "q931.uil1", FT_UINT8, BASE_HEX,
+                        VALS(q931_uil1_vals), 0x1f,"", HFILL }},
+
+               { &hf_q931_call_ref_len,
+                 { "Call reference value length", "q931.call_ref_len", FT_UINT8, BASE_DEC, NULL, 0x0,
+                       "", HFILL }},
+
                { &hf_q931_message_type,
                  { "Message type", "q931.message_type", FT_UINT8, BASE_HEX, VALS(q931_message_type_vals), 0x0,
                        "", HFILL }},
@@ -2779,6 +2878,19 @@ proto_register_q931(void)
                  { "Cause value", "q931.cause_value", FT_UINT8, BASE_DEC, VALS(q931_cause_code_vals), 0x0,
                        "", HFILL }},
 
+               { &hf_q931_number_type,
+                 { "Number type", "q931.number_type", FT_UINT8, BASE_HEX, VALS(q931_number_type_vals), 0x70,
+                       "", HFILL }},
+
+               { &hf_q931_numbering_plan,
+                 { "Numbering plan", "q931.numbering_plan", FT_UINT8, BASE_HEX, VALS(q931_numbering_plan_vals), 0x0f,
+                       "", HFILL }},
+
+               { &hf_q931_extension_ind,
+                 { "Extension indicator",  "q931.extension_ind",
+                       FT_BOOLEAN, 8, TFS(&q931_extension_ind_value), 0x80,
+                       "", HFILL }},
+
                { &hf_q931_calling_party_number,
                  { "Calling party number digits", "q931.calling_party_number.digits", FT_STRING, BASE_NONE, NULL, 0x0,
                        "", HFILL }},
@@ -2809,6 +2921,10 @@ proto_register_q931(void)
        q931_tpkt_pdu_handle = create_dissector_handle(dissect_q931_tpkt_pdu,
            proto_q931);
 
+       /* subdissector code */ 
+       codeset_dissector_table = register_dissector_table("q931.codeset", "Q.931 Codeset", FT_UINT8, BASE_HEX);
+       ie_dissector_table = register_dissector_table("q931.ie", "Q.931 IE", FT_UINT16, BASE_HEX);
+
        q931_module = prefs_register_protocol(proto_q931, NULL);
        prefs_register_bool_preference(q931_module, "desegment_h323_messages",
            "Desegment all Q.931 messages spanning multiple TCP segments",