wireshark/epan/dissectors/packet-sccp.c
Evan Huus 95f484a91e Add a very small hack to make the UAT update callback error string freeable, and
convert all existing UAT update callbacks to use glib memory instead of
ephemeral memory for that string.

UAT code paths are entirely distinct from packet dissection, so using ephemeral
memory was the wrong choice, because there was no guarantees about when it would
be freed.

The move away from emem still needs to be propogated deeper into the UAT code
itself at some point.

Net effect: remove another bunch of emem calls from dissectors, where replacing
with wmem would have caused assertions.

svn path=/trunk/; revision=52854
2013-10-25 22:14:25 +00:00

4108 lines
145 KiB
C

/* packet-sccp.c
* Routines for Signalling Connection Control Part (SCCP) dissection
*
* It is hopefully compliant to:
* ANSI T1.112.3-2001
* ITU-T Q.713 7/1996
* YDN 038-1997 (Chinese ITU variant)
* JT-Q713 and NTT-Q713 (Japan)
*
* Note that Japan-specific GTT is incomplete; in particular, the specific
* TTs that are defined in TTC and NTT are not decoded in detail.
*
* Copyright 2002, Jeff Morriss <jeff.morriss.ws [AT] gmail.com>
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* Copied from packet-m2pa.c
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <string.h>
#include <glib.h>
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/emem.h>
#include <epan/wmem/wmem.h>
#include <epan/reassemble.h>
#include <epan/asn1.h>
#include <epan/uat.h>
#include <epan/strutil.h>
#include <epan/expert.h>
#include <epan/tap.h>
#include <address.h>
#include "packet-mtp3.h"
#include "packet-tcap.h"
#include "packet-sccp.h"
#include "packet-e164.h"
#include "packet-e212.h"
#include "packet-frame.h"
/* function prototypes */
void proto_reg_handoff_sccp(void);
static Standard_Type decode_mtp3_standard;
#define SCCP_MSG_TYPE_OFFSET 0
#define SCCP_MSG_TYPE_LENGTH 1
#define POINTER_LENGTH 1
#define POINTER_LENGTH_LONG 2
#define INVALID_LR 0xffffff /* a reserved value */
/* 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 } };
/* 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
#define PARAMETER_END_OF_OPTIONAL_PARAMETERS 0x00
#define PARAMETER_DESTINATION_LOCAL_REFERENCE 0x01
#define PARAMETER_SOURCE_LOCAL_REFERENCE 0x02
#define PARAMETER_CALLED_PARTY_ADDRESS 0x03
#define PARAMETER_CALLING_PARTY_ADDRESS 0x04
#define PARAMETER_CLASS 0x05
#define PARAMETER_SEGMENTING_REASSEMBLING 0x06
#define PARAMETER_RECEIVE_SEQUENCE_NUMBER 0x07
#define PARAMETER_SEQUENCING_SEGMENTING 0x08
#define PARAMETER_CREDIT 0x09
#define PARAMETER_RELEASE_CAUSE 0x0a
#define PARAMETER_RETURN_CAUSE 0x0b
#define PARAMETER_RESET_CAUSE 0x0c
#define PARAMETER_ERROR_CAUSE 0x0d
#define PARAMETER_REFUSAL_CAUSE 0x0e
#define PARAMETER_DATA 0x0f
#define PARAMETER_SEGMENTATION 0x10
#define PARAMETER_HOP_COUNTER 0x11
/* Importance is ITU only */
#define PARAMETER_IMPORTANCE 0x12
#define PARAMETER_LONG_DATA 0x13
/* ISNI is ANSI only */
#define PARAMETER_ISNI 0xfa
static const value_string sccp_parameter_values[] = {
{ PARAMETER_END_OF_OPTIONAL_PARAMETERS, "End of Optional Parameters" },
{ PARAMETER_DESTINATION_LOCAL_REFERENCE, "Destination Local Reference" },
{ PARAMETER_SOURCE_LOCAL_REFERENCE, "Source Local Reference" },
{ PARAMETER_CALLED_PARTY_ADDRESS, "Called Party Address" },
{ PARAMETER_CALLING_PARTY_ADDRESS, "Calling Party Address" },
{ PARAMETER_CLASS, "Protocol Class" },
{ PARAMETER_SEGMENTING_REASSEMBLING, "Segmenting/Reassembling" },
{ PARAMETER_RECEIVE_SEQUENCE_NUMBER, "Receive Sequence Number" },
{ PARAMETER_SEQUENCING_SEGMENTING, "Sequencing/Segmenting" },
{ PARAMETER_CREDIT, "Credit" },
{ PARAMETER_RELEASE_CAUSE, "Release Cause" },
{ PARAMETER_RETURN_CAUSE, "Return Cause" },
{ PARAMETER_RESET_CAUSE, "Reset Cause" },
{ PARAMETER_ERROR_CAUSE, "Error Cause" },
{ PARAMETER_REFUSAL_CAUSE, "Refusal Cause" },
{ PARAMETER_DATA, "Data" },
{ PARAMETER_SEGMENTATION, "Segmentation" },
{ PARAMETER_HOP_COUNTER, "Hop Counter" },
{ PARAMETER_IMPORTANCE, "Importance (ITU)" },
{ PARAMETER_LONG_DATA, "Long Data" },
{ PARAMETER_ISNI, "Intermediate Signaling Network Identification (ANSI)" },
{ 0, NULL } };
#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 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
/* 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 ROUTING_INDICATOR_MASK 0x40
#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
#define ANSI_SSN_INDICATOR_MASK 0x01
static const value_string sccp_ansi_national_indicator_values[] = {
{ 0x0, "Address coded to International standard" },
{ 0x1, "Address coded to National standard" },
{ 0, NULL } };
#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" },
{ 0, NULL } };
#define AI_GTI_NO_GT 0x0
#define ITU_AI_GTI_NAI 0x1
#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" },
{ 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 } };
/* #define AI_GTI_NO_GT 0x0 */
#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" },
{ ANSI_AI_GTI_TT_NP_ES, "Translation Type, Numbering Plan, and Encoding Scheme included" },
{ AI_GTI_TT, "Translation Type only" },
{ 0, NULL } };
static const value_string sccp_ai_pci_values[] = {
{ 0x1, "Point Code present" },
{ 0x0, "Point Code not present" },
{ 0, NULL } };
static const value_string sccp_ai_ssni_values[] = {
{ 0x1, "SSN present" },
{ 0x0, "SSN not present" },
{ 0, NULL } };
#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.
*/
static const value_string sccp_ssn_values[] = {
{ 0x00, "SSN not known/not used" },
{ 0x01, "SCCP management" },
{ 0x02, "Reserved for ITU-T allocation" },
{ 0x03, "ISDN User Part" },
{ 0x04, "OMAP (Operation, Maintenance, and Administration Part)" },
{ 0x05, "MAP (Mobile Application Part)" },
{ 0x06, "HLR (Home Location Register)" },
{ 0x07, "VLR (Visitor Location Register)" },
{ 0x08, "MSC (Mobile Switching Center)" },
{ 0x09, "EIC/EIR (Equipment Identifier Center/Equipment Identification Register)" },
{ 0x0a, "AUC/AC (Authentication Center)" },
{ 0x0b, "ISDN supplementary services (ITU only)" },
{ 0x0c, "Reserved for international use (ITU only)" },
{ 0x0d, "Broadband ISDN edge-to-edge applications (ITU only)" },
{ 0x0e, "TC test responder (ITU only)" },
/* The following national network subsystem numbers have been allocated for use within and
* between GSM/UMTS networks:
*/
{ 0x8e, "RANAP" },
{ 0x8f, "RNSAP" },
{ 0x91, "GMLC(MAP)" },
{ 0x92, "CAP" },
{ 0x93, "gsmSCF (MAP) or IM-SSF (MAP) or Presence Network Agent" },
{ 0x94, "SIWF (MAP)" },
{ 0x95, "SGSN (MAP)" },
{ 0x96, "GGSN (MAP)" },
/* The following national network subsystem numbers have been allocated for use within GSM/UMTS networks:*/
{ 0xf9, "PCAP" },
{ 0xfa, "BSC (BSSAP-LE)" },
{ 0xfb, "MSC (BSSAP-LE)" },
{ 0xfc, "IOS or SMLC (BSSAP-LE)" },
{ 0xfd, "BSS O&M (A interface)" },
{ 0xfe, "BSSAP/BSAP" },
{ 0, NULL } };
/* * * * * * * * * * * * * * * * *
* Global Title: ITU GTI == 0001 *
* * * * * * * * * * * * * * * * */
#define GT_NAI_MASK 0x7F
#define GT_NAI_LENGTH 1
#define GT_NAI_UNKNOWN 0x00
#define GT_NAI_SUBSCRIBER_NUMBER 0x01
#define GT_NAI_RESERVED_NATIONAL 0x02
#define GT_NAI_NATIONAL_SIG_NUM 0x03
#define GT_NAI_INTERNATIONAL_NUM 0x04
static const value_string sccp_nai_values[] = {
{ GT_NAI_UNKNOWN, "NAI unknown" },
{ GT_NAI_SUBSCRIBER_NUMBER, "Subscriber Number" },
{ GT_NAI_RESERVED_NATIONAL, "Reserved for national use" },
{ GT_NAI_NATIONAL_SIG_NUM, "National significant number" },
{ GT_NAI_INTERNATIONAL_NUM, "International number" },
{ 0, NULL } };
#define GT_OE_MASK 0x80
#define GT_OE_EVEN 0
#define GT_OE_ODD 1
static const value_string sccp_oe_values[] = {
{ GT_OE_EVEN, "Even number of address signals" },
{ GT_OE_ODD, "Odd number of address signals" },
{ 0, NULL } };
const value_string sccp_address_signal_values[] = {
{ 0, "0" },
{ 1, "1" },
{ 2, "2" },
{ 3, "3" },
{ 4, "4" },
{ 5, "5" },
{ 6, "6" },
{ 7, "7" },
{ 8, "8" },
{ 9, "9" },
{ 10, "(spare)" },
{ 11, "11" },
{ 12, "12" },
{ 13, "(spare)" },
{ 14, "(spare)" },
{ 15, "ST" },
{ 0, NULL } };
/* * * * * * * * * * * * * * * * * * * * *
* Global Title: ITU and ANSI GTI == 0010 *
* * * * * * * * * * * * * * * * * * * * */
#define GT_TT_LENGTH 1
/* * * * * * * * * * * * * * * * * * * * * * * * * *
* 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_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_MARITIME_MOBILE 0x05
#define GT_NP_LAND_MOBILE 0x06
#define GT_NP_ISDN_MOBILE 0x07
#define GT_NP_PRIVATE_NETWORK 0x0e
#define GT_NP_RESERVED 0x0f
static const value_string sccp_np_values[] = {
{ 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_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" },
{ 0, NULL } };
#define GT_ES_MASK 0x0f
#define GT_ES_UNKNOWN 0x0
#define GT_ES_BCD_ODD 0x1
#define GT_ES_BCD_EVEN 0x2
#define GT_ES_NATIONAL 0x3
#define GT_ES_RESERVED 0xf
static const value_string sccp_es_values[] = {
{ GT_ES_UNKNOWN, "Unknown" },
{ GT_ES_BCD_ODD, "BCD, odd number of digits" },
{ GT_ES_BCD_EVEN, "BCD, even number of digits" },
{ GT_ES_NATIONAL, "National specific" },
{ GT_ES_RESERVED, "Reserved (ITU)/Spare (ANSI)" },
{ 0, NULL } };
/* Address signals above */
/* * * * * * * * * * * * * * * * *
* Global Title: ITU GTI == 0100 *
* * * * * * * * * * * * * * * * */
/* NP above */
/* ES above */
/* NAI above */
/* Address signals above */
#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" },
{ 0, NULL } };
#define SEGMENTING_REASSEMBLING_LENGTH 1
#define SEGMENTING_REASSEMBLING_MASK 0x01
#define NO_MORE_DATA 0
#define MORE_DATA 1
/* This is also used by sequencing-segmenting parameter */
static const value_string sccp_segmenting_reassembling_values [] = {
{ NO_MORE_DATA, "No more data" },
{ MORE_DATA, "More data" },
{ 0, NULL } };
#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 RECEIVE_SEQUENCE_NUMBER_MASK 0xfe
#define SEQUENCING_SEGMENTING_MORE_MASK 0x01
#define CREDIT_LENGTH 1
#define RELEASE_CAUSE_LENGTH 1
const value_string sccp_release_cause_values [] = {
{ 0x00, "End user originated" },
{ 0x01, "End user congestion" },
{ 0x02, "End user failure" },
{ 0x03, "SCCP user originated" },
{ 0x04, "Remote procedure error" },
{ 0x05, "Inconsistent connection data" },
{ 0x06, "Access failure" },
{ 0x07, "Access congestion" },
{ 0x08, "Subsystem failure" },
{ 0x09, "Subsystem congestion" },
{ 0x0a, "MTP failure" },
{ 0x0b, "Network congestion" },
{ 0x0c, "Expiration of reset timer" },
{ 0x0d, "Expiration of receive inactivity timer" },
{ 0x0e, "Reserved" },
{ 0x0f, "Unqualified" },
{ 0x10, "SCCP failure (ITU only)" },
{ 0, NULL } };
#define RETURN_CAUSE_LENGTH 1
const value_string sccp_return_cause_values [] = {
{ 0x00, "No translation for an address of such nature" },
{ 0x01, "No translation for this specific address" },
{ 0x02, "Subsystem congestion" },
{ 0x03, "Subsystem failure" },
{ 0x04, "Unequipped failure" },
{ 0x05, "MTP failure" },
{ 0x06, "Network congestion" },
{ 0x07, "Unqualified" },
{ 0x08, "Error in message transport" },
{ 0x09, "Error in local processing" },
{ 0x0a, "Destination cannot perform reassembly" },
{ 0x0b, "SCCP failure" },
{ 0x0c, "Hop counter violation" },
{ 0x0d, "Segmentation not supported" },
{ 0x0e, "Segmentation failure" },
{ 0xf7, "Message change failure (ANSI only)" },
{ 0xf8, "Invalid INS routing request (ANSI only)" },
{ 0xf9, "Invalid ISNI routing request (ANSI only)"},
{ 0xfa, "Unauthorized message (ANSI only)" },
{ 0xfb, "Message incompatibility (ANSI only)" },
{ 0xfc, "Cannot perform ISNI constrained routing (ANSI only)" },
{ 0xfd, "Redundant ISNI constrained routing (ANSI only)" },
{ 0xfe, "Unable to perform ISNI identification (ANSI only)" },
{ 0, NULL } };
#define RESET_CAUSE_LENGTH 1
const value_string sccp_reset_cause_values [] = {
{ 0x00, "End user originated" },
{ 0x01, "SCCP user originated" },
{ 0x02, "Message out of order - incorrect send sequence number" },
{ 0x03, "Message out of order - incorrect receive sequence number" },
{ 0x04, "Remote procedure error - message out of window" },
{ 0x05, "Remote procedure error - incorrect send sequence number after (re)initialization" },
{ 0x06, "Remote procedure error - general" },
{ 0x07, "Remote end user operational" },
{ 0x08, "Network operational" },
{ 0x09, "Access operational" },
{ 0x0a, "Network congestion" },
{ 0x0b, "Reserved (ITU)/Not obtainable (ANSI)" },
{ 0x0c, "Unqualified" },
{ 0, NULL } };
#define ERROR_CAUSE_LENGTH 1
const value_string sccp_error_cause_values [] = {
{ 0x00, "Local Reference Number (LRN) mismatch - unassigned destination LRN" },
{ 0x01, "Local Reference Number (LRN) mismatch - inconsistent source LRN" },
{ 0x02, "Point code mismatch" },
{ 0x03, "Service class mismatch" },
{ 0x04, "Unqualified" },
{ 0, NULL } };
#define REFUSAL_CAUSE_LENGTH 1
const value_string sccp_refusal_cause_values [] = {
{ 0x00, "End user originated" },
{ 0x01, "End user congestion" },
{ 0x02, "End user failure" },
{ 0x03, "SCCP user originated" },
{ 0x04, "Destination address unknown" },
{ 0x05, "Destination inaccessible" },
{ 0x06, "Network resource - QOS not available/non-transient" },
{ 0x07, "Network resource - QOS not available/transient" },
{ 0x08, "Access failure" },
{ 0x09, "Access congestion" },
{ 0x0a, "Subsystem failure" },
{ 0x0b, "Subsystem congestion" },
{ 0x0c, "Expiration of connection establishment timer" },
{ 0x0d, "Incompatible user data" },
{ 0x0e, "Reserved" },
{ 0x0f, "Unqualified" },
{ 0x10, "Hop counter violation" },
{ 0x11, "SCCP failure (ITU only)" },
{ 0x12, "No translation for an address of such nature" },
{ 0x13, "Unequipped user" },
{ 0, NULL } };
#define SEGMENTATION_LENGTH 4
#define SEGMENTATION_FIRST_SEGMENT_MASK 0x80
#define SEGMENTATION_CLASS_MASK 0x40
#define SEGMENTATION_SPARE_MASK 0x30
#define SEGMENTATION_REMAINING_MASK 0x0f
static const value_string sccp_segmentation_first_segment_values [] = {
{ 1, "First segment" },
{ 0, "Not first segment" },
{ 0, NULL } };
static const value_string sccp_segmentation_class_values [] = {
{ 0, "Class 0 selected" },
{ 1, "Class 1 selected" },
{ 0, NULL } };
#define HOP_COUNTER_LENGTH 1
#define IMPORTANCE_LENGTH 1
#define IMPORTANCE_IMPORTANCE_MASK 0x7
#define ANSI_ISNI_ROUTING_CONTROL_LENGTH 1
#define ANSI_ISNI_MI_MASK 0x01
#define ANSI_ISNI_IRI_MASK 0x06
#define ANSI_ISNI_RES_MASK 0x08
#define ANSI_ISNI_TI_MASK 0x10
#define ANSI_ISNI_TI_SHIFT 4
#define ANSI_ISNI_COUNTER_MASK 0xe0
#define ANSI_ISNI_NETSPEC_MASK 0x03
static const value_string sccp_isni_mark_for_id_values [] = {
{ 0x0, "Do not identify networks" },
{ 0x1, "Identify networks" },
{ 0, NULL } };
static const value_string sccp_isni_iri_values [] = {
{ 0x0, "Neither constrained nor suggested ISNI routing" },
{ 0x1, "Constrained ISNI routing" },
{ 0x2, "Reserved for suggested ISNI routing" },
{ 0x3, "Spare" },
{ 0, NULL } };
#define ANSI_ISNI_TYPE_0 0x0
#define ANSI_ISNI_TYPE_1 0x1
static const value_string sccp_isni_ti_values [] = {
{ ANSI_ISNI_TYPE_0, "Type zero ISNI parameter format" },
{ ANSI_ISNI_TYPE_1, "Type one ISNI parameter format" },
{ 0, NULL } };
/* Initialize the protocol and registered fields */
static int proto_sccp = -1;
static int hf_sccp_message_type = -1;
static int hf_sccp_variable_pointer1 = -1;
static int hf_sccp_variable_pointer2 = -1;
static int hf_sccp_variable_pointer3 = -1;
static int hf_sccp_optional_pointer = -1;
static int hf_sccp_param_length = -1;
static int hf_sccp_ssn = -1;
static int hf_sccp_gt_digits = -1;
/* Called Party address */
static int hf_sccp_called_ansi_national_indicator = -1;
static int hf_sccp_called_itu_natl_use_bit = -1;
static int hf_sccp_called_routing_indicator = -1;
static int hf_sccp_called_itu_global_title_indicator = -1;
static int hf_sccp_called_ansi_global_title_indicator = -1;
static int hf_sccp_called_itu_ssn_indicator = -1;
static int hf_sccp_called_itu_point_code_indicator = -1;
static int hf_sccp_called_ansi_ssn_indicator = -1;
static int hf_sccp_called_ansi_point_code_indicator = -1;
static int hf_sccp_called_ssn = -1;
static int hf_sccp_called_pc_member = -1;
static int hf_sccp_called_pc_cluster = -1;
static int hf_sccp_called_pc_network = -1;
static int hf_sccp_called_ansi_pc = -1;
static int hf_sccp_called_chinese_pc = -1;
static int hf_sccp_called_itu_pc = -1;
static int hf_sccp_called_japan_pc = -1;
static int hf_sccp_called_gt_nai = -1;
static int hf_sccp_called_gt_oe = -1;
static int hf_sccp_called_gt_tt = -1;
static int hf_sccp_called_gt_np = -1;
static int hf_sccp_called_gt_es = -1;
static int hf_sccp_called_gt_digits = -1;
static int hf_sccp_called_gt_digits_length = -1;
/* Calling party address */
static int hf_sccp_calling_ansi_national_indicator = -1;
static int hf_sccp_calling_itu_natl_use_bit = -1;
static int hf_sccp_calling_routing_indicator = -1;
static int hf_sccp_calling_itu_global_title_indicator = -1;
static int hf_sccp_calling_ansi_global_title_indicator = -1;
static int hf_sccp_calling_itu_ssn_indicator = -1;
static int hf_sccp_calling_itu_point_code_indicator = -1;
static int hf_sccp_calling_ansi_ssn_indicator = -1;
static int hf_sccp_calling_ansi_point_code_indicator = -1;
static int hf_sccp_calling_ssn = -1;
static int hf_sccp_calling_pc_member = -1;
static int hf_sccp_calling_pc_cluster = -1;
static int hf_sccp_calling_pc_network = -1;
static int hf_sccp_calling_ansi_pc = -1;
static int hf_sccp_calling_chinese_pc = -1;
static int hf_sccp_calling_itu_pc = -1;
static int hf_sccp_calling_japan_pc = -1;
static int hf_sccp_calling_gt_nai = -1;
static int hf_sccp_calling_gt_oe = -1;
static int hf_sccp_calling_gt_tt = -1;
static int hf_sccp_calling_gt_np = -1;
static int hf_sccp_calling_gt_es = -1;
static int hf_sccp_calling_gt_digits = -1;
static int hf_sccp_calling_gt_digits_length = -1;
/* Other parameter values */
static int hf_sccp_dlr = -1;
static int hf_sccp_slr = -1;
static int hf_sccp_lr = -1;
static int hf_sccp_class = -1;
static int hf_sccp_handling = -1;
static int hf_sccp_more = -1;
static int hf_sccp_rsn = -1;
static int hf_sccp_sequencing_segmenting_ssn = -1;
static int hf_sccp_sequencing_segmenting_rsn = -1;
static int hf_sccp_sequencing_segmenting_more = -1;
static int hf_sccp_credit = -1;
static int hf_sccp_release_cause = -1;
static int hf_sccp_return_cause = -1;
static int hf_sccp_reset_cause = -1;
static int hf_sccp_error_cause = -1;
static int hf_sccp_refusal_cause = -1;
static int hf_sccp_segmentation_first = -1;
static int hf_sccp_segmentation_class = -1;
static int hf_sccp_segmentation_remaining = -1;
static int hf_sccp_segmentation_slr = -1;
static int hf_sccp_hop_counter = -1;
static int hf_sccp_importance = -1;
static int hf_sccp_ansi_isni_mi = -1;
static int hf_sccp_ansi_isni_iri = -1;
static int hf_sccp_ansi_isni_ti = -1;
static int hf_sccp_ansi_isni_netspec = -1;
static int hf_sccp_ansi_isni_counter = -1;
static int hf_sccp_ansi_isni_network = -1;
static int hf_sccp_ansi_isni_cluster = -1;
static int hf_sccp_xudt_msg_fragments = -1;
static int hf_sccp_xudt_msg_fragment = -1;
static int hf_sccp_xudt_msg_fragment_overlap = -1;
static int hf_sccp_xudt_msg_fragment_overlap_conflicts = -1;
static int hf_sccp_xudt_msg_fragment_multiple_tails = -1;
static int hf_sccp_xudt_msg_fragment_too_long_fragment = -1;
static int hf_sccp_xudt_msg_fragment_error = -1;
static int hf_sccp_xudt_msg_fragment_count = -1;
static int hf_sccp_xudt_msg_reassembled_in = -1;
static int hf_sccp_xudt_msg_reassembled_length = -1;
static int hf_sccp_assoc_msg = -1;
static int hf_sccp_assoc_id = -1;
/* Initialize the subtree pointers */
static gint ett_sccp = -1;
static gint ett_sccp_called = -1;
static gint ett_sccp_called_ai = -1;
static gint ett_sccp_called_pc = -1;
static gint ett_sccp_called_gt = -1;
static gint ett_sccp_called_gt_digits = -1;
static gint ett_sccp_calling = -1;
static gint ett_sccp_calling_ai = -1;
static gint ett_sccp_calling_pc = -1;
static gint ett_sccp_calling_gt = -1;
static gint ett_sccp_calling_gt_digits = -1;
static gint ett_sccp_sequencing_segmenting = -1;
static gint ett_sccp_segmentation = -1;
static gint ett_sccp_ansi_isni_routing_control = -1;
static gint ett_sccp_xudt_msg_fragment = -1;
static gint ett_sccp_xudt_msg_fragments = -1;
static gint ett_sccp_assoc = -1;
static expert_field ei_sccp_wrong_length = EI_INIT;
static expert_field ei_sccp_international_standard_address = EI_INIT;
static expert_field ei_sccp_no_ssn_present = EI_INIT;
static expert_field ei_sccp_ssn_zero = EI_INIT;
static expert_field ei_sccp_class_unexpected = EI_INIT;
static expert_field ei_sccp_handling_invalid = EI_INIT;
/* Declarations to desegment XUDT Messages */
static gboolean sccp_xudt_desegment = TRUE;
static gboolean show_key_params = FALSE;
static gboolean set_addresses = FALSE;
static int sccp_tap = -1;
static const fragment_items sccp_xudt_msg_frag_items = {
/* Fragment subtrees */
&ett_sccp_xudt_msg_fragment,
&ett_sccp_xudt_msg_fragments,
/* Fragment fields */
&hf_sccp_xudt_msg_fragments,
&hf_sccp_xudt_msg_fragment,
&hf_sccp_xudt_msg_fragment_overlap,
&hf_sccp_xudt_msg_fragment_overlap_conflicts,
&hf_sccp_xudt_msg_fragment_multiple_tails,
&hf_sccp_xudt_msg_fragment_too_long_fragment,
&hf_sccp_xudt_msg_fragment_error,
&hf_sccp_xudt_msg_fragment_count,
/* Reassembled in field */
&hf_sccp_xudt_msg_reassembled_in,
/* Reassembled length field */
&hf_sccp_xudt_msg_reassembled_length,
/* Reassembled data field */
NULL,
/* Tag */
"SCCP XUDT Message fragments"
};
static reassembly_table sccp_xudt_msg_reassembly_table;
#define SCCP_USER_DATA 0
#define SCCP_USER_TCAP 1
#define SCCP_USER_RANAP 2
#define SCCP_USER_BSSAP 3
#define SCCP_USER_GSMMAP 4
#define SCCP_USER_CAMEL 5
#define SCCP_USER_INAP 6
typedef struct _sccp_user_t {
guint ni;
range_t *called_pc;
range_t *called_ssn;
guint user;
gboolean uses_tcap;
dissector_handle_t *handlep;
} sccp_user_t;
static sccp_user_t *sccp_users;
static guint num_sccp_users;
static dissector_handle_t data_handle;
static dissector_handle_t tcap_handle;
static dissector_handle_t ranap_handle;
static dissector_handle_t bssap_handle;
static dissector_handle_t gsmmap_handle;
static dissector_handle_t camel_handle;
static dissector_handle_t inap_handle;
static dissector_handle_t default_handle;
static const char *default_payload = NULL;
static const value_string sccp_users_vals[] = {
{ SCCP_USER_DATA, "Data"},
{ SCCP_USER_TCAP, "TCAP"},
{ SCCP_USER_RANAP, "RANAP"},
{ SCCP_USER_BSSAP, "BSSAP"},
{ SCCP_USER_GSMMAP, "GSM MAP"},
{ SCCP_USER_CAMEL, "CAMEL"},
{ SCCP_USER_INAP, "INAP"},
{ 0, NULL }
};
/*
* Here are the global variables associated with
* the various user definable characteristics of the dissection
*/
static guint32 sccp_source_pc_global = 0;
static gboolean sccp_show_length = FALSE;
static heur_dissector_list_t heur_subdissector_list;
/* Keep track of SSN value of current message so if/when we get to the data
* parameter, we can call appropriate sub-dissector. TODO: can this info
* be stored elsewhere?
*/
static guint8 message_type = 0;
static guint dlr = 0;
static guint slr = 0;
static dissector_table_t sccp_ssn_dissector_table;
static wmem_tree_t *assocs = NULL;
static sccp_assoc_info_t *assoc;
static sccp_msg_info_t *sccp_msg;
static sccp_assoc_info_t no_assoc = {0,0,0,0,0,FALSE,FALSE,NULL,NULL,SCCP_PLOAD_NONE,NULL,NULL,NULL,0};
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 }
};
#define is_connectionless(m) \
( m == SCCP_MSG_TYPE_UDT || m == SCCP_MSG_TYPE_UDTS \
|| m == SCCP_MSG_TYPE_XUDT|| m == SCCP_MSG_TYPE_XUDTS \
|| m == SCCP_MSG_TYPE_LUDT|| m == SCCP_MSG_TYPE_LUDTS)
#define RETURN_FALSE \
do { \
/*g_warning("Frame %d not protocol %d @ line %d", frame_num, my_mtp3_standard, __LINE__);*/ \
return FALSE; \
} while (0)
static gboolean
sccp_called_calling_looks_valid(guint32 frame_num _U_, tvbuff_t *tvb, guint8 my_mtp3_standard, gboolean is_co)
{
guint8 ai, ri, gti, ssni, pci;
guint8 len_needed = 1; /* need at least the Address Indicator */
guint len = tvb_length(tvb);
ai = tvb_get_guint8(tvb, 0);
if ((my_mtp3_standard == ANSI_STANDARD) && ((ai & ANSI_NATIONAL_MASK) == 0))
RETURN_FALSE;
gti = (ai & GTI_MASK) >> GTI_SHIFT;
if (my_mtp3_standard == ANSI_STANDARD) {
if (gti > 2)
RETURN_FALSE;
} else {
if (gti > 4)
RETURN_FALSE;
}
ri = (ai & ROUTING_INDICATOR_MASK) >> ROUTING_INDICATOR_SHIFT;
if (my_mtp3_standard == ANSI_STANDARD) {
pci = ai & ANSI_PC_INDICATOR_MASK;
ssni = ai & ANSI_SSN_INDICATOR_MASK;
} else {
ssni = ai & ITU_SSN_INDICATOR_MASK;
pci = ai & ITU_PC_INDICATOR_MASK;
}
/* Route on SSN with no SSN? */
if ((ri == ROUTE_ON_SSN) && (ssni == 0))
RETURN_FALSE;
/* Route on GT with no GT? */
if ((ri == ROUTE_ON_GT) && (gti == AI_GTI_NO_GT))
RETURN_FALSE;
/* GT routed and connection-oriented (Class-2)?
* Yes, that's theoretically possible, but it's not used.
*/
if ((ri == ROUTE_ON_GT) && is_co)
RETURN_FALSE;
if (ssni)
len_needed += ADDRESS_SSN_LENGTH;
if (pci) {
if (my_mtp3_standard == ANSI_STANDARD ||
my_mtp3_standard == CHINESE_ITU_STANDARD)
len_needed += ANSI_PC_LENGTH;
else
len_needed += ITU_PC_LENGTH;
}
if (gti)
len_needed += 2;
if (len_needed > len)
RETURN_FALSE;
return TRUE;
}
gboolean
looks_like_valid_sccp(guint32 frame_num _U_, tvbuff_t *tvb, guint8 my_mtp3_standard)
{
guint offset;
guint8 msgtype, msg_class, cause;
guint called_ptr = 0;
guint calling_ptr = 0;
guint data_ptr = 0;
guint opt_ptr = 0;
guint8 pointer_length = POINTER_LENGTH;
guint len = tvb_length(tvb);
/* Ensure we can do some basic checks without throwing an exception.
* Accesses beyond this length need to check the length first because
* we don't want to throw an exception in here...
*/
if (len < 5)
RETURN_FALSE;
msgtype = tvb_get_guint8(tvb, SCCP_MSG_TYPE_OFFSET);
if (!try_val_to_str(msgtype, sccp_message_type_acro_values)) {
RETURN_FALSE;
}
offset = SCCP_MSG_TYPE_LENGTH;
switch (msgtype) {
case SCCP_MSG_TYPE_UDT:
case SCCP_MSG_TYPE_XUDT:
case SCCP_MSG_TYPE_LUDT:
case SCCP_MSG_TYPE_UDTS:
case SCCP_MSG_TYPE_XUDTS:
case SCCP_MSG_TYPE_LUDTS:
{
if (msgtype == SCCP_MSG_TYPE_XUDT || msgtype == SCCP_MSG_TYPE_XUDTS) {
if (SCCP_MSG_TYPE_LENGTH +
PROTOCOL_CLASS_LENGTH + /* or Cause for XUDTS */
HOP_COUNTER_LENGTH +
POINTER_LENGTH +
POINTER_LENGTH +
POINTER_LENGTH +
POINTER_LENGTH > len)
RETURN_FALSE;
}
if (msgtype == SCCP_MSG_TYPE_LUDT || msgtype == SCCP_MSG_TYPE_LUDTS) {
if (SCCP_MSG_TYPE_LENGTH +
PROTOCOL_CLASS_LENGTH + /* or Cause for LUDTS */
HOP_COUNTER_LENGTH +
POINTER_LENGTH_LONG +
POINTER_LENGTH_LONG +
POINTER_LENGTH_LONG +
POINTER_LENGTH_LONG > len)
RETURN_FALSE;
pointer_length = POINTER_LENGTH_LONG;
}
if (msgtype == SCCP_MSG_TYPE_UDT || msgtype == SCCP_MSG_TYPE_XUDT ||
msgtype == SCCP_MSG_TYPE_LUDT) {
msg_class = tvb_get_guint8(tvb, offset) & CLASS_CLASS_MASK;
if (msg_class > 1)
RETURN_FALSE;
offset += PROTOCOL_CLASS_LENGTH;
}
if (msgtype == SCCP_MSG_TYPE_XUDT || msgtype == SCCP_MSG_TYPE_LUDT)
offset += HOP_COUNTER_LENGTH;
if (msgtype == SCCP_MSG_TYPE_UDTS ||
msgtype == SCCP_MSG_TYPE_XUDTS ||
msgtype == SCCP_MSG_TYPE_LUDTS) {
cause = tvb_get_guint8(tvb, offset);
if (!try_val_to_str(cause, sccp_return_cause_values))
RETURN_FALSE;
offset += RETURN_CAUSE_LENGTH;
}
if (msgtype == SCCP_MSG_TYPE_XUDTS || msgtype == SCCP_MSG_TYPE_LUDTS)
offset += HOP_COUNTER_LENGTH;
if (msgtype == SCCP_MSG_TYPE_LUDT || msgtype == SCCP_MSG_TYPE_LUDTS)
called_ptr = tvb_get_letohs(tvb, offset);
else
called_ptr = tvb_get_guint8(tvb, offset);
if (called_ptr == 0) /* Mandatory variable parameters must be present */
RETURN_FALSE;
called_ptr += offset;
offset += pointer_length;
if (msgtype == SCCP_MSG_TYPE_LUDT || msgtype == SCCP_MSG_TYPE_LUDTS)
calling_ptr = tvb_get_letohs(tvb, offset);
else
calling_ptr = tvb_get_guint8(tvb, offset);
if (calling_ptr == 0) /* Mandatory variable parameters must be present */
RETURN_FALSE;
calling_ptr += offset;
offset += pointer_length;
if (msgtype == SCCP_MSG_TYPE_LUDT || msgtype == SCCP_MSG_TYPE_LUDTS)
data_ptr = tvb_get_letohs(tvb, offset);
else
data_ptr = tvb_get_guint8(tvb, offset);
if (data_ptr == 0) /* Mandatory variable parameters must be present */
RETURN_FALSE;
data_ptr += offset;
offset += pointer_length;
if (msgtype == SCCP_MSG_TYPE_XUDT || msgtype == SCCP_MSG_TYPE_XUDTS) {
opt_ptr = tvb_get_guint8(tvb, offset);
offset += POINTER_LENGTH;
} else if (msgtype == SCCP_MSG_TYPE_LUDT || msgtype == SCCP_MSG_TYPE_LUDTS) {
opt_ptr = tvb_get_letohs(tvb, offset);
offset += POINTER_LENGTH_LONG;
}
if (msgtype == SCCP_MSG_TYPE_LUDT || msgtype == SCCP_MSG_TYPE_LUDTS) {
/* Long pointers count from the 2nd (MSB) octet of the pointer */
called_ptr += 1;
calling_ptr += 1;
data_ptr += 1;
if (opt_ptr)
opt_ptr += 1;
}
/* Check that the variable pointers are within bounds */
if (called_ptr > len || calling_ptr > len || data_ptr > len)
RETURN_FALSE;
/* Check that the lengths of the variable parameters are within bounds */
if (tvb_get_guint8(tvb, called_ptr)+called_ptr > len ||
tvb_get_guint8(tvb, calling_ptr)+calling_ptr > len)
RETURN_FALSE;
if (msgtype == SCCP_MSG_TYPE_LUDT || msgtype == SCCP_MSG_TYPE_LUDTS) {
if (tvb_get_letohs(tvb, data_ptr)+data_ptr > len)
RETURN_FALSE;
} else {
if (tvb_get_guint8(tvb, data_ptr)+data_ptr > len)
RETURN_FALSE;
}
}
break;
case SCCP_MSG_TYPE_CR:
{
if (len < SCCP_MSG_TYPE_LENGTH
+ DESTINATION_LOCAL_REFERENCE_LENGTH
+ PROTOCOL_CLASS_LENGTH
+ POINTER_LENGTH
+ POINTER_LENGTH)
RETURN_FALSE;
offset += DESTINATION_LOCAL_REFERENCE_LENGTH;
/* Class is only the lower 4 bits, but the upper 4 bits are spare
* in Class-2. Don't mask them off so the below comparison also
* fails if any of those spare bits are set.
*/
msg_class = tvb_get_guint8(tvb, offset);
if (msg_class != 2)
RETURN_FALSE;
offset += PROTOCOL_CLASS_LENGTH;
data_ptr = tvb_get_guint8(tvb, offset);
if (data_ptr == 0)
RETURN_FALSE;
offset += POINTER_LENGTH;
opt_ptr = tvb_get_guint8(tvb, offset);
if (opt_ptr == 0)
RETURN_FALSE;
offset += POINTER_LENGTH;
}
break;
case SCCP_MSG_TYPE_CC:
{
if (len < SCCP_MSG_TYPE_LENGTH
+ DESTINATION_LOCAL_REFERENCE_LENGTH
+ SOURCE_LOCAL_REFERENCE_LENGTH
+ PROTOCOL_CLASS_LENGTH
+ POINTER_LENGTH)
RETURN_FALSE;
offset += DESTINATION_LOCAL_REFERENCE_LENGTH;
offset += SOURCE_LOCAL_REFERENCE_LENGTH;
/* Class is only the lower 4 bits, but the upper 4 bits are spare
* in Class-2. Don't mask them off so the below comparison also
* fails if any of those spare bits are set.
*/
msg_class = tvb_get_guint8(tvb, offset);
if (msg_class != 2)
RETURN_FALSE;
offset += PROTOCOL_CLASS_LENGTH;
opt_ptr = tvb_get_guint8(tvb, offset);
offset += POINTER_LENGTH;
/* If the pointer isn't 0 (no optional parameters) or 1 (optional
* parameter starts immediately after the pointer) then what would
* be between the pointer and the parameter?
*/
if (opt_ptr > 1)
RETURN_FALSE;
/* If there are no optional parameters, are we at the end of the
* message?
*/
if ((opt_ptr == 0) && (offset != len))
RETURN_FALSE;
}
break;
case SCCP_MSG_TYPE_CREF:
{
if (len < SCCP_MSG_TYPE_LENGTH
+ DESTINATION_LOCAL_REFERENCE_LENGTH
+ REFUSAL_CAUSE_LENGTH
+ POINTER_LENGTH)
RETURN_FALSE;
offset += DESTINATION_LOCAL_REFERENCE_LENGTH;
cause = tvb_get_guint8(tvb, offset);
if (!try_val_to_str(cause, sccp_refusal_cause_values))
RETURN_FALSE;
offset += REFUSAL_CAUSE_LENGTH;
opt_ptr = tvb_get_guint8(tvb, offset);
offset += POINTER_LENGTH;
/* If the pointer isn't 0 (no optional parameters) or 1 (optional
* parameter starts immediately after the pointer) then what would
* be between the pointer and the parameter?
*/
if (opt_ptr > 1)
RETURN_FALSE;
/* If there are no optional parameters, are we at the end of the
* message?
*/
if ((opt_ptr == 0) && (offset != len))
RETURN_FALSE;
}
break;
case SCCP_MSG_TYPE_RLSD:
{
if (len < SCCP_MSG_TYPE_LENGTH
+ DESTINATION_LOCAL_REFERENCE_LENGTH
+ SOURCE_LOCAL_REFERENCE_LENGTH
+ RELEASE_CAUSE_LENGTH
+ POINTER_LENGTH)
RETURN_FALSE;
offset += DESTINATION_LOCAL_REFERENCE_LENGTH;
offset += SOURCE_LOCAL_REFERENCE_LENGTH;
cause = tvb_get_guint8(tvb, offset);
if (!try_val_to_str(cause, sccp_release_cause_values))
RETURN_FALSE;
offset += RELEASE_CAUSE_LENGTH;
opt_ptr = tvb_get_guint8(tvb, offset);
offset += POINTER_LENGTH;
/* If the pointer isn't 0 (no optional parameters) or 1 (optional
* parameter starts immediately after the pointer) then what would
* be between the pointer and the parameter?
*/
if (opt_ptr > 1)
RETURN_FALSE;
/* If there are no optional parameters, are we at the end of the
* message?
*/
if ((opt_ptr == 0) && (offset != len))
RETURN_FALSE;
}
break;
case SCCP_MSG_TYPE_RLC:
{
if (len != SCCP_MSG_TYPE_LENGTH
+ DESTINATION_LOCAL_REFERENCE_LENGTH
+ SOURCE_LOCAL_REFERENCE_LENGTH)
RETURN_FALSE;
}
break;
case SCCP_MSG_TYPE_ERR:
{
if (len != SCCP_MSG_TYPE_LENGTH
+ DESTINATION_LOCAL_REFERENCE_LENGTH
+ ERROR_CAUSE_LENGTH)
RETURN_FALSE;
offset += DESTINATION_LOCAL_REFERENCE_LENGTH;
cause = tvb_get_guint8(tvb, offset);
if (!try_val_to_str(cause, sccp_error_cause_values))
RETURN_FALSE;
}
break;
case SCCP_MSG_TYPE_DT1:
{
if (len < SCCP_MSG_TYPE_LENGTH
+ DESTINATION_LOCAL_REFERENCE_LENGTH
+ SEGMENTING_REASSEMBLING_LENGTH
+ POINTER_LENGTH
+ PARAMETER_LENGTH_LENGTH
+ 1) /* At least 1 byte of payload */
RETURN_FALSE;
offset += DESTINATION_LOCAL_REFERENCE_LENGTH;
/* Are any of the spare bits in set? */
if (tvb_get_guint8(tvb, offset) & ~SEGMENTING_REASSEMBLING_MASK)
RETURN_FALSE;
offset += SEGMENTING_REASSEMBLING_LENGTH;
data_ptr = tvb_get_guint8(tvb, offset) + offset;
/* Verify the data pointer is within bounds */
if (data_ptr > len)
RETURN_FALSE;
offset += POINTER_LENGTH;
/* Verify the data length uses the rest of the message */
if (tvb_get_guint8(tvb, data_ptr) + offset + 1U != len)
RETURN_FALSE;
}
break;
case SCCP_MSG_TYPE_IT:
{
if (len < SCCP_MSG_TYPE_LENGTH
+ DESTINATION_LOCAL_REFERENCE_LENGTH
+ SOURCE_LOCAL_REFERENCE_LENGTH
+ PROTOCOL_CLASS_LENGTH
+ SEQUENCING_SEGMENTING_LENGTH
+ CREDIT_LENGTH)
RETURN_FALSE;
offset += DESTINATION_LOCAL_REFERENCE_LENGTH;
offset += SOURCE_LOCAL_REFERENCE_LENGTH;
/* Class is only the lower 4 bits, but the upper 4 bits are spare
* in Class-2. Don't mask them off so the below comparison also
* fails if any of those spare bits are set.
*/
msg_class = tvb_get_guint8(tvb, offset);
if (msg_class != 2)
RETURN_FALSE;
offset += PROTOCOL_CLASS_LENGTH;
}
break;
case SCCP_MSG_TYPE_AK:
case SCCP_MSG_TYPE_DT2:
case SCCP_MSG_TYPE_EA:
case SCCP_MSG_TYPE_ED:
case SCCP_MSG_TYPE_RSC:
case SCCP_MSG_TYPE_RSR:
/* Class-3 is never actually used in the real world */
RETURN_FALSE;
break;
default:
DISSECTOR_ASSERT_NOT_REACHED();
}
if (called_ptr) {
guint8 param_len = tvb_get_guint8(tvb, called_ptr);
tvbuff_t *param_tvb;
if (param_len == 0)
RETURN_FALSE;
param_tvb = tvb_new_subset(tvb, called_ptr+1, param_len, param_len);
if (!sccp_called_calling_looks_valid(frame_num, param_tvb, my_mtp3_standard, !is_connectionless(msgtype)))
RETURN_FALSE;
}
if (calling_ptr) {
guint8 param_len = tvb_get_guint8(tvb, calling_ptr);
tvbuff_t *param_tvb;
if (param_len == 0)
RETURN_FALSE;
param_tvb = tvb_new_subset(tvb, calling_ptr+1, param_len, param_len);
if (!sccp_called_calling_looks_valid(frame_num, param_tvb, my_mtp3_standard, !is_connectionless(msgtype)))
RETURN_FALSE;
}
if (opt_ptr) {
guint8 opt_param;
opt_ptr += offset-pointer_length; /* (offset was already incremented) */
/* Check that the optional pointer is within bounds */
if (opt_ptr > len)
RETURN_FALSE;
opt_param = tvb_get_guint8(tvb, opt_ptr);
/* Check if the (1st) optional parameter tag is valid */
if (!try_val_to_str(opt_param, sccp_parameter_values))
RETURN_FALSE;
/* Check that the (1st) parameter length is within bounds */
if ((opt_param != PARAMETER_END_OF_OPTIONAL_PARAMETERS) &&
((opt_ptr+1U) <= len) &&
((tvb_get_guint8(tvb, opt_ptr+1U)+offset) > len))
RETURN_FALSE;
/* If we're at the end of the parameters, are we also at the end of the
* message?
*/
if ((opt_param == PARAMETER_END_OF_OPTIONAL_PARAMETERS) && ((opt_ptr+1U) != len))
RETURN_FALSE;
}
return TRUE;
}
static sccp_assoc_info_t *
new_assoc(guint32 calling, guint32 called)
{
sccp_assoc_info_t *a = wmem_new0(wmem_file_scope(), sccp_assoc_info_t);
a->id = next_assoc_id++;
a->calling_dpc = calling;
a->called_dpc = called;
a->calling_ssn = INVALID_SSN;
a->called_ssn = INVALID_SSN;
a->msgs = NULL;
a->curr_msg = NULL;
a->payload = SCCP_PLOAD_NONE;
a->calling_party = NULL;
a->called_party = NULL;
a->extra_info = NULL;
return a;
}
void
reset_sccp_assoc(void)
{
assoc = NULL;
}
sccp_assoc_info_t *
get_sccp_assoc(packet_info *pinfo, guint offset, guint32 src_lr, guint32 dst_lr, guint msg_type)
{
guint32 opck, dpck;
address *opc = &(pinfo->src);
address *dpc = &(pinfo->dst);
guint framenum = PINFO_FD_NUM(pinfo);
if (assoc)
return assoc;
opck = opc->type == AT_SS7PC ? mtp3_pc_hash((const mtp3_addr_pc_t *)opc->data) : g_str_hash(ep_address_to_str(opc));
dpck = dpc->type == AT_SS7PC ? mtp3_pc_hash((const mtp3_addr_pc_t *)dpc->data) : g_str_hash(ep_address_to_str(dpc));
switch (msg_type) {
case SCCP_MSG_TYPE_CR:
{
/* CR contains the opc,dpc,dlr key of backward messages swapped as dpc,opc,slr */
wmem_tree_key_t bw_key[] = {
{1, &dpck},
{1, &opck},
{1, &src_lr},
{0, NULL}
};
if (! ( assoc = (sccp_assoc_info_t *)wmem_tree_lookup32_array(assocs, bw_key) ) && ! PINFO_FD_VISITED(pinfo) ) {
assoc = new_assoc(opck, dpck);
wmem_tree_insert32_array(assocs, bw_key, assoc);
assoc->has_bw_key = TRUE;
}
pinfo->p2p_dir = P2P_DIR_SENT;
break;
}
case SCCP_MSG_TYPE_CC:
{
wmem_tree_key_t fw_key[] = {
{1, &dpck}, {1, &opck}, {1, &src_lr}, {0, NULL}
};
wmem_tree_key_t bw_key[] = {
{1, &opck}, {1, &dpck}, {1, &dst_lr}, {0, NULL}
};
if ( ( assoc = (sccp_assoc_info_t *)wmem_tree_lookup32_array(assocs, bw_key) ) ) {
goto got_assoc;
}
if ( (assoc = (sccp_assoc_info_t *)wmem_tree_lookup32_array(assocs, fw_key) ) ) {
goto got_assoc;
}
assoc = new_assoc(dpck, opck);
got_assoc:
pinfo->p2p_dir = P2P_DIR_RECV;
if ( ! PINFO_FD_VISITED(pinfo) && ! assoc->has_bw_key ) {
wmem_tree_insert32_array(assocs, bw_key, assoc);
assoc->has_bw_key = TRUE;
}
if ( ! PINFO_FD_VISITED(pinfo) && ! assoc->has_fw_key ) {
wmem_tree_insert32_array(assocs, fw_key, assoc);
assoc->has_fw_key = TRUE;
}
break;
}
case SCCP_MSG_TYPE_RLC:
{
wmem_tree_key_t bw_key[] = {
{1, &dpck}, {1, &opck}, {1, &src_lr}, {0, NULL}
};
wmem_tree_key_t fw_key[] = {
{1, &opck}, {1, &dpck}, {1, &dst_lr}, {0, NULL}
};
if ( ( assoc = (sccp_assoc_info_t *)wmem_tree_lookup32_array(assocs, bw_key) ) ) {
goto got_assoc_rlc;
}
if ( (assoc = (sccp_assoc_info_t *)wmem_tree_lookup32_array(assocs, fw_key) ) ) {
goto got_assoc_rlc;
}
assoc = new_assoc(dpck, opck);
got_assoc_rlc:
pinfo->p2p_dir = P2P_DIR_SENT;
if ( ! PINFO_FD_VISITED(pinfo) && ! assoc->has_bw_key ) {
wmem_tree_insert32_array(assocs, bw_key, assoc);
assoc->has_bw_key = TRUE;
}
if ( ! PINFO_FD_VISITED(pinfo) && ! assoc->has_fw_key ) {
wmem_tree_insert32_array(assocs, fw_key, assoc);
assoc->has_fw_key = TRUE;
}
break;
}
default:
{
wmem_tree_key_t key[] = {
{1, &opck}, {1, &dpck}, {1, &dst_lr}, {0, NULL}
};
assoc = (sccp_assoc_info_t *)wmem_tree_lookup32_array(assocs, key);
if (assoc) {
if (assoc->calling_dpc == dpck) {
pinfo->p2p_dir = P2P_DIR_RECV;
} else {
pinfo->p2p_dir = P2P_DIR_SENT;
}
}
break;
}
}
if (assoc && trace_sccp) {
if ( ! PINFO_FD_VISITED(pinfo)) {
sccp_msg_info_t *msg = wmem_new0(wmem_file_scope(), sccp_msg_info_t);
msg->framenum = framenum;
msg->offset = offset;
msg->data.co.next = NULL;
msg->data.co.assoc = assoc;
msg->data.co.label = NULL;
msg->data.co.comment = NULL;
msg->type = msg_type;
if (assoc->msgs) {
sccp_msg_info_t *m;
for (m = assoc->msgs; m->data.co.next; m = m->data.co.next) ;
m->data.co.next = msg;
} else {
assoc->msgs = msg;
}
assoc->curr_msg = msg;
} else {
sccp_msg_info_t *m;
for (m = assoc->msgs; m; m = m->data.co.next) {
if ((m->framenum == framenum) && (m->offset == offset)) {
assoc->curr_msg = m;
break;
}
}
}
}
return assoc ? assoc : &no_assoc;
}
static void
dissect_sccp_unknown_message(tvbuff_t *message_tvb, proto_tree *sccp_tree)
{
guint32 message_length;
message_length = tvb_length(message_tvb);
proto_tree_add_text(sccp_tree, message_tvb, 0, message_length,
"Unknown message (%u byte%s)",
message_length, plurality(message_length, "", "s"));
}
static void
dissect_sccp_unknown_param(tvbuff_t *tvb, proto_tree *tree, guint8 type, guint length)
{
proto_tree_add_text(tree, tvb, 0, length, "Unknown parameter 0x%x (%u byte%s)",
type, length, plurality(length, "", "s"));
}
static void
dissect_sccp_dlr_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
proto_item *lr_item;
if (length != 3) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 3, got %u", length);
return;
}
dlr = tvb_get_letoh24(tvb, 0);
proto_tree_add_uint(tree, hf_sccp_dlr, tvb, 0, length, dlr);
lr_item = proto_tree_add_uint(tree, hf_sccp_lr, tvb, 0, length, dlr);
PROTO_ITEM_SET_HIDDEN(lr_item);
if (show_key_params)
col_append_fstr(pinfo->cinfo, COL_INFO, "DLR=%d ", dlr);
}
static void
dissect_sccp_slr_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
proto_item *lr_item;
if (length != 3) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 3, got %u", length);
return;
}
slr = tvb_get_letoh24(tvb, 0);
proto_tree_add_uint(tree, hf_sccp_slr, tvb, 0, length, slr);
lr_item = proto_tree_add_uint(tree, hf_sccp_lr, tvb, 0, length, slr);
PROTO_ITEM_SET_HIDDEN(lr_item);
if (show_key_params)
col_append_fstr(pinfo->cinfo, COL_INFO, "SLR=%d ", slr);
}
static proto_tree *
dissect_sccp_gt_address_information(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, guint length,
gboolean even_length, gboolean called,
gboolean route_on_gt)
{
guint offset = 0;
guint8 odd_signal, even_signal;
proto_item *digits_item;
proto_tree *digits_tree;
char *gt_digits;
gt_digits = (char *)wmem_alloc0(wmem_packet_scope(), GT_MAX_SIGNALS+1);
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;
g_strlcat(gt_digits, val_to_str(odd_signal, sccp_address_signal_values,
"Unknown: %d"), GT_MAX_SIGNALS+1);
/* If the last signal is NOT filler */
if (offset != (length - 1) || even_length == TRUE)
g_strlcat(gt_digits, val_to_str(even_signal, sccp_address_signal_values,
"Unknown: %d"), GT_MAX_SIGNALS+1);
offset += GT_SIGNAL_LENGTH;
}
if (is_connectionless(message_type) && sccp_msg) {
guint8 **gt_ptr = called ? &(sccp_msg->data.ud.called_gt) : &(sccp_msg->data.ud.calling_gt);
*gt_ptr = (guint8 *)wmem_strdup(wmem_packet_scope(), gt_digits);
}
digits_item = proto_tree_add_string(tree, called ? hf_sccp_called_gt_digits
: hf_sccp_calling_gt_digits,
tvb, 0, length, gt_digits);
digits_tree = proto_item_add_subtree(digits_item, called ? ett_sccp_called_gt_digits
: ett_sccp_calling_gt_digits);
if (set_addresses && route_on_gt) {
if (called) {
SET_ADDRESS(&pinfo->dst, AT_STRINGZ, 1+(int)strlen(gt_digits), gt_digits);
} else {
SET_ADDRESS(&pinfo->src, AT_STRINGZ, 1+(int)strlen(gt_digits), gt_digits);
}
}
proto_tree_add_string(digits_tree, hf_sccp_gt_digits, tvb, 0, length, gt_digits);
proto_tree_add_uint(digits_tree, called ? hf_sccp_called_gt_digits_length
: hf_sccp_calling_gt_digits_length,
tvb, 0, length, (guint32)strlen(gt_digits));
return digits_tree;
}
static void
dissect_sccp_global_title(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length,
guint8 gti, gboolean route_on_gt, gboolean called)
{
proto_item *gt_item;
proto_tree *gt_tree;
proto_tree *digits_tree;
tvbuff_t *signals_tvb;
guint offset = 0;
guint8 odd_even, nai = 0, np = 0, es;
gboolean even = TRUE;
/* Shift GTI to where we can work with it */
gti >>= GTI_SHIFT;
gt_item = proto_tree_add_text(tree, tvb, offset, length,
"Global Title 0x%x (%u byte%s)",
gti, length, plurality(length,"", "s"));
gt_tree = proto_item_add_subtree(gt_item, called ? ett_sccp_called_gt
: ett_sccp_calling_gt);
/* Decode Transation Type (if present) */
if ((gti == AI_GTI_TT) ||
((decode_mtp3_standard != ANSI_STANDARD) &&
((gti == ITU_AI_GTI_TT_NP_ES) || (gti == ITU_AI_GTI_TT_NP_ES_NAI))) ||
((decode_mtp3_standard == ANSI_STANDARD) && (gti == ANSI_AI_GTI_TT_NP_ES))) {
proto_tree_add_item(gt_tree, called ? hf_sccp_called_gt_tt
: hf_sccp_calling_gt_tt,
tvb, offset, GT_TT_LENGTH, ENC_NA);
offset += GT_TT_LENGTH;
}
if (gti == AI_GTI_TT) {
/* Protocol doesn't tell us, so we ASSUME even... */
even = TRUE;
}
/* Decode Numbering Plan and Encoding Scheme (if present) */
if (((decode_mtp3_standard != ANSI_STANDARD) &&
((gti == ITU_AI_GTI_TT_NP_ES) || (gti == ITU_AI_GTI_TT_NP_ES_NAI))) ||
((decode_mtp3_standard == ANSI_STANDARD) && (gti == ANSI_AI_GTI_TT_NP_ES))) {
np = tvb_get_guint8(tvb, offset) & GT_NP_MASK;
proto_tree_add_uint(gt_tree, called ? hf_sccp_called_gt_np
: hf_sccp_calling_gt_np,
tvb, offset, GT_NP_ES_LENGTH, np);
es = tvb_get_guint8(tvb, offset) & GT_ES_MASK;
proto_tree_add_uint(gt_tree, called ? hf_sccp_called_gt_es
: hf_sccp_calling_gt_es,
tvb, offset, GT_NP_ES_LENGTH, es);
even = (es == GT_ES_BCD_EVEN) ? TRUE : FALSE;
offset += GT_NP_ES_LENGTH;
}
/* Decode Nature of Address Indicator (if present) */
if ((decode_mtp3_standard != ANSI_STANDARD) &&
((gti == ITU_AI_GTI_NAI) || (gti == ITU_AI_GTI_TT_NP_ES_NAI))) {
/* Decode Odd/Even Indicator (if present) */
if (gti == ITU_AI_GTI_NAI) {
odd_even = tvb_get_guint8(tvb, offset) & GT_OE_MASK;
proto_tree_add_uint(gt_tree, called ? hf_sccp_called_gt_oe
: hf_sccp_calling_gt_oe,
tvb, offset, GT_NAI_LENGTH, odd_even);
even = (odd_even == GT_OE_EVEN) ? TRUE : FALSE;
}
nai = tvb_get_guint8(tvb, offset) & GT_NAI_MASK;
proto_tree_add_uint(gt_tree, called ? hf_sccp_called_gt_nai
: hf_sccp_calling_gt_nai,
tvb, offset, GT_NAI_LENGTH, nai);
offset += GT_NAI_LENGTH;
}
/* Decode address signal(s) */
if (length < offset)
return;
signals_tvb = tvb_new_subset(tvb, offset, (length - offset),
(length - offset));
digits_tree = dissect_sccp_gt_address_information(signals_tvb, pinfo, gt_tree,
(length - offset),
even, called, route_on_gt);
/* Display the country code (if we can) */
switch (np >> GT_NP_SHIFT) {
case GT_NP_ISDN:
case GT_NP_ISDN_MOBILE:
if (nai == GT_NAI_INTERNATIONAL_NUM) {
dissect_e164_cc(signals_tvb, digits_tree, 0, TRUE);
}
break;
case GT_NP_LAND_MOBILE:
dissect_e212_mcc_mnc_in_address(signals_tvb, pinfo, digits_tree, 0);
break;
default:
break;
}
}
static int
dissect_sccp_3byte_pc(tvbuff_t *tvb, proto_tree *call_tree, guint offset,
gboolean called)
{
int hf_pc;
if (decode_mtp3_standard == ANSI_STANDARD)
{
if (called)
hf_pc = hf_sccp_called_ansi_pc;
else
hf_pc = hf_sccp_calling_ansi_pc;
} else /* CHINESE_ITU_STANDARD */ {
if (called)
hf_pc = hf_sccp_called_chinese_pc;
else
hf_pc = hf_sccp_calling_chinese_pc;
}
/* create and fill the PC tree */
dissect_mtp3_3byte_pc(tvb, offset, call_tree,
called ? ett_sccp_called_pc : ett_sccp_calling_pc,
hf_pc,
called ? hf_sccp_called_pc_network : hf_sccp_calling_pc_network,
called ? hf_sccp_called_pc_cluster : hf_sccp_calling_pc_cluster,
called ? hf_sccp_called_pc_member : hf_sccp_calling_pc_member,
0, 0);
return(offset + ANSI_PC_LENGTH);
}
/* FUNCTION dissect_sccp_called_calling_param():
* Dissect the Calling or Called Party Address parameters.
*
* The boolean 'called' describes whether this function is decoding a
* called (TRUE) or calling (FALSE) party address. There is simply too
* much code in this function to have 2 copies of it (one for called, one
* for calling).
*
* NOTE: this function is called even when (!tree) so that we can get
* the SSN and subsequently call subdissectors (if and when there's a data
* parameter). Realistically we should put if (!tree)'s around a lot of the
* code, but I think that would make it unreadable--and the expense of not
* doing so does not appear to be very high.
*/
static void
dissect_sccp_called_calling_param(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo,
guint length, gboolean called)
{
proto_item *call_item = 0, *call_ai_item = 0, *item, *hidden_item, *expert_item;
proto_tree *call_tree = 0, *call_ai_tree = 0;
guint offset;
guint8 national = 0xFFU, routing_ind, gti, pci, ssni, ssn;
tvbuff_t *gt_tvb;
dissector_handle_t ssn_dissector = NULL, tcap_ssn_dissector = NULL;
const char *ssn_dissector_short_name = NULL;
const char *tcap_ssn_dissector_short_name = NULL;
call_item = proto_tree_add_text(tree, tvb, 0, length,
"%s Party address (%u byte%s)",
called ? "Called" : "Calling", length,
plurality(length, "", "s"));
call_tree = proto_item_add_subtree(call_item, called ? ett_sccp_called : ett_sccp_calling);
call_ai_item = proto_tree_add_text(call_tree, tvb, 0,
ADDRESS_INDICATOR_LENGTH,
"Address Indicator");
call_ai_tree = proto_item_add_subtree(call_ai_item, called ? ett_sccp_called_ai : ett_sccp_calling_ai);
if (decode_mtp3_standard == ANSI_STANDARD) {
national = tvb_get_guint8(tvb, 0) & ANSI_NATIONAL_MASK;
expert_item = proto_tree_add_uint(call_ai_tree, called ? hf_sccp_called_ansi_national_indicator
: hf_sccp_calling_ansi_national_indicator,
tvb, 0, ADDRESS_INDICATOR_LENGTH, national);
if (national == 0)
expert_add_info(pinfo, expert_item, &ei_sccp_international_standard_address);
} else {
guint8 natl_use_bit = tvb_get_guint8(tvb, 0) & ITU_RESERVED_MASK;
proto_tree_add_uint(call_ai_tree, called ? hf_sccp_called_itu_natl_use_bit
: hf_sccp_calling_itu_natl_use_bit,
tvb, 0, ADDRESS_INDICATOR_LENGTH, natl_use_bit);
}
routing_ind = tvb_get_guint8(tvb, 0) & ROUTING_INDICATOR_MASK;
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;
if (decode_mtp3_standard == ITU_STANDARD ||
decode_mtp3_standard == CHINESE_ITU_STANDARD ||
decode_mtp3_standard == JAPAN_STANDARD ||
national == 0) {
proto_tree_add_uint(call_ai_tree,
called ? hf_sccp_called_itu_global_title_indicator : hf_sccp_calling_itu_global_title_indicator,
tvb, 0, ADDRESS_INDICATOR_LENGTH, gti);
ssni = tvb_get_guint8(tvb, 0) & ITU_SSN_INDICATOR_MASK;
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(pinfo, expert_item, &ei_sccp_no_ssn_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 : hf_sccp_calling_itu_point_code_indicator,
tvb, 0, ADDRESS_INDICATOR_LENGTH, pci);
offset = ADDRESS_INDICATOR_LENGTH;
/* Dissect PC (if present) */
if (pci) {
if (decode_mtp3_standard == ITU_STANDARD || national == 0) {
if (length < offset + ITU_PC_LENGTH) {
proto_tree_add_expert_format(call_tree, pinfo, &ei_sccp_wrong_length, tvb, 0, -1,
"Wrong length indicated (%u) should be at least %u, PC is %u octets",
length, offset + ITU_PC_LENGTH, ITU_PC_LENGTH);
return;
}
proto_tree_add_item(call_tree, called ? hf_sccp_called_itu_pc : hf_sccp_calling_itu_pc,
tvb, offset, ITU_PC_LENGTH, ENC_LITTLE_ENDIAN);
offset += ITU_PC_LENGTH;
} else if (decode_mtp3_standard == JAPAN_STANDARD) {
if (length < offset + JAPAN_PC_LENGTH) {
proto_tree_add_expert_format(call_tree, pinfo, &ei_sccp_wrong_length, tvb, 0, -1,
"Wrong length indicated (%u) should be at least %u, PC is %u octets",
length, offset + JAPAN_PC_LENGTH, JAPAN_PC_LENGTH);
return;
}
proto_tree_add_item(call_tree, called ? hf_sccp_called_japan_pc : hf_sccp_calling_japan_pc,
tvb, offset, JAPAN_PC_LENGTH, ENC_LITTLE_ENDIAN);
offset += JAPAN_PC_LENGTH;
} else /* CHINESE_ITU_STANDARD */ {
if (length < offset + ANSI_PC_LENGTH) {
proto_tree_add_expert_format(call_tree, pinfo, &ei_sccp_wrong_length, tvb, 0, -1,
"Wrong length indicated (%u) should be at least %u, PC is %u octets",
length, offset + ANSI_PC_LENGTH, ANSI_PC_LENGTH);
return;
}
offset = dissect_sccp_3byte_pc(tvb, call_tree, offset, called);
}
}
/* Dissect SSN (if present) */
if (ssni) {
ssn = tvb_get_guint8(tvb, offset);
if ((routing_ind == ROUTE_ON_SSN) && (ssn == 0)) {
expert_add_info(pinfo, expert_item, &ei_sccp_ssn_zero);
}
if (called && assoc)
assoc->called_ssn = ssn;
else if (assoc)
assoc->calling_ssn = ssn;
if (is_connectionless(message_type) && sccp_msg) {
guint *ssn_ptr = called ? &(sccp_msg->data.ud.called_ssn) : &(sccp_msg->data.ud.calling_ssn);
*ssn_ptr = ssn;
}
proto_tree_add_uint(call_tree, called ? hf_sccp_called_ssn
: hf_sccp_calling_ssn,
tvb, offset, ADDRESS_SSN_LENGTH, ssn);
hidden_item = proto_tree_add_uint(call_tree, hf_sccp_ssn, tvb, offset,
ADDRESS_SSN_LENGTH, ssn);
PROTO_ITEM_SET_HIDDEN(hidden_item);
offset += ADDRESS_SSN_LENGTH;
/* Get the dissector handle of the dissector registered for this ssn
* And print its name.
*/
ssn_dissector = dissector_get_uint_handle(sccp_ssn_dissector_table, ssn);
if (ssn_dissector) {
ssn_dissector_short_name = dissector_handle_get_short_name(ssn_dissector);
if (ssn_dissector_short_name) {
item = proto_tree_add_text(call_tree, tvb, offset - 1, ADDRESS_SSN_LENGTH,
"Linked to %s", ssn_dissector_short_name);
PROTO_ITEM_SET_GENERATED(item);
if (g_ascii_strncasecmp("TCAP", ssn_dissector_short_name, 4)== 0) {
tcap_ssn_dissector = get_itu_tcap_subdissector(ssn);
if (tcap_ssn_dissector) {
tcap_ssn_dissector_short_name = dissector_handle_get_short_name(tcap_ssn_dissector);
proto_item_append_text(item,", TCAP SSN linked to %s", tcap_ssn_dissector_short_name);
}
}
} /* short name */
} /* ssn_dissector */
} /* ssni */
/* Dissect GT (if present) */
if (gti != AI_GTI_NO_GT) {
if (length < offset)
return;
gt_tvb = tvb_new_subset(tvb, offset, (length - offset),
(length - offset));
dissect_sccp_global_title(gt_tvb, pinfo, call_tree, (length - offset), gti,
(routing_ind == ROUTE_ON_GT), called);
}
} else if (decode_mtp3_standard == ANSI_STANDARD) {
proto_tree_add_uint(call_ai_tree, called ? hf_sccp_called_ansi_global_title_indicator
: hf_sccp_calling_ansi_global_title_indicator,
tvb, 0, ADDRESS_INDICATOR_LENGTH, gti);
pci = tvb_get_guint8(tvb, 0) & ANSI_PC_INDICATOR_MASK;
proto_tree_add_uint(call_ai_tree, called ? hf_sccp_called_ansi_point_code_indicator
: hf_sccp_calling_ansi_point_code_indicator,
tvb, 0, ADDRESS_INDICATOR_LENGTH, pci);
ssni = tvb_get_guint8(tvb, 0) & ANSI_SSN_INDICATOR_MASK;
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(pinfo, expert_item, &ei_sccp_no_ssn_present);
}
offset = ADDRESS_INDICATOR_LENGTH;
/* Dissect SSN (if present) */
if (ssni) {
ssn = tvb_get_guint8(tvb, offset);
if ((routing_ind == ROUTE_ON_SSN) && (ssn == 0)) {
expert_add_info(pinfo, expert_item, &ei_sccp_ssn_zero);
}
if (called && assoc) {
assoc->called_ssn = ssn;
} else if (assoc) {
assoc->calling_ssn = ssn;
}
if (is_connectionless(message_type) && sccp_msg) {
guint *ssn_ptr = called ? &(sccp_msg->data.ud.called_ssn) : &(sccp_msg->data.ud.calling_ssn);
*ssn_ptr = ssn;
}
proto_tree_add_uint(call_tree, called ? hf_sccp_called_ssn
: hf_sccp_calling_ssn,
tvb, offset, ADDRESS_SSN_LENGTH, ssn);
hidden_item = proto_tree_add_uint(call_tree, hf_sccp_ssn, tvb, offset,
ADDRESS_SSN_LENGTH, ssn);
PROTO_ITEM_SET_HIDDEN(hidden_item);
offset += ADDRESS_SSN_LENGTH;
}
/* Dissect PC (if present) */
if (pci) {
offset = dissect_sccp_3byte_pc(tvb, call_tree, offset, called);
}
/* Dissect GT (if present) */
if (gti != AI_GTI_NO_GT) {
if (length < offset)
return;
gt_tvb = tvb_new_subset(tvb, offset, (length - offset),
(length - offset));
dissect_sccp_global_title(gt_tvb, pinfo, call_tree, (length - offset), gti,
(routing_ind == ROUTE_ON_GT), called);
}
}
}
static void
dissect_sccp_called_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
dissect_sccp_called_calling_param(tvb, tree, pinfo, length, TRUE);
}
static void
dissect_sccp_calling_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
dissect_sccp_called_calling_param(tvb, tree, pinfo, length, FALSE);
}
static void
dissect_sccp_class_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
guint8 msg_class;
proto_item *pi;
gboolean invalid_class = FALSE;
if (length != 1) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 1, got %u", length);
return;
}
msg_class = tvb_get_guint8(tvb, 0) & CLASS_CLASS_MASK;
pi = proto_tree_add_uint(tree, hf_sccp_class, tvb, 0, length, msg_class);
switch (message_type) {
case SCCP_MSG_TYPE_DT1:
if (msg_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 (msg_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 ((msg_class != 2) && (msg_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 ((msg_class != 0) && (msg_class != 1))
invalid_class = TRUE;
break;
}
if (invalid_class)
expert_add_info(pinfo, pi, &ei_sccp_class_unexpected);
if (msg_class == 0 || msg_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 (try_val_to_str(handling, sccp_class_handling_values) == NULL) {
expert_add_info(pinfo, pi, &ei_sccp_handling_invalid);
}
}
}
static void
dissect_sccp_segmenting_reassembling_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
if (length != 1) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 1, got %u", length);
return;
}
proto_tree_add_item(tree, hf_sccp_more, tvb, 0, length, ENC_BIG_ENDIAN);
}
static void
dissect_sccp_receive_sequence_number_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
guint8 rsn;
if (length != 1) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 1, got %u", length);
return;
}
rsn = tvb_get_guint8(tvb, 0) >> 1;
proto_tree_add_uint(tree, hf_sccp_rsn, tvb, 0, length, rsn);
}
static void
dissect_sccp_sequencing_segmenting_param(tvbuff_t *tvb, proto_tree *tree, guint length)
{
guint8 rsn, ssn;
proto_item *param_item;
proto_tree *param_tree;
ssn = tvb_get_guint8(tvb, 0) >> 1;
rsn = tvb_get_guint8(tvb, SEQUENCING_SEGMENTING_SSN_LENGTH) >> 1;
param_item = proto_tree_add_text(tree, tvb, 0, length, "%s",
val_to_str(PARAMETER_SEQUENCING_SEGMENTING,
sccp_parameter_values, "Unknown: %d"));
param_tree = proto_item_add_subtree(param_item,
ett_sccp_sequencing_segmenting);
proto_tree_add_uint(param_tree, hf_sccp_sequencing_segmenting_ssn, tvb, 0,
SEQUENCING_SEGMENTING_SSN_LENGTH, ssn);
proto_tree_add_uint(param_tree, hf_sccp_sequencing_segmenting_rsn, tvb,
SEQUENCING_SEGMENTING_SSN_LENGTH,
SEQUENCING_SEGMENTING_RSN_LENGTH, rsn);
proto_tree_add_item(param_tree, hf_sccp_sequencing_segmenting_more, tvb,
SEQUENCING_SEGMENTING_SSN_LENGTH,
SEQUENCING_SEGMENTING_RSN_LENGTH, ENC_NA);
}
static void
dissect_sccp_credit_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
if (length != 1) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 1, got %u", length);
return;
}
proto_tree_add_item(tree, hf_sccp_credit, tvb, 0, length, ENC_NA);
}
static void
dissect_sccp_release_cause_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
if (length != 1) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 1, got %u", length);
return;
}
proto_tree_add_item(tree, hf_sccp_release_cause, tvb, 0, length, ENC_LITTLE_ENDIAN);
if (show_key_params)
col_append_fstr(pinfo->cinfo, COL_INFO, "Cause=%d ", tvb_get_guint8(tvb, 0));
}
static void
dissect_sccp_return_cause_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
if (length != 1) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 1, got %u", length);
return;
}
proto_tree_add_item(tree, hf_sccp_return_cause, tvb, 0, length, ENC_LITTLE_ENDIAN);
if (show_key_params)
col_append_fstr(pinfo->cinfo, COL_INFO, "Cause=%d ", tvb_get_guint8(tvb, 0));
}
static void
dissect_sccp_reset_cause_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
if (length != 1) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 1, got %u", length);
return;
}
proto_tree_add_item(tree, hf_sccp_reset_cause, tvb, 0, length, ENC_LITTLE_ENDIAN);
if (show_key_params)
col_append_fstr(pinfo->cinfo, COL_INFO, "Cause=%d ", tvb_get_guint8(tvb, 0));
}
static void
dissect_sccp_error_cause_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
if (length != 1) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 1, got %u", length);
return;
}
proto_tree_add_item(tree, hf_sccp_error_cause, tvb, 0, length, ENC_LITTLE_ENDIAN);
if (show_key_params)
col_append_fstr(pinfo->cinfo, COL_INFO, "Cause=%d ", tvb_get_guint8(tvb, 0));
}
static void
dissect_sccp_refusal_cause_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
if (length != 1) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 1, got %u", length);
return;
}
proto_tree_add_item(tree, hf_sccp_refusal_cause, tvb, 0, length, ENC_LITTLE_ENDIAN);
if (show_key_params)
col_append_fstr(pinfo->cinfo, COL_INFO, "Cause=%d ", tvb_get_guint8(tvb, 0));
}
/* This function is used for both data and long data (ITU only) parameters */
static void
dissect_sccp_data_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
guint8 ssn = INVALID_SSN;
guint8 other_ssn = INVALID_SSN;
const mtp3_addr_pc_t *dpc = NULL;
const mtp3_addr_pc_t *opc = NULL;
if ((trace_sccp) && (assoc && assoc != &no_assoc)) {
pinfo->sccp_info = assoc->curr_msg;
} else {
pinfo->sccp_info = NULL;
}
if (assoc) {
switch (pinfo->p2p_dir) {
case P2P_DIR_SENT:
ssn = assoc->calling_ssn;
other_ssn = assoc->called_ssn;
dpc = (const mtp3_addr_pc_t*)pinfo->dst.data;
opc = (const mtp3_addr_pc_t*)pinfo->src.data;
break;
case P2P_DIR_RECV:
ssn = assoc->called_ssn;
other_ssn = assoc->calling_ssn;
dpc = (const mtp3_addr_pc_t*)pinfo->src.data;
opc = (const mtp3_addr_pc_t*)pinfo->dst.data;
break;
default:
ssn = assoc->called_ssn;
other_ssn = assoc->calling_ssn;
dpc = (const mtp3_addr_pc_t*)pinfo->dst.data;
opc = (const mtp3_addr_pc_t*)pinfo->src.data;
break;
}
}
if ((num_sccp_users) && (pinfo->src.type == AT_SS7PC)) {
guint i;
dissector_handle_t handle = NULL;
gboolean uses_tcap = FALSE;
for (i=0; i < num_sccp_users; i++) {
sccp_user_t *u = &(sccp_users[i]);
if (!dpc || dpc->ni != u->ni) continue;
if (value_is_in_range(u->called_ssn, ssn) && value_is_in_range(u->called_pc, dpc->pc) ) {
handle = *(u->handlep);
uses_tcap = u->uses_tcap;
break;
} else if (value_is_in_range(u->called_ssn, other_ssn) && opc && value_is_in_range(u->called_pc, opc->pc) ) {
handle = *(u->handlep);
uses_tcap = u->uses_tcap;
break;
}
}
if (handle) {
if (uses_tcap) {
call_tcap_dissector(handle, tvb, pinfo, tree);
} else {
call_dissector(handle, tvb, pinfo, tree);
}
return;
}
}
if ((ssn != INVALID_SSN) && dissector_try_uint(sccp_ssn_dissector_table, ssn, tvb, pinfo, tree)) {
return;
}
if ((other_ssn != INVALID_SSN) && dissector_try_uint(sccp_ssn_dissector_table, other_ssn, tvb, pinfo, tree)) {
return;
}
/* try heuristic subdissector list to see if there are any takers */
if (dissector_try_heuristic(heur_subdissector_list, tvb, pinfo, tree, NULL)) {
return;
}
/* try user default subdissector */
if (default_handle) {
call_dissector(default_handle, tvb, pinfo, tree);
return;
}
/* No sub-dissection occurred, treat it as raw data */
call_dissector(data_handle, tvb, pinfo, tree);
}
static void
dissect_sccp_segmentation_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
proto_item *param_item;
proto_tree *param_tree;
param_item = proto_tree_add_text(tree, tvb, 0, length, "%s",
val_to_str(PARAMETER_SEGMENTATION,
sccp_parameter_values, "Unknown: %d"));
param_tree = proto_item_add_subtree(param_item, ett_sccp_segmentation);
proto_tree_add_item(param_tree, hf_sccp_segmentation_first, tvb, 0, 1, ENC_NA);
proto_tree_add_item(param_tree, hf_sccp_segmentation_class, tvb, 0, 1, ENC_NA);
proto_tree_add_item(param_tree, hf_sccp_segmentation_remaining, tvb, 0, 1, ENC_NA);
if (length-1 != 3) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length-1,
"Wrong length indicated. Expected 3, got %u", length-1);
return;
}
proto_tree_add_item(param_tree, hf_sccp_segmentation_slr, tvb, 1, length-1, ENC_LITTLE_ENDIAN);
}
static void
dissect_sccp_hop_counter_param(tvbuff_t *tvb, proto_tree *tree, guint length)
{
guint8 hops;
hops = tvb_get_guint8(tvb, 0);
proto_tree_add_uint(tree, hf_sccp_hop_counter, tvb, 0, length, hops);
}
static void
dissect_sccp_importance_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
{
if (length != 1) {
proto_tree_add_expert_format(tree, pinfo, &ei_sccp_wrong_length, tvb, 0, length,
"Wrong length indicated. Expected 1, got %u", length);
return;
}
proto_tree_add_item(tree, hf_sccp_importance, tvb, 0, length, ENC_NA);
}
static void
dissect_sccp_isni_param(tvbuff_t *tvb, proto_tree *tree, guint length)
{
guint8 ti;
guint offset = 0;
proto_item *param_item;
proto_tree *param_tree;
/* Create a subtree for ISNI Routing Control */
param_item = proto_tree_add_text(tree, tvb, offset, ANSI_ISNI_ROUTING_CONTROL_LENGTH,
"ISNI Routing Control");
param_tree = proto_item_add_subtree(param_item,
ett_sccp_ansi_isni_routing_control);
proto_tree_add_item(param_tree, hf_sccp_ansi_isni_mi, tvb, offset,
ANSI_ISNI_ROUTING_CONTROL_LENGTH, ENC_NA);
proto_tree_add_item(param_tree, hf_sccp_ansi_isni_iri, tvb, offset,
ANSI_ISNI_ROUTING_CONTROL_LENGTH, ENC_NA);
ti = tvb_get_guint8(tvb, offset) & ANSI_ISNI_TI_MASK;
proto_tree_add_uint(param_tree, hf_sccp_ansi_isni_ti, tvb, offset,
ANSI_ISNI_ROUTING_CONTROL_LENGTH, ti);
proto_tree_add_item(param_tree, hf_sccp_ansi_isni_counter, tvb, offset,
ANSI_ISNI_ROUTING_CONTROL_LENGTH, ENC_NA);
offset += ANSI_ISNI_ROUTING_CONTROL_LENGTH;
if ((ti >> ANSI_ISNI_TI_SHIFT) == ANSI_ISNI_TYPE_1) {
proto_tree_add_uint(param_tree, hf_sccp_ansi_isni_netspec, tvb, offset,
ANSI_ISNI_ROUTING_CONTROL_LENGTH, ti);
offset += ANSI_ISNI_ROUTING_CONTROL_LENGTH;
}
while (offset < length) {
proto_tree_add_item(tree, hf_sccp_ansi_isni_network, tvb, offset,
ANSI_NCM_LENGTH, ENC_NA);
offset++;
proto_tree_add_item(tree, hf_sccp_ansi_isni_cluster, tvb, offset,
ANSI_NCM_LENGTH, ENC_NA);
offset++;
}
}
/* FUNCTION dissect_sccp_parameter():
* Dissect a parameter given its type, offset into tvb, and length.
*/
static guint16
dissect_sccp_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *sccp_tree,
proto_tree *tree, guint8 parameter_type, guint16 offset,
guint16 parameter_length)
{
tvbuff_t *parameter_tvb;
switch (parameter_type) {
case PARAMETER_CALLED_PARTY_ADDRESS:
case PARAMETER_CALLING_PARTY_ADDRESS:
case PARAMETER_DATA:
case PARAMETER_LONG_DATA:
case PARAMETER_SOURCE_LOCAL_REFERENCE:
case PARAMETER_DESTINATION_LOCAL_REFERENCE:
case PARAMETER_RELEASE_CAUSE:
case PARAMETER_RETURN_CAUSE:
case PARAMETER_RESET_CAUSE:
case PARAMETER_ERROR_CAUSE:
case PARAMETER_REFUSAL_CAUSE:
/* These parameters must be dissected even if !sccp_tree (so that
* assoc information can be created).
*/
break;
default:
if (!sccp_tree) return(parameter_length);
}
parameter_tvb = tvb_new_subset(tvb, offset, parameter_length, parameter_length);
switch (parameter_type) {
case PARAMETER_END_OF_OPTIONAL_PARAMETERS:
proto_tree_add_text(sccp_tree, tvb, offset, parameter_length,
"End of Optional");
break;
case PARAMETER_DESTINATION_LOCAL_REFERENCE:
dissect_sccp_dlr_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_SOURCE_LOCAL_REFERENCE:
dissect_sccp_slr_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_CALLED_PARTY_ADDRESS:
dissect_sccp_called_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_CALLING_PARTY_ADDRESS:
dissect_sccp_calling_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_CLASS:
dissect_sccp_class_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_SEGMENTING_REASSEMBLING:
dissect_sccp_segmenting_reassembling_param(parameter_tvb, pinfo, sccp_tree,
parameter_length);
break;
case PARAMETER_RECEIVE_SEQUENCE_NUMBER:
dissect_sccp_receive_sequence_number_param(parameter_tvb, pinfo, sccp_tree,
parameter_length);
break;
case PARAMETER_SEQUENCING_SEGMENTING:
dissect_sccp_sequencing_segmenting_param(parameter_tvb, sccp_tree,
parameter_length);
break;
case PARAMETER_CREDIT:
dissect_sccp_credit_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_RELEASE_CAUSE:
dissect_sccp_release_cause_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_RETURN_CAUSE:
dissect_sccp_return_cause_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_RESET_CAUSE:
dissect_sccp_reset_cause_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_ERROR_CAUSE:
dissect_sccp_error_cause_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_REFUSAL_CAUSE:
dissect_sccp_refusal_cause_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_DATA:
dissect_sccp_data_param(parameter_tvb, pinfo, tree);
/* TODO? Re-adjust length of SCCP item since it may be sub-dissected */
/* sccp_length = proto_item_get_len(sccp_item);
* sccp_length -= parameter_length;
* proto_item_set_len(sccp_item, sccp_length);
*/
break;
case PARAMETER_SEGMENTATION:
dissect_sccp_segmentation_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
break;
case PARAMETER_HOP_COUNTER:
dissect_sccp_hop_counter_param(parameter_tvb, sccp_tree, parameter_length);
break;
case PARAMETER_IMPORTANCE:
if (decode_mtp3_standard != ANSI_STANDARD)
dissect_sccp_importance_param(parameter_tvb, pinfo, sccp_tree, parameter_length);
else
dissect_sccp_unknown_param(parameter_tvb, sccp_tree, parameter_type,
parameter_length);
break;
case PARAMETER_LONG_DATA:
dissect_sccp_data_param(parameter_tvb, pinfo, tree);
break;
case PARAMETER_ISNI:
if (decode_mtp3_standard != ANSI_STANDARD)
dissect_sccp_unknown_param(parameter_tvb, sccp_tree, parameter_type,
parameter_length);
else
dissect_sccp_isni_param(parameter_tvb, sccp_tree, parameter_length);
break;
default:
dissect_sccp_unknown_param(parameter_tvb, sccp_tree, parameter_type,
parameter_length);
break;
}
return(parameter_length);
}
/* FUNCTION dissect_sccp_variable_parameter():
* Dissect a variable parameter given its type and offset into tvb. Length
* of the parameter is gotten from tvb[0].
* Length returned is sum of (length + parameter).
*/
static guint16
dissect_sccp_variable_parameter(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *sccp_tree, proto_tree *tree,
guint8 parameter_type, guint16 offset)
{
guint16 parameter_length;
guint8 length_length;
proto_item *pi;
if (parameter_type != PARAMETER_LONG_DATA) {
parameter_length = tvb_get_guint8(tvb, offset);
length_length = PARAMETER_LENGTH_LENGTH;
} else {
/* Long data parameter has 16 bit length */
parameter_length = tvb_get_letohs(tvb, offset);
length_length = PARAMETER_LONG_DATA_LENGTH_LENGTH;
}
pi = proto_tree_add_uint_format(sccp_tree, hf_sccp_param_length, tvb, offset,
length_length, parameter_length, "%s length: %d",
val_to_str(parameter_type, sccp_parameter_values,
"Unknown: %d"),
parameter_length);
if (!sccp_show_length) {
/* The user doesn't want to see it... */
PROTO_ITEM_SET_HIDDEN(pi);
}
offset += length_length;
dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree, parameter_type, offset,
parameter_length);
return(parameter_length + length_length);
}
/* FUNCTION dissect_sccp_optional_parameters():
* Dissect all the optional parameters given the start of the optional
* parameters into tvb. Parameter types and lengths are read from tvb.
*/
static void
dissect_sccp_optional_parameters(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *sccp_tree, proto_tree *tree,
guint16 offset)
{
guint8 parameter_type;
while ((parameter_type = tvb_get_guint8(tvb, offset)) !=
PARAMETER_END_OF_OPTIONAL_PARAMETERS) {
offset += PARAMETER_TYPE_LENGTH;
offset += dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
parameter_type, offset);
}
/* Process end of optional parameters */
dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree, parameter_type, offset,
END_OF_OPTIONAL_PARAMETERS_LENGTH);
}
static sccp_msg_info_t *
new_ud_msg(packet_info *pinfo, guint32 msg_type _U_)
{
sccp_msg_info_t *m = wmem_new0(wmem_packet_scope(), sccp_msg_info_t);
m->framenum = PINFO_FD_NUM(pinfo);
m->data.ud.calling_gt = NULL;
m->data.ud.called_gt = NULL;
register_frame_end_routine(pinfo, reset_sccp_assoc);
return m;
}
static int
dissect_sccp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *sccp_tree,
proto_tree *tree)
{
guint16 variable_pointer1 = 0, variable_pointer2 = 0, variable_pointer3 = 0;
guint16 optional_pointer = 0, orig_opt_ptr = 0;
guint16 offset = 0;
gboolean save_fragmented;
tvbuff_t *new_tvb = NULL;
fragment_head *frag_msg = NULL;
guint32 source_local_ref = 0;
guint8 more;
guint msg_offset = tvb_offset_from_real_beginning(tvb);
/* Macro for getting pointer to mandatory variable parameters */
#define VARIABLE_POINTER(var, hf_var, ptr_size) \
do { \
if (ptr_size == POINTER_LENGTH) \
var = tvb_get_guint8(tvb, offset); \
else \
var = tvb_get_letohs(tvb, offset); \
proto_tree_add_uint(sccp_tree, hf_var, tvb, \
offset, ptr_size, var); \
var += offset; \
if (ptr_size == POINTER_LENGTH_LONG) \
var += 1; \
offset += ptr_size; \
} while (0)
/* Macro for getting pointer to optional parameters */
#define OPTIONAL_POINTER(ptr_size) \
do { \
if (ptr_size == POINTER_LENGTH) \
orig_opt_ptr = optional_pointer = tvb_get_guint8(tvb, offset); \
else \
orig_opt_ptr = optional_pointer = tvb_get_letohs(tvb, offset); \
proto_tree_add_uint(sccp_tree, hf_sccp_optional_pointer, tvb, \
offset, ptr_size, optional_pointer); \
optional_pointer += offset; \
if (ptr_size == POINTER_LENGTH_LONG) \
optional_pointer += 1; \
offset += ptr_size; \
} while (0)
/* Extract the message type; all other processing is based on this */
message_type = tvb_get_guint8(tvb, SCCP_MSG_TYPE_OFFSET);
offset = SCCP_MSG_TYPE_LENGTH;
/* Do not change col_add_fstr() to col_append_fstr() here: we _want_
* this call to overwrite whatever's currently in the INFO column (e.g.,
* "DATA" from the SCTP dissector).
*
* If there's something there that should not be overwritten, whoever
* put that info there should call col_set_fence() to protect it.
*/
col_add_fstr(pinfo->cinfo, COL_INFO, "%s ",
val_to_str(message_type, sccp_message_type_acro_values, "Unknown: %d"));
if (sccp_tree) {
/* add the message type to the protocol tree */
proto_tree_add_uint(sccp_tree, hf_sccp_message_type, tvb,
SCCP_MSG_TYPE_OFFSET, SCCP_MSG_TYPE_LENGTH, message_type);
};
/* Starting a new message dissection; clear the global assoc, SLR, and DLR values */
dlr = INVALID_LR;
slr = INVALID_LR;
assoc = NULL;
no_assoc.calling_dpc = 0;
no_assoc.called_dpc = 0;
no_assoc.calling_ssn = INVALID_SSN;
no_assoc.called_ssn = INVALID_SSN;
no_assoc.has_fw_key = FALSE;
no_assoc.has_bw_key = FALSE;
no_assoc.payload = SCCP_PLOAD_NONE;
no_assoc.called_party = NULL;
no_assoc.calling_party = NULL;
no_assoc.extra_info = NULL;
switch (message_type) {
case SCCP_MSG_TYPE_CR:
/* TTC and NTT (Japan) say that the connection-oriented messages are
* deleted (not standardized), but they appear to be used anyway, so
* we'll dissect it...
*/
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_SOURCE_LOCAL_REFERENCE,
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CLASS, offset,
PROTOCOL_CLASS_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
OPTIONAL_POINTER(POINTER_LENGTH);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLED_PARTY_ADDRESS,
variable_pointer1);
break;
case SCCP_MSG_TYPE_CC:
/* TODO: connection has been established; theoretically we could keep
* keep track of the SLR/DLR with the called/calling from the CR and
* track the connection (e.g., on subsequent messages regarding this
* SLR we could set the global vars "call*_ssn" so data could get
* sub-dissected).
*/
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_SOURCE_LOCAL_REFERENCE,
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CLASS, offset,
PROTOCOL_CLASS_LENGTH);
OPTIONAL_POINTER(POINTER_LENGTH);
break;
case SCCP_MSG_TYPE_CREF:
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_REFUSAL_CAUSE, offset,
REFUSAL_CAUSE_LENGTH);
OPTIONAL_POINTER(POINTER_LENGTH);
break;
case SCCP_MSG_TYPE_RLSD:
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_SOURCE_LOCAL_REFERENCE,
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_RELEASE_CAUSE, offset,
RELEASE_CAUSE_LENGTH);
OPTIONAL_POINTER(POINTER_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
break;
case SCCP_MSG_TYPE_RLC:
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_SOURCE_LOCAL_REFERENCE,
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
break;
case SCCP_MSG_TYPE_DT1:
source_local_ref = tvb_get_letoh24(tvb, offset);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
more = tvb_get_guint8(tvb, offset) & SEGMENTING_REASSEMBLING_MASK;
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_SEGMENTING_REASSEMBLING,
offset, SEGMENTING_REASSEMBLING_LENGTH);
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
/* Reassemble */
if (!sccp_xudt_desegment) {
proto_tree_add_text(sccp_tree, tvb, variable_pointer1,
tvb_get_guint8(tvb, variable_pointer1)+1,
"Segmented Data");
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DATA, variable_pointer1);
} else {
save_fragmented = pinfo->fragmented;
pinfo->fragmented = TRUE;
frag_msg = fragment_add_seq_next(&sccp_xudt_msg_reassembly_table,
tvb, variable_pointer1 + 1,
pinfo,
source_local_ref, /* ID for fragments belonging together */
NULL,
tvb_get_guint8(tvb, variable_pointer1), /* fragment length - to the end */
more); /* More fragments? */
new_tvb = process_reassembled_data(tvb, variable_pointer1 + 1, pinfo,
"Reassembled SCCP", frag_msg,
&sccp_xudt_msg_frag_items, NULL,
tree);
if (frag_msg && frag_msg->next) { /* Reassembled */
col_append_str(pinfo->cinfo, COL_INFO, "(Message reassembled) ");
} else if (more) { /* Not last packet of reassembled message */
col_append_str(pinfo->cinfo, COL_INFO, "(Message fragment) ");
}
pinfo->fragmented = save_fragmented;
if (new_tvb)
dissect_sccp_data_param(new_tvb, pinfo, tree);
}
/* End reassemble */
break;
case SCCP_MSG_TYPE_DT2:
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_SEQUENCING_SEGMENTING, offset,
SEQUENCING_SEGMENTING_LENGTH);
break;
case SCCP_MSG_TYPE_AK:
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_RECEIVE_SEQUENCE_NUMBER,
offset, RECEIVE_SEQUENCE_NUMBER_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CREDIT, offset, CREDIT_LENGTH);
break;
case SCCP_MSG_TYPE_UDT:
pinfo->sccp_info = sccp_msg = new_ud_msg(pinfo, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CLASS, offset,
PROTOCOL_CLASS_LENGTH);
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
VARIABLE_POINTER(variable_pointer2, hf_sccp_variable_pointer2, POINTER_LENGTH);
VARIABLE_POINTER(variable_pointer3, hf_sccp_variable_pointer3, POINTER_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLED_PARTY_ADDRESS,
variable_pointer1);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLING_PARTY_ADDRESS,
variable_pointer2);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree, PARAMETER_DATA,
variable_pointer3);
break;
case SCCP_MSG_TYPE_UDTS:
{
gboolean save_in_error_pkt = pinfo->flags.in_error_pkt;
pinfo->flags.in_error_pkt = TRUE;
pinfo->sccp_info = sccp_msg = new_ud_msg(pinfo, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_RETURN_CAUSE, offset,
RETURN_CAUSE_LENGTH);
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
VARIABLE_POINTER(variable_pointer2, hf_sccp_variable_pointer2, POINTER_LENGTH);
VARIABLE_POINTER(variable_pointer3, hf_sccp_variable_pointer3, POINTER_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLED_PARTY_ADDRESS,
variable_pointer1);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLING_PARTY_ADDRESS,
variable_pointer2);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree, PARAMETER_DATA,
variable_pointer3);
pinfo->flags.in_error_pkt = save_in_error_pkt;
break;
}
case SCCP_MSG_TYPE_ED:
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree, PARAMETER_DATA,
variable_pointer1);
break;
case SCCP_MSG_TYPE_EA:
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
break;
case SCCP_MSG_TYPE_RSR:
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_SOURCE_LOCAL_REFERENCE,
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_RESET_CAUSE, offset,
RESET_CAUSE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
break;
case SCCP_MSG_TYPE_RSC:
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_SOURCE_LOCAL_REFERENCE,
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
break;
case SCCP_MSG_TYPE_ERR:
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_ERROR_CAUSE, offset,
ERROR_CAUSE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
break;
case SCCP_MSG_TYPE_IT:
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DESTINATION_LOCAL_REFERENCE,
offset,
DESTINATION_LOCAL_REFERENCE_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_SOURCE_LOCAL_REFERENCE,
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CLASS, offset,
PROTOCOL_CLASS_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_SEQUENCING_SEGMENTING,
offset, SEQUENCING_SEGMENTING_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CREDIT, offset, CREDIT_LENGTH);
break;
case SCCP_MSG_TYPE_XUDT:
pinfo->sccp_info = sccp_msg = new_ud_msg(pinfo, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CLASS, offset,
PROTOCOL_CLASS_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_HOP_COUNTER, offset,
HOP_COUNTER_LENGTH);
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
VARIABLE_POINTER(variable_pointer2, hf_sccp_variable_pointer2, POINTER_LENGTH);
VARIABLE_POINTER(variable_pointer3, hf_sccp_variable_pointer3, POINTER_LENGTH);
OPTIONAL_POINTER(POINTER_LENGTH);
/* Optional parameters are Segmentation and Importance
* NOTE 2 - Segmentation Should not be present in case of a single XUDT
* message.
*/
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLED_PARTY_ADDRESS,
variable_pointer1);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLING_PARTY_ADDRESS,
variable_pointer2);
if (tvb_get_guint8(tvb, optional_pointer) == PARAMETER_SEGMENTATION) {
if (!sccp_xudt_desegment) {
proto_tree_add_text(sccp_tree, tvb, variable_pointer3, tvb_get_guint8(tvb, variable_pointer3)+1, "Segmented Data");
} else {
guint8 octet;
gboolean more_frag = TRUE;
/* Get the first octet of parameter Segmentation, Ch 3.17 in Q.713
* Bit 8 of octet 1 is used for First segment indication
* Bit 7 of octet 1 is used to keep in the message in sequence
* delivery option required by the SCCP user
* Bits 6 and 5 in octet 1 are spare bits.
* Bits 4-1 of octet 1 are used to indicate the number of
* remaining segments.
* The values 0000 to 1111 are possible; the value 0000 indicates
* the last segment.
*/
octet = tvb_get_guint8(tvb, optional_pointer+2);
source_local_ref = tvb_get_letoh24(tvb, optional_pointer+3);
if ((octet & 0x0f) == 0)
more_frag = FALSE;
save_fragmented = pinfo->fragmented;
pinfo->fragmented = TRUE;
frag_msg = fragment_add_seq_next(&sccp_xudt_msg_reassembly_table,
tvb, variable_pointer3 + 1,
pinfo,
source_local_ref, /* ID for fragments belonging together */
NULL,
tvb_get_guint8(tvb, variable_pointer3), /* fragment length - to the end */
more_frag); /* More fragments? */
if ((octet & 0x80) == 0x80) /*First segment, set number of segments*/
fragment_set_tot_len(&sccp_xudt_msg_reassembly_table,
pinfo, source_local_ref, NULL, (octet & 0xf));
new_tvb = process_reassembled_data(tvb, variable_pointer3 + 1,
pinfo, "Reassembled SCCP",
frag_msg,
&sccp_xudt_msg_frag_items,
NULL, tree);
if (frag_msg) { /* Reassembled */
col_append_str(pinfo->cinfo, COL_INFO,"(Message reassembled) ");
} else { /* Not last packet of reassembled message */
col_append_str(pinfo->cinfo, COL_INFO,"(Message fragment) ");
}
pinfo->fragmented = save_fragmented;
if (new_tvb)
dissect_sccp_data_param(new_tvb, pinfo, tree);
}
} else {
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DATA, variable_pointer3);
}
break;
case SCCP_MSG_TYPE_XUDTS:
{
gboolean save_in_error_pkt = pinfo->flags.in_error_pkt;
pinfo->flags.in_error_pkt = TRUE;
pinfo->sccp_info = sccp_msg = new_ud_msg(pinfo, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_RETURN_CAUSE, offset,
RETURN_CAUSE_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_HOP_COUNTER, offset,
HOP_COUNTER_LENGTH);
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
VARIABLE_POINTER(variable_pointer2, hf_sccp_variable_pointer2, POINTER_LENGTH);
VARIABLE_POINTER(variable_pointer3, hf_sccp_variable_pointer3, POINTER_LENGTH);
OPTIONAL_POINTER(POINTER_LENGTH);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLED_PARTY_ADDRESS,
variable_pointer1);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLING_PARTY_ADDRESS,
variable_pointer2);
if (tvb_get_guint8(tvb, optional_pointer) == PARAMETER_SEGMENTATION) {
if (!sccp_xudt_desegment) {
proto_tree_add_text(sccp_tree, tvb, variable_pointer3, tvb_get_guint8(tvb, variable_pointer3)+1, "Segmented Data");
} else {
guint8 octet;
gboolean more_frag = TRUE;
/* Get the first octet of parameter Segmentation, Ch 3.17 in Q.713
* Bit 8 of octet 1 is used for First segment indication
* Bit 7 of octet 1 is used to keep in the message in sequence
* delivery option required by the SCCP user
* Bits 6 and 5 in octet 1 are spare bits.
* Bits 4-1 of octet 1 are used to indicate the number of
* remaining segments.
* The values 0000 to 1111 are possible; the value 0000 indicates
* the last segment.
*/
octet = tvb_get_guint8(tvb, optional_pointer+2);
source_local_ref = tvb_get_letoh24(tvb, optional_pointer+3);
if ((octet & 0x0f) == 0)
more_frag = FALSE;
save_fragmented = pinfo->fragmented;
pinfo->fragmented = TRUE;
frag_msg = fragment_add_seq_next(&sccp_xudt_msg_reassembly_table,
tvb, variable_pointer3 + 1,
pinfo,
source_local_ref, /* ID for fragments belonging together */
NULL,
tvb_get_guint8(tvb, variable_pointer3), /* fragment length - to the end */
more_frag); /* More fragments? */
if ((octet & 0x80) == 0x80) /*First segment, set number of segments*/
fragment_set_tot_len(&sccp_xudt_msg_reassembly_table,
pinfo, source_local_ref, NULL, (octet & 0xf));
new_tvb = process_reassembled_data(tvb, variable_pointer3 + 1,
pinfo, "Reassembled SCCP",
frag_msg,
&sccp_xudt_msg_frag_items,
NULL, tree);
if (frag_msg) { /* Reassembled */
col_append_str(pinfo->cinfo, COL_INFO, "(Message reassembled) ");
} else { /* Not last packet of reassembled message */
col_append_str(pinfo->cinfo, COL_INFO, "(Message fragment) ");
}
pinfo->fragmented = save_fragmented;
if (new_tvb)
dissect_sccp_data_param(new_tvb, pinfo, tree);
}
} else {
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_DATA, variable_pointer3);
}
pinfo->flags.in_error_pkt = save_in_error_pkt;
break;
}
case SCCP_MSG_TYPE_LUDT:
pinfo->sccp_info = sccp_msg = new_ud_msg(pinfo, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CLASS, offset,
PROTOCOL_CLASS_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_HOP_COUNTER, offset,
HOP_COUNTER_LENGTH);
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH_LONG);
VARIABLE_POINTER(variable_pointer2, hf_sccp_variable_pointer2, POINTER_LENGTH_LONG);
VARIABLE_POINTER(variable_pointer3, hf_sccp_variable_pointer3, POINTER_LENGTH_LONG);
OPTIONAL_POINTER(POINTER_LENGTH_LONG);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLED_PARTY_ADDRESS,
variable_pointer1);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLING_PARTY_ADDRESS,
variable_pointer2);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_LONG_DATA, variable_pointer3);
break;
case SCCP_MSG_TYPE_LUDTS:
pinfo->sccp_info = sccp_msg = new_ud_msg(pinfo, message_type);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_RETURN_CAUSE, offset,
RETURN_CAUSE_LENGTH);
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_HOP_COUNTER, offset,
HOP_COUNTER_LENGTH);
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH_LONG);
VARIABLE_POINTER(variable_pointer2, hf_sccp_variable_pointer2, POINTER_LENGTH_LONG);
VARIABLE_POINTER(variable_pointer3, hf_sccp_variable_pointer3, POINTER_LENGTH_LONG);
OPTIONAL_POINTER(POINTER_LENGTH_LONG);
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLED_PARTY_ADDRESS,
variable_pointer1);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_CALLING_PARTY_ADDRESS,
variable_pointer2);
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
PARAMETER_LONG_DATA, variable_pointer3);
break;
default:
dissect_sccp_unknown_message(tvb, sccp_tree);
}
if (orig_opt_ptr)
dissect_sccp_optional_parameters(tvb, pinfo, sccp_tree, tree,
optional_pointer);
if (trace_sccp && assoc && (assoc != &no_assoc)) {
proto_item *pi = proto_tree_add_uint(sccp_tree, hf_sccp_assoc_id, tvb, 0, 0, assoc->id);
proto_tree *pt = proto_item_add_subtree(pi, ett_sccp_assoc);
PROTO_ITEM_SET_GENERATED(pi);
if (assoc->msgs) {
sccp_msg_info_t *m;
for(m = assoc->msgs; m ; m = m->data.co.next) {
pi = proto_tree_add_uint(pt, hf_sccp_assoc_msg, tvb, 0, 0, m->framenum);
if (assoc->payload != SCCP_PLOAD_NONE)
proto_item_append_text(pi," %s", val_to_str(assoc->payload, assoc_protos, "Unknown: %d"));
if (m->data.co.label)
proto_item_append_text(pi," %s", m->data.co.label);
if ((m->framenum == PINFO_FD_NUM(pinfo)) && (m->offset == msg_offset) ) {
tap_queue_packet(sccp_tap, pinfo, m);
proto_item_append_text(pi," (current)");
}
PROTO_ITEM_SET_GENERATED(pi);
}
}
}
return offset;
}
static void
dissect_sccp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
proto_item *sccp_item = NULL;
proto_tree *sccp_tree = NULL;
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)) {
/*
* Allow a protocol beneath to specify how the SCCP layer should be
* dissected.
*
* It is possible to have multiple sets of SCCP traffic some of which is
* ITU and some of which is ANSI.
* An example is A-interface traffic having ANSI MTP3/ANSI SCCP/3GPP2 IOS
* and at the same time ITU MTP3/ITU SCCP/ANSI TCAP/ANSI MAP.
*/
decode_mtp3_standard = mtp3_addr_p->type;
} else {
decode_mtp3_standard = (Standard_Type)mtp3_standard;
}
/* Make entry in the Protocol column on summary display */
switch (decode_mtp3_standard) {
case ITU_STANDARD:
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SCCP (Int. ITU)");
break;
case ANSI_STANDARD:
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SCCP (ANSI)");
break;
case CHINESE_ITU_STANDARD:
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SCCP (Chin. ITU)");
break;
case JAPAN_STANDARD:
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SCCP (Japan)");
break;
};
/* In the interest of speed, if "tree" is NULL, don't do any work not
necessary to generate protocol tree items. */
if (tree) {
/* create the sccp protocol tree */
sccp_item = proto_tree_add_item(tree, proto_sccp, tvb, 0, -1, ENC_NA);
sccp_tree = proto_item_add_subtree(sccp_item, ett_sccp);
}
/* Set whether message is UPLINK, DOWNLINK, or of UNKNOWN direction */
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
* we can safely cast them.
*/
mtp3_addr_p = (const mtp3_addr_pc_t *)pinfo->src.data;
if (sccp_source_pc_global == mtp3_addr_p->pc) {
pinfo->p2p_dir = P2P_DIR_SENT;
} 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 {
pinfo->p2p_dir = P2P_DIR_UNKNOWN;
}
}
}
/* dissect the message */
dissect_sccp_message(tvb, pinfo, sccp_tree, tree);
}
/*** SccpUsers Table **/
static struct _sccp_ul {
guint id;
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}
};
static void
sccp_users_update_cb(void *r, const char **err)
{
sccp_user_t *u = (sccp_user_t *)r;
struct _sccp_ul *c;
range_t *empty;
empty = range_empty();
if (ranges_are_equal(u->called_pc, empty)) {
*err = g_strdup("Must specify a PC");
return;
}
if (ranges_are_equal(u->called_ssn, empty)) {
*err = g_strdup("Must specify an SSN");
return;
}
for (c=user_list; c->handlep; c++) {
if (c->id == u->user) {
u->uses_tcap = c->uses_tcap;
u->handlep = c->handlep;
return;
}
}
u->uses_tcap = FALSE;
u->handlep = &data_handle;
}
static void *
sccp_users_copy_cb(void *n, const void *o, size_t siz _U_)
{
const sccp_user_t *u = (const sccp_user_t *)o;
sccp_user_t *un = (sccp_user_t *)n;
un->ni = u->ni;
un->user = u->user;
un->uses_tcap = u->uses_tcap;
un->handlep = u->handlep;
if (u->called_pc)
un->called_pc = range_copy(u->called_pc);
if (u->called_ssn)
un->called_ssn = range_copy(u->called_ssn);
return n;
}
static void
sccp_users_free_cb(void *r)
{
sccp_user_t *u = (sccp_user_t *)r;
if (u->called_pc) g_free(u->called_pc);
if (u->called_ssn) g_free(u->called_ssn);
}
UAT_DEC_CB_DEF(sccp_users, ni, sccp_user_t)
UAT_RANGE_CB_DEF(sccp_users, called_pc, sccp_user_t)
UAT_RANGE_CB_DEF(sccp_users, called_ssn, sccp_user_t)
UAT_VS_DEF(sccp_users, user, sccp_user_t, guint, SCCP_USER_DATA, "Data")
/** End SccpUsersTable **/
static void
init_sccp(void)
{
next_assoc_id = 1;
reassembly_table_init (&sccp_xudt_msg_reassembly_table,
&addresses_reassembly_table_functions);
}
/* Register the protocol with Wireshark */
void
proto_register_sccp(void)
{
/* Setup list of header fields */
static hf_register_info hf[] = {
{ &hf_sccp_message_type,
{ "Message Type", "sccp.message_type",
FT_UINT8, BASE_HEX, VALS(sccp_message_type_values), 0x0,
NULL, HFILL}
},
{ &hf_sccp_variable_pointer1,
{ "Pointer to first Mandatory Variable parameter", "sccp.variable_pointer1",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_variable_pointer2,
{ "Pointer to second Mandatory Variable parameter", "sccp.variable_pointer2",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_variable_pointer3,
{ "Pointer to third Mandatory Variable parameter", "sccp.variable_pointer3",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_optional_pointer,
{ "Pointer to Optional parameter", "sccp.optional_pointer",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_param_length,
{ "Variable parameter length", "sccp.parameter_length",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_ssn,
{ "Called or Calling SubSystem Number", "sccp.ssn",
FT_UINT8, BASE_DEC, VALS(sccp_ssn_values), 0x0,
NULL, HFILL}
},
{ &hf_sccp_gt_digits,
{ "Called or Calling GT Digits", "sccp.digits",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_sccp_called_ansi_national_indicator,
{ "National Indicator", "sccp.called.ni",
FT_UINT8, BASE_HEX, VALS(sccp_ansi_national_indicator_values), ANSI_NATIONAL_MASK,
NULL, HFILL}
},
{ &hf_sccp_called_itu_natl_use_bit,
{ "Reserved for national use", "sccp.called.reserved",
FT_UINT8, BASE_HEX, NULL, ITU_RESERVED_MASK,
NULL, HFILL}
},
{ &hf_sccp_called_routing_indicator,
{ "Routing Indicator", "sccp.called.ri",
FT_UINT8, BASE_HEX, VALS(sccp_routing_indicator_values), ROUTING_INDICATOR_MASK,
NULL, HFILL}
},
{ &hf_sccp_called_itu_global_title_indicator,
{ "Global Title Indicator", "sccp.called.gti",
FT_UINT8, BASE_HEX, VALS(sccp_itu_global_title_indicator_values), GTI_MASK,
NULL, HFILL}
},
{ &hf_sccp_called_ansi_global_title_indicator,
{ "Global Title Indicator", "sccp.called.gti",
FT_UINT8, BASE_HEX, VALS(sccp_ansi_global_title_indicator_values), GTI_MASK,
NULL, HFILL}
},
{ &hf_sccp_called_itu_ssn_indicator,
{ "SubSystem Number Indicator", "sccp.called.ssni",
FT_UINT8, BASE_HEX, VALS(sccp_ai_ssni_values), ITU_SSN_INDICATOR_MASK,
NULL, HFILL}
},
{ &hf_sccp_called_itu_point_code_indicator,
{ "Point Code Indicator", "sccp.called.pci",
FT_UINT8, BASE_HEX, VALS(sccp_ai_pci_values), ITU_PC_INDICATOR_MASK,
NULL, HFILL}
},
{ &hf_sccp_called_ansi_ssn_indicator,
{ "SubSystem Number Indicator", "sccp.called.ssni",
FT_UINT8, BASE_HEX, VALS(sccp_ai_ssni_values), ANSI_SSN_INDICATOR_MASK,
NULL, HFILL}
},
{ &hf_sccp_called_ansi_point_code_indicator,
{ "Point Code Indicator", "sccp.called.pci",
FT_UINT8, BASE_HEX, VALS(sccp_ai_pci_values), ANSI_PC_INDICATOR_MASK,
NULL, HFILL}
},
{ &hf_sccp_called_ssn,
{ "SubSystem Number", "sccp.called.ssn",
FT_UINT8, BASE_DEC, VALS(sccp_ssn_values), 0x0,
NULL, HFILL}
},
{ &hf_sccp_called_itu_pc,
{ "PC", "sccp.called.pc",
FT_UINT16, BASE_DEC, NULL, ITU_PC_MASK,
NULL, HFILL}
},
{ &hf_sccp_called_ansi_pc,
{ "PC", "sccp.called.ansi_pc",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_called_chinese_pc,
{ "PC", "sccp.called.chinese_pc",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_called_japan_pc,
{ "PC", "sccp.called.pc",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_called_pc_network,
{ "PC Network", "sccp.called.network",
FT_UINT24, BASE_DEC, NULL, ANSI_NETWORK_MASK,
NULL, HFILL }
},
{ &hf_sccp_called_pc_cluster,
{ "PC Cluster", "sccp.called.cluster",
FT_UINT24, BASE_DEC, NULL, ANSI_CLUSTER_MASK,
NULL, HFILL }
},
{ &hf_sccp_called_pc_member,
{ "PC Member", "sccp.called.member",
FT_UINT24, BASE_DEC, NULL, ANSI_MEMBER_MASK,
NULL, HFILL }
},
{ &hf_sccp_called_gt_nai,
{ "Nature of Address Indicator", "sccp.called.nai",
FT_UINT8, BASE_HEX, VALS(sccp_nai_values), GT_NAI_MASK,
NULL, HFILL }
},
{ &hf_sccp_called_gt_oe,
{ "Odd/Even Indicator", "sccp.called.oe",
FT_UINT8, BASE_HEX, VALS(sccp_oe_values), GT_OE_MASK,
NULL, HFILL }
},
{ &hf_sccp_called_gt_tt,
{ "Translation Type", "sccp.called.tt",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_sccp_called_gt_np,
{ "Numbering Plan", "sccp.called.np",
FT_UINT8, BASE_HEX, VALS(sccp_np_values), GT_NP_MASK,
NULL, HFILL }
},
{ &hf_sccp_called_gt_es,
{ "Encoding Scheme", "sccp.called.es",
FT_UINT8, BASE_HEX, VALS(sccp_es_values), GT_ES_MASK,
NULL, HFILL }
},
{ &hf_sccp_called_gt_digits,
{ "Called Party Digits", "sccp.called.digits",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_sccp_called_gt_digits_length,
{ "Number of Called Party Digits", "sccp.called.digits.length",
FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_sccp_calling_ansi_national_indicator,
{ "National Indicator", "sccp.calling.ni",
FT_UINT8, BASE_HEX, VALS(sccp_ansi_national_indicator_values), ANSI_NATIONAL_MASK,
NULL, HFILL}
},
{ &hf_sccp_calling_itu_natl_use_bit,
{ "Reserved for national use", "sccp.calling.reserved",
FT_UINT8, BASE_HEX, NULL, ITU_RESERVED_MASK,
NULL, HFILL}
},
{ &hf_sccp_calling_routing_indicator,
{ "Routing Indicator", "sccp.calling.ri",
FT_UINT8, BASE_HEX, VALS(sccp_routing_indicator_values), ROUTING_INDICATOR_MASK,
NULL, HFILL}
},
{ &hf_sccp_calling_itu_global_title_indicator,
{ "Global Title Indicator", "sccp.calling.gti",
FT_UINT8, BASE_HEX, VALS(sccp_itu_global_title_indicator_values), GTI_MASK,
NULL, HFILL}
},
{ &hf_sccp_calling_ansi_global_title_indicator,
{ "Global Title Indicator", "sccp.calling.gti",
FT_UINT8, BASE_HEX, VALS(sccp_ansi_global_title_indicator_values), GTI_MASK,
NULL, HFILL}
},
{ &hf_sccp_calling_itu_ssn_indicator,
{ "SubSystem Number Indicator", "sccp.calling.ssni",
FT_UINT8, BASE_HEX, VALS(sccp_ai_ssni_values), ITU_SSN_INDICATOR_MASK,
NULL, HFILL}
},
{ &hf_sccp_calling_itu_point_code_indicator,
{ "Point Code Indicator", "sccp.calling.pci",
FT_UINT8, BASE_HEX, VALS(sccp_ai_pci_values), ITU_PC_INDICATOR_MASK,
NULL, HFILL}
},
{ &hf_sccp_calling_ansi_ssn_indicator,
{ "SubSystem Number Indicator", "sccp.calling.ssni",
FT_UINT8, BASE_HEX, VALS(sccp_ai_ssni_values), ANSI_SSN_INDICATOR_MASK,
NULL, HFILL}
},
{ &hf_sccp_calling_ansi_point_code_indicator,
{ "Point Code Indicator", "sccp.calling.pci",
FT_UINT8, BASE_HEX, VALS(sccp_ai_pci_values), ANSI_PC_INDICATOR_MASK,
NULL, HFILL}
},
{ &hf_sccp_calling_ssn,
{ "SubSystem Number", "sccp.calling.ssn",
FT_UINT8, BASE_DEC, VALS(sccp_ssn_values), 0x0,
NULL, HFILL}
},
{ &hf_sccp_calling_itu_pc,
{ "PC", "sccp.calling.pc",
FT_UINT16, BASE_DEC, NULL, ITU_PC_MASK,
NULL, HFILL}
},
{ &hf_sccp_calling_ansi_pc,
{ "PC", "sccp.calling.ansi_pc",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_calling_chinese_pc,
{ "PC", "sccp.calling.chinese_pc",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_calling_japan_pc,
{ "PC", "sccp.calling.pc",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_calling_pc_network,
{ "PC Network", "sccp.calling.network",
FT_UINT24, BASE_DEC, NULL, ANSI_NETWORK_MASK,
NULL, HFILL }
},
{ &hf_sccp_calling_pc_cluster,
{ "PC Cluster", "sccp.calling.cluster",
FT_UINT24, BASE_DEC, NULL, ANSI_CLUSTER_MASK,
NULL, HFILL }
},
{ &hf_sccp_calling_pc_member,
{ "PC Member", "sccp.calling.member",
FT_UINT24, BASE_DEC, NULL, ANSI_MEMBER_MASK,
NULL, HFILL }
},
{ &hf_sccp_calling_gt_nai,
{ "Nature of Address Indicator", "sccp.calling.nai",
FT_UINT8, BASE_HEX, VALS(sccp_nai_values), GT_NAI_MASK,
NULL, HFILL }
},
{ &hf_sccp_calling_gt_oe,
{ "Odd/Even Indicator", "sccp.calling.oe",
FT_UINT8, BASE_HEX, VALS(sccp_oe_values), GT_OE_MASK,
NULL, HFILL }
},
{ &hf_sccp_calling_gt_tt,
{ "Translation Type", "sccp.calling.tt",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_sccp_calling_gt_np,
{ "Numbering Plan", "sccp.calling.np",
FT_UINT8, BASE_HEX, VALS(sccp_np_values), GT_NP_MASK,
NULL, HFILL }
},
{ &hf_sccp_calling_gt_es,
{ "Encoding Scheme", "sccp.calling.es",
FT_UINT8, BASE_HEX, VALS(sccp_es_values), GT_ES_MASK,
NULL, HFILL }
},
{ &hf_sccp_calling_gt_digits,
{ "Calling Party Digits", "sccp.calling.digits",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_sccp_calling_gt_digits_length,
{ "Number of Calling Party Digits", "sccp.calling.digits.length",
FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_sccp_dlr,
{ "Destination Local Reference", "sccp.dlr",
FT_UINT24, BASE_HEX, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_slr,
{ "Source Local Reference", "sccp.slr",
FT_UINT24, BASE_HEX, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_lr,
{ "Local Reference", "sccp.lr",
FT_UINT24, BASE_HEX, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_class,
{ "Class", "sccp.class",
FT_UINT8, BASE_HEX, NULL, CLASS_CLASS_MASK,
NULL, HFILL}
},
{ &hf_sccp_handling,
{ "Message handling", "sccp.handling",
FT_UINT8, BASE_HEX, VALS(sccp_class_handling_values), CLASS_SPARE_HANDLING_MASK,
NULL, HFILL}
},
{ &hf_sccp_more,
{ "More data", "sccp.more",
FT_UINT8, BASE_HEX, VALS(sccp_segmenting_reassembling_values), SEGMENTING_REASSEMBLING_MASK,
NULL, HFILL}
},
{ &hf_sccp_rsn,
{ "Receive Sequence Number", "sccp.rsn",
FT_UINT8, BASE_HEX, NULL, RSN_MASK,
NULL, HFILL}
},
{ &hf_sccp_sequencing_segmenting_ssn,
{ "Sequencing Segmenting: Send Sequence Number", "sccp.sequencing_segmenting.ssn",
FT_UINT8, BASE_HEX, NULL, SEND_SEQUENCE_NUMBER_MASK,
NULL, HFILL}
},
{ &hf_sccp_sequencing_segmenting_rsn,
{ "Sequencing Segmenting: Receive Sequence Number", "sccp.sequencing_segmenting.rsn",
FT_UINT8, BASE_HEX, NULL, RECEIVE_SEQUENCE_NUMBER_MASK,
NULL, HFILL}
},
{ &hf_sccp_sequencing_segmenting_more,
{ "Sequencing Segmenting: More", "sccp.sequencing_segmenting.more",
FT_UINT8, BASE_HEX, VALS(sccp_segmenting_reassembling_values), SEQUENCING_SEGMENTING_MORE_MASK,
NULL, HFILL}
},
{ &hf_sccp_credit,
{ "Credit", "sccp.credit",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_release_cause,
{ "Release Cause", "sccp.release_cause",
FT_UINT8, BASE_HEX, VALS(sccp_release_cause_values), 0x0,
NULL, HFILL}
},
{ &hf_sccp_return_cause,
{ "Return Cause", "sccp.return_cause",
FT_UINT8, BASE_HEX, VALS(sccp_return_cause_values), 0x0,
NULL, HFILL}
},
{ &hf_sccp_reset_cause,
{ "Reset Cause", "sccp.reset_cause",
FT_UINT8, BASE_HEX, VALS(sccp_reset_cause_values), 0x0,
NULL, HFILL}
},
{ &hf_sccp_error_cause,
{ "Error Cause", "sccp.error_cause",
FT_UINT8, BASE_HEX, VALS(sccp_error_cause_values), 0x0,
NULL, HFILL}
},
{ &hf_sccp_refusal_cause,
{ "Refusal Cause", "sccp.refusal_cause",
FT_UINT8, BASE_HEX, VALS(sccp_refusal_cause_values), 0x0,
NULL, HFILL}
},
{ &hf_sccp_segmentation_first,
{ "Segmentation: First", "sccp.segmentation.first",
FT_UINT8, BASE_HEX, VALS(sccp_segmentation_first_segment_values), SEGMENTATION_FIRST_SEGMENT_MASK,
NULL, HFILL}
},
{ &hf_sccp_segmentation_class,
{ "Segmentation: Class", "sccp.segmentation.class",
FT_UINT8, BASE_HEX, VALS(sccp_segmentation_class_values), SEGMENTATION_CLASS_MASK,
NULL, HFILL}
},
{ &hf_sccp_segmentation_remaining,
{ "Segmentation: Remaining", "sccp.segmentation.remaining",
FT_UINT8, BASE_HEX, NULL, SEGMENTATION_REMAINING_MASK,
NULL, HFILL}
},
{ &hf_sccp_segmentation_slr,
{ "Segmentation: Source Local Reference", "sccp.segmentation.slr",
FT_UINT24, BASE_HEX, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_hop_counter,
{ "Hop Counter", "sccp.hops",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_importance,
{ "Importance", "sccp.importance",
FT_UINT8, BASE_HEX, NULL, IMPORTANCE_IMPORTANCE_MASK,
NULL, HFILL}
},
/* ISNI is ANSI only */
{ &hf_sccp_ansi_isni_mi,
{ "ISNI Mark for Identification Indicator", "sccp.isni.mi",
FT_UINT8, BASE_HEX, VALS(sccp_isni_mark_for_id_values), ANSI_ISNI_MI_MASK,
NULL, HFILL}
},
{ &hf_sccp_ansi_isni_iri,
{ "ISNI Routing Indicator", "sccp.isni.iri",
FT_UINT8, BASE_HEX, VALS(sccp_isni_iri_values), ANSI_ISNI_IRI_MASK,
NULL, HFILL}
},
{ &hf_sccp_ansi_isni_ti,
{ "ISNI Type Indicator", "sccp.isni.ti",
FT_UINT8, BASE_HEX, VALS(sccp_isni_ti_values), ANSI_ISNI_TI_MASK,
NULL, HFILL}
},
{ &hf_sccp_ansi_isni_netspec,
{ "ISNI Network Specific (Type 1)", "sccp.isni.netspec",
FT_UINT8, BASE_HEX, NULL, ANSI_ISNI_NETSPEC_MASK,
NULL, HFILL}
},
{ &hf_sccp_ansi_isni_counter,
{ "ISNI Counter", "sccp.isni.counter",
FT_UINT8, BASE_DEC, NULL, ANSI_ISNI_COUNTER_MASK,
NULL, HFILL}
},
{ &hf_sccp_ansi_isni_network,
{ "Network ID network", "sccp.isni.network",
FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sccp_ansi_isni_cluster,
{ "Network ID cluster", "sccp.isni.cluster",
FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL}
},
{&hf_sccp_xudt_msg_fragments,
{ "Message fragments", "sccp.msg.fragments",
FT_NONE, BASE_NONE, NULL, 0x00,
NULL, HFILL }
},
{&hf_sccp_xudt_msg_fragment,
{ "Message fragment", "sccp.msg.fragment",
FT_FRAMENUM, BASE_NONE, NULL, 0x00,
NULL, HFILL }
},
{&hf_sccp_xudt_msg_fragment_overlap,
{ "Message fragment overlap", "sccp.msg.fragment.overlap",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{&hf_sccp_xudt_msg_fragment_overlap_conflicts,
{ "Message fragment overlapping with conflicting data", "sccp.msg.fragment.overlap.conflicts",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{&hf_sccp_xudt_msg_fragment_multiple_tails,
{ "Message has multiple tail fragments", "sccp.msg.fragment.multiple_tails",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{&hf_sccp_xudt_msg_fragment_too_long_fragment,
{ "Message fragment too long", "sccp.msg.fragment.too_long_fragment",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{&hf_sccp_xudt_msg_fragment_error,
{ "Message defragmentation error", "sccp.msg.fragment.error",
FT_FRAMENUM, BASE_NONE, NULL, 0x00,
NULL, HFILL }
},
{&hf_sccp_xudt_msg_fragment_count,
{ "Message fragment count", "sccp.msg.fragment.count",
FT_UINT32, BASE_DEC, NULL, 0x00,
NULL, HFILL }
},
{&hf_sccp_xudt_msg_reassembled_in,
{ "Reassembled in", "sccp.msg.reassembled.in",
FT_FRAMENUM, BASE_NONE, NULL, 0x00,
NULL, HFILL }
},
{&hf_sccp_xudt_msg_reassembled_length,
{ "Reassembled SCCP length", "sccp.msg.reassembled.length",
FT_UINT32, BASE_DEC, NULL, 0x00,
NULL, HFILL }
},
{ &hf_sccp_assoc_id,
{ "Association ID", "sccp.assoc.id",
FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL}
},
{&hf_sccp_assoc_msg,
{ "Message in frame", "sccp.assoc.msg",
FT_FRAMENUM, BASE_NONE, NULL, 0x00,
NULL, HFILL }
},
};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_sccp,
&ett_sccp_called,
&ett_sccp_called_ai,
&ett_sccp_called_pc,
&ett_sccp_called_gt,
&ett_sccp_called_gt_digits,
&ett_sccp_calling,
&ett_sccp_calling_ai,
&ett_sccp_calling_pc,
&ett_sccp_calling_gt,
&ett_sccp_calling_gt_digits,
&ett_sccp_sequencing_segmenting,
&ett_sccp_segmentation,
&ett_sccp_ansi_isni_routing_control,
&ett_sccp_xudt_msg_fragment,
&ett_sccp_xudt_msg_fragments,
&ett_sccp_assoc
};
static ei_register_info ei[] = {
{ &ei_sccp_wrong_length, { "sccp.wrong_length", PI_MALFORMED, PI_ERROR, "Wrong length indicated.", EXPFILL }},
{ &ei_sccp_international_standard_address, { "sccp.international_standard_address", PI_MALFORMED, PI_WARN,
"Address is coded to international standards. This doesn't normally happen in ANSI networks.", EXPFILL }},
{ &ei_sccp_no_ssn_present, { "sccp.ssn.not_present", PI_PROTOCOL, PI_WARN, "Message is routed on SSN, but SSN is not present", EXPFILL }},
{ &ei_sccp_ssn_zero, { "sccp.ssn.is_zero", PI_PROTOCOL, PI_WARN, "Message is routed on SSN, but SSN is zero (unspecified)", EXPFILL }},
{ &ei_sccp_class_unexpected, { "sccp.class_unexpected", PI_MALFORMED, PI_ERROR, "Unexpected message class for this message type", EXPFILL }},
{ &ei_sccp_handling_invalid, { "sccp.handling_invalid", PI_MALFORMED, PI_ERROR, "Invalid message handling", EXPFILL }},
};
module_t *sccp_module;
expert_module_t* expert_sccp;
static uat_field_t users_flds[] = {
UAT_FLD_DEC(sccp_users, ni, "Network Indicator", "Network Indicator"),
UAT_FLD_RANGE(sccp_users, called_pc, "Called DPCs", 0xFFFFFF, "DPCs for which this protocol is to be used"),
UAT_FLD_RANGE(sccp_users, called_ssn, "Called SSNs", 255, "Called SSNs for which this protocol is to be used"),
UAT_FLD_VS(sccp_users, user, "User protocol", sccp_users_vals, "The User Protocol"),
UAT_END_FIELDS
};
uat_t *users_uat = uat_new("SCCP Users Table", sizeof(sccp_user_t),
"sccp_users", TRUE, (void**) &sccp_users,
&num_sccp_users, UAT_AFFECTS_DISSECTION,
"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",
"SCCP", "sccp");
register_dissector("sccp", dissect_sccp, proto_sccp);
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_sccp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_sccp = expert_register_protocol(proto_sccp);
expert_register_field_array(expert_sccp, ei, array_length(ei));
sccp_ssn_dissector_table = register_dissector_table("sccp.ssn", "SCCP SSN", FT_UINT8, BASE_DEC);
register_heur_dissector_list("sccp", &heur_subdissector_list);
sccp_module = prefs_register_protocol(proto_sccp, proto_reg_handoff_sccp);
prefs_register_uint_preference(sccp_module, "source_pc",
"Source PC (in hex)",
"The source point code (usually MSC) (to determine whether message is uplink or downlink)",
16, &sccp_source_pc_global);
prefs_register_bool_preference(sccp_module, "show_length", "Show length",
"Show parameter length in the protocol tree",
&sccp_show_length);
prefs_register_bool_preference(sccp_module, "defragment_xudt",
"Reassemble XUDT messages",
"Whether XUDT messages should be reassembled",
&sccp_xudt_desegment);
prefs_register_bool_preference(sccp_module, "trace_sccp",
"Trace Associations",
"Whether to keep information about messages and their associations",
&trace_sccp);
prefs_register_bool_preference(sccp_module, "show_more_info",
"Show key parameters in Info Column",
"Show SLR, DLR, and CAUSE Parameters in the Information Column of the Summary",
&show_key_params);
prefs_register_uat_preference(sccp_module, "users_table", "Users Table",
"A table that enumerates user protocols to be used against specific PCs and SSNs",
users_uat);
prefs_register_bool_preference(sccp_module, "set_addresses", "Set source and destination GT addresses",
"Set the source and destination addresses to the GT digits (if RI=GT)."
" 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);
register_init_routine(&init_sccp);
assocs = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
sccp_tap = register_tap("sccp");
}
void
proto_reg_handoff_sccp(void)
{
dissector_handle_t sccp_handle;
static gboolean initialised = FALSE;
if (!initialised) {
sccp_handle = find_dissector("sccp");
dissector_add_uint("wtap_encap", WTAP_ENCAP_SCCP, sccp_handle);
dissector_add_uint("mtp3.service_indicator", MTP_SI_SCCP, sccp_handle);
dissector_add_string("tali.opcode", "sccp", sccp_handle);
data_handle = find_dissector("data");
tcap_handle = find_dissector("tcap");
ranap_handle = find_dissector("ranap");
bssap_handle = find_dissector("bssap");
gsmmap_handle = find_dissector("gsm_map");
camel_handle = find_dissector("camel");
inap_handle = find_dissector("inap");
initialised = TRUE;
}
default_handle = find_dissector(default_payload);
}