wireshark/epan/dissectors/packet-gcsna.c

462 lines
16 KiB
C

/*
* packet-gcsna.c
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Ref GCSNA: 3GPP2 C.S0097 v2.0
*/
# include "config.h"
#include <epan/packet.h>
#include <epan/expert.h>
void proto_reg_handoff_gcsna(void);
void proto_register_gcsna(void);
/* gcsna Handle for the dissection */
static dissector_handle_t gcsna_handle;
static dissector_handle_t cdma2k_handle;
/* Function handlers for each message/information fields */
static void gcsna_message_decode(proto_item *item, tvbuff_t *tvb, proto_tree *tree, guint *offset, proto_tree *mainTree, guint16 *noerror, packet_info *pinfo);
static void gcsna_message_GCSNA1xCircuitService(proto_item *item, tvbuff_t *tvb, packet_info *pinfo, proto_tree *mainTree, proto_tree *tree, guint *offset);
static void gcsna_message_GCSNAL2Ack(proto_item *item, tvbuff_t *tvb, proto_tree *tree, guint *offset);
static void gcsna_message_GCSNAServiceReject(proto_item *item, tvbuff_t *tvb, proto_tree *tree, guint *offset);
/*Initialize all the header parameters that are to be displayed*/
static int proto_gcsna = -1;
static int hf_gcsna_msghdr = -1;
static int hf_gcsna_msgid = -1;
static int hf_gcsna_rejSequence = -1;
static int hf_gcsna_cause = -1;
static int hf_gcsna_ackSequence = -1;
static int hf_gcsna_recordType = -1;
static int hf_gcsna_1xProtocolRevision = -1;
static int hf_gcsna_invalidMessageId = -1;
static int hf_gcsna_l2ack = -1;
static int hf_gcsna_servicereject = -1;
static int hf_gcsna_gcsna_option = -1;
static int hf_gcsna_gcsnaClass = -1;
static int hf_gcsna_gcsnaClassRev = -1;
static int hf_gcsna_altGCSNAOption = -1;
static int hf_gcsna_altGCSNAOptionIncluded = -1;
static int hf_gcsna_NumaltGCSNAOption = -1;
static int hf_gcsna_ackRequired = -1;
static int hf_gcsna_stopDupDetect = -1;
static int hf_gcsna_msgSequence = -1;
static int hf_gcsna_tlacEncapsulated = -1;
static int hf_gcsna_NumTLACEncapsulated1xL3PDU = -1;
static int hf_gcsna_tlacReserved = -1;
static int hf_gcsna_iwsidIncluded = -1;
static int hf_gcsna_iwsidValue = -1;
static int hf_gcsna_unsupported_reject_seq = -1;
/* Toggle sub-tree items */
static gint ett_gcsna_msghdr = -1;
static gint ett_gcsna_subtree = -1;
static gint ett_gcsna_option = -1;
static expert_field ei_gcsna_error = EI_INIT;
#define GCSNA1XCIRCUITSERVICE 0x01
#define GCSNAL2ACK 0x02
#define GCSNASERVICEREJECT 0x03
/* Msg Types */
static const value_string gcsna_message_types[] = {
{ 0x01, "GCSNA 1X Circuitservice" },
{ 0x02, "GCSNA L2 Ack"},
{ 0x03, "GCSNA Servicereject"},
{ 0, NULL },
};
/* Cause Types */
static const value_string gcsna_cause_types[] = {
{ 0, "Invalid GCSNAOption" },
{ 1, "Invalid 1xProtocolRevision" },
{ 2, "Invalid GCSNAOption and 1xProtocolRevision"},
{ 3, "Invalid Message Id"},
{ 4, "GCSNA 1xParameters provisioning is not supported" },
{ 5, "Unsupported RecordType in GCSNA 1xParameters message"},
{ 0, NULL },
};
/*
GCSNA Class GCSNA ClassRevision 1x Service
+----------+-------------------+--------------------------------+
| | 0 | Release 8 1xCSFB from E-UTRAN |
| +-------------------+--------------------------------+
| 0 | 1 | Release 9 e1xCSFB from E-UTRAN |
| +-------------------+--------------------------------+
| | 2 | C.S0097-A supported eCSFB |
+----------+-------------------+--------------------------------+
| 1 | 0 | SRVCC from E-UTRAN |
+----------+-------------------+--------------------------------+
*/
static const value_string gcsna_option_values[] = {
{ 0, "Release 8 1xCSFB from E-UTRAN" },
{ 1, "Release 9 e1xCSFB from E-UTRAN" },
{ 2, "C.S0097-A supported eCSFB"},
{ 8, "SRVCC from E-UTRAN"},
{ 0, NULL },
};
static const value_string gcsna_tru_false_values[] = {
{ 0, "False" },
{ 1, "True" },
{ 0, NULL },
};
/* Decoder for all the information elements of A21 Message Type */
static void
gcsna_message_decode(proto_item *item, tvbuff_t *tvb, proto_tree *tree, guint *offset, proto_tree *mainTree, guint16 *noerror, packet_info *pinfo)
{
guint16 msgId = -1;
msgId = tvb_get_guint8(tvb, *offset);
*offset += 1;
switch (msgId)
{
case GCSNA1XCIRCUITSERVICE:
{
gcsna_message_GCSNA1xCircuitService(item, tvb, pinfo, mainTree, tree, offset);
break;
}
case GCSNAL2ACK:
{
gcsna_message_GCSNAL2Ack(item, tvb, tree, offset);
break;
}
case GCSNASERVICEREJECT:
{
gcsna_message_GCSNAServiceReject(item, tvb, tree, offset);
break;
}
default:
{
*noerror = 0;
break;
}
}
}
static void
gcsna_message_GCSNA1xCircuitService(proto_item *item, tvbuff_t *tvb, packet_info *pinfo, proto_tree *mainTree, proto_tree *tree, guint *offset)
{
guint16 alt_gcsna_incl = 0, num_alt_gcsna_opt = -1, iws_incl = 0;
guint8 num_res;
guint bit_offset = *offset * 8;
proto_tree *subtree = NULL;
tvbuff_t *new_tvb;
/* GCSNAOption 8 bits */
item = proto_tree_add_item(tree, hf_gcsna_gcsna_option, tvb, *offset, 1, ENC_BIG_ENDIAN);
subtree = proto_item_add_subtree(item, ett_gcsna_option);
proto_tree_add_bits_item(subtree, hf_gcsna_gcsnaClass, tvb, bit_offset, 5, ENC_BIG_ENDIAN);
bit_offset += 5;
proto_tree_add_bits_item(subtree, hf_gcsna_gcsnaClassRev, tvb, bit_offset, 3, ENC_BIG_ENDIAN);
bit_offset += 3;
alt_gcsna_incl = tvb_get_bits8(tvb, bit_offset, 1);
proto_tree_add_bits_item(tree, hf_gcsna_altGCSNAOptionIncluded, tvb, bit_offset, 1, ENC_BIG_ENDIAN);
bit_offset += 1;
if (alt_gcsna_incl)
{
num_alt_gcsna_opt = tvb_get_bits8(tvb, bit_offset, 8);
proto_tree_add_bits_item(tree, hf_gcsna_NumaltGCSNAOption, tvb, bit_offset, 8, ENC_BIG_ENDIAN);
bit_offset += 8;
while (num_alt_gcsna_opt != 0)
{
proto_tree_add_bits_item(tree, hf_gcsna_altGCSNAOption, tvb, bit_offset, 8, ENC_BIG_ENDIAN);
bit_offset += 8;
num_alt_gcsna_opt--;
}
}
iws_incl = tvb_get_bits8(tvb, bit_offset, 1);
proto_tree_add_bits_item(tree, hf_gcsna_iwsidIncluded, tvb, bit_offset, 1, ENC_BIG_ENDIAN);
bit_offset++;
if (iws_incl)
{
proto_tree_add_bits_item(tree, hf_gcsna_iwsidValue, tvb, bit_offset, 16, ENC_BIG_ENDIAN);
bit_offset += 16;
}
proto_tree_add_bits_item(tree, hf_gcsna_ackRequired, tvb, bit_offset, 1, ENC_BIG_ENDIAN);
bit_offset++;
proto_tree_add_bits_item(tree, hf_gcsna_stopDupDetect, tvb, bit_offset, 1, ENC_BIG_ENDIAN);
bit_offset++;
proto_tree_add_bits_item(tree, hf_gcsna_msgSequence, tvb, bit_offset, 6, ENC_BIG_ENDIAN);
bit_offset += 6;
proto_tree_add_bits_item(tree, hf_gcsna_NumTLACEncapsulated1xL3PDU, tvb, bit_offset, 2, ENC_BIG_ENDIAN);
bit_offset += 2;
/* The sender shall include reserved bits to make this message integral number of octets up to TLACEncapsulated1xL3PDU field.
* The sender shall set all bits in this field to '0'. The receiver shall ignore this field.
*/
/* calculate number of reserved bits */
num_res = 8 - (bit_offset & 0x3);
proto_tree_add_bits_item(tree, hf_gcsna_tlacReserved, tvb, bit_offset, num_res, ENC_BIG_ENDIAN);
bit_offset = bit_offset + num_res;
*offset = bit_offset >> 3;
proto_tree_add_item(tree, hf_gcsna_tlacEncapsulated, tvb, *offset, -1, ENC_NA);
if (cdma2k_handle) {
new_tvb = tvb_new_subset_length_caplen(tvb, *offset, -1, -1);
call_dissector(cdma2k_handle, new_tvb, pinfo, mainTree);
}
/* set the offset to the end of the message */
*offset += tvb_reported_length_remaining(tvb, *offset);
}
static void gcsna_message_GCSNAL2Ack(proto_item *item, tvbuff_t *tvb, proto_tree *tree, guint *offset)
{
proto_tree *subtree = NULL;
item = proto_tree_add_item(tree, hf_gcsna_l2ack, tvb, *offset, 1, ENC_NA);
subtree = proto_item_add_subtree(item, ett_gcsna_subtree);
proto_tree_add_bits_item(subtree, hf_gcsna_ackSequence, tvb, *offset * 8, 6, ENC_BIG_ENDIAN);
*offset += 1;
}
static void gcsna_message_GCSNAServiceReject(proto_item *item, tvbuff_t *tvb, proto_tree *tree, guint *offset)
{
guint16 cause_val = -1, num_fields = -1, l_offset = -1;
proto_tree *subtree = NULL;
item = proto_tree_add_item(tree, hf_gcsna_servicereject, tvb, *offset, 1, ENC_NA);
subtree = proto_item_add_subtree(item, ett_gcsna_subtree);
l_offset = *offset * 8;
proto_tree_add_bits_item(subtree, hf_gcsna_rejSequence, tvb, l_offset, 6, ENC_BIG_ENDIAN);
l_offset += 6;
proto_tree_add_bits_item(subtree, hf_gcsna_cause, tvb, l_offset, 8, ENC_BIG_ENDIAN);
cause_val = tvb_get_bits8(tvb, *offset * 8 + 6, 8);
l_offset += 8;
switch (cause_val)
{
case 0:
case 2:
{
num_fields = tvb_get_bits8(tvb, l_offset, 8);
l_offset += 8;
while (num_fields > 0)
{
proto_tree_add_bits_item(subtree, hf_gcsna_gcsnaClass, tvb, l_offset, 5, ENC_BIG_ENDIAN);
l_offset += 5;
proto_tree_add_bits_item(subtree, hf_gcsna_gcsnaClassRev, tvb, l_offset, 3, ENC_BIG_ENDIAN);
l_offset += 3;
num_fields--;
}
if (cause_val == 2)
{
proto_tree_add_bits_item(subtree, hf_gcsna_1xProtocolRevision, tvb, l_offset, 8, ENC_BIG_ENDIAN);
l_offset += 8;
}
break;
}
case 1:
{
proto_tree_add_bits_item(subtree, hf_gcsna_1xProtocolRevision, tvb, l_offset, 8, ENC_BIG_ENDIAN);
l_offset += 8;
break;
}
case 3:
{
proto_tree_add_bits_item(subtree, hf_gcsna_invalidMessageId, tvb, l_offset, 8, ENC_BIG_ENDIAN);
l_offset += 8;
break;
}
/*This Cause Value is not supported in IWS Stack*/
case 5:
{
num_fields = tvb_get_bits8(tvb, l_offset, 8);
l_offset += 8;
while (num_fields > 0)
{
proto_tree_add_bits_item(subtree, hf_gcsna_recordType, tvb, l_offset, 8, ENC_BIG_ENDIAN);
l_offset += 8;
num_fields--;
}
break;
}
default:
{
proto_tree_add_item(subtree, hf_gcsna_unsupported_reject_seq, tvb, l_offset, -1, ENC_NA);
break;
}
}
if (l_offset % 8 == 0)
{
*offset = (l_offset / 8);
} else
{
*offset = (l_offset / 8) + 1;
}
}
/*Method called when the dissection starts.....Starting point*/
static int
dissect_gcsna(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
{
/* Initialization*/
proto_tree *gcsna_msghdr_tree_start = NULL;
proto_item *item = NULL;
guint32 offset = 0;
guint16 noerror = 1;
/*Add the protocol name to display*/
col_set_str(pinfo->cinfo, COL_PROTOCOL, "gcsna");
col_add_fstr(pinfo->cinfo, COL_INFO, "[gcsna]");
item = proto_tree_add_item(tree, hf_gcsna_msghdr, tvb, 0, -1, ENC_NA);
gcsna_msghdr_tree_start = proto_item_add_subtree(item, ett_gcsna_msghdr);
if (tree)
{
proto_tree_add_item(gcsna_msghdr_tree_start, hf_gcsna_msgid, tvb, offset, 1, ENC_BIG_ENDIAN);
while (tvb_captured_length_remaining(tvb, offset) != 0 && noerror == 1)
gcsna_message_decode(item, tvb, gcsna_msghdr_tree_start, &offset, tree, &noerror, pinfo);
if (noerror == 0)
{
expert_add_info(pinfo, item, &ei_gcsna_error);
}
}
return tvb_reported_length(tvb);
}
/*Register gcsna to be accessed by other dissectors/plugins*/
void
proto_register_gcsna(void)
{
static hf_register_info hf[] = {
{ &hf_gcsna_servicereject,
{ "GCSNA SERVICEREJECT", "gcsna.servicereject", FT_NONE, BASE_NONE,NULL, 0x0, NULL, HFILL } },
/*{ & hf_gcsna_msgid,
{ "GCSNA Message Type", "gcsna.MsgType", FT_UINT8, BASE_HEX_DEC, VALS(A21_Message_Types), 0x0, NULL, HFILL } },*/
{ &hf_gcsna_l2ack,
{ "L2ACK", "gcsna.l2ack", FT_NONE, BASE_NONE,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_msghdr,
{ "General Circuit Services Notification Application Protocol", "gcsna.msghdr", FT_NONE, BASE_NONE,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_NumTLACEncapsulated1xL3PDU,
{ "NumTLACEncapsulated1xL3PDU", "gcsna.NumTLACEncapsulated1xL3PDU", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_tlacReserved,
{ "Reserved", "gcsna.tlacReserved", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_tlacEncapsulated,
{ "TLAC Encapsulated", "gcsna.tlacEncapsulated", FT_BYTES, BASE_NONE,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_msgSequence,
{ "Msg Sequence", "gcsna.msgSequence", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_stopDupDetect,
{ "Stop Dup Detect", "gcsna.stopDupDetect", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_ackRequired,
{ "Ack Required", "gcsna.ackRequired", FT_UINT8, BASE_DEC,VALS(gcsna_tru_false_values), 0x0, NULL, HFILL } },
{ &hf_gcsna_altGCSNAOptionIncluded,
{ "AlternativeGCSNAOption_INCL", "gcsna.altGCSNAOptionIncluded", FT_UINT8, BASE_DEC,VALS(gcsna_tru_false_values), 0x0, NULL, HFILL } },
{ &hf_gcsna_altGCSNAOption,
{ "Alternate GCSNA Option", "gcsna.altGCSNAOption", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_gcsna_option,
{ "GCSNA Option", "gcsna.Option", FT_UINT8, BASE_HEX, VALS(gcsna_option_values), 0x0, NULL, HFILL } },
{ &hf_gcsna_NumaltGCSNAOption,
{ "NumAlternativeGCSNAOptions", "gcsna.NumaltGCSNAOption", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_iwsidValue,
{ "IWS_ID", "gcsna.iwsidValue", FT_UINT16, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_iwsidIncluded,
{ "IWSIDIncl", "gcsna.iwsidIncluded", FT_UINT8, BASE_DEC,VALS(gcsna_tru_false_values), 0x0, NULL, HFILL } },
{ &hf_gcsna_gcsnaClassRev,
{ "GCSNA Class revision", "gcsna.ClassRev", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_gcsnaClass,
{ "GCSNA Class", "gcsna.Class", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_invalidMessageId,
{ "InvalidMessageId", "gcsna.invalidMessageId", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_1xProtocolRevision,
{ "1xProtocolRevision", "gcsna.1xProtocolRevision", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_recordType,
{ "Record Type", "gcsna.recordType", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_ackSequence,
{ "Ack Sequence", "gcsna.ackSequence", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_cause,
{ "Cause", "gcsna.cause", FT_UINT8, BASE_HEX_DEC,VALS(gcsna_cause_types), 0x0, NULL, HFILL } },
{ &hf_gcsna_rejSequence,
{ "Reject Sequence", "gcsna.rejSequence", FT_UINT8, BASE_HEX_DEC,NULL, 0x0, NULL, HFILL } },
{ &hf_gcsna_msgid,
{ "GCSNA Message Type", "gcsna.msgId", FT_UINT8, BASE_HEX_DEC,VALS(gcsna_message_types), 0x0, NULL, HFILL } },
{ &hf_gcsna_unsupported_reject_seq,
{ "Invalid / Unsupported GCSNA Message Reject Sequence", "gcsna.unsupportedrejectseq", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }
};
static gint *ett[] = {
&ett_gcsna_msghdr,
&ett_gcsna_subtree,
&ett_gcsna_option
};
static ei_register_info ei[] = {
{ &ei_gcsna_error, { "gcsna.error", PI_PROTOCOL, PI_ERROR, "Violation of protocol specs (e.g. invalid information element)", EXPFILL }},
};
expert_module_t* expert_gcsna;
proto_gcsna = proto_register_protocol(
"GCSNA", /* name */
"GCSNA", /* short name */
"gcsna" /* abbrev */
);
register_dissector("gcsna", dissect_gcsna, proto_gcsna);
proto_register_field_array(proto_gcsna, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_gcsna = expert_register_protocol(proto_gcsna);
expert_register_field_array(expert_gcsna, ei, array_length(ei));
}
void
proto_reg_handoff_gcsna(void)
{
static int once = 1;
if (once == 1)
{
cdma2k_handle = find_dissector("cdma2k");
gcsna_handle = create_dissector_handle(dissect_gcsna, proto_gcsna);
once = 0;
}
}