/*
 * camel-persistentdata.c
 * Source for lists and hash tables used in wireshark's camel dissector
 * for calculation of delays in camel calls
 * Copyright 2006 Florent Drouin
 *
 * $Id$
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "config.h"

#include <glib.h>
#include <stdio.h>
#include <string.h>

#include <epan/emem.h>
#include <epan/packet.h>
#include <epan/tap.h>
#include <epan/asn1.h>
#include <epan/camel-persistentdata.h>
#include <epan/dissectors/packet-tcap.h>
#include <epan/dissectors/packet-mtp3.h>

const value_string  camelSRTtype_naming[]= {
  { CAMELSRT_SESSION,         "TCAP_Session" },
  { CAMELSRT_VOICE_INITIALDP, "InialDP/Continue" },
  { CAMELSRT_VOICE_ACR1,      "Slice1_ACR/ACH" },
  { CAMELSRT_VOICE_ACR2,      "Slice2_ACR/ACH" },
  { CAMELSRT_VOICE_ACR3,      "Slice3_ACR/ACH" },
  { CAMELSRT_VOICE_DISC,      "EvtRepBSCM/Release" },
  { CAMELSRT_SMS_INITIALDP,   "InitialDP/ContinueSMS" },
  { CAMELSRT_GPRS_INITIALDP,  "InitialDP/ContinueGPRS" },
  { CAMELSRT_GPRS_REPORT,     "EvtRepGPRS/ContinueGPRS" },
  { 0,NULL}
};

static gint camelsrt_call_equal(gconstpointer k1, gconstpointer k2);
static guint camelsrt_call_hash(gconstpointer k);
static struct camelsrt_call_t *find_camelsrt_call(struct camelsrt_call_info_key_t *p_camelsrt_call_key);
static struct camelsrt_call_t *new_camelsrt_call(struct camelsrt_call_info_key_t *p_camelsrt_call_key);

static void update_camelsrt_call(struct camelsrt_call_t *p_camelsrt_call,
				 packet_info *pinfo, guint msg_category);

static void camelsrt_begin_call_matching(packet_info *pinfo,
					 struct camelsrt_info_t *p_camelsrt_info);

static void camelsrt_request_call_matching(tvbuff_t *tvb, packet_info *pinfo,
					   proto_tree *tree,
					   struct camelsrt_info_t *p_camelsrt_info,
					   guint srt_type);

static void camelsrt_report_call_matching(tvbuff_t *tvb, packet_info *pinfo,
					  proto_tree *tree,
					  struct camelsrt_info_t *p_camelsrt_info,
					  guint srt_type);

static void camelsrt_close_call_matching(packet_info *pinfo,
					 struct camelsrt_info_t *p_camelsrt_info);

static void camelsrt_display_DeltaTime(proto_tree *tree, tvbuff_t *tvb,
				       nstime_t *value_ptr, guint category);

static void raz_camelsrt_call (struct camelsrt_call_t *p_camelsrt_call);

void camelsrt_tcap_matching(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
			    struct tcaphash_context_t *tcap_context);

/* When several Camel components are received in a single TCAP message,
   we have to use several buffers for the stored parameters
   because else this data are erased during TAP dissector call */
#define MAX_CAMEL_INSTANCE 10
int camelsrt_global_current=0;
struct camelsrt_info_t camelsrt_global_info[MAX_CAMEL_INSTANCE];

/* Configuration parameters to enable or disable the Service Response Time */
extern gboolean gcamel_HandleSRT;
gboolean gcamel_PersistentSRT=FALSE;
gboolean gcamel_DisplaySRT=FALSE;
gboolean gcamel_StatSRT=FALSE;

extern int camel_tap;

extern int hf_camelsrt_SessionId;
extern int hf_camelsrt_RequestNumber;
extern int hf_camelsrt_Duplicate;
extern int hf_camelsrt_RequestFrame;
extern int hf_camelsrt_ResponseFrame;
extern int hf_camelsrt_DeltaTime;
extern int hf_camelsrt_SessionTime;
extern int hf_camelsrt_DeltaTime31;
extern int hf_camelsrt_DeltaTime75;
extern int hf_camelsrt_DeltaTime65;
extern int hf_camelsrt_DeltaTime22;
extern int hf_camelsrt_DeltaTime35;
extern int hf_camelsrt_DeltaTime80;

/* Global hash tables*/
static GHashTable *srt_calls = NULL;
guint32 camelsrt_global_SessionId=1;

/*
 * DEBUG fonctions
 */

#undef DEBUG_CAMELSRT
/* #define DEBUG_CAMELSRT */

#ifdef DEBUG_CAMELSRT
#include <stdio.h>
#include <stdarg.h>
static guint debug_level = 99;

static void dbg(guint level, char *fmt, ...) {
  va_list ap;

  if (level > debug_level) return;
  va_start(ap,fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);
}
#endif

/*
 * Functions needed for Hash-Table
 */

/* compare 2 keys */
static gint
camelsrt_call_equal(gconstpointer k1, gconstpointer k2)
{
  const struct camelsrt_call_info_key_t *key1 = (const struct camelsrt_call_info_key_t *) k1;
  const struct camelsrt_call_info_key_t *key2 = (const struct camelsrt_call_info_key_t *) k2;

  return (key1->SessionIdKey == key2->SessionIdKey) ;
}

/* calculate a hash key */
static guint
camelsrt_call_hash(gconstpointer k)
{
  const struct camelsrt_call_info_key_t *key = (const struct camelsrt_call_info_key_t *) k;
  return key->SessionIdKey;
}

/*
 * Find the dialog by Key and Time
 */
static struct camelsrt_call_t *
find_camelsrt_call(struct camelsrt_call_info_key_t *p_camelsrt_call_key)
{
  struct camelsrt_call_t *p_camelsrt_call = NULL;
  p_camelsrt_call = (struct camelsrt_call_t *)g_hash_table_lookup(srt_calls, p_camelsrt_call_key);

#ifdef DEBUG_CAMELSRT
  if(p_camelsrt_call) {
    dbg(10,"D%d ", p_camelsrt_call->session_id);
  } else {
    dbg(23,"Not in hash ");
  }
#endif

  return p_camelsrt_call;
}

/*
 * New record to create, to identify a new transaction
 */
static struct camelsrt_call_t *
new_camelsrt_call(struct camelsrt_call_info_key_t *p_camelsrt_call_key)
{
  struct camelsrt_call_info_key_t *p_new_camelsrt_call_key;
  struct camelsrt_call_t *p_new_camelsrt_call = NULL;

  /* Register the transaction in the hash table
     with the tcap transaction Id as main Key
     Once created, this entry will be updated later */

  p_new_camelsrt_call_key = se_new(struct camelsrt_call_info_key_t);
  p_new_camelsrt_call_key->SessionIdKey = p_camelsrt_call_key->SessionIdKey;
  p_new_camelsrt_call = se_new(struct camelsrt_call_t);
  raz_camelsrt_call(p_new_camelsrt_call);
  p_new_camelsrt_call->session_id = camelsrt_global_SessionId++;
#ifdef DEBUG_CAMELSRT
  dbg(10,"D%d ", p_new_camelsrt_call->session_id);
#endif
  /* store it */
  g_hash_table_insert(srt_calls, p_new_camelsrt_call_key, p_new_camelsrt_call);
  return p_new_camelsrt_call;
}

/*
 * Update a record with the data of the Request
 */
static void
update_camelsrt_call(struct camelsrt_call_t *p_camelsrt_call, packet_info *pinfo,
		     guint msg_category)
{
  p_camelsrt_call->category[msg_category].req_num = pinfo->fd->num;
  p_camelsrt_call->category[msg_category].rsp_num = 0;
  p_camelsrt_call->category[msg_category].responded = FALSE;
  p_camelsrt_call->category[msg_category].req_time = pinfo->fd->abs_ts;
}


/*
 * Routine called when the TAP is initialized.
 * so hash table are (re)created
 */
void
camelsrt_init_routine(void)
{

  /* free hash-table for SRT */
  if (srt_calls != NULL) {
#ifdef DEBUG_CAMELSRT
    dbg(16,"Destroy hash ");
#endif
    g_hash_table_destroy(srt_calls);
  }

  /* create new hash-table for SRT */
  srt_calls = g_hash_table_new(camelsrt_call_hash, camelsrt_call_equal);
#ifdef DEBUG_CAMELSRT
  dbg(16,"Create hash ");
#endif
  /* Reset the session counter */
  camelsrt_global_SessionId=1;

  /* The Display of SRT is enable
   * 1) For wireshark only if Persistent Stat is enable
   * 2) For Tshark, if the SRT handling is enable
   */
  gcamel_DisplaySRT=gcamel_PersistentSRT || gcamel_HandleSRT&gcamel_StatSRT;
}

/*
 * Service Response Time analyze, called just after the camel dissector
 * According to the camel operation, we
 * - open/close a context for the camel session
 * - look for a request, or look for the corresponding response
 */
void
camelsrt_call_matching(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
		       struct camelsrt_info_t *p_camelsrt_info)
{

#ifdef DEBUG_CAMELSRT
  dbg(10,"tcap_session #%d ", p_camelsrt_info->tcap_session_id);
#endif

  switch (p_camelsrt_info->opcode) {

  case 0:  /*InitialDP*/
    camelsrt_begin_call_matching(pinfo, p_camelsrt_info);
    camelsrt_request_call_matching(tvb, pinfo, tree, p_camelsrt_info,
				   CAMELSRT_VOICE_INITIALDP);
    break;
  case 60: /*InitialDPSMS*/
    camelsrt_begin_call_matching(pinfo, p_camelsrt_info);
    camelsrt_request_call_matching(tvb, pinfo, tree, p_camelsrt_info,
				   CAMELSRT_SMS_INITIALDP);
    break;
  case 78: /*InitialDPGPRS*/
    camelsrt_begin_call_matching(pinfo, p_camelsrt_info);
    camelsrt_request_call_matching(tvb, pinfo, tree, p_camelsrt_info,
				   CAMELSRT_GPRS_INITIALDP);
    break;

  case 23: /*RequestReportBCSMEvent*/
    break;

  case 63: /*RequestReportSMSEvent*/
    break;

  case 81: /*RequestReportGPRSEvent*/
    break;

  case 24: /*EventReportBCSMEvent*/
    camelsrt_request_call_matching(tvb, pinfo, tree, p_camelsrt_info,
				   CAMELSRT_VOICE_DISC );
    break;

  case 64: /*EventReportSMS*/
    /* Session has been explicity closed without TC_END */
    camelsrt_close_call_matching(pinfo, p_camelsrt_info);
    tcapsrt_close((struct tcaphash_context_t *)p_camelsrt_info->tcap_context, pinfo);
    break;

  case 80: /*EventReportGPRS*/
    camelsrt_begin_call_matching(pinfo, p_camelsrt_info);
    camelsrt_request_call_matching(tvb, pinfo, tree, p_camelsrt_info,
				   CAMELSRT_GPRS_REPORT);
    break;

  case 35: /*ApplyCharging*/
    camelsrt_report_call_matching(tvb, pinfo, tree, p_camelsrt_info,
				  CAMELSRT_VOICE_ACR1 );
    break;

  case 71: /*ApplyChargingGPRS*/
    break;

  case 36: /*ApplyChargingReport*/
    camelsrt_request_call_matching(tvb, pinfo, tree, p_camelsrt_info,
				   CAMELSRT_VOICE_ACR1 );
    break;

  case 72: /*ApplyChargingReportGPRS*/
    break;

  case 31: /*Continue*/
    camelsrt_report_call_matching(tvb, pinfo, tree, p_camelsrt_info,
    				  CAMELSRT_VOICE_INITIALDP);
    break;
  case 65: /*ContinueSMS*/
    camelsrt_report_call_matching(tvb, pinfo, tree, p_camelsrt_info,
				  CAMELSRT_SMS_INITIALDP);
    break;
  case 75: /*ContinueGPRS*/
    camelsrt_report_call_matching(tvb, pinfo, tree, p_camelsrt_info,
				  CAMELSRT_GPRS_INITIALDP);
    camelsrt_report_call_matching(tvb, pinfo, tree, p_camelsrt_info,
				  CAMELSRT_GPRS_REPORT);
    break;

  case 22: /*ReleaseCall*/
    camelsrt_report_call_matching(tvb, pinfo, tree, p_camelsrt_info,
    				  CAMELSRT_VOICE_DISC);
    /* Session has been closed by Network */
    camelsrt_close_call_matching(pinfo, p_camelsrt_info);
    break;

  case 66: /*ReleaseSMS*/
    /* Session has been closed by Network */
    camelsrt_close_call_matching(pinfo, p_camelsrt_info);
    tcapsrt_close((struct tcaphash_context_t *)p_camelsrt_info->tcap_context,pinfo);
    break;

  case 79: /*ReleaseGPRS*/
    /* Session has been closed by Network */
    camelsrt_close_call_matching(pinfo, p_camelsrt_info);
    break;
  } /* switch opcode */
}

/*
 * Callback function for the TCAP dissector
 * This callback function is used to inform the camel layer, that the session
 * has been Closed or Aborted by a TCAP message without Camel component
 * So, we can close the context for camel session, and update the stats.
 */
void
camelsrt_tcap_matching(tvbuff_t *tvb _U_, packet_info *pinfo,
		       proto_tree *tree _U_,
		       struct tcaphash_context_t *p_tcap_context)
{
  struct camelsrt_info_t *p_camelsrt_info;

#ifdef DEBUG_CAMELSRT
  dbg(11,"Camel_CallBack ");
#endif
  p_camelsrt_info=camelsrt_razinfo();

  p_camelsrt_info->tcap_context=p_tcap_context;
  if (p_tcap_context) {
#ifdef DEBUG_CAMELSRT
    dbg(11,"Close TCAP ");
#endif
    p_camelsrt_info->tcap_session_id = p_tcap_context->session_id;
    camelsrt_close_call_matching(pinfo, p_camelsrt_info);
    tap_queue_packet(camel_tap, pinfo, p_camelsrt_info);
  }
}


/*
 * Create the record identifiying the Camel session
 * As the Tcap session id given by the TCAP dissector is uniq, it will be
 * used as main key.
 */
static void
camelsrt_begin_call_matching(packet_info *pinfo,
			     struct camelsrt_info_t *p_camelsrt_info)
{
  struct camelsrt_call_t *p_camelsrt_call;
  struct camelsrt_call_info_key_t camelsrt_call_key;

  p_camelsrt_info->bool_msginfo[CAMELSRT_SESSION]=TRUE;

  /* prepare the key data */
  camelsrt_call_key.SessionIdKey = p_camelsrt_info->tcap_session_id;

  /* look up the request */
#ifdef DEBUG_CAMELSRT
  dbg(10,"\n Session begin #%u\n", pinfo->fd->num);
  dbg(11,"Search key %lu ",camelsrt_call_key.SessionIdKey);
#endif
  p_camelsrt_call = (struct camelsrt_call_t *)g_hash_table_lookup(srt_calls, &camelsrt_call_key);
  if (p_camelsrt_call) {
    /* We have seen this request before -> do nothing */
#ifdef DEBUG_CAMELSRT
    dbg(22,"Already seen ");
#endif
  } else { /* p_camelsrt_call has not been found */
#ifdef DEBUG_CAMELSRT
    dbg(10,"New key %lu ",camelsrt_call_key.SessionIdKey);
#endif
    p_camelsrt_call = new_camelsrt_call(&camelsrt_call_key);
    p_camelsrt_call->tcap_context=(struct tcaphash_context_t *)p_camelsrt_info->tcap_context;
    update_camelsrt_call(p_camelsrt_call, pinfo,CAMELSRT_SESSION);

#ifdef DEBUG_CAMELSRT
    dbg(11,"Update Callback ");
#endif
    p_camelsrt_call->tcap_context->callback=camelsrt_tcap_matching;
  }
}

/*
 * Register the request, and try to find the response
 *
 */
static void
camelsrt_request_call_matching(tvbuff_t *tvb, packet_info *pinfo,
			       proto_tree *tree,
			       struct camelsrt_info_t *p_camelsrt_info,
			       guint srt_type )
{
  struct camelsrt_call_t *p_camelsrt_call;
  struct camelsrt_call_info_key_t camelsrt_call_key;
  proto_item *ti, *hidden_item;

#ifdef DEBUG_CAMELSRT
  dbg(10,"\n %s #%u\n", val_to_str_const(srt_type, camelSRTtype_naming, "Unk"),pinfo->fd->num);
#endif

  /* look only for matching request, if matching conversation is available. */
  camelsrt_call_key.SessionIdKey = p_camelsrt_info->tcap_session_id;

#ifdef DEBUG_CAMELSRT
  dbg(11,"Search key %lu ", camelsrt_call_key.SessionIdKey);
#endif
  p_camelsrt_call = find_camelsrt_call(&camelsrt_call_key);
  if(p_camelsrt_call) {
#ifdef DEBUG_CAMELSRT
    dbg(12,"Found ");
#endif
    if (gcamel_DisplaySRT)
      proto_tree_add_uint(tree, hf_camelsrt_SessionId, tvb, 0,0, p_camelsrt_call->session_id);


    /* Hmm.. As there are several slices ApplyChargingReport/ApplyCharging
     * we will prepare the measurement for 3 slices with 3 categories */
    if (srt_type==CAMELSRT_VOICE_ACR1) {
      if (p_camelsrt_call->category[CAMELSRT_VOICE_ACR1].req_num == 0) {
	srt_type=CAMELSRT_VOICE_ACR1;
      } else  if ( (p_camelsrt_call->category[CAMELSRT_VOICE_ACR2].req_num == 0)
		   && (p_camelsrt_call->category[CAMELSRT_VOICE_ACR1].rsp_num != 0)
		   && (p_camelsrt_call->category[CAMELSRT_VOICE_ACR1].rsp_num < pinfo->fd->num) ) {
	srt_type=CAMELSRT_VOICE_ACR2;
      } else  if ( (p_camelsrt_call->category[CAMELSRT_VOICE_ACR3].req_num == 0)
		   && (p_camelsrt_call->category[CAMELSRT_VOICE_ACR2].rsp_num != 0)
		   && (p_camelsrt_call->category[CAMELSRT_VOICE_ACR2].rsp_num < pinfo->fd->num) ) {
	srt_type=CAMELSRT_VOICE_ACR3;
      } else if (p_camelsrt_call->category[CAMELSRT_VOICE_ACR1].rsp_num != 0
		 && p_camelsrt_call->category[CAMELSRT_VOICE_ACR1].rsp_num > pinfo->fd->num) {
	srt_type=CAMELSRT_VOICE_ACR1;
      } else  if ( p_camelsrt_call->category[CAMELSRT_VOICE_ACR2].rsp_num != 0
		   && p_camelsrt_call->category[CAMELSRT_VOICE_ACR2].rsp_num > pinfo->fd->num) {
	srt_type=CAMELSRT_VOICE_ACR2;
      } else  if (p_camelsrt_call->category[CAMELSRT_VOICE_ACR1].rsp_num != 0
		  && p_camelsrt_call->category[CAMELSRT_VOICE_ACR3].rsp_num > pinfo->fd->num) {
	srt_type=CAMELSRT_VOICE_ACR3;
      }
#ifdef DEBUG_CAMELSRT
      dbg(70,"Request ACR %u ",srt_type);
      dbg(70,"ACR1 %u %u",p_camelsrt_call->category[CAMELSRT_VOICE_ACR1].req_num, p_camelsrt_call->category[CAMELSRT_VOICE_ACR1].rsp_num);
      dbg(70,"ACR2 %u %u",p_camelsrt_call->category[CAMELSRT_VOICE_ACR2].req_num, p_camelsrt_call->category[CAMELSRT_VOICE_ACR2].rsp_num);
      dbg(70,"ACR3 %u %u",p_camelsrt_call->category[CAMELSRT_VOICE_ACR3].req_num, p_camelsrt_call->category[CAMELSRT_VOICE_ACR3].rsp_num);
#endif
    } /* not ACR */
    p_camelsrt_info->bool_msginfo[srt_type]=TRUE;


    if (p_camelsrt_call->category[srt_type].req_num == 0) {
      /* We have not yet seen a request to that call, so this must be the first request
	 remember its frame number. */
#ifdef DEBUG_CAMELSRT
      dbg(5,"Set reqlink #%u ", pinfo->fd->num);
#endif
      update_camelsrt_call(p_camelsrt_call, pinfo, srt_type);
    } else {
      /* We have seen a request to this call - but was it *this* request? */
      if (p_camelsrt_call->category[srt_type].req_num != pinfo->fd->num) {

	if (srt_type!=CAMELSRT_VOICE_DISC) {
	  /* No, so it's a duplicate resquest. Mark it as such. */
#ifdef DEBUG_CAMELSRT
	  dbg(21,"Display_duplicate with req %d ", p_camelsrt_call->category[srt_type].req_num);
#endif
	  p_camelsrt_info->msginfo[srt_type].is_duplicate = TRUE;
	  if (gcamel_DisplaySRT){
	    hidden_item = proto_tree_add_uint(tree, hf_camelsrt_Duplicate, tvb, 0,0, 77);
		PROTO_ITEM_SET_HIDDEN(hidden_item);
	  }

	} else {
	  /* Ignore duplicate frame */
	  if (pinfo->fd->num > p_camelsrt_call->category[srt_type].req_num) {
	    p_camelsrt_call->category[srt_type].req_num = pinfo->fd->num;
#ifdef DEBUG_CAMELSRT
	    dbg(5,"DISC Set reqlink #%u ", pinfo->fd->num);
#endif
	    update_camelsrt_call(p_camelsrt_call, pinfo, srt_type);
	  } /* greater frame */
	} /* DISC */
      } /* req_num already seen */
    } /* req_num != 0 */

      /* add link to response frame, if available */
    if ( gcamel_DisplaySRT &&
	 (p_camelsrt_call->category[srt_type].rsp_num != 0) &&
	 (p_camelsrt_call->category[srt_type].req_num != 0) &&
	 (p_camelsrt_call->category[srt_type].req_num == pinfo->fd->num) ) {
#ifdef DEBUG_CAMELSRT
      dbg(20,"Display_framersplink %d ",p_camelsrt_call->category[srt_type].rsp_num);
#endif
      ti = proto_tree_add_uint_format(tree, hf_camelsrt_RequestFrame, tvb, 0, 0,
				      p_camelsrt_call->category[srt_type].rsp_num,
				      "Linked response %s in frame %u",
				      val_to_str_const(srt_type, camelSRTtype_naming, "Unk"),
				      p_camelsrt_call->category[srt_type].rsp_num);
      PROTO_ITEM_SET_GENERATED(ti);
    } /* frame valid */
  } /* call reference */
}


/*
 * Check if the received message is a response to a previous request
 * registered is the camel session context.
 */
static void
camelsrt_report_call_matching(tvbuff_t *tvb, packet_info *pinfo,
			      proto_tree *tree,
			      struct camelsrt_info_t *p_camelsrt_info,
			      guint srt_type)
{
  struct camelsrt_call_t *p_camelsrt_call;
  struct camelsrt_call_info_key_t camelsrt_call_key;
  nstime_t delta;
  proto_item *ti, *hidden_item;

#ifdef DEBUG_CAMELSRT
  dbg(10,"\n %s #%u\n", val_to_str_const(srt_type, camelSRTtype_naming, "Unk"),pinfo->fd->num);
#endif
  camelsrt_call_key.SessionIdKey = p_camelsrt_info->tcap_session_id;
  /* look only for matching request, if matching conversation is available. */

#ifdef DEBUG_CAMELSRT
  dbg(11,"Search key %lu ",camelsrt_call_key.SessionIdKey);
#endif
  p_camelsrt_call = find_camelsrt_call(&camelsrt_call_key);
  if(p_camelsrt_call) {
#ifdef DEBUG_CAMELSRT
    dbg(12,"Found, req=%d ",p_camelsrt_call->category[srt_type].req_num);
#endif
    if ( gcamel_DisplaySRT )
      proto_tree_add_uint(tree, hf_camelsrt_SessionId, tvb, 0,0, p_camelsrt_call->session_id);

    if (srt_type==CAMELSRT_VOICE_ACR1) {
      if (p_camelsrt_call->category[CAMELSRT_VOICE_ACR3].req_num != 0
	  && p_camelsrt_call->category[CAMELSRT_VOICE_ACR3].req_num < pinfo->fd->num) {
	srt_type=CAMELSRT_VOICE_ACR1;
      } else  if ( p_camelsrt_call->category[CAMELSRT_VOICE_ACR2].req_num != 0
		   && p_camelsrt_call->category[CAMELSRT_VOICE_ACR2].req_num < pinfo->fd->num) {
	srt_type=CAMELSRT_VOICE_ACR2;
      } else  if (p_camelsrt_call->category[CAMELSRT_VOICE_ACR1].req_num != 0
		  && p_camelsrt_call->category[CAMELSRT_VOICE_ACR1].req_num < pinfo->fd->num) {
	srt_type=CAMELSRT_VOICE_ACR1;
      }
#ifdef DEBUG_CAMELSRT
      dbg(70,"Report ACR %u ",srt_type);
#endif
    } /* not ACR */
    p_camelsrt_info->bool_msginfo[srt_type]=TRUE;

    if (p_camelsrt_call->category[srt_type].rsp_num == 0) {
      if  ( (p_camelsrt_call->category[srt_type].req_num != 0)
	    && (pinfo->fd->num > p_camelsrt_call->category[srt_type].req_num) ){
	/* We have not yet seen a response to that call, so this must be the first response;
	   remember its frame number only if response comes after request */
#ifdef DEBUG_CAMELSRT
	dbg(14,"Set reslink #%d req %u ",pinfo->fd->num, p_camelsrt_call->category[srt_type].req_num);
#endif
	p_camelsrt_call->category[srt_type].rsp_num = pinfo->fd->num;

      } else {
#ifdef DEBUG_CAMELSRT
	dbg(2,"badreslink #%u req %u ",pinfo->fd->num, p_camelsrt_call->category[srt_type].req_num);
#endif
      } /* req_num != 0 */
    } else { /* rsp_num != 0 */
      /* We have seen a response to this call - but was it *this* response? */
      if (p_camelsrt_call->category[srt_type].rsp_num != pinfo->fd->num) {
	/* No, so it's a duplicate response. Mark it as such. */
#ifdef DEBUG_CAMELSRT
	dbg(21,"Display_duplicate rsp=%d ", p_camelsrt_call->category[srt_type].rsp_num);
#endif
	p_camelsrt_info->msginfo[srt_type].is_duplicate = TRUE;
	if ( gcamel_DisplaySRT ){
	  hidden_item = proto_tree_add_uint(tree, hf_camelsrt_Duplicate, tvb, 0,0, 77);
	  PROTO_ITEM_SET_HIDDEN(hidden_item);
	}
      }
    } /* rsp_num != 0 */

    if ( (p_camelsrt_call->category[srt_type].req_num != 0) &&
	 (p_camelsrt_call->category[srt_type].rsp_num != 0) &&
	 (p_camelsrt_call->category[srt_type].rsp_num == pinfo->fd->num) ) {

      p_camelsrt_call->category[srt_type].responded = TRUE;
      p_camelsrt_info->msginfo[srt_type].request_available = TRUE;
#ifdef DEBUG_CAMELSRT
      dbg(20,"Display_frameReqlink %d ",p_camelsrt_call->category[srt_type].req_num);
#endif
      /* Indicate the frame to which this is a reply. */
      if ( gcamel_DisplaySRT ) {
	ti = proto_tree_add_uint_format(tree, hf_camelsrt_ResponseFrame, tvb, 0, 0,
					p_camelsrt_call->category[srt_type].req_num,
					"Linked request %s in frame %u",
					val_to_str_const(srt_type, camelSRTtype_naming, "Unk"),
					p_camelsrt_call->category[srt_type].req_num);
	PROTO_ITEM_SET_GENERATED(ti);
      }
      /* Calculate Service Response Time */
      nstime_delta(&delta, &pinfo->fd->abs_ts, &p_camelsrt_call->category[srt_type].req_time);

      p_camelsrt_info->msginfo[srt_type].is_delta_time = TRUE;
      p_camelsrt_info->msginfo[srt_type].delta_time = delta; /* give it to tap */
      p_camelsrt_info->msginfo[srt_type].req_time = p_camelsrt_call->category[srt_type].req_time;

      /* display Service Response Time and make it filterable */
      camelsrt_display_DeltaTime(tree, tvb, &delta, srt_type);

    } /*req_num != 0 && not duplicate */
  } /* call reference found */
}

/*
 * Update the Camel session info, and close the session.
 * Then remove the associated context, if we do not have persistentSRT enable
 */
static void
camelsrt_close_call_matching(packet_info *pinfo,
			     struct camelsrt_info_t *p_camelsrt_info)
{
  struct camelsrt_call_t *p_camelsrt_call;
  struct camelsrt_call_info_key_t camelsrt_call_key;
  nstime_t delta;

  p_camelsrt_info->bool_msginfo[CAMELSRT_SESSION]=TRUE;
#ifdef DEBUG_CAMELSRT
  dbg(10,"\n Session end #%u\n", pinfo->fd->num);
#endif
  /* look only for matching request, if matching conversation is available. */
  camelsrt_call_key.SessionIdKey = p_camelsrt_info->tcap_session_id;

#ifdef DEBUG_CAMELSRT
  dbg(11,"Search key %lu ",camelsrt_call_key.SessionIdKey);
#endif
  p_camelsrt_call = find_camelsrt_call(&camelsrt_call_key);
  if(p_camelsrt_call) {
#ifdef DEBUG_CAMELSRT
    dbg(12,"Found ");
#endif
    /* Calculate Service Response Time */
    nstime_delta(&delta, &pinfo->fd->abs_ts, &p_camelsrt_call->category[CAMELSRT_SESSION].req_time);
    p_camelsrt_call->category[CAMELSRT_SESSION].responded = TRUE;
    p_camelsrt_info->msginfo[CAMELSRT_SESSION].request_available = TRUE;
    p_camelsrt_info->msginfo[CAMELSRT_SESSION].is_delta_time = TRUE;
    p_camelsrt_info->msginfo[CAMELSRT_SESSION].delta_time = delta; /* give it to tap */
    p_camelsrt_info->msginfo[CAMELSRT_SESSION].req_time = p_camelsrt_call->category[CAMELSRT_SESSION].req_time;

    if ( !gcamel_PersistentSRT ) {
      g_hash_table_remove(srt_calls, &camelsrt_call_key);
#ifdef DEBUG_CAMELSRT
      dbg(20,"remove hash ");
#endif
    } else {
#ifdef DEBUG_CAMELSRT
      dbg(20,"keep hash ");
#endif
    }
  } /* call reference found */
}

/*
 * Display the delta time between two messages in a field corresponding
 * to the category (hf_camelsrt_DeltaTimexx).
 */
static void
camelsrt_display_DeltaTime(proto_tree *tree, tvbuff_t *tvb, nstime_t *value_ptr,
			   guint category)
{
  proto_item *ti;

  if ( gcamel_DisplaySRT ) {
    switch(category) {
    case CAMELSRT_VOICE_INITIALDP:
      ti = proto_tree_add_time(tree, hf_camelsrt_DeltaTime31, tvb, 0, 0, value_ptr);
      PROTO_ITEM_SET_GENERATED(ti);
      break;

    case CAMELSRT_VOICE_ACR1:
    case CAMELSRT_VOICE_ACR2:
    case CAMELSRT_VOICE_ACR3:
      ti = proto_tree_add_time(tree, hf_camelsrt_DeltaTime22, tvb, 0, 0, value_ptr);
      PROTO_ITEM_SET_GENERATED(ti);
      break;

    case CAMELSRT_VOICE_DISC:
      ti = proto_tree_add_time(tree, hf_camelsrt_DeltaTime35, tvb, 0, 0, value_ptr);
      PROTO_ITEM_SET_GENERATED(ti);
      break;

    case CAMELSRT_GPRS_INITIALDP:
      ti = proto_tree_add_time(tree, hf_camelsrt_DeltaTime75, tvb, 0, 0, value_ptr);
      PROTO_ITEM_SET_GENERATED(ti);
      break;

    case CAMELSRT_GPRS_REPORT:
      ti = proto_tree_add_time(tree, hf_camelsrt_DeltaTime80, tvb, 0, 0, value_ptr);
      PROTO_ITEM_SET_GENERATED(ti);
      break;

    case CAMELSRT_SMS_INITIALDP:
      ti = proto_tree_add_time(tree, hf_camelsrt_DeltaTime65, tvb, 0, 0, value_ptr);
      PROTO_ITEM_SET_GENERATED(ti);
      break;

    default:
      break;
    }
  }
}

/*
 * Initialize the Message Info used by the main dissector
 * Data are linked to a TCAP transaction
 */
struct camelsrt_info_t *
camelsrt_razinfo(void)
{
  struct camelsrt_info_t *p_camelsrt_info ;

  /* Global buffer for packet extraction */
  camelsrt_global_current++;
  if(camelsrt_global_current==MAX_CAMEL_INSTANCE){
    camelsrt_global_current=0;
  }

  p_camelsrt_info=&camelsrt_global_info[camelsrt_global_current];
  memset(p_camelsrt_info,0,sizeof(struct camelsrt_info_t));

  p_camelsrt_info->opcode=255;

  return p_camelsrt_info;
}

/*
 * Initialize the data per call for the Service Response Time Statistics
 * Data are linked to a Camel operation in a TCAP transaction
 */
static void
raz_camelsrt_call (struct camelsrt_call_t *p_camelsrt_call)
{
  memset(p_camelsrt_call,0,sizeof(struct camelsrt_call_t));
}
