Add some expert infos for unusual messages:
[obnox/wireshark/wip.git] / epan / dissectors / packet-sccp.c
index 3044447d1a3be164fed8199e91b502b467b1d525..e745fc2124456773cdc49758232e38aa489e17a5 100644 (file)
@@ -77,55 +77,55 @@ static Standard_Type decode_mtp3_standard;
 
 /* Same as below but with names typed out */
 static const value_string sccp_message_type_values[] = {
-  { SCCP_MSG_TYPE_CR,     "Connection Request" },
-  { SCCP_MSG_TYPE_CC,     "Connection Confirm" },
-  { SCCP_MSG_TYPE_CREF,   "Connection Refused" },
-  { SCCP_MSG_TYPE_RLSD,   "Released" },
-  { SCCP_MSG_TYPE_RLC,    "Release Complete" },
-  { SCCP_MSG_TYPE_DT1,    "Data Form 1" },
-  { SCCP_MSG_TYPE_DT2,    "Data Form 2" },
-  { SCCP_MSG_TYPE_AK,     "Data Acknowledgement" },
-  { SCCP_MSG_TYPE_UDT,    "Unitdata" },
-  { SCCP_MSG_TYPE_UDTS,   "Unitdata Service" },
-  { SCCP_MSG_TYPE_ED,     "Expedited Data" },
-  { SCCP_MSG_TYPE_EA,     "Expedited Data Acknowledgement" },
-  { SCCP_MSG_TYPE_RSR,    "Reset Request" },
-  { SCCP_MSG_TYPE_RSC,    "Reset Confirmation" },
-  { SCCP_MSG_TYPE_ERR,    "Error" },
-  { SCCP_MSG_TYPE_IT,     "Inactivity Timer" },
-  { SCCP_MSG_TYPE_XUDT,   "Extended Unitdata" },
-  { SCCP_MSG_TYPE_XUDTS,  "Extended Unitdata Service" },
-  { SCCP_MSG_TYPE_LUDT,   "Long Unitdata" },
-  { SCCP_MSG_TYPE_LUDTS,  "Long Unitdata Service" },
-  { 0,                  NULL } };
+  { SCCP_MSG_TYPE_CR,          "Connection Request" },
+  { SCCP_MSG_TYPE_CC,          "Connection Confirm" },
+  { SCCP_MSG_TYPE_CREF,                "Connection Refused" },
+  { SCCP_MSG_TYPE_RLSD,                "Released" },
+  { SCCP_MSG_TYPE_RLC,         "Release Complete" },
+  { SCCP_MSG_TYPE_DT1,         "Data Form 1" },
+  { SCCP_MSG_TYPE_DT2,         "Data Form 2" },
+  { SCCP_MSG_TYPE_AK,          "Data Acknowledgement" },
+  { SCCP_MSG_TYPE_UDT,         "Unitdata" },
+  { SCCP_MSG_TYPE_UDTS,                "Unitdata Service" },
+  { SCCP_MSG_TYPE_ED,          "Expedited Data" },
+  { SCCP_MSG_TYPE_EA,          "Expedited Data Acknowledgement" },
+  { SCCP_MSG_TYPE_RSR,         "Reset Request" },
+  { SCCP_MSG_TYPE_RSC,         "Reset Confirmation" },
+  { SCCP_MSG_TYPE_ERR,         "Error" },
+  { SCCP_MSG_TYPE_IT,          "Inactivity Timer" },
+  { SCCP_MSG_TYPE_XUDT,                "Extended Unitdata" },
+  { SCCP_MSG_TYPE_XUDTS,       "Extended Unitdata Service" },
+  { SCCP_MSG_TYPE_LUDT,                "Long Unitdata" },
+  { SCCP_MSG_TYPE_LUDTS,       "Long Unitdata Service" },
+  { 0,                         NULL } };
 
 /* Same as above but in acronym form (for the Info column) */
 const value_string sccp_message_type_acro_values[] = {
-  { SCCP_MSG_TYPE_CR,     "CR" },
-  { SCCP_MSG_TYPE_CC,     "CC" },
-  { SCCP_MSG_TYPE_CREF,   "CREF" },
-  { SCCP_MSG_TYPE_RLSD,   "RLSD" },
-  { SCCP_MSG_TYPE_RLC,    "RLC" },
-  { SCCP_MSG_TYPE_DT1,    "DT1" },
-  { SCCP_MSG_TYPE_DT2,    "DT2" },
-  { SCCP_MSG_TYPE_AK,     "AK" },
-  { SCCP_MSG_TYPE_UDT,    "UDT" },
-  { SCCP_MSG_TYPE_UDTS,   "UDTS" },
-  { SCCP_MSG_TYPE_ED,     "ED" },
-  { SCCP_MSG_TYPE_EA,     "EA" },
-  { SCCP_MSG_TYPE_RSR,    "RSR" },
-  { SCCP_MSG_TYPE_RSC,    "RSC" },
-  { SCCP_MSG_TYPE_ERR,    "ERR" },
-  { SCCP_MSG_TYPE_IT,     "IT" },
-  { SCCP_MSG_TYPE_XUDT,   "XUDT" },
-  { SCCP_MSG_TYPE_XUDTS,  "XUDTS" },
-  { SCCP_MSG_TYPE_LUDT,   "LUDT" },
-  { SCCP_MSG_TYPE_LUDTS,  "LUDTS" },
-  { 0,                  NULL } };
-
-#define PARAMETER_LENGTH_LENGTH 1
-#define PARAMETER_LONG_DATA_LENGTH_LENGTH 2
-#define PARAMETER_TYPE_LENGTH 1
+  { SCCP_MSG_TYPE_CR,          "CR" },
+  { SCCP_MSG_TYPE_CC,          "CC" },
+  { SCCP_MSG_TYPE_CREF,                "CREF" },
+  { SCCP_MSG_TYPE_RLSD,                "RLSD" },
+  { SCCP_MSG_TYPE_RLC,         "RLC" },
+  { SCCP_MSG_TYPE_DT1,         "DT1" },
+  { SCCP_MSG_TYPE_DT2,         "DT2" },
+  { SCCP_MSG_TYPE_AK,          "AK" },
+  { SCCP_MSG_TYPE_UDT,         "UDT" },
+  { SCCP_MSG_TYPE_UDTS,                "UDTS" },
+  { SCCP_MSG_TYPE_ED,          "ED" },
+  { SCCP_MSG_TYPE_EA,          "EA" },
+  { SCCP_MSG_TYPE_RSR,         "RSR" },
+  { SCCP_MSG_TYPE_RSC,         "RSC" },
+  { SCCP_MSG_TYPE_ERR,         "ERR" },
+  { SCCP_MSG_TYPE_IT,          "IT" },
+  { SCCP_MSG_TYPE_XUDT,                "XUDT" },
+  { SCCP_MSG_TYPE_XUDTS,       "XUDTS" },
+  { SCCP_MSG_TYPE_LUDT,                "LUDT" },
+  { SCCP_MSG_TYPE_LUDTS,       "LUDTS" },
+  { 0,                         NULL } };
+
+#define PARAMETER_LENGTH_LENGTH                        1
+#define PARAMETER_LONG_DATA_LENGTH_LENGTH      2
+#define PARAMETER_TYPE_LENGTH                  1
 
 #define PARAMETER_END_OF_OPTIONAL_PARAMETERS   0x00
 #define PARAMETER_DESTINATION_LOCAL_REFERENCE  0x01
@@ -179,26 +179,26 @@ static const value_string sccp_parameter_values[] = {
 #define END_OF_OPTIONAL_PARAMETERS_LENGTH      1
 #define DESTINATION_LOCAL_REFERENCE_LENGTH     3
 #define SOURCE_LOCAL_REFERENCE_LENGTH          3
-#define PROTOCOL_CLASS_LENGTH                          1
+#define PROTOCOL_CLASS_LENGTH                  1
 #define RECEIVE_SEQUENCE_NUMBER_LENGTH         1
-#define CREDIT_LENGTH                                          1
-#define RELEASE_CAUSE_LENGTH                           1
-#define RETURN_CAUSE_LENGTH                                    1
-#define RESET_CAUSE_LENGTH                                     1
-#define ERROR_CAUSE_LENGTH                                     1
-#define REFUSAL_CAUSE_LENGTH                           1
-#define HOP_COUNTER_LENGTH                                     1
-#define IMPORTANCE_LENGTH                                      1
+#define CREDIT_LENGTH                          1
+#define RELEASE_CAUSE_LENGTH                   1
+#define RETURN_CAUSE_LENGTH                    1
+#define RESET_CAUSE_LENGTH                     1
+#define ERROR_CAUSE_LENGTH                     1
+#define REFUSAL_CAUSE_LENGTH                   1
+#define HOP_COUNTER_LENGTH                     1
+#define IMPORTANCE_LENGTH                      1
 
 
 /* Parts of the Called and Calling Address parameters */
 /* Address Indicator */
 #define ADDRESS_INDICATOR_LENGTH       1
-#define ITU_RESERVED_MASK                      0x80
-#define ANSI_NATIONAL_MASK                     0x80
+#define ITU_RESERVED_MASK              0x80
+#define ANSI_NATIONAL_MASK             0x80
 #define ROUTING_INDICATOR_MASK         0x40
-#define GTI_MASK                                       0x3C
-#define GTI_SHIFT                                      2
+#define GTI_MASK                       0x3C
+#define GTI_SHIFT                      2
 #define ITU_SSN_INDICATOR_MASK         0x02
 #define ITU_PC_INDICATOR_MASK          0x01
 #define ANSI_PC_INDICATOR_MASK         0x02
@@ -209,8 +209,9 @@ static const value_string sccp_national_indicator_values[] = {
   { 0x1,  "Address coded to National standard" },
   { 0,    NULL } };
 
-#define ROUTE_ON_GT  0x0
-#define ROUTE_ON_SSN 0x1
+#define ROUTE_ON_GT            0x0
+#define ROUTE_ON_SSN           0x1
+#define ROUTING_INDICATOR_SHIFT        6
 static const value_string sccp_routing_indicator_values[] = {
   { ROUTE_ON_GT,  "Route on GT" },
   { ROUTE_ON_SSN, "Route on SSN" },
@@ -218,13 +219,13 @@ static const value_string sccp_routing_indicator_values[] = {
 
 #define AI_GTI_NO_GT                   0x0
 #define ITU_AI_GTI_NAI                 0x1
-#define AI_GTI_TT                              0x2
+#define AI_GTI_TT                      0x2
 #define ITU_AI_GTI_TT_NP_ES            0x3
 #define ITU_AI_GTI_TT_NP_ES_NAI        0x4
 static const value_string sccp_itu_global_title_indicator_values[] = {
-  { AI_GTI_NO_GT,                      "No Global Title" },
-  { ITU_AI_GTI_NAI,                    "Nature of Address Indicator only" },
-  { AI_GTI_TT,                         "Translation Type only" },
+  { AI_GTI_NO_GT,              "No Global Title" },
+  { ITU_AI_GTI_NAI,            "Nature of Address Indicator only" },
+  { AI_GTI_TT,                 "Translation Type only" },
   { ITU_AI_GTI_TT_NP_ES,       "Translation Type, Numbering Plan, and Encoding Scheme included" },
   { ITU_AI_GTI_TT_NP_ES_NAI,   "Translation Type, Numbering Plan, Encoding Scheme, and Nature of Address Indicator included" },
   { 0,                         NULL } };
@@ -233,9 +234,9 @@ static const value_string sccp_itu_global_title_indicator_values[] = {
 #define ANSI_AI_GTI_TT_NP_ES   0x1
 /* #define AI_GTI_TT           0x2 */
 static const value_string sccp_ansi_global_title_indicator_values[] = {
-  { AI_GTI_NO_GT,                      "No Global Title" },
+  { AI_GTI_NO_GT,              "No Global Title" },
   { ANSI_AI_GTI_TT_NP_ES,      "Translation Type, Numbering Plan, and Encoding Scheme included" },
-  { AI_GTI_TT,                         "Translation Type only" },
+  { AI_GTI_TT,                 "Translation Type only" },
   { 0,                         NULL } };
 
 static const value_string sccp_ai_pci_values[] = {
@@ -248,8 +249,8 @@ static const value_string sccp_ai_ssni_values[] = {
   { 0x0,  "SSN not present" },
   { 0,    NULL } };
 
-#define ADDRESS_SSN_LENGTH    1
-#define INVALID_SSN 0xff
+#define ADDRESS_SSN_LENGTH     1
+#define INVALID_SSN            0xff
   /* Some values from 3GPP TS 23.003 */
   /*  Japan TTC and NTT define a lot of SSNs, some of which conflict with
    *  these.  They are not added for now.
@@ -347,30 +348,30 @@ const value_string sccp_address_signal_values[] = {
 /* * * * * * * * * * * * * * * * * * * * * * * * * *
  * Global Title: ITU GTI == 0011, ANSI GTI == 0001 *
  * * * * * * * * * * * * * * * * * * * * * * * * * */
-#define GT_NP_MASK 0xf0
-#define GT_NP_SHIFT 4
-#define GT_NP_ES_LENGTH 1
+#define GT_NP_MASK             0xf0
+#define GT_NP_SHIFT            4
+#define GT_NP_ES_LENGTH                1
 #define GT_NP_UNKNOWN          0x00
 #define GT_NP_ISDN             0x01
 #define GT_NP_GENERIC_RESERVED 0x02
-#define GT_NP_DATA                             0x03
-#define GT_NP_TELEX                            0x04
+#define GT_NP_DATA             0x03
+#define GT_NP_TELEX            0x04
 #define GT_NP_MARITIME_MOBILE  0x05
-#define GT_NP_LAND_MOBILE              0x06
-#define GT_NP_ISDN_MOBILE              0x07
+#define GT_NP_LAND_MOBILE      0x06
+#define GT_NP_ISDN_MOBILE      0x07
 #define GT_NP_PRIVATE_NETWORK  0x0e
-#define GT_NP_RESERVED                 0x0f
+#define GT_NP_RESERVED         0x0f
 static const value_string sccp_np_values[] = {
-  { GT_NP_UNKNOWN,                     "Unknown" },
-  { GT_NP_ISDN,                                "ISDN/telephony" },
+  { GT_NP_UNKNOWN,             "Unknown" },
+  { GT_NP_ISDN,                        "ISDN/telephony" },
   { GT_NP_GENERIC_RESERVED,    "Generic (ITU)/Reserved (ANSI)" },
-  { GT_NP_DATA,                                "Data" },
-  { GT_NP_TELEX,                       "Telex" },
+  { GT_NP_DATA,                        "Data" },
+  { GT_NP_TELEX,               "Telex" },
   { GT_NP_MARITIME_MOBILE,     "Maritime mobile" },
   { GT_NP_LAND_MOBILE,         "Land mobile" },
   { GT_NP_ISDN_MOBILE,         "ISDN/mobile" },
   { GT_NP_PRIVATE_NETWORK,     "Private network or network-specific" },
-  { GT_NP_RESERVED,                    "Reserved" },
+  { GT_NP_RESERVED,            "Reserved" },
   { 0,                         NULL } };
 
 #define GT_ES_MASK     0x0f
@@ -399,8 +400,9 @@ static const value_string sccp_es_values[] = {
 /* Address signals above */
 
 
-#define CLASS_CLASS_MASK 0xf
-#define CLASS_SPARE_HANDLING_MASK 0xf0
+#define CLASS_CLASS_MASK               0xf
+#define CLASS_SPARE_HANDLING_MASK      0xf0
+#define CLASS_SPARE_HANDLING_SHIFT     4
 static const value_string sccp_class_handling_values [] = {
   { 0x0,  "No special options" },
   { 0x8,  "Return message on error" },
@@ -418,13 +420,13 @@ static const value_string sccp_segmenting_reassembling_values [] = {
   { 0,                 NULL } };
 
 
-#define RECEIVE_SEQUENCE_NUMBER_LENGTH 1
-#define RSN_MASK                       0xfe
+#define RECEIVE_SEQUENCE_NUMBER_LENGTH         1
+#define RSN_MASK                               0xfe
 
 #define SEQUENCING_SEGMENTING_LENGTH           2
 #define SEQUENCING_SEGMENTING_SSN_LENGTH       1
 #define SEQUENCING_SEGMENTING_RSN_LENGTH       1
-#define SEND_SEQUENCE_NUMBER_MASK                      0xfe
+#define SEND_SEQUENCE_NUMBER_MASK              0xfe
 #define RECEIVE_SEQUENCE_NUMBER_MASK           0xfe
 #define SEQUENCING_SEGMENTING_MORE_MASK                0x01
 
@@ -811,9 +813,9 @@ static gboolean trace_sccp = FALSE;
 static guint32 next_assoc_id = 0;
 
 static const value_string assoc_protos[] = {
-       {  SCCP_PLOAD_BSSAP, "BSSAP" },
-       {  SCCP_PLOAD_RANAP, "RANAP" },
-       { 0 , NULL }
+       { SCCP_PLOAD_BSSAP,     "BSSAP" },
+       { SCCP_PLOAD_RANAP,     "RANAP" },
+       { 0,                    NULL }
 };
 
 static sccp_assoc_info_t *
@@ -1082,8 +1084,7 @@ dissect_sccp_gt_address_information(tvbuff_t *tvb, packet_info *pinfo,
 
   gt_digits = ep_alloc0(GT_MAX_SIGNALS+1);
 
-  while(offset < length)
-  {
+  while(offset < length) {
     odd_signal = tvb_get_guint8(tvb, offset) & GT_ODD_SIGNAL_MASK;
     even_signal = tvb_get_guint8(tvb, offset) & GT_EVEN_SIGNAL_MASK;
     even_signal >>= GT_EVEN_SIGNAL_SHIFT;
@@ -1320,6 +1321,8 @@ dissect_sccp_called_calling_param(tvbuff_t *tvb, proto_tree *tree, packet_info *
   proto_tree_add_uint(call_ai_tree, called ? hf_sccp_called_routing_indicator
                                           : hf_sccp_calling_routing_indicator,
                      tvb, 0, ADDRESS_INDICATOR_LENGTH, routing_ind);
+  /* Only shift off the other bits after adding the item */
+  routing_ind >>= ROUTING_INDICATOR_SHIFT;
 
   gti = tvb_get_guint8(tvb, 0) & GTI_MASK;
 
@@ -1333,9 +1336,13 @@ dissect_sccp_called_calling_param(tvbuff_t *tvb, proto_tree *tree, packet_info *
                        tvb, 0, ADDRESS_INDICATOR_LENGTH, gti);
 
     ssni = tvb_get_guint8(tvb, 0) & ITU_SSN_INDICATOR_MASK;
-    proto_tree_add_uint(call_ai_tree, called ? hf_sccp_called_itu_ssn_indicator
-                                            : hf_sccp_calling_itu_ssn_indicator,
-                       tvb, 0, ADDRESS_INDICATOR_LENGTH, ssni);
+    expert_item = proto_tree_add_uint(call_ai_tree, called ? hf_sccp_called_itu_ssn_indicator
+                                                          : hf_sccp_calling_itu_ssn_indicator,
+                                     tvb, 0, ADDRESS_INDICATOR_LENGTH, ssni);
+    if (routing_ind == ROUTE_ON_SSN && ssni == 0) {
+      expert_add_info_format(pinfo, expert_item, PI_PROTOCOL, PI_WARN,
+                            "Message is routed on SSN, but SSN is not present");
+    }
 
     pci = tvb_get_guint8(tvb, 0) & ITU_PC_INDICATOR_MASK;
     proto_tree_add_uint(call_ai_tree, called ? hf_sccp_called_itu_point_code_indicator
@@ -1389,13 +1396,18 @@ dissect_sccp_called_calling_param(tvbuff_t *tvb, proto_tree *tree, packet_info *
     if (ssni) {
       ssn = tvb_get_guint8(tvb, offset);
 
+      if (routing_ind == ROUTE_ON_SSN && ssn == 0) {
+       expert_add_info_format(pinfo, expert_item, PI_PROTOCOL, PI_WARN,
+                              "Message is routed on SSN, but SSN is zero (unspecified)");
+      }
+
       if (called && assoc)
        assoc->called_ssn = ssn;
       else if (assoc)
        assoc->calling_ssn = ssn;
 
       if (is_connectionless(message_type) && sccp_msg) {
-       guintssn_ptr = called ? &(sccp_msg->data.ud.called_ssn) : &(sccp_msg->data.ud.calling_ssn);
+       guint *ssn_ptr = called ? &(sccp_msg->data.ud.called_ssn) : &(sccp_msg->data.ud.calling_ssn);
 
        *ssn_ptr  = ssn;
       }
@@ -1456,9 +1468,13 @@ dissect_sccp_called_calling_param(tvbuff_t *tvb, proto_tree *tree, packet_info *
                        tvb, 0, ADDRESS_INDICATOR_LENGTH, pci);
 
     ssni = tvb_get_guint8(tvb, 0) & ANSI_SSN_INDICATOR_MASK;
-    proto_tree_add_uint(call_ai_tree, called ? hf_sccp_called_ansi_ssn_indicator
-                                            : hf_sccp_calling_ansi_ssn_indicator,
-                       tvb, 0, ADDRESS_INDICATOR_LENGTH, ssni);
+    expert_item = proto_tree_add_uint(call_ai_tree, called ? hf_sccp_called_ansi_ssn_indicator
+                                                          : hf_sccp_calling_ansi_ssn_indicator,
+                                     tvb, 0, ADDRESS_INDICATOR_LENGTH, ssni);
+    if (routing_ind == ROUTE_ON_SSN && ssni == 0) {
+      expert_add_info_format(pinfo, expert_item, PI_PROTOCOL, PI_WARN,
+                            "Message is routed on SSN, but SSN is not present");
+    }
 
     offset = ADDRESS_INDICATOR_LENGTH;
 
@@ -1466,14 +1482,19 @@ dissect_sccp_called_calling_param(tvbuff_t *tvb, proto_tree *tree, packet_info *
     if (ssni) {
       ssn = tvb_get_guint8(tvb, offset);
 
-      if (called && assoc){
+      if (routing_ind == ROUTE_ON_SSN && ssn == 0) {
+       expert_add_info_format(pinfo, expert_item, PI_PROTOCOL, PI_WARN,
+                              "Message is routed on SSN, but SSN is zero (unspecified)");
+      }
+
+      if (called && assoc) {
        assoc->called_ssn = ssn;
-      }else if (assoc){
+      } else if (assoc) {
        assoc->calling_ssn = ssn;
       }
 
       if (is_connectionless(message_type) && sccp_msg) {
-       guintssn_ptr = called ? &(sccp_msg->data.ud.called_ssn) : &(sccp_msg->data.ud.calling_ssn);
+       guint *ssn_ptr = called ? &(sccp_msg->data.ud.called_ssn) : &(sccp_msg->data.ud.calling_ssn);
 
        *ssn_ptr  = ssn;
       }
@@ -1523,21 +1544,67 @@ static void
 dissect_sccp_class_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
 {
   guint8 class;
+  proto_item *pi;
+  gboolean invalid_class = FALSE;
 
   if (length != 1) {
-    proto_item *expert_item;
-    expert_item = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 1, got %u", length);
-    expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated. Expected 1, got %u", length);
-    PROTO_ITEM_SET_GENERATED(expert_item);
+    pi = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 1, got %u", length);
+    expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_ERROR, "Wrong length indicated. Expected 1, got %u", length);
+    PROTO_ITEM_SET_GENERATED(pi);
     return;
   }
 
   class = tvb_get_guint8(tvb, 0) & CLASS_CLASS_MASK;
+  pi = proto_tree_add_uint(tree, hf_sccp_class, tvb, 0, length, class);
 
-  proto_tree_add_uint(tree, hf_sccp_class, tvb, 0, length, class);
+  switch(message_type) {
+  case SCCP_MSG_TYPE_DT1:
+    if (class != 2)
+      invalid_class = TRUE;
+    break;
+  case SCCP_MSG_TYPE_DT2:
+  case SCCP_MSG_TYPE_AK:
+  case SCCP_MSG_TYPE_ED:
+  case SCCP_MSG_TYPE_EA:
+  case SCCP_MSG_TYPE_RSR:
+  case SCCP_MSG_TYPE_RSC:
+    if (class != 3)
+      invalid_class = TRUE;
+    break;
+  case SCCP_MSG_TYPE_CR:
+  case SCCP_MSG_TYPE_CC:
+  case SCCP_MSG_TYPE_CREF:
+  case SCCP_MSG_TYPE_RLSD:
+  case SCCP_MSG_TYPE_RLC:
+  case SCCP_MSG_TYPE_ERR:
+  case SCCP_MSG_TYPE_IT:
+    if (class != 2 && class != 3)
+      invalid_class = TRUE;
+    break;
+  case SCCP_MSG_TYPE_UDT:
+  case SCCP_MSG_TYPE_UDTS:
+  case SCCP_MSG_TYPE_XUDT:
+  case SCCP_MSG_TYPE_XUDTS:
+  case SCCP_MSG_TYPE_LUDT:
+  case SCCP_MSG_TYPE_LUDTS:
+    if (class != 0 && class != 1)
+      invalid_class = TRUE;
+    break;
+  }
+
+  if (invalid_class)
+    expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_ERROR, "Unexpected message class for this message type");
 
-  if (class == 0 || class == 1)
-    proto_tree_add_item(tree, hf_sccp_handling, tvb, 0, length, ENC_NA);
+  if (class == 0 || class == 1) {
+    guint8 handling = tvb_get_guint8(tvb, 0) & CLASS_SPARE_HANDLING_MASK;
+
+    pi = proto_tree_add_item(tree, hf_sccp_handling, tvb, 0, length, ENC_NA);
+    handling >>= CLASS_SPARE_HANDLING_SHIFT;
+
+    if (match_strval(handling, sccp_class_handling_values) == NULL) {
+      expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_ERROR, "Invalid message handling");
+    }
+  }
 }
 
 static void
@@ -2085,7 +2152,6 @@ dissect_sccp_variable_parameter(tvbuff_t *tvb, packet_info *pinfo,
     PROTO_ITEM_SET_HIDDEN(pi);
   }
 
-
   offset += length_length;
 
   dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree, parameter_type, offset,
@@ -2119,7 +2185,9 @@ dissect_sccp_optional_parameters(tvbuff_t *tvb, packet_info *pinfo,
 
 }
 
-static sccp_msg_info_t* new_ud_msg(packet_info* pinfo, guint32 msg_type _U_) {
+static sccp_msg_info_t *
+new_ud_msg(packet_info* pinfo, guint32 msg_type _U_)
+{
        sccp_msg_info_t* m = ep_alloc0(sizeof(sccp_msg_info_t));
        m->framenum = pinfo->fd->num;
        m->data.ud.calling_gt = NULL;
@@ -2781,8 +2849,7 @@ dissect_sccp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   const mtp3_addr_pc_t *mtp3_addr_p;
 
   if ((pinfo->src.type == AT_SS7PC) &&
-    ((mtp3_addr_p = (const mtp3_addr_pc_t *)pinfo->src.data)->type <= CHINESE_ITU_STANDARD))
-  {
+      ((mtp3_addr_p = (const mtp3_addr_pc_t *)pinfo->src.data)->type <= CHINESE_ITU_STANDARD)) {
     /*
      *  Allow a protocol beneath to specify how the SCCP layer should be
      *  dissected.
@@ -2793,9 +2860,7 @@ dissect_sccp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
      *  and at the same time ITU MTP3/ITU SCCP/ANSI TCAP/ANSI MAP.
      */
     decode_mtp3_standard = mtp3_addr_p->type;
-  }
-  else
-  {
+  } else {
     decode_mtp3_standard = mtp3_standard;
   }
 
@@ -2826,8 +2891,7 @@ dissect_sccp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
   /* Set whether message is UPLINK, DOWNLINK, or of UNKNOWN direction */
 
-  if (pinfo->src.type == AT_SS7PC)
-  {
+  if (pinfo->src.type == AT_SS7PC) {
     /*
      * XXX - we assume that the "data" pointers of the source and destination
      * addresses are set to point to "mtp3_addr_pc_t" structures, so that
@@ -2835,21 +2899,16 @@ dissect_sccp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
      */
     mtp3_addr_p = (const mtp3_addr_pc_t *)pinfo->src.data;
 
-    if (sccp_source_pc_global == mtp3_addr_p->pc)
-    {
+    if (sccp_source_pc_global == mtp3_addr_p->pc) {
        pinfo->p2p_dir = P2P_DIR_SENT;
-    }
-    else
-    {
+    } else {
       /* assuming if src was SS7 PC then dst will be too */
       mtp3_addr_p = (const mtp3_addr_pc_t *)pinfo->dst.data;
 
       if (sccp_source_pc_global == mtp3_addr_p->pc)
       {
         pinfo->p2p_dir = P2P_DIR_RECV;
-      }
-      else
-      {
+      } else {
         pinfo->p2p_dir = P2P_DIR_UNKNOWN;
       }
     }
@@ -2867,17 +2926,19 @@ static struct _sccp_ul {
        gboolean uses_tcap;
        dissector_handle_t* handlep;
        } user_list[] = {
-       {SCCP_USER_DATA,FALSE,&data_handle},
-       {SCCP_USER_TCAP,FALSE,&tcap_handle},
-       {SCCP_USER_RANAP,FALSE,&ranap_handle},
-       {SCCP_USER_BSSAP,FALSE,&bssap_handle},
-       {SCCP_USER_GSMMAP,TRUE,&gsmmap_handle},
-       {SCCP_USER_CAMEL,TRUE,&camel_handle},
-       {SCCP_USER_INAP,TRUE,&inap_handle},
-       {0,FALSE,NULL}
+       {SCCP_USER_DATA, FALSE, &data_handle},
+       {SCCP_USER_TCAP, FALSE, &tcap_handle},
+       {SCCP_USER_RANAP, FALSE, &ranap_handle},
+       {SCCP_USER_BSSAP, FALSE, &bssap_handle},
+       {SCCP_USER_GSMMAP, TRUE, &gsmmap_handle},
+       {SCCP_USER_CAMEL, TRUE, &camel_handle},
+       {SCCP_USER_INAP, TRUE, &inap_handle},
+       {0, FALSE, NULL}
 };
 
-static void sccp_users_update_cb(void* r, const char** err _U_) {
+static void
+sccp_users_update_cb(void* r, const char** err _U_)
+{
        sccp_user_t* u = r;
        struct _sccp_ul* c;
 
@@ -2893,7 +2954,9 @@ static void sccp_users_update_cb(void* r, const char** err _U_) {
        u->handlep = &data_handle;
 }
 
-static void* sccp_users_copy_cb(void* n, const void* o, size_t siz _U_) {
+static void *
+sccp_users_copy_cb(void* n, const void* o, size_t siz _U_)
+{
        const sccp_user_t* u = o;
        sccp_user_t* un = n;
 
@@ -2907,7 +2970,9 @@ static void* sccp_users_copy_cb(void* n, const void* o, size_t siz _U_) {
        return n;
 }
 
-static void sccp_users_free_cb(void*r) {
+static void
+sccp_users_free_cb(void*r)
+{
        sccp_user_t* u = r;
        if (u->called_pc) g_free(u->called_pc);
        if (u->called_ssn) g_free(u->called_ssn);
@@ -2922,7 +2987,9 @@ UAT_VS_DEF(sccp_users, user, sccp_user_t, SCCP_USER_DATA, "Data")
 /** End SccpUsersTable **/
 
 
-static void init_sccp(void) {
+static void
+init_sccp(void)
+{
     next_assoc_id = 1;
     fragment_table_init (&sccp_xudt_msg_fragment_table);
     reassembled_table_init(&sccp_xudt_msg_reassembled_table);
@@ -3368,19 +3435,11 @@ proto_register_sccp(void)
   };
 
 
-  uat_t* users_uat = uat_new("SCCP Users Table",
-       sizeof(sccp_user_t),
-       "sccp_users",
-       TRUE,
-       (void*) &sccp_users,
-       &num_sccp_users,
-       UAT_CAT_PORTS,
-       "ChSccpUsers",
-       sccp_users_copy_cb,
-       sccp_users_update_cb,
-       sccp_users_free_cb,
-       NULL,
-       users_flds );
+  uat_t* users_uat = uat_new("SCCP Users Table", sizeof(sccp_user_t),
+                            "sccp_users", TRUE, (void*) &sccp_users,
+                            &num_sccp_users, UAT_CAT_PORTS, "ChSccpUsers",
+                            sccp_users_copy_cb, sccp_users_update_cb,
+                            sccp_users_free_cb, NULL, users_flds );
 
  /* Register the protocol name and description */
   proto_sccp = proto_register_protocol("Signalling Connection Control Part",
@@ -3434,8 +3493,8 @@ proto_register_sccp(void)
                                 "  This may affect TCAP's ability to recognize which messages belong to which TCAP session.", &set_addresses);
 
   prefs_register_string_preference(sccp_module, "default_payload", "Default Payload",
-                 "The protocol which should be used to dissect the payload if nothing else has claimed it",
-                 &default_payload);
+                                  "The protocol which should be used to dissect the payload if nothing else has claimed it",
+                                  &default_payload);
 
   register_init_routine(&init_sccp);