![Guy Harris](/assets/img/avatar_default.png)
implicitly by the #define name and string they were defined to; not all UATs neatly fit into any of the categories, so some of them were put into categories that weren't obviously correct for them, and one - the display filter macro UAT - wasn't put into any category at all (which caused crashes when editing them, as the GUI code that handled UAT changes from a dialog assumed the category field was non-null). The category was, in practice, used only to decide, in the aforementioned GUI code, whether the packet summary pane needed to be updated or not. It also offered no option of "don't update the packet summary pane *and* don't redissect anything", which is what would be appropriate for the display filter macro UAT. Replace the category with a set of fields indicating what the UAT affects; we currently offer "dissection", which applies to most UATs (any UAT in libwireshark presumably affects dissection at a minimum) and "the set of named fields that exist". Changing any UAT that affects dissection requires a redissection; changing any UAT that affects the set of named fields that exist requires a redissection *and* rebuilding the packet summary pane. Perhaps we also need "filtering", so that if you change a display filter macro, we re-filter, in case the display is currently filtered with a display filter that uses a macro that changed. svn path=/trunk/; revision=43603
4101 lines
146 KiB
C
4101 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>
|
|
*
|
|
* $Id$
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* Copied from packet-m2pa.c
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include <epan/packet.h>
|
|
#include <epan/prefs.h>
|
|
#include <epan/emem.h>
|
|
#include <epan/reassemble.h>
|
|
#include <epan/asn1.h>
|
|
#include <epan/uat.h>
|
|
#include <epan/strutil.h>
|
|
#include <epan/expert.h>
|
|
#include <epan/tap.h>
|
|
#include <address.h>
|
|
#include "packet-mtp3.h"
|
|
#include "packet-tcap.h"
|
|
#include "packet-sccp.h"
|
|
#include "packet-e164.h"
|
|
#include "packet-e212.h"
|
|
#include "packet-frame.h"
|
|
|
|
/* function prototypes */
|
|
void proto_reg_handoff_sccp(void);
|
|
|
|
static Standard_Type decode_mtp3_standard;
|
|
|
|
#define SCCP_MSG_TYPE_OFFSET 0
|
|
#define SCCP_MSG_TYPE_LENGTH 1
|
|
#define POINTER_LENGTH 1
|
|
#define POINTER_LENGTH_LONG 2
|
|
|
|
#define INVALID_LR 0xffffff /* a reserved value */
|
|
|
|
/* Same as below but with names typed out */
|
|
static const value_string sccp_message_type_values[] = {
|
|
{ SCCP_MSG_TYPE_CR, "Connection Request" },
|
|
{ SCCP_MSG_TYPE_CC, "Connection Confirm" },
|
|
{ SCCP_MSG_TYPE_CREF, "Connection Refused" },
|
|
{ SCCP_MSG_TYPE_RLSD, "Released" },
|
|
{ SCCP_MSG_TYPE_RLC, "Release Complete" },
|
|
{ SCCP_MSG_TYPE_DT1, "Data Form 1" },
|
|
{ SCCP_MSG_TYPE_DT2, "Data Form 2" },
|
|
{ SCCP_MSG_TYPE_AK, "Data Acknowledgement" },
|
|
{ SCCP_MSG_TYPE_UDT, "Unitdata" },
|
|
{ SCCP_MSG_TYPE_UDTS, "Unitdata Service" },
|
|
{ SCCP_MSG_TYPE_ED, "Expedited Data" },
|
|
{ SCCP_MSG_TYPE_EA, "Expedited Data Acknowledgement" },
|
|
{ SCCP_MSG_TYPE_RSR, "Reset Request" },
|
|
{ SCCP_MSG_TYPE_RSC, "Reset Confirmation" },
|
|
{ SCCP_MSG_TYPE_ERR, "Error" },
|
|
{ SCCP_MSG_TYPE_IT, "Inactivity Timer" },
|
|
{ SCCP_MSG_TYPE_XUDT, "Extended Unitdata" },
|
|
{ SCCP_MSG_TYPE_XUDTS, "Extended Unitdata Service" },
|
|
{ SCCP_MSG_TYPE_LUDT, "Long Unitdata" },
|
|
{ SCCP_MSG_TYPE_LUDTS, "Long Unitdata Service" },
|
|
{ 0, NULL } };
|
|
|
|
/* Same as above but in acronym form (for the Info column) */
|
|
const value_string sccp_message_type_acro_values[] = {
|
|
{ SCCP_MSG_TYPE_CR, "CR" },
|
|
{ SCCP_MSG_TYPE_CC, "CC" },
|
|
{ SCCP_MSG_TYPE_CREF, "CREF" },
|
|
{ SCCP_MSG_TYPE_RLSD, "RLSD" },
|
|
{ SCCP_MSG_TYPE_RLC, "RLC" },
|
|
{ SCCP_MSG_TYPE_DT1, "DT1" },
|
|
{ SCCP_MSG_TYPE_DT2, "DT2" },
|
|
{ SCCP_MSG_TYPE_AK, "AK" },
|
|
{ SCCP_MSG_TYPE_UDT, "UDT" },
|
|
{ SCCP_MSG_TYPE_UDTS, "UDTS" },
|
|
{ SCCP_MSG_TYPE_ED, "ED" },
|
|
{ SCCP_MSG_TYPE_EA, "EA" },
|
|
{ SCCP_MSG_TYPE_RSR, "RSR" },
|
|
{ SCCP_MSG_TYPE_RSC, "RSC" },
|
|
{ SCCP_MSG_TYPE_ERR, "ERR" },
|
|
{ SCCP_MSG_TYPE_IT, "IT" },
|
|
{ SCCP_MSG_TYPE_XUDT, "XUDT" },
|
|
{ SCCP_MSG_TYPE_XUDTS, "XUDTS" },
|
|
{ SCCP_MSG_TYPE_LUDT, "LUDT" },
|
|
{ SCCP_MSG_TYPE_LUDTS, "LUDTS" },
|
|
{ 0, NULL } };
|
|
|
|
#define PARAMETER_LENGTH_LENGTH 1
|
|
#define PARAMETER_LONG_DATA_LENGTH_LENGTH 2
|
|
#define PARAMETER_TYPE_LENGTH 1
|
|
|
|
#define PARAMETER_END_OF_OPTIONAL_PARAMETERS 0x00
|
|
#define PARAMETER_DESTINATION_LOCAL_REFERENCE 0x01
|
|
#define PARAMETER_SOURCE_LOCAL_REFERENCE 0x02
|
|
#define PARAMETER_CALLED_PARTY_ADDRESS 0x03
|
|
#define PARAMETER_CALLING_PARTY_ADDRESS 0x04
|
|
#define PARAMETER_CLASS 0x05
|
|
#define PARAMETER_SEGMENTING_REASSEMBLING 0x06
|
|
#define PARAMETER_RECEIVE_SEQUENCE_NUMBER 0x07
|
|
#define PARAMETER_SEQUENCING_SEGMENTING 0x08
|
|
#define PARAMETER_CREDIT 0x09
|
|
#define PARAMETER_RELEASE_CAUSE 0x0a
|
|
#define PARAMETER_RETURN_CAUSE 0x0b
|
|
#define PARAMETER_RESET_CAUSE 0x0c
|
|
#define PARAMETER_ERROR_CAUSE 0x0d
|
|
#define PARAMETER_REFUSAL_CAUSE 0x0e
|
|
#define PARAMETER_DATA 0x0f
|
|
#define PARAMETER_SEGMENTATION 0x10
|
|
#define PARAMETER_HOP_COUNTER 0x11
|
|
/* Importance is ITU only */
|
|
#define PARAMETER_IMPORTANCE 0x12
|
|
#define PARAMETER_LONG_DATA 0x13
|
|
/* ISNI is ANSI only */
|
|
#define PARAMETER_ISNI 0xfa
|
|
|
|
static const value_string sccp_parameter_values[] = {
|
|
{ PARAMETER_END_OF_OPTIONAL_PARAMETERS, "End of Optional Parameters" },
|
|
{ PARAMETER_DESTINATION_LOCAL_REFERENCE, "Destination Local Reference" },
|
|
{ PARAMETER_SOURCE_LOCAL_REFERENCE, "Source Local Reference" },
|
|
{ PARAMETER_CALLED_PARTY_ADDRESS, "Called Party Address" },
|
|
{ PARAMETER_CALLING_PARTY_ADDRESS, "Calling Party Address" },
|
|
{ PARAMETER_CLASS, "Protocol Class" },
|
|
{ PARAMETER_SEGMENTING_REASSEMBLING, "Segmenting/Reassembling" },
|
|
{ PARAMETER_RECEIVE_SEQUENCE_NUMBER, "Receive Sequence Number" },
|
|
{ PARAMETER_SEQUENCING_SEGMENTING, "Sequencing/Segmenting" },
|
|
{ PARAMETER_CREDIT, "Credit" },
|
|
{ PARAMETER_RELEASE_CAUSE, "Release Cause" },
|
|
{ PARAMETER_RETURN_CAUSE, "Return Cause" },
|
|
{ PARAMETER_RESET_CAUSE, "Reset Cause" },
|
|
{ PARAMETER_ERROR_CAUSE, "Error Cause" },
|
|
{ PARAMETER_REFUSAL_CAUSE, "Refusal Cause" },
|
|
{ PARAMETER_DATA, "Data" },
|
|
{ PARAMETER_SEGMENTATION, "Segmentation" },
|
|
{ PARAMETER_HOP_COUNTER, "Hop Counter" },
|
|
{ PARAMETER_IMPORTANCE, "Importance (ITU)" },
|
|
{ PARAMETER_LONG_DATA, "Long Data" },
|
|
{ PARAMETER_ISNI, "Intermediate Signaling Network Identification (ANSI)" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
#define END_OF_OPTIONAL_PARAMETERS_LENGTH 1
|
|
#define DESTINATION_LOCAL_REFERENCE_LENGTH 3
|
|
#define SOURCE_LOCAL_REFERENCE_LENGTH 3
|
|
#define PROTOCOL_CLASS_LENGTH 1
|
|
#define RECEIVE_SEQUENCE_NUMBER_LENGTH 1
|
|
#define CREDIT_LENGTH 1
|
|
#define RELEASE_CAUSE_LENGTH 1
|
|
#define RETURN_CAUSE_LENGTH 1
|
|
#define RESET_CAUSE_LENGTH 1
|
|
#define ERROR_CAUSE_LENGTH 1
|
|
#define REFUSAL_CAUSE_LENGTH 1
|
|
#define HOP_COUNTER_LENGTH 1
|
|
#define IMPORTANCE_LENGTH 1
|
|
|
|
|
|
/* Parts of the Called and Calling Address parameters */
|
|
/* Address Indicator */
|
|
#define ADDRESS_INDICATOR_LENGTH 1
|
|
#define ITU_RESERVED_MASK 0x80
|
|
#define ANSI_NATIONAL_MASK 0x80
|
|
#define ROUTING_INDICATOR_MASK 0x40
|
|
#define GTI_MASK 0x3C
|
|
#define GTI_SHIFT 2
|
|
#define ITU_SSN_INDICATOR_MASK 0x02
|
|
#define ITU_PC_INDICATOR_MASK 0x01
|
|
#define ANSI_PC_INDICATOR_MASK 0x02
|
|
#define ANSI_SSN_INDICATOR_MASK 0x01
|
|
|
|
static const value_string sccp_ansi_national_indicator_values[] = {
|
|
{ 0x0, "Address coded to International standard" },
|
|
{ 0x1, "Address coded to National standard" },
|
|
{ 0, NULL } };
|
|
|
|
#define ROUTE_ON_GT 0x0
|
|
#define ROUTE_ON_SSN 0x1
|
|
#define ROUTING_INDICATOR_SHIFT 6
|
|
static const value_string sccp_routing_indicator_values[] = {
|
|
{ ROUTE_ON_GT, "Route on GT" },
|
|
{ ROUTE_ON_SSN, "Route on SSN" },
|
|
{ 0, NULL } };
|
|
|
|
#define AI_GTI_NO_GT 0x0
|
|
#define ITU_AI_GTI_NAI 0x1
|
|
#define AI_GTI_TT 0x2
|
|
#define ITU_AI_GTI_TT_NP_ES 0x3
|
|
#define ITU_AI_GTI_TT_NP_ES_NAI 0x4
|
|
static const value_string sccp_itu_global_title_indicator_values[] = {
|
|
{ AI_GTI_NO_GT, "No Global Title" },
|
|
{ ITU_AI_GTI_NAI, "Nature of Address Indicator only" },
|
|
{ AI_GTI_TT, "Translation Type only" },
|
|
{ ITU_AI_GTI_TT_NP_ES, "Translation Type, Numbering Plan, and Encoding Scheme included" },
|
|
{ ITU_AI_GTI_TT_NP_ES_NAI, "Translation Type, Numbering Plan, Encoding Scheme, and Nature of Address Indicator included" },
|
|
{ 0, NULL } };
|
|
|
|
/* #define AI_GTI_NO_GT 0x0 */
|
|
#define ANSI_AI_GTI_TT_NP_ES 0x1
|
|
/* #define AI_GTI_TT 0x2 */
|
|
static const value_string sccp_ansi_global_title_indicator_values[] = {
|
|
{ AI_GTI_NO_GT, "No Global Title" },
|
|
{ ANSI_AI_GTI_TT_NP_ES, "Translation Type, Numbering Plan, and Encoding Scheme included" },
|
|
{ AI_GTI_TT, "Translation Type only" },
|
|
{ 0, NULL } };
|
|
|
|
static const value_string sccp_ai_pci_values[] = {
|
|
{ 0x1, "Point Code present" },
|
|
{ 0x0, "Point Code not present" },
|
|
{ 0, NULL } };
|
|
|
|
static const value_string sccp_ai_ssni_values[] = {
|
|
{ 0x1, "SSN present" },
|
|
{ 0x0, "SSN not present" },
|
|
{ 0, NULL } };
|
|
|
|
#define ADDRESS_SSN_LENGTH 1
|
|
#define INVALID_SSN 0xff
|
|
/* Some values from 3GPP TS 23.003 */
|
|
/* Japan TTC and NTT define a lot of SSNs, some of which conflict with
|
|
* these. They are not added for now.
|
|
*/
|
|
static const value_string sccp_ssn_values[] = {
|
|
{ 0x00, "SSN not known/not used" },
|
|
{ 0x01, "SCCP management" },
|
|
{ 0x02, "Reserved for ITU-T allocation" },
|
|
{ 0x03, "ISDN User Part" },
|
|
{ 0x04, "OMAP (Operation, Maintenance, and Administration Part)" },
|
|
{ 0x05, "MAP (Mobile Application Part)" },
|
|
{ 0x06, "HLR (Home Location Register)" },
|
|
{ 0x07, "VLR (Visitor Location Register)" },
|
|
{ 0x08, "MSC (Mobile Switching Center)" },
|
|
{ 0x09, "EIC/EIR (Equipment Identifier Center/Equipment Identification Register)" },
|
|
{ 0x0a, "AUC/AC (Authentication Center)" },
|
|
{ 0x0b, "ISDN supplementary services (ITU only)" },
|
|
{ 0x0c, "Reserved for international use (ITU only)" },
|
|
{ 0x0d, "Broadband ISDN edge-to-edge applications (ITU only)" },
|
|
{ 0x0e, "TC test responder (ITU only)" },
|
|
/* The following national network subsystem numbers have been allocated for use within and
|
|
* between GSM/UMTS networks:
|
|
*/
|
|
{ 0x8e, "RANAP" },
|
|
{ 0x8f, "RNSAP" },
|
|
{ 0x91, "GMLC(MAP)" },
|
|
{ 0x92, "CAP" },
|
|
{ 0x93, "gsmSCF (MAP) or IM-SSF (MAP) or Presence Network Agent" },
|
|
{ 0x94, "SIWF (MAP)" },
|
|
{ 0x95, "SGSN (MAP)" },
|
|
{ 0x96, "GGSN (MAP)" },
|
|
/* The following national network subsystem numbers have been allocated for use within GSM/UMTS networks:*/
|
|
{ 0xf9, "PCAP" },
|
|
{ 0xfa, "BSC (BSSAP-LE)" },
|
|
{ 0xfb, "MSC (BSSAP-LE)" },
|
|
{ 0xfc, "IOS or SMLC (BSSAP-LE)" },
|
|
{ 0xfd, "BSS O&M (A interface)" },
|
|
{ 0xfe, "BSSAP/BSAP" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * *
|
|
* Global Title: ITU GTI == 0001 *
|
|
* * * * * * * * * * * * * * * * */
|
|
#define GT_NAI_MASK 0x7F
|
|
#define GT_NAI_LENGTH 1
|
|
#define GT_NAI_UNKNOWN 0x00
|
|
#define GT_NAI_SUBSCRIBER_NUMBER 0x01
|
|
#define GT_NAI_RESERVED_NATIONAL 0x02
|
|
#define GT_NAI_NATIONAL_SIG_NUM 0x03
|
|
#define GT_NAI_INTERNATIONAL_NUM 0x04
|
|
static const value_string sccp_nai_values[] = {
|
|
{ GT_NAI_UNKNOWN, "NAI unknown" },
|
|
{ GT_NAI_SUBSCRIBER_NUMBER, "Subscriber Number" },
|
|
{ GT_NAI_RESERVED_NATIONAL, "Reserved for national use" },
|
|
{ GT_NAI_NATIONAL_SIG_NUM, "National significant number" },
|
|
{ GT_NAI_INTERNATIONAL_NUM, "International number" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
#define GT_OE_MASK 0x80
|
|
#define GT_OE_EVEN 0
|
|
#define GT_OE_ODD 1
|
|
static const value_string sccp_oe_values[] = {
|
|
{ GT_OE_EVEN, "Even number of address signals" },
|
|
{ GT_OE_ODD, "Odd number of address signals" },
|
|
{ 0, NULL } };
|
|
|
|
const value_string sccp_address_signal_values[] = {
|
|
{ 0, "0" },
|
|
{ 1, "1" },
|
|
{ 2, "2" },
|
|
{ 3, "3" },
|
|
{ 4, "4" },
|
|
{ 5, "5" },
|
|
{ 6, "6" },
|
|
{ 7, "7" },
|
|
{ 8, "8" },
|
|
{ 9, "9" },
|
|
{ 10, "(spare)" },
|
|
{ 11, "11" },
|
|
{ 12, "12" },
|
|
{ 13, "(spare)" },
|
|
{ 14, "(spare)" },
|
|
{ 15, "ST" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * *
|
|
* Global Title: ITU and ANSI GTI == 0010 *
|
|
* * * * * * * * * * * * * * * * * * * * */
|
|
#define GT_TT_LENGTH 1
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Global Title: ITU GTI == 0011, ANSI GTI == 0001 *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
#define GT_NP_MASK 0xf0
|
|
#define GT_NP_SHIFT 4
|
|
#define GT_NP_ES_LENGTH 1
|
|
#define GT_NP_UNKNOWN 0x00
|
|
#define GT_NP_ISDN 0x01
|
|
#define GT_NP_GENERIC_RESERVED 0x02
|
|
#define GT_NP_DATA 0x03
|
|
#define GT_NP_TELEX 0x04
|
|
#define GT_NP_MARITIME_MOBILE 0x05
|
|
#define GT_NP_LAND_MOBILE 0x06
|
|
#define GT_NP_ISDN_MOBILE 0x07
|
|
#define GT_NP_PRIVATE_NETWORK 0x0e
|
|
#define GT_NP_RESERVED 0x0f
|
|
static const value_string sccp_np_values[] = {
|
|
{ GT_NP_UNKNOWN, "Unknown" },
|
|
{ GT_NP_ISDN, "ISDN/telephony" },
|
|
{ GT_NP_GENERIC_RESERVED, "Generic (ITU)/Reserved (ANSI)" },
|
|
{ GT_NP_DATA, "Data" },
|
|
{ GT_NP_TELEX, "Telex" },
|
|
{ GT_NP_MARITIME_MOBILE, "Maritime mobile" },
|
|
{ GT_NP_LAND_MOBILE, "Land mobile" },
|
|
{ GT_NP_ISDN_MOBILE, "ISDN/mobile" },
|
|
{ GT_NP_PRIVATE_NETWORK, "Private network or network-specific" },
|
|
{ GT_NP_RESERVED, "Reserved" },
|
|
{ 0, NULL } };
|
|
|
|
#define GT_ES_MASK 0x0f
|
|
#define GT_ES_UNKNOWN 0x0
|
|
#define GT_ES_BCD_ODD 0x1
|
|
#define GT_ES_BCD_EVEN 0x2
|
|
#define GT_ES_NATIONAL 0x3
|
|
#define GT_ES_RESERVED 0xf
|
|
static const value_string sccp_es_values[] = {
|
|
{ GT_ES_UNKNOWN, "Unknown" },
|
|
{ GT_ES_BCD_ODD, "BCD, odd number of digits" },
|
|
{ GT_ES_BCD_EVEN, "BCD, even number of digits" },
|
|
{ GT_ES_NATIONAL, "National specific" },
|
|
{ GT_ES_RESERVED, "Reserved (ITU)/Spare (ANSI)" },
|
|
{ 0, NULL } };
|
|
|
|
/* Address signals above */
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * *
|
|
* Global Title: ITU GTI == 0100 *
|
|
* * * * * * * * * * * * * * * * */
|
|
/* NP above */
|
|
/* ES above */
|
|
/* NAI above */
|
|
/* Address signals above */
|
|
|
|
|
|
#define CLASS_CLASS_MASK 0xf
|
|
#define CLASS_SPARE_HANDLING_MASK 0xf0
|
|
#define CLASS_SPARE_HANDLING_SHIFT 4
|
|
static const value_string sccp_class_handling_values [] = {
|
|
{ 0x0, "No special options" },
|
|
{ 0x8, "Return message on error" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
#define SEGMENTING_REASSEMBLING_LENGTH 1
|
|
#define SEGMENTING_REASSEMBLING_MASK 0x01
|
|
#define NO_MORE_DATA 0
|
|
#define MORE_DATA 1
|
|
/* This is also used by sequencing-segmenting parameter */
|
|
static const value_string sccp_segmenting_reassembling_values [] = {
|
|
{ NO_MORE_DATA, "No more data" },
|
|
{ MORE_DATA, "More data" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
#define RECEIVE_SEQUENCE_NUMBER_LENGTH 1
|
|
#define RSN_MASK 0xfe
|
|
|
|
#define SEQUENCING_SEGMENTING_LENGTH 2
|
|
#define SEQUENCING_SEGMENTING_SSN_LENGTH 1
|
|
#define SEQUENCING_SEGMENTING_RSN_LENGTH 1
|
|
#define SEND_SEQUENCE_NUMBER_MASK 0xfe
|
|
#define RECEIVE_SEQUENCE_NUMBER_MASK 0xfe
|
|
#define SEQUENCING_SEGMENTING_MORE_MASK 0x01
|
|
|
|
|
|
#define CREDIT_LENGTH 1
|
|
|
|
#define RELEASE_CAUSE_LENGTH 1
|
|
const value_string sccp_release_cause_values [] = {
|
|
{ 0x00, "End user originated" },
|
|
{ 0x01, "End user congestion" },
|
|
{ 0x02, "End user failure" },
|
|
{ 0x03, "SCCP user originated" },
|
|
{ 0x04, "Remote procedure error" },
|
|
{ 0x05, "Inconsistent connection data" },
|
|
{ 0x06, "Access failure" },
|
|
{ 0x07, "Access congestion" },
|
|
{ 0x08, "Subsystem failure" },
|
|
{ 0x09, "Subsystem congestion" },
|
|
{ 0x0a, "MTP failure" },
|
|
{ 0x0b, "Network congestion" },
|
|
{ 0x0c, "Expiration of reset timer" },
|
|
{ 0x0d, "Expiration of receive inactivity timer" },
|
|
{ 0x0e, "Reserved" },
|
|
{ 0x0f, "Unqualified" },
|
|
{ 0x10, "SCCP failure (ITU only)" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
#define RETURN_CAUSE_LENGTH 1
|
|
const value_string sccp_return_cause_values [] = {
|
|
{ 0x00, "No translation for an address of such nature" },
|
|
{ 0x01, "No translation for this specific address" },
|
|
{ 0x02, "Subsystem congestion" },
|
|
{ 0x03, "Subsystem failure" },
|
|
{ 0x04, "Unequipped failure" },
|
|
{ 0x05, "MTP failure" },
|
|
{ 0x06, "Network congestion" },
|
|
{ 0x07, "Unqualified" },
|
|
{ 0x08, "Error in message transport" },
|
|
{ 0x09, "Error in local processing" },
|
|
{ 0x0a, "Destination cannot perform reassembly" },
|
|
{ 0x0b, "SCCP failure" },
|
|
{ 0x0c, "Hop counter violation" },
|
|
{ 0x0d, "Segmentation not supported" },
|
|
{ 0x0e, "Segmentation failure" },
|
|
{ 0xf7, "Message change failure (ANSI only)" },
|
|
{ 0xf8, "Invalid INS routing request (ANSI only)" },
|
|
{ 0xf9, "Invalid ISNI routing request (ANSI only)"},
|
|
{ 0xfa, "Unauthorized message (ANSI only)" },
|
|
{ 0xfb, "Message incompatibility (ANSI only)" },
|
|
{ 0xfc, "Cannot perform ISNI constrained routing (ANSI only)" },
|
|
{ 0xfd, "Redundant ISNI constrained routing (ANSI only)" },
|
|
{ 0xfe, "Unable to perform ISNI identification (ANSI only)" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
#define RESET_CAUSE_LENGTH 1
|
|
const value_string sccp_reset_cause_values [] = {
|
|
{ 0x00, "End user originated" },
|
|
{ 0x01, "SCCP user originated" },
|
|
{ 0x02, "Message out of order - incorrect send sequence number" },
|
|
{ 0x03, "Message out of order - incorrect receive sequence number" },
|
|
{ 0x04, "Remote procedure error - message out of window" },
|
|
{ 0x05, "Remote procedure error - incorrect send sequence number after (re)initialization" },
|
|
{ 0x06, "Remote procedure error - general" },
|
|
{ 0x07, "Remote end user operational" },
|
|
{ 0x08, "Network operational" },
|
|
{ 0x09, "Access operational" },
|
|
{ 0x0a, "Network congestion" },
|
|
{ 0x0b, "Reserved (ITU)/Not obtainable (ANSI)" },
|
|
{ 0x0c, "Unqualified" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
#define ERROR_CAUSE_LENGTH 1
|
|
const value_string sccp_error_cause_values [] = {
|
|
{ 0x00, "Local Reference Number (LRN) mismatch - unassigned destination LRN" },
|
|
{ 0x01, "Local Reference Number (LRN) mismatch - inconsistent source LRN" },
|
|
{ 0x02, "Point code mismatch" },
|
|
{ 0x03, "Service class mismatch" },
|
|
{ 0x04, "Unqualified" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
#define REFUSAL_CAUSE_LENGTH 1
|
|
const value_string sccp_refusal_cause_values [] = {
|
|
{ 0x00, "End user originated" },
|
|
{ 0x01, "End user congestion" },
|
|
{ 0x02, "End user failure" },
|
|
{ 0x03, "SCCP user originated" },
|
|
{ 0x04, "Destination address unknown" },
|
|
{ 0x05, "Destination inaccessible" },
|
|
{ 0x06, "Network resource - QOS not available/non-transient" },
|
|
{ 0x07, "Network resource - QOS not available/transient" },
|
|
{ 0x08, "Access failure" },
|
|
{ 0x09, "Access congestion" },
|
|
{ 0x0a, "Subsystem failure" },
|
|
{ 0x0b, "Subsystem congestion" },
|
|
{ 0x0c, "Expiration of connection establishment timer" },
|
|
{ 0x0d, "Incompatible user data" },
|
|
{ 0x0e, "Reserved" },
|
|
{ 0x0f, "Unqualified" },
|
|
{ 0x10, "Hop counter violation" },
|
|
{ 0x11, "SCCP failure (ITU only)" },
|
|
{ 0x12, "No translation for an address of such nature" },
|
|
{ 0x13, "Unequipped user" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
#define SEGMENTATION_LENGTH 4
|
|
#define SEGMENTATION_FIRST_SEGMENT_MASK 0x80
|
|
#define SEGMENTATION_CLASS_MASK 0x40
|
|
#define SEGMENTATION_SPARE_MASK 0x30
|
|
#define SEGMENTATION_REMAINING_MASK 0x0f
|
|
static const value_string sccp_segmentation_first_segment_values [] = {
|
|
{ 1, "First segment" },
|
|
{ 0, "Not first segment" },
|
|
{ 0, NULL } };
|
|
static const value_string sccp_segmentation_class_values [] = {
|
|
{ 0, "Class 0 selected" },
|
|
{ 1, "Class 1 selected" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
#define HOP_COUNTER_LENGTH 1
|
|
|
|
#define IMPORTANCE_LENGTH 1
|
|
#define IMPORTANCE_IMPORTANCE_MASK 0x7
|
|
|
|
|
|
#define ANSI_ISNI_ROUTING_CONTROL_LENGTH 1
|
|
#define ANSI_ISNI_MI_MASK 0x01
|
|
#define ANSI_ISNI_IRI_MASK 0x06
|
|
#define ANSI_ISNI_RES_MASK 0x08
|
|
#define ANSI_ISNI_TI_MASK 0x10
|
|
#define ANSI_ISNI_TI_SHIFT 4
|
|
#define ANSI_ISNI_COUNTER_MASK 0xe0
|
|
#define ANSI_ISNI_NETSPEC_MASK 0x03
|
|
|
|
static const value_string sccp_isni_mark_for_id_values [] = {
|
|
{ 0x0, "Do not identify networks" },
|
|
{ 0x1, "Identify networks" },
|
|
{ 0, NULL } };
|
|
|
|
static const value_string sccp_isni_iri_values [] = {
|
|
{ 0x0, "Neither constrained nor suggested ISNI routing" },
|
|
{ 0x1, "Constrained ISNI routing" },
|
|
{ 0x2, "Reserved for suggested ISNI routing" },
|
|
{ 0x3, "Spare" },
|
|
{ 0, NULL } };
|
|
|
|
#define ANSI_ISNI_TYPE_0 0x0
|
|
#define ANSI_ISNI_TYPE_1 0x1
|
|
static const value_string sccp_isni_ti_values [] = {
|
|
{ ANSI_ISNI_TYPE_0, "Type zero ISNI parameter format" },
|
|
{ ANSI_ISNI_TYPE_1, "Type one ISNI parameter format" },
|
|
{ 0, NULL } };
|
|
|
|
|
|
/* Initialize the protocol and registered fields */
|
|
static int proto_sccp = -1;
|
|
static int hf_sccp_message_type = -1;
|
|
static int hf_sccp_variable_pointer1 = -1;
|
|
static int hf_sccp_variable_pointer2 = -1;
|
|
static int hf_sccp_variable_pointer3 = -1;
|
|
static int hf_sccp_optional_pointer = -1;
|
|
static int hf_sccp_param_length = -1;
|
|
static int hf_sccp_ssn = -1;
|
|
static int hf_sccp_gt_digits = -1;
|
|
|
|
/* Called Party address */
|
|
static int hf_sccp_called_ansi_national_indicator = -1;
|
|
static int hf_sccp_called_itu_natl_use_bit = -1;
|
|
static int hf_sccp_called_routing_indicator = -1;
|
|
static int hf_sccp_called_itu_global_title_indicator = -1;
|
|
static int hf_sccp_called_ansi_global_title_indicator = -1;
|
|
static int hf_sccp_called_itu_ssn_indicator = -1;
|
|
static int hf_sccp_called_itu_point_code_indicator = -1;
|
|
static int hf_sccp_called_ansi_ssn_indicator = -1;
|
|
static int hf_sccp_called_ansi_point_code_indicator = -1;
|
|
static int hf_sccp_called_ssn = -1;
|
|
static int hf_sccp_called_pc_member = -1;
|
|
static int hf_sccp_called_pc_cluster = -1;
|
|
static int hf_sccp_called_pc_network = -1;
|
|
static int hf_sccp_called_ansi_pc = -1;
|
|
static int hf_sccp_called_chinese_pc = -1;
|
|
static int hf_sccp_called_itu_pc = -1;
|
|
static int hf_sccp_called_japan_pc = -1;
|
|
static int hf_sccp_called_gt_nai = -1;
|
|
static int hf_sccp_called_gt_oe = -1;
|
|
static int hf_sccp_called_gt_tt = -1;
|
|
static int hf_sccp_called_gt_np = -1;
|
|
static int hf_sccp_called_gt_es = -1;
|
|
static int hf_sccp_called_gt_digits = -1;
|
|
static int hf_sccp_called_gt_digits_length = -1;
|
|
|
|
/* Calling party address */
|
|
static int hf_sccp_calling_ansi_national_indicator = -1;
|
|
static int hf_sccp_calling_itu_natl_use_bit = -1;
|
|
static int hf_sccp_calling_routing_indicator = -1;
|
|
static int hf_sccp_calling_itu_global_title_indicator = -1;
|
|
static int hf_sccp_calling_ansi_global_title_indicator = -1;
|
|
static int hf_sccp_calling_itu_ssn_indicator = -1;
|
|
static int hf_sccp_calling_itu_point_code_indicator = -1;
|
|
static int hf_sccp_calling_ansi_ssn_indicator = -1;
|
|
static int hf_sccp_calling_ansi_point_code_indicator = -1;
|
|
static int hf_sccp_calling_ssn = -1;
|
|
static int hf_sccp_calling_pc_member = -1;
|
|
static int hf_sccp_calling_pc_cluster = -1;
|
|
static int hf_sccp_calling_pc_network = -1;
|
|
static int hf_sccp_calling_ansi_pc = -1;
|
|
static int hf_sccp_calling_chinese_pc = -1;
|
|
static int hf_sccp_calling_itu_pc = -1;
|
|
static int hf_sccp_calling_japan_pc = -1;
|
|
static int hf_sccp_calling_gt_nai = -1;
|
|
static int hf_sccp_calling_gt_oe = -1;
|
|
static int hf_sccp_calling_gt_tt = -1;
|
|
static int hf_sccp_calling_gt_np = -1;
|
|
static int hf_sccp_calling_gt_es = -1;
|
|
static int hf_sccp_calling_gt_digits = -1;
|
|
static int hf_sccp_calling_gt_digits_length = -1;
|
|
|
|
/* Other parameter values */
|
|
static int hf_sccp_dlr = -1;
|
|
static int hf_sccp_slr = -1;
|
|
static int hf_sccp_lr = -1;
|
|
static int hf_sccp_class = -1;
|
|
static int hf_sccp_handling = -1;
|
|
static int hf_sccp_more = -1;
|
|
static int hf_sccp_rsn = -1;
|
|
static int hf_sccp_sequencing_segmenting_ssn = -1;
|
|
static int hf_sccp_sequencing_segmenting_rsn = -1;
|
|
static int hf_sccp_sequencing_segmenting_more = -1;
|
|
static int hf_sccp_credit = -1;
|
|
static int hf_sccp_release_cause = -1;
|
|
static int hf_sccp_return_cause = -1;
|
|
static int hf_sccp_reset_cause = -1;
|
|
static int hf_sccp_error_cause = -1;
|
|
static int hf_sccp_refusal_cause = -1;
|
|
static int hf_sccp_segmentation_first = -1;
|
|
static int hf_sccp_segmentation_class = -1;
|
|
static int hf_sccp_segmentation_remaining = -1;
|
|
static int hf_sccp_segmentation_slr = -1;
|
|
static int hf_sccp_hop_counter = -1;
|
|
static int hf_sccp_importance = -1;
|
|
static int hf_sccp_ansi_isni_mi = -1;
|
|
static int hf_sccp_ansi_isni_iri = -1;
|
|
static int hf_sccp_ansi_isni_ti = -1;
|
|
static int hf_sccp_ansi_isni_netspec = -1;
|
|
static int hf_sccp_ansi_isni_counter = -1;
|
|
static int hf_sccp_ansi_isni_network = -1;
|
|
static int hf_sccp_ansi_isni_cluster = -1;
|
|
static int hf_sccp_xudt_msg_fragments = -1;
|
|
static int hf_sccp_xudt_msg_fragment = -1;
|
|
static int hf_sccp_xudt_msg_fragment_overlap = -1;
|
|
static int hf_sccp_xudt_msg_fragment_overlap_conflicts = -1;
|
|
static int hf_sccp_xudt_msg_fragment_multiple_tails = -1;
|
|
static int hf_sccp_xudt_msg_fragment_too_long_fragment = -1;
|
|
static int hf_sccp_xudt_msg_fragment_error = -1;
|
|
static int hf_sccp_xudt_msg_fragment_count = -1;
|
|
static int hf_sccp_xudt_msg_reassembled_in = -1;
|
|
static int hf_sccp_xudt_msg_reassembled_length = -1;
|
|
static int hf_sccp_assoc_msg = -1;
|
|
static int hf_sccp_assoc_id = -1;
|
|
|
|
/* Initialize the subtree pointers */
|
|
static gint ett_sccp = -1;
|
|
static gint ett_sccp_called = -1;
|
|
static gint ett_sccp_called_ai = -1;
|
|
static gint ett_sccp_called_pc = -1;
|
|
static gint ett_sccp_called_gt = -1;
|
|
static gint ett_sccp_called_gt_digits = -1;
|
|
static gint ett_sccp_calling = -1;
|
|
static gint ett_sccp_calling_ai = -1;
|
|
static gint ett_sccp_calling_pc = -1;
|
|
static gint ett_sccp_calling_gt = -1;
|
|
static gint ett_sccp_calling_gt_digits = -1;
|
|
static gint ett_sccp_sequencing_segmenting = -1;
|
|
static gint ett_sccp_segmentation = -1;
|
|
static gint ett_sccp_ansi_isni_routing_control = -1;
|
|
static gint ett_sccp_xudt_msg_fragment = -1;
|
|
static gint ett_sccp_xudt_msg_fragments = -1;
|
|
static gint ett_sccp_assoc = -1;
|
|
|
|
/* Declarations to desegment XUDT Messages */
|
|
static gboolean sccp_xudt_desegment = TRUE;
|
|
static gboolean show_key_params = FALSE;
|
|
static gboolean set_addresses = FALSE;
|
|
|
|
static int sccp_tap = -1;
|
|
|
|
|
|
static const fragment_items sccp_xudt_msg_frag_items = {
|
|
/* Fragment subtrees */
|
|
&ett_sccp_xudt_msg_fragment,
|
|
&ett_sccp_xudt_msg_fragments,
|
|
/* Fragment fields */
|
|
&hf_sccp_xudt_msg_fragments,
|
|
&hf_sccp_xudt_msg_fragment,
|
|
&hf_sccp_xudt_msg_fragment_overlap,
|
|
&hf_sccp_xudt_msg_fragment_overlap_conflicts,
|
|
&hf_sccp_xudt_msg_fragment_multiple_tails,
|
|
&hf_sccp_xudt_msg_fragment_too_long_fragment,
|
|
&hf_sccp_xudt_msg_fragment_error,
|
|
&hf_sccp_xudt_msg_fragment_count,
|
|
/* Reassembled in field */
|
|
&hf_sccp_xudt_msg_reassembled_in,
|
|
/* Reassembled length field */
|
|
&hf_sccp_xudt_msg_reassembled_length,
|
|
/* Tag */
|
|
"SCCP XUDT Message fragments"
|
|
};
|
|
|
|
static GHashTable *sccp_xudt_msg_fragment_table = NULL;
|
|
static GHashTable *sccp_xudt_msg_reassembled_table = NULL;
|
|
|
|
|
|
#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 module_t *sccp_module;
|
|
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 emem_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 < 6)
|
|
RETURN_FALSE;
|
|
|
|
msgtype = tvb_get_guint8(tvb, SCCP_MSG_TYPE_OFFSET);
|
|
if (!match_strval(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 (!match_strval(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:
|
|
{
|
|
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;
|
|
}
|
|
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:
|
|
{
|
|
offset += DESTINATION_LOCAL_REFERENCE_LENGTH;
|
|
|
|
cause = tvb_get_guint8(tvb, offset);
|
|
if (!match_strval(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 (!match_strval(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 (!match_strval(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-1; /* (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 (!match_strval(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 = se_alloc0(sizeof(sccp_assoc_info_t));
|
|
|
|
a->id = next_assoc_id++;
|
|
a->calling_dpc = calling;
|
|
a->called_dpc = called;
|
|
a->calling_ssn = INVALID_SSN;
|
|
a->called_ssn = INVALID_SSN;
|
|
a->msgs = NULL;
|
|
a->curr_msg = NULL;
|
|
a->payload = SCCP_PLOAD_NONE;
|
|
a->calling_party = NULL;
|
|
a->called_party = NULL;
|
|
a->extra_info = NULL;
|
|
|
|
return a;
|
|
}
|
|
|
|
void
|
|
reset_sccp_assoc(void)
|
|
{
|
|
assoc = NULL;
|
|
}
|
|
|
|
sccp_assoc_info_t *
|
|
get_sccp_assoc(packet_info *pinfo, guint offset, guint32 src_lr, guint32 dst_lr, guint msg_type)
|
|
{
|
|
guint32 opck, dpck;
|
|
address *opc = &(pinfo->src);
|
|
address *dpc = &(pinfo->dst);
|
|
guint framenum = PINFO_FD_NUM(pinfo);
|
|
|
|
if (assoc)
|
|
return assoc;
|
|
|
|
opck = opc->type == AT_SS7PC ? mtp3_pc_hash((const mtp3_addr_pc_t *)opc->data) : g_str_hash(ep_address_to_str(opc));
|
|
dpck = dpc->type == AT_SS7PC ? mtp3_pc_hash((const mtp3_addr_pc_t *)dpc->data) : g_str_hash(ep_address_to_str(dpc));
|
|
|
|
|
|
switch (msg_type) {
|
|
case SCCP_MSG_TYPE_CR:
|
|
{
|
|
/* CR contains the opc,dpc,dlr key of backward messages swapped as dpc,opc,slr */
|
|
emem_tree_key_t bw_key[] = {
|
|
{1, &dpck},
|
|
{1, &opck},
|
|
{1, &src_lr},
|
|
{0, NULL}
|
|
};
|
|
|
|
if (! ( assoc = se_tree_lookup32_array(assocs,bw_key) ) && ! PINFO_FD_VISITED(pinfo) ) {
|
|
assoc = new_assoc(opck, dpck);
|
|
se_tree_insert32_array(assocs, bw_key, assoc);
|
|
assoc->has_bw_key = TRUE;
|
|
}
|
|
|
|
pinfo->p2p_dir = P2P_DIR_SENT;
|
|
|
|
break;
|
|
}
|
|
case SCCP_MSG_TYPE_CC:
|
|
{
|
|
emem_tree_key_t fw_key[] = {
|
|
{1, &dpck}, {1, &opck}, {1, &src_lr}, {0, NULL}
|
|
};
|
|
emem_tree_key_t bw_key[] = {
|
|
{1, &opck}, {1, &dpck}, {1, &dst_lr}, {0, NULL}
|
|
};
|
|
|
|
if ( ( assoc = se_tree_lookup32_array(assocs, bw_key) ) ) {
|
|
goto got_assoc;
|
|
}
|
|
|
|
if ( (assoc = se_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 ) {
|
|
se_tree_insert32_array(assocs, bw_key, assoc);
|
|
assoc->has_bw_key = TRUE;
|
|
}
|
|
|
|
if ( ! PINFO_FD_VISITED(pinfo) && ! assoc->has_fw_key ) {
|
|
se_tree_insert32_array(assocs, fw_key, assoc);
|
|
assoc->has_fw_key = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case SCCP_MSG_TYPE_RLC:
|
|
{
|
|
emem_tree_key_t bw_key[] = {
|
|
{1, &dpck}, {1, &opck}, {1, &src_lr}, {0, NULL}
|
|
};
|
|
emem_tree_key_t fw_key[] = {
|
|
{1, &opck}, {1, &dpck}, {1, &dst_lr}, {0, NULL}
|
|
};
|
|
if ( ( assoc = se_tree_lookup32_array(assocs, bw_key) ) ) {
|
|
goto got_assoc_rlc;
|
|
}
|
|
|
|
if ( (assoc = se_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 ) {
|
|
se_tree_insert32_array(assocs, bw_key, assoc);
|
|
assoc->has_bw_key = TRUE;
|
|
}
|
|
|
|
if ( ! PINFO_FD_VISITED(pinfo) && ! assoc->has_fw_key ) {
|
|
se_tree_insert32_array(assocs, fw_key, assoc);
|
|
assoc->has_fw_key = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
emem_tree_key_t key[] = {
|
|
{1, &opck}, {1, &dpck}, {1, &dst_lr}, {0, NULL}
|
|
};
|
|
|
|
assoc = se_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 = se_alloc0(sizeof(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, *expert_item;
|
|
|
|
if (length != 3) {
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 3, got %u", length);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated. Expected 3, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
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, *expert_item;
|
|
|
|
if (length != 3) {
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 3, got %u", length);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated. Expected 3, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
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 = ep_alloc0(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 *)ep_strdup(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_format(pinfo, expert_item, PI_MALFORMED, PI_WARN, "Address is coded to "
|
|
"international standards. This doesn't normally happen in ANSI "
|
|
"networks.");
|
|
} 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_format(pinfo, expert_item, PI_PROTOCOL, PI_WARN,
|
|
"Message is routed on SSN, but SSN is not present");
|
|
}
|
|
|
|
pci = tvb_get_guint8(tvb, 0) & ITU_PC_INDICATOR_MASK;
|
|
proto_tree_add_uint(call_ai_tree, called ? hf_sccp_called_itu_point_code_indicator : 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) {
|
|
expert_item = proto_tree_add_text(call_tree, tvb, 0, -1,
|
|
"Wrong length indicated (%u) should be at least %u, PC is %u octets",
|
|
length, offset + ITU_PC_LENGTH, ITU_PC_LENGTH);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated");
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
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) {
|
|
expert_item = proto_tree_add_text(call_tree, tvb, 0, -1,
|
|
"Wrong length indicated (%u) should be at least %u, PC is %u octets",
|
|
length, offset + JAPAN_PC_LENGTH, JAPAN_PC_LENGTH);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated");
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
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) {
|
|
expert_item = proto_tree_add_text(call_tree, tvb, 0, -1,
|
|
"Wrong length indicated (%u) should be at least %u, PC is %u octets",
|
|
length, offset + ANSI_PC_LENGTH, ANSI_PC_LENGTH);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated");
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
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_format(pinfo, expert_item, PI_PROTOCOL, PI_WARN,
|
|
"Message is routed on SSN, but SSN is zero (unspecified)");
|
|
}
|
|
|
|
if (called && assoc)
|
|
assoc->called_ssn = ssn;
|
|
else if (assoc)
|
|
assoc->calling_ssn = ssn;
|
|
|
|
if (is_connectionless(message_type) && sccp_msg) {
|
|
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 it's 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_format(pinfo, expert_item, PI_PROTOCOL, PI_WARN,
|
|
"Message is routed on SSN, but SSN is not 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_format(pinfo, expert_item, PI_PROTOCOL, PI_WARN,
|
|
"Message is routed on SSN, but SSN is zero (unspecified)");
|
|
}
|
|
|
|
if (called && assoc) {
|
|
assoc->called_ssn = ssn;
|
|
} else if (assoc) {
|
|
assoc->calling_ssn = ssn;
|
|
}
|
|
|
|
if (is_connectionless(message_type) && sccp_msg) {
|
|
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) {
|
|
pi = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 1, got %u", length);
|
|
expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_ERROR, "Wrong length indicated. Expected 1, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(pi);
|
|
return;
|
|
}
|
|
|
|
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_format(pinfo, pi, PI_MALFORMED, PI_ERROR, "Unexpected message class for this message type");
|
|
|
|
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 (match_strval(handling, sccp_class_handling_values) == NULL) {
|
|
expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_ERROR, "Invalid message handling");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
dissect_sccp_segmenting_reassembling_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
|
|
{
|
|
if (length != 1) {
|
|
proto_item *expert_item;
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 1, got %u", length);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated. Expected 1, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
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_item *expert_item;
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 1, got %u", length);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated. Expected 1, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
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_item *expert_item;
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 1, got %u", length);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR,
|
|
"Wrong length indicated. Expected 1, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
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)
|
|
{
|
|
guint8 cause;
|
|
|
|
if (length != 1) {
|
|
proto_item *expert_item;
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length,
|
|
"Wrong length indicated. Expected 1, got %u", length);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR,
|
|
"Wrong length indicated. Expected 1, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
return;
|
|
}
|
|
|
|
cause = tvb_get_guint8(tvb, 0);
|
|
proto_tree_add_uint(tree, hf_sccp_release_cause, tvb, 0, length, cause);
|
|
|
|
if (show_key_params)
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, "Cause=%d ", cause);
|
|
}
|
|
|
|
static void
|
|
dissect_sccp_return_cause_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
|
|
{
|
|
guint8 cause;
|
|
|
|
if (length != 1) {
|
|
proto_item *expert_item;
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 1, got %u", length);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR,
|
|
"Wrong length indicated. Expected 1, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
return;
|
|
}
|
|
|
|
cause = tvb_get_guint8(tvb, 0);
|
|
proto_tree_add_uint(tree, hf_sccp_return_cause, tvb, 0, length, cause);
|
|
|
|
if (show_key_params)
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, "Cause=%d ", cause);
|
|
}
|
|
|
|
static void
|
|
dissect_sccp_reset_cause_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
|
|
{
|
|
guint8 cause;
|
|
|
|
if (length != 1) {
|
|
proto_item *expert_item;
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 1, got %u", length);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR,
|
|
"Wrong length indicated. Expected 1, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
return;
|
|
}
|
|
|
|
cause = tvb_get_guint8(tvb, 0);
|
|
proto_tree_add_uint(tree, hf_sccp_reset_cause, tvb, 0, length, cause);
|
|
|
|
if (show_key_params)
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, "Cause=%d ", cause);
|
|
}
|
|
|
|
static void
|
|
dissect_sccp_error_cause_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
|
|
{
|
|
guint8 cause;
|
|
|
|
if (length != 1) {
|
|
proto_item *expert_item;
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 1, got %u", length);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated. Expected 1, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
return;
|
|
}
|
|
|
|
cause = tvb_get_guint8(tvb, 0);
|
|
proto_tree_add_uint(tree, hf_sccp_error_cause, tvb, 0, length, cause);
|
|
|
|
if (show_key_params)
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, "Cause=%d ", cause);
|
|
}
|
|
|
|
static void
|
|
dissect_sccp_refusal_cause_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
|
|
{
|
|
guint8 cause;
|
|
|
|
if (length != 1) {
|
|
proto_item *expert_item;
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 1, got %u", length);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated. Expected 1, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
return;
|
|
}
|
|
|
|
cause = tvb_get_guint8(tvb, 0);
|
|
proto_tree_add_uint(tree, hf_sccp_refusal_cause, tvb, 0, length, cause);
|
|
|
|
if (show_key_params)
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, "Cause=%d ", cause);
|
|
}
|
|
|
|
|
|
/* 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)) {
|
|
return;
|
|
}
|
|
|
|
/* try user default subdissector */
|
|
if (default_handle) {
|
|
call_dissector(default_handle, tvb, pinfo, tree);
|
|
return;
|
|
}
|
|
|
|
/* No sub-dissection occured, 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_item *expert_item;
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length-1, "Wrong length indicated. Expected 3, got %u", length-1);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated. Expected 3, got %u", length-1);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
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_item *expert_item;
|
|
expert_item = proto_tree_add_text(tree, tvb, 0, length, "Wrong length indicated. Expected 1, got %u", length);
|
|
expert_add_info_format(pinfo, expert_item, PI_MALFORMED, PI_ERROR, "Wrong length indicated. Expected 1, got %u", length);
|
|
PROTO_ITEM_SET_GENERATED(expert_item);
|
|
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 = ep_alloc0(sizeof(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(reset_sccp_assoc);
|
|
return m;
|
|
}
|
|
|
|
static void
|
|
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_data *frag_msg = NULL;
|
|
guint32 source_local_ref = 0;
|
|
guint8 more;
|
|
guint msg_offset = tvb_offset_from_real_beginning(tvb);
|
|
|
|
/* Macro for getting pointer to mandatory variable parameters */
|
|
#define VARIABLE_POINTER(var, hf_var, ptr_size) \
|
|
do { \
|
|
if (ptr_size == POINTER_LENGTH) \
|
|
var = tvb_get_guint8(tvb, offset); \
|
|
else \
|
|
var = tvb_get_letohs(tvb, offset); \
|
|
proto_tree_add_uint(sccp_tree, hf_var, tvb, \
|
|
offset, ptr_size, var); \
|
|
var += offset; \
|
|
if (ptr_size == POINTER_LENGTH_LONG) \
|
|
var += 1; \
|
|
offset += ptr_size; \
|
|
} while (0)
|
|
|
|
/* Macro for getting pointer to optional parameters */
|
|
#define OPTIONAL_POINTER(ptr_size) \
|
|
do { \
|
|
if (ptr_size == POINTER_LENGTH) \
|
|
orig_opt_ptr = optional_pointer = tvb_get_guint8(tvb, offset); \
|
|
else \
|
|
orig_opt_ptr = optional_pointer = tvb_get_letohs(tvb, offset); \
|
|
proto_tree_add_uint(sccp_tree, hf_sccp_optional_pointer, tvb, \
|
|
offset, ptr_size, optional_pointer); \
|
|
optional_pointer += offset; \
|
|
if (ptr_size == POINTER_LENGTH_LONG) \
|
|
optional_pointer += 1; \
|
|
offset += ptr_size; \
|
|
} while (0)
|
|
|
|
|
|
/* Extract the message type; all other processing is based on this */
|
|
message_type = tvb_get_guint8(tvb, SCCP_MSG_TYPE_OFFSET);
|
|
offset = SCCP_MSG_TYPE_LENGTH;
|
|
|
|
/* Do not change col_add_fstr() to col_append_fstr() here: we _want_
|
|
* this call to overwrite whatever's currently in the INFO column (e.g.,
|
|
* "DATA" from the SCTP dissector).
|
|
*
|
|
* If there's something there that should not be overwritten, whoever
|
|
* put that info there should call col_set_fence() to protect it.
|
|
*/
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "%s ",
|
|
val_to_str(message_type, sccp_message_type_acro_values, "Unknown: %d"));
|
|
|
|
if (sccp_tree) {
|
|
/* add the message type to the protocol tree */
|
|
proto_tree_add_uint(sccp_tree, hf_sccp_message_type, tvb,
|
|
SCCP_MSG_TYPE_OFFSET, SCCP_MSG_TYPE_LENGTH, message_type);
|
|
|
|
};
|
|
|
|
/* Starting a new message dissection; clear the global assoc, SLR, and DLR values */
|
|
dlr = INVALID_LR;
|
|
slr = INVALID_LR;
|
|
assoc = NULL;
|
|
|
|
no_assoc.calling_dpc = 0;
|
|
no_assoc.called_dpc = 0;
|
|
no_assoc.calling_ssn = INVALID_SSN;
|
|
no_assoc.called_ssn = INVALID_SSN;
|
|
no_assoc.has_fw_key = FALSE;
|
|
no_assoc.has_bw_key = FALSE;
|
|
no_assoc.payload = SCCP_PLOAD_NONE;
|
|
no_assoc.called_party = NULL;
|
|
no_assoc.calling_party = NULL;
|
|
no_assoc.extra_info = NULL;
|
|
|
|
switch (message_type) {
|
|
case SCCP_MSG_TYPE_CR:
|
|
/* TTC and NTT (Japan) say that the connection-oriented messages are
|
|
* deleted (not standardized), but they appear to be used anyway, so
|
|
* we'll dissect it...
|
|
*/
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_SOURCE_LOCAL_REFERENCE,
|
|
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CLASS, offset,
|
|
PROTOCOL_CLASS_LENGTH);
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
|
|
OPTIONAL_POINTER(POINTER_LENGTH);
|
|
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CALLED_PARTY_ADDRESS,
|
|
variable_pointer1);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_CC:
|
|
/* TODO: connection has been established; theoretically we could keep
|
|
* keep track of the SLR/DLR with the called/calling from the CR and
|
|
* track the connection (e.g., on subsequent messages regarding this
|
|
* SLR we could set the global vars "call*_ssn" so data could get
|
|
* sub-dissected).
|
|
*/
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_SOURCE_LOCAL_REFERENCE,
|
|
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CLASS, offset,
|
|
PROTOCOL_CLASS_LENGTH);
|
|
OPTIONAL_POINTER(POINTER_LENGTH);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_CREF:
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_REFUSAL_CAUSE, offset,
|
|
REFUSAL_CAUSE_LENGTH);
|
|
OPTIONAL_POINTER(POINTER_LENGTH);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_RLSD:
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_SOURCE_LOCAL_REFERENCE,
|
|
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_RELEASE_CAUSE, offset,
|
|
RELEASE_CAUSE_LENGTH);
|
|
|
|
OPTIONAL_POINTER(POINTER_LENGTH);
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_RLC:
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_SOURCE_LOCAL_REFERENCE,
|
|
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_DT1:
|
|
source_local_ref = tvb_get_letoh24(tvb, offset);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
more = tvb_get_guint8(tvb, offset) & SEGMENTING_REASSEMBLING_MASK;
|
|
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_SEGMENTING_REASSEMBLING,
|
|
offset, SEGMENTING_REASSEMBLING_LENGTH);
|
|
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
|
|
|
|
/* Reassemble */
|
|
if (!sccp_xudt_desegment) {
|
|
proto_tree_add_text(sccp_tree, tvb, variable_pointer1,
|
|
tvb_get_guint8(tvb, variable_pointer1)+1,
|
|
"Segmented Data");
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DATA, variable_pointer1);
|
|
|
|
} else {
|
|
save_fragmented = pinfo->fragmented;
|
|
pinfo->fragmented = TRUE;
|
|
frag_msg = fragment_add_seq_next(tvb, variable_pointer1 + 1, pinfo,
|
|
source_local_ref, /* ID for fragments belonging together */
|
|
sccp_xudt_msg_fragment_table, /* list of message fragments */
|
|
sccp_xudt_msg_reassembled_table, /* list of reassembled messages */
|
|
tvb_get_guint8(tvb,variable_pointer1), /* fragment length - to the end */
|
|
more); /* More fragments? */
|
|
|
|
new_tvb = process_reassembled_data(tvb, variable_pointer1 + 1, pinfo,
|
|
"Reassembled SCCP", frag_msg,
|
|
&sccp_xudt_msg_frag_items, NULL,
|
|
tree);
|
|
|
|
if (frag_msg && frag_msg->next) { /* Reassembled */
|
|
col_append_str(pinfo->cinfo, COL_INFO, "(Message reassembled) ");
|
|
} else if (more) { /* Not last packet of reassembled message */
|
|
col_append_str(pinfo->cinfo, COL_INFO, "(Message fragment) ");
|
|
}
|
|
|
|
pinfo->fragmented = save_fragmented;
|
|
|
|
if (new_tvb)
|
|
dissect_sccp_data_param(new_tvb, pinfo, tree);
|
|
}
|
|
|
|
/* End reassemble */
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_DT2:
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_SEQUENCING_SEGMENTING, offset,
|
|
SEQUENCING_SEGMENTING_LENGTH);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_AK:
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_RECEIVE_SEQUENCE_NUMBER,
|
|
offset, RECEIVE_SEQUENCE_NUMBER_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CREDIT, offset, CREDIT_LENGTH);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_UDT:
|
|
pinfo->sccp_info = sccp_msg = new_ud_msg(pinfo,message_type);
|
|
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CLASS, offset,
|
|
PROTOCOL_CLASS_LENGTH);
|
|
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
|
|
VARIABLE_POINTER(variable_pointer2, hf_sccp_variable_pointer2, POINTER_LENGTH);
|
|
VARIABLE_POINTER(variable_pointer3, hf_sccp_variable_pointer3, POINTER_LENGTH);
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CALLED_PARTY_ADDRESS,
|
|
variable_pointer1);
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CALLING_PARTY_ADDRESS,
|
|
variable_pointer2);
|
|
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree, PARAMETER_DATA,
|
|
variable_pointer3);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_UDTS:
|
|
{
|
|
gboolean save_in_error_pkt = pinfo->flags.in_error_pkt;
|
|
pinfo->flags.in_error_pkt = TRUE;
|
|
|
|
pinfo->sccp_info = sccp_msg = new_ud_msg(pinfo,message_type);
|
|
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_RETURN_CAUSE, offset,
|
|
RETURN_CAUSE_LENGTH);
|
|
|
|
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
|
|
VARIABLE_POINTER(variable_pointer2, hf_sccp_variable_pointer2, POINTER_LENGTH);
|
|
VARIABLE_POINTER(variable_pointer3, hf_sccp_variable_pointer3, POINTER_LENGTH);
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CALLED_PARTY_ADDRESS,
|
|
variable_pointer1);
|
|
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CALLING_PARTY_ADDRESS,
|
|
variable_pointer2);
|
|
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree, PARAMETER_DATA,
|
|
variable_pointer3);
|
|
pinfo->flags.in_error_pkt = save_in_error_pkt;
|
|
break;
|
|
}
|
|
|
|
case SCCP_MSG_TYPE_ED:
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
|
|
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree, PARAMETER_DATA,
|
|
variable_pointer1);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_EA:
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_RSR:
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_SOURCE_LOCAL_REFERENCE,
|
|
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_RESET_CAUSE, offset,
|
|
RESET_CAUSE_LENGTH);
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_RSC:
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_SOURCE_LOCAL_REFERENCE,
|
|
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_ERR:
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_ERROR_CAUSE, offset,
|
|
ERROR_CAUSE_LENGTH);
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_IT:
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DESTINATION_LOCAL_REFERENCE,
|
|
offset,
|
|
DESTINATION_LOCAL_REFERENCE_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_SOURCE_LOCAL_REFERENCE,
|
|
offset, SOURCE_LOCAL_REFERENCE_LENGTH);
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CLASS, offset,
|
|
PROTOCOL_CLASS_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_SEQUENCING_SEGMENTING,
|
|
offset, SEQUENCING_SEGMENTING_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CREDIT, offset, CREDIT_LENGTH);
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_XUDT:
|
|
pinfo->sccp_info = sccp_msg = new_ud_msg(pinfo,message_type);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CLASS, offset,
|
|
PROTOCOL_CLASS_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_HOP_COUNTER, offset,
|
|
HOP_COUNTER_LENGTH);
|
|
|
|
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
|
|
VARIABLE_POINTER(variable_pointer2, hf_sccp_variable_pointer2, POINTER_LENGTH);
|
|
VARIABLE_POINTER(variable_pointer3, hf_sccp_variable_pointer3, POINTER_LENGTH);
|
|
OPTIONAL_POINTER(POINTER_LENGTH);
|
|
|
|
/* Optional parameters are Segmentation and Importance
|
|
* NOTE 2 - Segmentation Should not be present in case of a single XUDT
|
|
* message.
|
|
*/
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CALLED_PARTY_ADDRESS,
|
|
variable_pointer1);
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CALLING_PARTY_ADDRESS,
|
|
variable_pointer2);
|
|
|
|
if (tvb_get_guint8(tvb, optional_pointer) == PARAMETER_SEGMENTATION) {
|
|
if (!sccp_xudt_desegment) {
|
|
proto_tree_add_text(sccp_tree, tvb, variable_pointer3, tvb_get_guint8(tvb, variable_pointer3)+1, "Segmented Data");
|
|
} else {
|
|
guint8 octet;
|
|
gboolean more_frag = TRUE;
|
|
|
|
/* Get the first octet of parameter Segmentation, Ch 3.17 in Q.713
|
|
* Bit 8 of octet 1 is used for First segment indication
|
|
* Bit 7 of octet 1 is used to keep in the message in sequence
|
|
* delivery option required by the SCCP user
|
|
* Bits 6 and 5 in octet 1 are spare bits.
|
|
* Bits 4-1 of octet 1 are used to indicate the number of
|
|
* remaining segments.
|
|
* The values 0000 to 1111 are possible; the value 0000 indicates
|
|
* the last segment.
|
|
*/
|
|
octet = tvb_get_guint8(tvb,optional_pointer+2);
|
|
source_local_ref = tvb_get_letoh24(tvb, optional_pointer+3);
|
|
|
|
if ((octet & 0x0f) == 0)
|
|
more_frag = FALSE;
|
|
|
|
save_fragmented = pinfo->fragmented;
|
|
pinfo->fragmented = TRUE;
|
|
frag_msg = fragment_add_seq_next(tvb, variable_pointer3 + 1, pinfo,
|
|
source_local_ref, /* ID for fragments belonging together */
|
|
sccp_xudt_msg_fragment_table, /* list of message fragments */
|
|
sccp_xudt_msg_reassembled_table, /* list of reassembled messages */
|
|
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(pinfo, source_local_ref, sccp_xudt_msg_fragment_table,(octet & 0xf));
|
|
|
|
new_tvb = process_reassembled_data(tvb, variable_pointer3 + 1,
|
|
pinfo, "Reassembled SCCP",
|
|
frag_msg,
|
|
&sccp_xudt_msg_frag_items,
|
|
NULL, tree);
|
|
|
|
if (frag_msg) { /* Reassembled */
|
|
col_append_str(pinfo->cinfo, COL_INFO,"(Message reassembled) ");
|
|
} else { /* Not last packet of reassembled message */
|
|
col_append_str(pinfo->cinfo, COL_INFO,"(Message fragment) ");
|
|
}
|
|
|
|
pinfo->fragmented = save_fragmented;
|
|
|
|
if (new_tvb)
|
|
dissect_sccp_data_param(new_tvb, pinfo, tree);
|
|
}
|
|
} else {
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_DATA, variable_pointer3);
|
|
}
|
|
break;
|
|
|
|
case SCCP_MSG_TYPE_XUDTS:
|
|
{
|
|
gboolean save_in_error_pkt = pinfo->flags.in_error_pkt;
|
|
pinfo->flags.in_error_pkt = TRUE;
|
|
|
|
pinfo->sccp_info = sccp_msg = new_ud_msg(pinfo,message_type);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_RETURN_CAUSE, offset,
|
|
RETURN_CAUSE_LENGTH);
|
|
offset += dissect_sccp_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_HOP_COUNTER, offset,
|
|
HOP_COUNTER_LENGTH);
|
|
|
|
VARIABLE_POINTER(variable_pointer1, hf_sccp_variable_pointer1, POINTER_LENGTH);
|
|
VARIABLE_POINTER(variable_pointer2, hf_sccp_variable_pointer2, POINTER_LENGTH);
|
|
VARIABLE_POINTER(variable_pointer3, hf_sccp_variable_pointer3, POINTER_LENGTH);
|
|
OPTIONAL_POINTER(POINTER_LENGTH);
|
|
|
|
assoc = get_sccp_assoc(pinfo, msg_offset, slr, dlr, message_type);
|
|
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CALLED_PARTY_ADDRESS,
|
|
variable_pointer1);
|
|
dissect_sccp_variable_parameter(tvb, pinfo, sccp_tree, tree,
|
|
PARAMETER_CALLING_PARTY_ADDRESS,
|
|
variable_pointer2);
|
|
|
|
if (tvb_get_guint8(tvb, optional_pointer) == PARAMETER_SEGMENTATION) {
|
|
if (!sccp_xudt_desegment) {
|
|
proto_tree_add_text(sccp_tree, tvb, variable_pointer3, tvb_get_guint8(tvb, variable_pointer3)+1, "Segmented Data");
|
|
|
|
} else {
|
|
guint8 octet;
|
|
gboolean more_frag = TRUE;
|
|
|
|
|
|
/* Get the first octet of parameter Segmentation, Ch 3.17 in Q.713
|
|
* Bit 8 of octet 1 is used for First segment indication
|
|
* Bit 7 of octet 1 is used to keep in the message in sequence
|
|
* delivery option required by the SCCP user
|
|
* Bits 6 and 5 in octet 1 are spare bits.
|
|
* Bits 4-1 of octet 1 are used to indicate the number of
|
|
* remaining segments.
|
|
* The values 0000 to 1111 are possible; the value 0000 indicates
|
|
* the last segment.
|
|
*/
|
|
octet = tvb_get_guint8(tvb,optional_pointer+2);
|
|
source_local_ref = tvb_get_letoh24(tvb, optional_pointer+3);
|
|
|
|
if ((octet & 0x0f) == 0)
|
|
more_frag = FALSE;
|
|
|
|
save_fragmented = pinfo->fragmented;
|
|
pinfo->fragmented = TRUE;
|
|
frag_msg = fragment_add_seq_next(tvb, variable_pointer3 + 1, pinfo,
|
|
source_local_ref, /* ID for fragments belonging together */
|
|
sccp_xudt_msg_fragment_table, /* list of message fragments */
|
|
sccp_xudt_msg_reassembled_table, /* list of reassembled messages */
|
|
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(pinfo, source_local_ref, sccp_xudt_msg_fragment_table,(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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 = 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 _U_)
|
|
{
|
|
sccp_user_t *u = r;
|
|
struct _sccp_ul *c;
|
|
|
|
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 = o;
|
|
sccp_user_t *un = 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 = 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, SCCP_USER_DATA, "Data")
|
|
|
|
/** End SccpUsersTable **/
|
|
|
|
|
|
static void
|
|
init_sccp(void)
|
|
{
|
|
next_assoc_id = 1;
|
|
fragment_table_init (&sccp_xudt_msg_fragment_table);
|
|
reassembled_table_init(&sccp_xudt_msg_reassembled_table);
|
|
|
|
}
|
|
|
|
/* 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 uat_field_t users_flds[] = {
|
|
UAT_FLD_DEC(sccp_users, ni, "Network Indicator", "Network Indicator"),
|
|
UAT_FLD_RANGE(sccp_users, called_pc, "Called DPCs", 0xFFFFFF, "DPCs for which this protocol is to be used"),
|
|
UAT_FLD_RANGE(sccp_users, called_ssn, "Called SSNs", 255, "Called SSNs for which this protocol is to be used"),
|
|
UAT_FLD_VS(sccp_users, user, "User protocol", sccp_users_vals, "The User Protocol"),
|
|
UAT_END_FIELDS
|
|
};
|
|
|
|
|
|
uat_t *users_uat = uat_new("SCCP Users Table", sizeof(sccp_user_t),
|
|
"sccp_users", TRUE, (void*) &sccp_users,
|
|
&num_sccp_users, UAT_AFFECTS_DISSECTION,
|
|
"ChSccpUsers", sccp_users_copy_cb,
|
|
sccp_users_update_cb, sccp_users_free_cb,
|
|
NULL, users_flds );
|
|
|
|
/* Register the protocol name and description */
|
|
proto_sccp = proto_register_protocol("Signalling Connection Control Part",
|
|
"SCCP", "sccp");
|
|
|
|
register_dissector("sccp", dissect_sccp, proto_sccp);
|
|
|
|
/* Required function calls to register the header fields and subtrees used */
|
|
proto_register_field_array(proto_sccp, hf, array_length(hf));
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
|
|
|
|
sccp_ssn_dissector_table = register_dissector_table("sccp.ssn", "SCCP SSN", FT_UINT8, BASE_DEC);
|
|
|
|
register_heur_dissector_list("sccp", &heur_subdissector_list);
|
|
|
|
sccp_module = prefs_register_protocol(proto_sccp, proto_reg_handoff_sccp);
|
|
|
|
prefs_register_uint_preference(sccp_module, "source_pc",
|
|
"Source PC (in hex)",
|
|
"The source point code (usually MSC) (to determine whether message is uplink or downlink)",
|
|
16, &sccp_source_pc_global);
|
|
|
|
prefs_register_bool_preference(sccp_module, "show_length", "Show length",
|
|
"Show parameter length in the protocol tree",
|
|
&sccp_show_length);
|
|
|
|
prefs_register_bool_preference(sccp_module, "defragment_xudt",
|
|
"Reassemble XUDT messages",
|
|
"Whether XUDT messages should be reassembled",
|
|
&sccp_xudt_desegment);
|
|
|
|
prefs_register_bool_preference(sccp_module, "trace_sccp",
|
|
"Trace Associations",
|
|
"Whether to keep information about messages and their associations",
|
|
&trace_sccp);
|
|
|
|
|
|
prefs_register_bool_preference(sccp_module, "show_more_info",
|
|
"Show key parameters in Info Column",
|
|
"Show SLR, DLR, and CAUSE Parameters in the Information Column of the Summary",
|
|
&show_key_params);
|
|
|
|
|
|
prefs_register_uat_preference(sccp_module, "users_table", "Users Table",
|
|
"A table that enumerates user protocols to be used against specific PCs and SSNs",
|
|
users_uat);
|
|
|
|
prefs_register_bool_preference(sccp_module, "set_addresses", "Set source and destination GT addresses",
|
|
"Set the source and destination addresses to the GT digits (if RI=GT)."
|
|
" This may affect TCAP's ability to recognize which messages belong to which TCAP session.",
|
|
&set_addresses);
|
|
|
|
prefs_register_string_preference(sccp_module, "default_payload", "Default Payload",
|
|
"The protocol which should be used to dissect the payload if nothing else has claimed it",
|
|
&default_payload);
|
|
|
|
register_init_routine(&init_sccp);
|
|
|
|
assocs = se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "sccp_associations");
|
|
|
|
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);
|
|
}
|