1 /* packet-ansi_tcap-template.c
2 * Routines for ANSI TCAP
3 * Copyright 2007 Anders Broman <anders.broman@ericsson.com>
4 * Built from the gsm-map dissector Copyright 2004 - 2005, Anders Broman <anders.broman@ericsson.com>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include <epan/packet.h>
17 #include <epan/prefs.h>
18 #include <epan/expert.h>
19 #include <epan/oids.h>
20 #include <epan/asn1.h>
21 #include <epan/strutil.h>
22 #include <wsutil/array.h>
24 #include "packet-ber.h"
25 #include "packet-tcap.h"
26 #include "packet-ansi_tcap.h"
28 #define PNAME "ANSI Transaction Capabilities Application Part"
29 #define PSNAME "ANSI_TCAP"
30 #define PFNAME "ansi_tcap"
32 void proto_register_ansi_tcap(void);
33 void proto_reg_handoff_ansi_tcap(void);
35 /* Preference settings */
36 #define ANSI_TCAP_TID_ONLY 0
37 #define ANSI_TCAP_TID_AND_SOURCE 1
38 #define ANSI_TCAP_TID_SOURCE_AND_DEST 2
39 static int ansi_tcap_response_matching_type
= ANSI_TCAP_TID_ONLY
;
41 /* Initialize the protocol and registered fields */
42 static int proto_ansi_tcap
;
45 static int hf_ansi_tcapsrt_SessionId
;
46 static int hf_ansi_tcapsrt_Duplicate
;
47 static int hf_ansi_tcapsrt_BeginSession
;
48 static int hf_ansi_tcapsrt_EndSession
;
49 static int hf_ansi_tcapsrt_SessionTime
;
51 static int hf_ansi_tcap_bit_h
;
52 static int hf_ansi_tcap_op_family
;
53 static int hf_ansi_tcap_op_specifier
;
55 #include "packet-ansi_tcap-hf.c"
57 /* Initialize the subtree pointers */
60 static int ett_ansi_tcap_op_code_nat
;
64 static int ett_ansi_tcap_stat
;
66 static expert_field ei_ansi_tcap_dissector_not_implemented
;
68 static struct tcapsrt_info_t
* gp_tcapsrt_info
;
69 static bool tcap_subdissector_used
=false;
71 static struct tcaphash_context_t
* gp_tcap_context
;
73 /* Note the high bit should be masked off when registering in this table (0x7fff)*/
74 static dissector_table_t ansi_tcap_national_opcode_table
; /* National Operation Codes */
76 #include "packet-ansi_tcap-ett.c"
80 /* When several Tcap components are received in a single TCAP message,
81 we have to use several buffers for the stored parameters
82 because else this data are erased during TAP dissector call */
83 #define MAX_TCAP_INSTANCE 10
84 int tcapsrt_global_current
=0;
85 struct tcapsrt_info_t tcapsrt_global_info
[MAX_TCAP_INSTANCE
];
87 static dissector_table_t ber_oid_dissector_table
;
88 static const char * cur_oid
;
89 static const char * tcapext_oid
;
91 static dissector_handle_t ansi_map_handle
;
92 static dissector_handle_t ain_handle
;
94 struct ansi_tcap_private_t ansi_tcap_private
;
95 #define MAX_TID_STR_LEN 1024
97 static void ansi_tcap_ctx_init(struct ansi_tcap_private_t
*a_tcap_ctx
) {
98 memset(a_tcap_ctx
, '\0', sizeof(*a_tcap_ctx
));
99 a_tcap_ctx
->signature
= ANSI_TCAP_CTX_SIGNATURE
;
100 a_tcap_ctx
->oid_is_present
= false;
101 a_tcap_ctx
->TransactionID_str
= NULL
;
104 static const value_string ansi_tcap_national_op_code_family_vals
[] = {
105 { 0x0, "All Families" },
106 { 0x1, "Parameter" },
108 { 0x3, "Provide Instructions" },
109 { 0x4, "Connection Control" },
110 { 0x5, "Caller Interaction" },
111 { 0x6, "Send Notification" },
112 { 0x7, "Network Management" },
113 { 0x8, "Procedural" },
114 { 0x9, "Operation Control" },
115 { 0xa, "Report Event" },
117 { 0x7e, "Miscellaneous" },
118 { 0x7f, "Reserved" },
122 /* Transaction tracking */
123 /* Transaction table */
124 struct ansi_tcap_invokedata_t
{
130 int32_t OperationCode_private
;
131 int32_t OperationCode_national
;
134 static wmem_multimap_t
*TransactionId_table
;
136 /* Store Invoke information needed for the corresponding reply */
138 save_invoke_data(packet_info
*pinfo
, proto_tree
*tree _U_
, tvbuff_t
*tvb _U_
){
139 struct ansi_tcap_invokedata_t
*ansi_tcap_saved_invokedata
;
143 src
= address_to_str(pinfo
->pool
, &(pinfo
->src
));
144 dst
= address_to_str(pinfo
->pool
, &(pinfo
->dst
));
146 if ((!pinfo
->fd
->visited
)&&(ansi_tcap_private
.TransactionID_str
)){
148 /* Only do this once XXX I hope it's the right thing to do */
149 /* The hash string needs to contain src and dest to distinguish different flows */
150 switch(ansi_tcap_response_matching_type
){
151 case ANSI_TCAP_TID_ONLY
:
152 buf
= wmem_strdup(pinfo
->pool
, ansi_tcap_private
.TransactionID_str
);
154 case ANSI_TCAP_TID_AND_SOURCE
:
155 buf
= wmem_strdup_printf(pinfo
->pool
, "%s%s",ansi_tcap_private
.TransactionID_str
,src
);
157 case ANSI_TCAP_TID_SOURCE_AND_DEST
:
159 buf
= wmem_strdup_printf(pinfo
->pool
, "%s%s%s",ansi_tcap_private
.TransactionID_str
,src
,dst
);
163 ansi_tcap_saved_invokedata
= wmem_new(wmem_file_scope(), struct ansi_tcap_invokedata_t
);
164 ansi_tcap_saved_invokedata
->OperationCode
= ansi_tcap_private
.d
.OperationCode
;
165 ansi_tcap_saved_invokedata
->OperationCode_national
= ansi_tcap_private
.d
.OperationCode_national
;
166 ansi_tcap_saved_invokedata
->OperationCode_private
= ansi_tcap_private
.d
.OperationCode_private
;
168 wmem_multimap_insert32(TransactionId_table
,
169 wmem_strdup(wmem_file_scope(), buf
),
171 ansi_tcap_saved_invokedata
);
173 ws_warning("Tcap Invoke Hash string %s",buf);
179 find_saved_invokedata(packet_info
*pinfo
, proto_tree
*tree _U_
, tvbuff_t
*tvb _U_
){
180 struct ansi_tcap_invokedata_t
*ansi_tcap_saved_invokedata
;
184 if (!ansi_tcap_private
.TransactionID_str
) {
188 src
= address_to_str(pinfo
->pool
, &(pinfo
->src
));
189 dst
= address_to_str(pinfo
->pool
, &(pinfo
->dst
));
191 /* The hash string needs to contain src and dest to distinguish different flows */
192 buf
= (char *)wmem_alloc(pinfo
->pool
, MAX_TID_STR_LEN
);
194 /* Reverse order to invoke */
195 switch(ansi_tcap_response_matching_type
){
196 case ANSI_TCAP_TID_ONLY
:
197 snprintf(buf
,MAX_TID_STR_LEN
,"%s",ansi_tcap_private
.TransactionID_str
);
199 case ANSI_TCAP_TID_AND_SOURCE
:
200 snprintf(buf
,MAX_TID_STR_LEN
,"%s%s",ansi_tcap_private
.TransactionID_str
,dst
);
202 case ANSI_TCAP_TID_SOURCE_AND_DEST
:
204 snprintf(buf
,MAX_TID_STR_LEN
,"%s%s%s",ansi_tcap_private
.TransactionID_str
,dst
,src
);
208 ansi_tcap_saved_invokedata
= (struct ansi_tcap_invokedata_t
*)wmem_multimap_lookup32_le(TransactionId_table
, buf
, pinfo
->num
);
209 if(ansi_tcap_saved_invokedata
){
210 ansi_tcap_private
.d
.OperationCode
= ansi_tcap_saved_invokedata
->OperationCode
;
211 ansi_tcap_private
.d
.OperationCode_national
= ansi_tcap_saved_invokedata
->OperationCode_national
;
212 ansi_tcap_private
.d
.OperationCode_private
= ansi_tcap_saved_invokedata
->OperationCode_private
;
218 /* As currently ANSI MAP is the only possible sub dissector this function
219 * must be improved to handle general cases.
224 * 1)Handle national codes
226 * - Create a ansi.tcap.national dissector table and have dissectors for
227 * national codes register there and let ansi tcap call them.
228 * 2)Handle Private codes properly
230 * Unclear how to differentiate between different private "code sets".
231 * Use SCCP SSN table as before? or a ansi.tcap.private dissector table?
235 find_tcap_subdissector(tvbuff_t
*tvb
, asn1_ctx_t
*actx
, proto_tree
*tree
){
238 /* If "DialoguePortion objectApplicationId ObjectIDApplicationContext
239 * points to the subdissector this code can be used.
241 if(ansi_tcap_private.d.oid_is_present){
242 call_ber_oid_callback(ansi_tcap_private.objectApplicationId_oid, tvb, 0, actx-pinfo, tree, NULL);
246 if(ansi_tcap_private
.d
.pdu
== 1){
247 /* Save Invoke data for this transaction */
248 save_invoke_data(actx
->pinfo
, tree
, tvb
);
250 /* Get saved data for this transaction */
251 if(find_saved_invokedata(actx
->pinfo
, tree
, tvb
)){
252 if(ansi_tcap_private
.d
.OperationCode
== 0){
254 item
= proto_tree_add_int(tree
, hf_ansi_tcap_national
, tvb
, 0, 0, ansi_tcap_private
.d
.OperationCode_national
);
256 item
= proto_tree_add_int(tree
, hf_ansi_tcap_private
, tvb
, 0, 0, ansi_tcap_private
.d
.OperationCode_private
);
258 proto_item_set_generated(item
);
259 ansi_tcap_private
.d
.OperationCode_item
= item
;
262 if(ansi_tcap_private
.d
.OperationCode
== 0){
264 uint8_t family
= (ansi_tcap_private
.d
.OperationCode_national
& 0x7f00)>>8;
265 uint8_t specifier
= (uint8_t)(ansi_tcap_private
.d
.OperationCode_national
& 0xff);
266 if(!dissector_try_uint(ansi_tcap_national_opcode_table
, ansi_tcap_private
.d
.OperationCode_national
, tvb
, actx
->pinfo
, actx
->subtree
.top_tree
)){
267 proto_tree_add_expert_format(tree
, actx
->pinfo
, &ei_ansi_tcap_dissector_not_implemented
, tvb
, 0, -1,
268 "Dissector for ANSI TCAP NATIONAL code:0x%x(Family %u, Specifier %u) \n"
269 "not implemented. Contact Wireshark developers if you want this supported(Spec required)",
270 ansi_tcap_private
.d
.OperationCode_national
, family
, specifier
);
274 }else if(ansi_tcap_private
.d
.OperationCode
== 1){
276 if((ansi_tcap_private
.d
.OperationCode_private
& 0xff00) == 0x0900){
277 /* This is abit of a hack as it assumes the private codes with a "family" of 0x09 is ANSI MAP
279 * N.S0005-0 v 1.0 TCAP Formats and Procedures 5-16 Application Services
280 * 6.3.2 Component Portion
281 * The Operation Code is partitioned into an Operation Family followed by a
282 * Specifier associated with each Operation Family member. For TIA/EIA-41 the
283 * Operation Family is coded as decimal 9. Bit H of the Operation Family is always
286 call_dissector_with_data(ansi_map_handle
, tvb
, actx
->pinfo
, actx
->subtree
.top_tree
, &ansi_tcap_private
);
289 } else if ((ansi_tcap_private
.d
.OperationCode_private
& 0xf000) == 0x6000) {
290 call_dissector_with_data(ain_handle
, tvb
, actx
->pinfo
, actx
->subtree
.top_tree
, &ansi_tcap_private
);
294 proto_tree_add_expert_format(tree
, actx
->pinfo
, &ei_ansi_tcap_dissector_not_implemented
, tvb
, 0, -1,
295 "Dissector for ANSI TCAP PRIVATE code:%u not implemented.\n"
296 "Contact Wireshark developers if you want this supported(Spec required)",
297 ansi_tcap_private
.d
.OperationCode_private
);
301 #include "packet-ansi_tcap-fn.c"
307 dissect_ansi_tcap(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, void* data _U_
)
309 proto_item
*item
=NULL
;
310 proto_tree
*tree
=NULL
;
312 proto_item
*stat_item
=NULL
;
313 proto_tree
*stat_tree
=NULL
;
315 struct tcaphash_context_t
* p_tcap_context
;
316 dissector_handle_t subdissector_handle
;
320 asn1_ctx_init(&asn1_ctx
, ASN1_ENC_BER
, true, pinfo
);
321 ansi_tcap_ctx_init(&ansi_tcap_private
);
323 asn1_ctx
.subtree
.top_tree
= parent_tree
;
324 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "ANSI TCAP");
326 /* create display subtree for the protocol */
328 item
= proto_tree_add_item(parent_tree
, proto_ansi_tcap
, tvb
, 0, -1, ENC_NA
);
329 tree
= proto_item_add_subtree(item
, ett_tcap
);
334 gp_tcapsrt_info
=tcapsrt_razinfo();
335 tcap_subdissector_used
=false;
336 gp_tcap_context
=NULL
;
337 dissect_ansi_tcap_PackageType(false, tvb
, 0, &asn1_ctx
, tree
, -1);
339 #if 0 /* Skip this part for now it will be rewritten */
340 if (g_ansi_tcap_HandleSRT
&& !tcap_subdissector_used
) {
341 if (gtcap_DisplaySRT
&& tree
) {
342 stat_tree
= proto_tree_add_subtree(tree
, tvb
, 0, 0, ett_ansi_tcap_stat
, &stat_item
, "Stat");
343 proto_item_set_generated(stat_item
);
345 p_tcap_context
=tcapsrt_call_matching(tvb
, pinfo
, stat_tree
, gp_tcapsrt_info
);
346 ansi_tcap_private
.context
=p_tcap_context
;
348 /* If the current message is TCAP only,
349 * save the Application contexte name for the next messages
351 if ( p_tcap_context
&& cur_oid
&& !p_tcap_context
->oid_present
) {
352 /* Save the application context and the sub dissector */
353 (void) g_strlcpy(p_tcap_context
->oid
, cur_oid
, sizeof(p_tcap_context
->oid
));
354 if ( (subdissector_handle
= dissector_get_string_handle(ber_oid_dissector_table
, cur_oid
)) ) {
355 p_tcap_context
->subdissector_handle
=subdissector_handle
;
356 p_tcap_context
->oid_present
=true;
359 if (g_ansi_tcap_HandleSRT
&& p_tcap_context
&& p_tcap_context
->callback
) {
360 /* Callback function for the upper layer */
361 (p_tcap_context
->callback
)(tvb
, pinfo
, stat_tree
, p_tcap_context
);
365 return tvb_captured_length(tvb
);
370 proto_reg_handoff_ansi_tcap(void)
372 ansi_map_handle
= find_dissector_add_dependency("ansi_map", proto_ansi_tcap
);
373 ain_handle
= find_dissector_add_dependency("ain", proto_ansi_tcap
);
374 ber_oid_dissector_table
= find_dissector_table("ber.oid");
380 proto_register_ansi_tcap(void)
382 module_t
*ansi_tcap_module
;
385 /* Setup list of header fields See Section 1.6.1 for details*/
386 static hf_register_info hf
[] = {
388 /* Tcap Service Response Time */
389 { &hf_ansi_tcapsrt_SessionId
,
391 "ansi_tcap.srt.session_id",
392 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
395 { &hf_ansi_tcapsrt_BeginSession
,
397 "ansi_tcap.srt.begin",
398 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
399 "SRT Begin of Session", HFILL
}
401 { &hf_ansi_tcapsrt_EndSession
,
404 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
405 "SRT End of Session", HFILL
}
407 { &hf_ansi_tcapsrt_SessionTime
,
408 { "Session duration",
409 "ansi_tcap.srt.sessiontime",
410 FT_RELATIVE_TIME
, BASE_NONE
, NULL
, 0x0,
411 "Duration of the TCAP session", HFILL
}
413 { &hf_ansi_tcapsrt_Duplicate
,
414 { "Request Duplicate",
415 "ansi_tcap.srt.duplicate",
416 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
420 { &hf_ansi_tcap_bit_h
,
421 { "Require Reply", "ansi_tcap.req_rep",
422 FT_BOOLEAN
, 16, NULL
, 0x8000,
425 { &hf_ansi_tcap_op_family
,
427 "ansi_tcap.op_family",
428 FT_UINT16
, BASE_DEC
, VALS(ansi_tcap_national_op_code_family_vals
), 0x7f00,
431 { &hf_ansi_tcap_op_specifier
,
433 "ansi_tcap.op_specifier",
434 FT_UINT16
, BASE_DEC
, NULL
, 0x00ff,
437 #include "packet-ansi_tcap-hfarr.c"
440 /* Setup protocol subtree array */
441 static int *ett
[] = {
447 &ett_ansi_tcap_op_code_nat
,
448 #include "packet-ansi_tcap-ettarr.c"
451 static ei_register_info ei
[] = {
452 { &ei_ansi_tcap_dissector_not_implemented
, { "ansi_tcap.dissector_not_implemented", PI_UNDECODED
, PI_WARN
, "Dissector not implemented", EXPFILL
}},
455 expert_module_t
* expert_ansi_tcap
;
457 static const enum_val_t ansi_tcap_response_matching_type_values
[] = {
458 {"tid", "Transaction ID only", ANSI_TCAP_TID_ONLY
},
459 {"tid_source", "Transaction ID and Source", ANSI_TCAP_TID_AND_SOURCE
},
460 {"tid_source_dest", "Transaction ID Source and Destination", ANSI_TCAP_TID_SOURCE_AND_DEST
},
464 /* Register the protocol name and description */
465 proto_ansi_tcap
= proto_register_protocol(PNAME
, PSNAME
, PFNAME
);
466 register_dissector("ansi_tcap", dissect_ansi_tcap
, proto_ansi_tcap
);
468 /* Note the high bit should be masked off when registering in this table (0x7fff)*/
469 ansi_tcap_national_opcode_table
= register_dissector_table("ansi_tcap.nat.opcode", "ANSI TCAP National Opcodes", proto_ansi_tcap
, FT_UINT16
, BASE_DEC
);
470 /* Required function calls to register the header fields and subtrees used */
471 proto_register_field_array(proto_ansi_tcap
, hf
, array_length(hf
));
472 proto_register_subtree_array(ett
, array_length(ett
));
473 expert_ansi_tcap
= expert_register_protocol(proto_ansi_tcap
);
474 expert_register_field_array(expert_ansi_tcap
, ei
, array_length(ei
));
476 ansi_tcap_module
= prefs_register_protocol(proto_ansi_tcap
, proto_reg_handoff_ansi_tcap
);
478 prefs_register_enum_preference(ansi_tcap_module
, "transaction.matchtype",
479 "Type of matching invoke/response",
480 "Type of matching invoke/response, risk of mismatch if loose matching chosen",
481 &ansi_tcap_response_matching_type
, ansi_tcap_response_matching_type_values
, false);
483 TransactionId_table
= wmem_multimap_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wmem_str_hash
, g_str_equal
);