![Michael Mann](/assets/img/avatar_default.png)
Change-Id: I7489e2fb3a1f2630ca17b0a5fe1aa873992f1061 Reviewed-on: https://code.wireshark.org/review/975 Reviewed-by: Michael Mann <mmann78@netscape.net>
4168 lines
146 KiB
C
4168 lines
146 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>
|
|
*
|
|
* 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 <epan/to_str.h>
|
|
#include <wiretap/wtap.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_register_sccp(void);
|
|
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;
|
|
|
|
|
|
static gboolean sccp_reassemble = 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(address_to_str(wmem_packet_scope(), opc));
|
|
dpck = dpc->type == AT_SS7PC ? mtp3_pc_hash((const mtp3_addr_pc_t *)dpc->data) : g_str_hash(address_to_str(wmem_packet_scope(), 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[4];
|
|
|
|
bw_key[0].length = 1;
|
|
bw_key[0].key = &dpck;
|
|
|
|
bw_key[1].length = 1;
|
|
bw_key[1].key = &opck;
|
|
|
|
bw_key[2].length = 1;
|
|
bw_key[2].key = &src_lr;
|
|
|
|
bw_key[3].length = 0;
|
|
bw_key[3].key = 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[4];
|
|
wmem_tree_key_t bw_key[4];
|
|
|
|
fw_key[0].length = 1;
|
|
fw_key[0].key = &dpck;
|
|
|
|
fw_key[1].length = 1;
|
|
fw_key[1].key = &opck;
|
|
|
|
fw_key[2].length = 1;
|
|
fw_key[2].key = &src_lr;
|
|
|
|
fw_key[3].length = 0;
|
|
fw_key[3].key = NULL;
|
|
|
|
bw_key[0].length = 1;
|
|
bw_key[0].key = &opck;
|
|
|
|
bw_key[1].length = 1;
|
|
bw_key[1].key = &dpck;
|
|
|
|
bw_key[2].length = 1;
|
|
bw_key[2].key = &dst_lr;
|
|
|
|
bw_key[3].length = 0;
|
|
bw_key[3].key = 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 fw_key[4];
|
|
wmem_tree_key_t bw_key[4];
|
|
|
|
fw_key[0].length = 1;
|
|
fw_key[0].key = &dpck;
|
|
|
|
fw_key[1].length = 1;
|
|
fw_key[1].key = &opck;
|
|
|
|
fw_key[2].length = 1;
|
|
fw_key[2].key = &src_lr;
|
|
|
|
fw_key[3].length = 0;
|
|
fw_key[3].key = NULL;
|
|
|
|
bw_key[0].length = 1;
|
|
bw_key[0].key = &opck;
|
|
|
|
bw_key[1].length = 1;
|
|
bw_key[1].key = &dpck;
|
|
|
|
bw_key[2].length = 1;
|
|
bw_key[2].key = &dst_lr;
|
|
|
|
bw_key[3].length = 0;
|
|
bw_key[3].key = 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[4];
|
|
|
|
key[0].length = 1;
|
|
key[0].key = &opck;
|
|
|
|
key[1].length = 1;
|
|
key[1].key = &dpck;
|
|
|
|
key[2].length = 1;
|
|
key[2].key = &dst_lr;
|
|
|
|
key[3].length = 0;
|
|
key[3].key = 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_reassemble) {
|
|
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_reassemble) {
|
|
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_reassemble) {
|
|
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, &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 SCCP messages",
|
|
"Whether SCCP messages should be reassembled",
|
|
&sccp_reassemble);
|
|
|
|
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);
|
|
}
|