Huge thanks to our Platinum Members Endace and LiveAction,
and our Silver Member Veeam, for supporting the Wireshark Foundation and project.

Ethereal-dev: Re: [Ethereal-dev] new tap for mgcp

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

Date: Thu, 6 Mar 2003 01:36:08 +0100 (MET)
Hello all,

I have attached all files and patches for a new mgcp tap including the
patches for the plugin_api of ethereal. I have only very little knowledge about
automake and configure, .. so I have added only a patch to the nmake makefile. 
I exported some struct definitions into the new header file
\plugins\mgcp\packet-mgcp.h .

Thanks to Ronnie Sahlberg for his input. Please checkin, if acceptable.

Request/Response matching stuff has been moved to mgcp dissector routine and
provides now a display filter field for response delay.
MGCP responses have now a field containing the frame number of the matching
request. 

One question is left for now. How can I give the frame number of the
response to the request?

the changes to packet-mgcp.c are quiet big, so
we should also think about bumping the mgcp-plugin version to 0.0.9 . 

All inputs are welcome.


-- 
Best Regards,

Lars Roland
/* tap-mgcpstat.c
 * mgcpstat   2003 Lars Roland
 *
 * $Id: $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#include <string.h>
#include "epan/packet_info.h"
#include "tap.h"
#include "epan/value_string.h"
#include "register.h"
#include "plugins/mgcp/packet-mgcp.h"


/* Summary of response-time calculations*/
typedef struct _rtd_t {
	long int num;
	nstime_t min;
	nstime_t max;
	nstime_t tot;
} rtd_t;

/* used to keep track of the statistics for an entire program interface */
typedef struct _mgcpstat_t {
	char *filter;
        rtd_t rtd;
	long int open_req_num;
	long int disc_rsp_num;
	long int req_dup_num;
	long int rsp_dup_num;
} mgcpstat_t;

/* A Function to update a mgcp_rtd_t struct */

void
rtd_stat_update(rtd_t *rtd,nstime_t delta)
{
	rtd->num++;
	if((rtd->max.secs==0)
	&& (rtd->max.nsecs==0) ){
		rtd->max.secs=delta.secs;
		rtd->max.nsecs=delta.nsecs;
	}
	
	if((rtd->min.secs==0)
	&& (rtd->min.nsecs==0) ){
		rtd->min.secs=delta.secs;
		rtd->min.nsecs=delta.nsecs;
	}
	
	if( (delta.secs<rtd->min.secs)
	||( (delta.secs==rtd->min.secs)
	  &&(delta.nsecs<rtd->min.nsecs) ) ){
		rtd->min.secs=delta.secs;
		rtd->min.nsecs=delta.nsecs;
	}
	
	if( (delta.secs>rtd->max.secs)
	||( (delta.secs==rtd->max.secs)
	  &&(delta.nsecs>rtd->max.nsecs) ) ){
		rtd->max.secs=delta.secs;
		rtd->max.nsecs=delta.nsecs;
	}
		
	rtd->tot.secs += delta.secs;
	rtd->tot.nsecs += delta.nsecs;
	if(rtd->tot.nsecs>1000000000){
		rtd->tot.nsecs-=1000000000;
		rtd->tot.secs++;	
	}
	
	
}

static int
mgcpstat_packet(void *pms, packet_info *pinfo, epan_dissect_t *edt _U_, void *pmi)
{
	mgcpstat_t *ms=(mgcpstat_t *)pms;
	mgcp_info_t *mi=pmi;
	nstime_t delta;

	switch (mi->mgcp_type) {
	
	case MGCP_REQUEST:
		if(mi->is_duplicate){
			/* Duplicate is ignored */
			ms->req_dup_num++;
			return 0;
		}
		else {
			ms->open_req_num++;
			return 0;
		}
	break;
			
	case MGCP_RESPONSE:
		if(mi->is_duplicate){
			/* Duplicate is ignored */
			ms->rsp_dup_num++;
			return 0;
		}
		else if (!mi->request_available) {
			/* no request was seen */
			ms->disc_rsp_num++;
			return 0;
		}
		else {
			ms->open_req_num--;
			/* calculate time delta between request and response */
			delta.secs=pinfo->fd->abs_secs-mi->req_time.secs;
			delta.nsecs=pinfo->fd->abs_usecs*1000-mi->req_time.nsecs;
			if(delta.nsecs<0){
				delta.nsecs+=1000000000;
				delta.secs--;
			}
			
			rtd_stat_update(&(ms->rtd),delta);
			return 1;
		}
	break;

	default:
		return 0;
	break;
	}
}

static void
mgcpstat_draw(void *pms)
{
	mgcpstat_t *ms=(mgcpstat_t *)pms;
	
#ifdef G_HAVE_UINT64
	guint64 avg;
#else
	guint32 avg;
#endif
 

	/* calculating average rtd */
	/* scale it to units of 10us.*/
	/* for long captures with a large tot time, this can overflow on 32bit */
	avg=(int)ms->rtd.tot.secs;
	avg=avg*100000+(int)ms->rtd.tot.nsecs/10000;
	if(ms->rtd.num){
		avg/=ms->rtd.num;
	} else {
		avg=0;
	}

	/* printing results */
	printf("\n");
	printf("===================================================================\n");
	printf("MGCP Response Time Delay (RTD) Statistics:\n");
	printf("Filter: %s\n",ms->filter?ms->filter:"");
        printf("Duplicate requests: %d\n",ms->req_dup_num);
        printf("Duplicate responses: %d\n",ms->rsp_dup_num);
        printf("Open requests: %d\n",ms->open_req_num);
        printf("Discarded responses: %d\n",ms->disc_rsp_num);
        printf("Messages   |     Min RTD     |     Max RTD     |     Avg RTD \n");
        printf("%7d    |  %5d.%03d msec |  %5d.%03d msec | %5d.%03d msec\n",
        	ms->rtd.num,
		(int)((ms->rtd.min.secs*1000)+(ms->rtd.min.nsecs/1000000)),(ms->rtd.min.nsecs%1000000)/1000,
		(int)((ms->rtd.max.secs*1000)+(ms->rtd.max.nsecs/1000000)),(ms->rtd.min.nsecs%1000000)/1000,
		avg/100, avg%100
	);
        printf("===================================================================\n");
}


static void
mgcpstat_init(char *optarg)
{
	mgcpstat_t *ms;
	char *filter=NULL;


	if(!strncmp(optarg,"mgcp,rtd,",9)){
		filter=optarg+9;
	} else {
		filter=NULL;
	}

	ms=g_malloc(sizeof(mgcpstat_t));
	if(filter){
		ms->filter=g_malloc(strlen(filter)+1);
		strcpy(ms->filter, filter);
	} else {
		ms->filter=NULL;
	}

	ms->rtd.num=0;
	ms->rtd.min.secs=0;
        ms->rtd.min.nsecs=0;
        ms->rtd.max.secs=0;
        ms->rtd.max.nsecs=0;
        ms->rtd.tot.secs=0;
        ms->rtd.tot.nsecs=0;

	ms->open_req_num=0;
	ms->disc_rsp_num=0;
	ms->req_dup_num=0;
	ms->rsp_dup_num=0;

	if(register_tap_listener("mgcp", ms, filter, NULL, mgcpstat_packet, mgcpstat_draw)){
		/* error, we failed to attach to the tap. clean up */
		g_free(ms->filter);
		g_free(ms);

		fprintf(stderr,"tethereal: mgcpstat_init() failed to attach to tap.\n");
		exit(1);
	}
}


void
register_tap_listener_mgcpstat(void)
{
	register_ethereal_tap("mgcp,rtd", mgcpstat_init, NULL, NULL);
}

/* packet-mgcp.h
 * Routines for mgcp packet disassembly
 * RFC 2705
 *
 * $Id:  $
 *
 * Copyright (c) 2000 by Ed Warnicke <hagbard@xxxxxxxxxxxxxxxxxxx>
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * Copyright 1999 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 
 /* A simple MGCP type that is occasionally handy */
typedef enum _mgcp_type {
  MGCP_REQUEST,
  MGCP_RESPONSE,
  MGCP_OTHERS
} mgcp_type_t;

/* Container for tapping relevant data */
typedef struct _mgcp_info_t {
  mgcp_type_t mgcp_type;
  char code[5];
  guint32 transid;
  nstime_t req_time;
  gboolean is_duplicate;
  gboolean request_available;
} mgcp_info_t;

/* Item of request list */
typedef struct _mgcp_call_t {
	guint32 transid;
	char code[5];
	guint32	req_num;	/* frame number request seen */
	guint32	rsp_num;	/* frame number response seen */
	nstime_t req_time;
	gboolean responded;
} mgcp_call_t;	
Index: packet-mgcp.c
===================================================================
RCS file: /cvsroot/ethereal/plugins/mgcp/packet-mgcp.c,v
retrieving revision 1.35
diff -u -r1.35 packet-mgcp.c
--- packet-mgcp.c	28 Aug 2002 20:39:07 -0000	1.35
+++ packet-mgcp.c	5 Mar 2003 23:06:59 -0000
@@ -43,6 +43,8 @@
 #include <epan/resolv.h>
 #include "prefs.h"
 #include <epan/strutil.h>
+#include <epan/conversation.h>
+#include "packet-mgcp.h"
 
 #include "plugins/plugin_api_defs.h"
 
@@ -65,7 +67,10 @@
 static int hf_mgcp_req = -1;
 static int hf_mgcp_req_verb = -1;
 static int hf_mgcp_req_endpoint = -1;
+static int hf_mgcp_req_frame = -1;
 static int hf_mgcp_rsp = -1;
+static int hf_mgcp_rsp_frame = -1;
+static int hf_mgcp_time = -1;
 static int hf_mgcp_transid = -1;
 static int hf_mgcp_version = -1;
 static int hf_mgcp_rsp_rspcode = -1;
@@ -97,6 +102,9 @@
 static int hf_mgcp_param_extention = -1;
 static int hf_mgcp_param_invalid = -1;
 static int hf_mgcp_messagecount = -1;
+static int hf_mgcp_dup = -1;
+static int hf_mgcp_req_dup = -1;
+static int hf_mgcp_rsp_dup = -1;
 
 /*
  * Define the trees for mgcp
@@ -105,6 +113,10 @@
 static int ett_mgcp = -1;
 static int ett_mgcp_param = -1;
 
+/*
+ * Define the tap for mgcp
+ */
+static int mgcp_tap = -1;
 
 /*
  * Here are the global variables associated with
@@ -140,14 +152,6 @@
 static int callagent_tcp_port = 0;
 static int callagent_udp_port = 0;
 
-
-/* A simple MGCP type that is occasionally handy */
-typedef enum _mgcp_type {
-  MGCP_REQUEST,
-  MGCP_RESPONSE,
-  MGCP_OTHERS
-} mgcp_type_t;
-
 /* Some basic utility functions that are specific to this dissector */
 static gboolean is_mgcp_verb(tvbuff_t *tvb, gint offset, gint maxlength);
 static gboolean is_mgcp_rspcode(tvbuff_t *tvb, gint offset, gint maxlength);
@@ -161,8 +165,8 @@
  */
 static void dissect_mgcp_message(tvbuff_t *tvb, packet_info *pinfo,
 				 proto_tree *tree,proto_tree *mgcp_tree, proto_tree *ti);
-static void dissect_mgcp_firstline(tvbuff_t *tvb,
-				   proto_tree *tree);
+static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo,
+				   proto_tree *tree, mgcp_info_t *mi);
 static void dissect_mgcp_params(tvbuff_t *tvb,
 				proto_tree *tree);
 static void mgcp_raw_text_add(tvbuff_t *tvb,
@@ -181,6 +185,43 @@
 
 static dissector_handle_t sdp_handle;
 
+
+/*
+ * Init Hash table stuff
+ */
+ 
+typedef struct _mgcp_call_info_key {
+	guint32	transid;
+	conversation_t *conversation;
+} mgcp_call_info_key;
+
+static GMemChunk *mgcp_call_info_key_chunk;
+
+static GMemChunk *mgcp_call_info_value_chunk;
+
+static GHashTable *mgcp_calls;
+
+/* compare 2 keys */
+static gint
+mgcp_call_equal(gconstpointer k1, gconstpointer k2)
+{
+	const mgcp_call_info_key* key1 = (const mgcp_call_info_key*) k1;
+	const mgcp_call_info_key* key2 = (const mgcp_call_info_key*) k2;
+
+	return (key1->transid == key2->transid &&
+	    key1->conversation == key2->conversation);
+}
+
+
+/* calculate a hash key */
+static guint
+mgcp_call_hash(gconstpointer k)
+{
+	const mgcp_call_info_key* key = (const mgcp_call_info_key*) k;
+
+	return key->transid  + (guint32)(key->conversation);
+}
+
 /*
  * dissect_mgcp - The dissector for the Media Gateway Control Protocol
  */
@@ -273,7 +314,7 @@
     if (check_col(pinfo->cinfo, COL_INFO) ){
       sectionlen = tvb_find_line_end(tvb, tvb_sectionbegin,-1,
 				     &tvb_sectionend,FALSE);
-      col_add_fstr(pinfo->cinfo, COL_INFO, "%s",
+      col_prepend_fstr(pinfo->cinfo, COL_INFO, "%s",
 		   tvb_format_text(tvb,tvb_sectionbegin,sectionlen));
     }
   }
@@ -287,7 +328,8 @@
   gint sectionlen;
   gint tvb_sectionend,tvb_sectionbegin, tvb_len, tvb_current_len;
   tvbuff_t *next_tvb;
-
+  static mgcp_info_t mi;
+    
   /* Initialize variables */
   tvb_sectionend = 0;
   tvb_sectionbegin = tvb_sectionend;
@@ -312,8 +354,8 @@
       sectionlen = tvb_find_line_end(tvb,0,-1,&tvb_sectionend,FALSE);
       if( sectionlen > 0){
 	dissect_mgcp_firstline(tvb_new_subset(tvb, tvb_sectionbegin,
-					      sectionlen,-1),
-			       mgcp_tree);
+					      sectionlen,-1), pinfo,
+			       mgcp_tree, &mi);
       }
       tvb_sectionbegin = tvb_sectionend;
 
@@ -373,6 +415,35 @@
   } while ( tvb_lineend < tvb_len );
 }
 
+/* Discard and init any state we've saved */
+
+static void
+mgcp_init_protocol(void)
+{
+	if (mgcp_calls != NULL) {
+		g_hash_table_destroy(mgcp_calls);
+		mgcp_calls = NULL;
+	}
+	if (mgcp_call_info_key_chunk != NULL) {
+		g_mem_chunk_destroy(mgcp_call_info_key_chunk);
+		mgcp_call_info_key_chunk = NULL;
+	}
+	if (mgcp_call_info_value_chunk != NULL) {
+		g_mem_chunk_destroy(mgcp_call_info_value_chunk);
+		mgcp_call_info_value_chunk = NULL;
+	}
+
+	mgcp_calls = g_hash_table_new(mgcp_call_hash, mgcp_call_equal);
+	mgcp_call_info_key_chunk = g_mem_chunk_new("call_info_key_chunk",
+	    sizeof(mgcp_call_info_key),
+	    200 * sizeof(mgcp_call_info_key),
+	    G_ALLOC_ONLY);
+	mgcp_call_info_value_chunk = g_mem_chunk_new("call_info_value_chunk",
+	    sizeof(mgcp_call_t),
+	    200 * sizeof(mgcp_call_t),
+	    G_ALLOC_ONLY);	
+}
+
 /* Register all the bits needed with the filtering engine */
 
 void
@@ -385,6 +456,15 @@
     { &hf_mgcp_rsp,
       { "Response", "mgcp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
 	"TRUE if MGCP response", HFILL }},
+    { &hf_mgcp_req_frame, 
+      {	"Request Frame", "mgcp.reqframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
+        "Request Frame", HFILL }},
+    { &hf_mgcp_rsp_frame, 
+      {	"Response Frame", "mgcp.rspframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
+        "Response Frame", HFILL }},
+    { &hf_mgcp_time,
+      {	"Time from request", "mgcp.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
+        "Timedelta between Request and Response", HFILL }},
     { &hf_mgcp_req_verb,
       { "Verb", "mgcp.req.verb", FT_STRING, BASE_DEC, NULL, 0x0,
 	"Name of the verb", HFILL }},
@@ -484,6 +564,15 @@
     { &hf_mgcp_messagecount,
       { "MGCP Message Count", "mgcp.messagecount", FT_UINT32,
 	BASE_DEC, NULL, 0x0, "Number of MGCP message in a packet", HFILL }},
+    { &hf_mgcp_dup,
+      {	"Duplicate Message", "mgcp.dup", FT_UINT32, BASE_DEC,
+	NULL, 0, "Duplicate Message", HFILL }},
+    { &hf_mgcp_req_dup,
+      {	"Duplicate Request", "mgcp.req.dup", FT_UINT32, BASE_DEC,
+	NULL, 0, "Duplicate Request", HFILL }},
+    { &hf_mgcp_rsp_dup,
+      {	"Duplicate Response", "mgcp.rsp.dup", FT_UINT32, BASE_DEC,
+	NULL, 0, "Duplicate Response", HFILL }},
     /* Add more fields here */
   };
   static gint *ett[] = {
@@ -497,6 +586,7 @@
 
   proto_register_field_array(proto_mgcp, hf, array_length(hf));
   proto_register_subtree_array(ett, array_length(ett));
+  register_init_routine(&mgcp_init_protocol);
 
   /* Register our configuration options for , particularly our ports */
 
@@ -548,6 +638,8 @@
                                  "Display the number of MGCP messages "
                                  "found in a packet in the protocol column.",
                                  &global_mgcp_message_count);
+  
+  mgcp_tap = register_tap("mgcp");
 }
 
 /* The registration hand-off routine */
@@ -864,17 +956,28 @@
  * tree - The tree from which to hang the structured information parsed
  *        from the first line of the MGCP message.
  */
-static void dissect_mgcp_firstline(tvbuff_t *tvb,
-				   proto_tree *tree){
+static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo,
+				   proto_tree *tree, mgcp_info_t *mi){
   gint tvb_current_offset,tvb_previous_offset,tvb_len,tvb_current_len;
   gint tokennum, tokenlen;
+  char *transid = NULL;
+  char *code = NULL;
   mgcp_type_t mgcp_type = MGCP_OTHERS;
+  conversation_t* conversation;
+  mgcp_call_info_key mgcp_call_key;
+  mgcp_call_info_key *new_mgcp_call_key = NULL;
+  mgcp_call_t *mgcp_call = NULL;
+  nstime_t delta;
+  
+  static address null_address = { AT_NONE, 0, NULL };
   proto_item* (*my_proto_tree_add_string)(proto_tree*, int, tvbuff_t*, gint,
 					  gint, const char*);
   tvb_previous_offset = 0;
   tvb_len = tvb_length(tvb);
   tvb_current_len = tvb_len;
   tvb_current_offset = tvb_previous_offset;
+  mi->is_duplicate = FALSE;
+  mi->request_available = FALSE;
 
   if(tree){
     tokennum = 0;
@@ -898,29 +1001,33 @@
 	tokenlen = tvb_current_offset - tvb_previous_offset;
       }
       if(tokennum == 0){
+        code = g_malloc(tokenlen);
+        code = tvb_format_text(tvb,tvb_previous_offset,tokenlen);
+        strncpy(mi->code,code,4);
+        mi->code[4] = '\0';
 	if(is_mgcp_verb(tvb,tvb_previous_offset,tvb_current_len)){
 	  mgcp_type = MGCP_REQUEST;
 	  my_proto_tree_add_string(tree,hf_mgcp_req_verb, tvb,
 				   tvb_previous_offset, tokenlen,
-				   tvb_format_text(tvb,tvb_previous_offset
-						   ,tokenlen));
+				   code);
 	}
 	else if (is_mgcp_rspcode(tvb,tvb_previous_offset,tvb_current_len)){
 	  mgcp_type = MGCP_RESPONSE;
 	  my_proto_tree_add_string(tree,hf_mgcp_rsp_rspcode, tvb,
 				   tvb_previous_offset, tokenlen,
-				   tvb_format_text(tvb,tvb_previous_offset
-						   ,tokenlen));
+				   code);
 	}
 	else {
 	  break;
 	}
       }
       if(tokennum == 1){
+      	transid = g_malloc(tokenlen);
+        transid = tvb_format_text(tvb,tvb_previous_offset,tokenlen);
+        mi->transid = atol(transid);
 	my_proto_tree_add_string(tree,hf_mgcp_transid, tvb,
 				 tvb_previous_offset, tokenlen,
-				 tvb_format_text(tvb,tvb_previous_offset,
-						 tokenlen));
+				 transid);
       }
       if(tokennum == 2){
 	if(mgcp_type == MGCP_REQUEST){
@@ -967,15 +1074,200 @@
 	     && tokennum <= 3);
     switch (mgcp_type){
     case MGCP_RESPONSE:
-      proto_tree_add_boolean_hidden(tree, hf_mgcp_rsp, tvb, 0, 0, TRUE);
+	proto_tree_add_boolean_hidden(tree, hf_mgcp_rsp, tvb, 0, 0, TRUE);
+	/* Check for MGCP response.  A response must match a call that
+	   we've seen, and the response must be sent to the same
+	   port and address that the call came from, and must
+	   come from the port to which the call was sent.
+
+	   If the transport is connection-oriented (we check, for
+	   now, only for "pinfo->ptype" of PT_TCP), we take
+	   into account the address from which the call was sent
+	   and the address to which the call was sent, because
+	   the addresses of the two endpoints should be the same
+	   for all calls and replies.
+
+	   If the transport is connectionless, we don't worry
+	   about the address to which the call was sent and from
+	   which the reply was sent, because there's no
+	   guarantee that the reply will come from the address
+	   to which the call was sent. */
+	if (pinfo->ptype == PT_TCP) {
+		conversation = find_conversation(&pinfo->src,
+		    &pinfo->dst, pinfo->ptype, pinfo->srcport,
+		    pinfo->destport, 0);
+	} else {
+		/*
+		 * XXX - can we just use NO_ADDR_B?  Unfortunately,
+		 * you currently still have to pass a non-null
+		 * pointer for the second address argument even
+		 * if you do that.
+		 */
+		conversation = find_conversation(&null_address,
+		    &pinfo->dst, pinfo->ptype, pinfo->srcport,
+		    pinfo->destport, 0);
+	}
+	if (conversation != NULL) {
+		/* look only for matching request, if
+		   matching conversation is available. */
+		mgcp_call_key.transid = mi->transid;
+		mgcp_call_key.conversation = conversation;
+		mgcp_call = g_hash_table_lookup(mgcp_calls, &mgcp_call_key);
+		if(mgcp_call) {
+			/* Indicate the frame to which this is a reply. */
+			if(mgcp_call->req_num){
+				mi->request_available = TRUE;
+				mgcp_call->responded = TRUE;
+				proto_tree_add_uint_format(tree, hf_mgcp_req_frame,
+				    tvb, 0, 0, mgcp_call->req_num,
+				    "This is a response to a request in frame %u",
+				    mgcp_call->req_num);
+				delta.secs= pinfo->fd->abs_secs-mgcp_call->req_time.secs;
+				delta.nsecs=pinfo->fd->abs_usecs*1000-mgcp_call->req_time.nsecs;
+				if(delta.nsecs<0){
+					delta.nsecs+=1000000000;
+					delta.secs--;
+				}
+				proto_tree_add_time(tree, hf_mgcp_time, tvb, 0, 0,
+					&delta);
+			}
+
+			if (mgcp_call->rsp_num == 0) {
+				/* We have not yet seen a response to that call, so
+				   this must be the first response; remember its
+				   frame number. */
+				mgcp_call->rsp_num = pinfo->fd->num;
+			} else {
+				/* We have seen a response to this call - but was it
+				   *this* response? */
+				if (mgcp_call->rsp_num != pinfo->fd->num) {
+					/* No, so it's a duplicate response.
+					   Mark it as such. */
+					mi->is_duplicate = TRUE;   
+					if (check_col(pinfo->cinfo, COL_INFO)) {
+						col_append_fstr(pinfo->cinfo, COL_INFO,
+							", Duplicate Response %ld",mi->transid);
+						if (tree) {
+							proto_tree_add_uint_hidden(tree,
+								hf_mgcp_dup, tvb, 0,0, mi->transid);
+							proto_tree_add_uint_hidden(tree,
+								hf_mgcp_rsp_dup, tvb, 0,0, mi->transid);
+						}
+					}
+				}
+			}
+		}
+	}
       break;
     case MGCP_REQUEST:
-      proto_tree_add_boolean_hidden(tree, hf_mgcp_req, tvb, 0, 0, TRUE);
+	proto_tree_add_boolean_hidden(tree, hf_mgcp_req, tvb, 0, 0, TRUE);
+	/* Keep track of the address and port whence the call came,
+	   and the port to which the call is being sent, so that
+	   we can match up calls with replies.
+
+	   If the transport is connection-oriented (we check, for
+	   now, only for "pinfo->ptype" of PT_TCP), we take
+	   into account the address from which the call was sent
+	   and the address to which the call was sent, because
+	   the addresses of the two endpoints should be the same
+	   for all calls and replies.
+
+	   If the transport is connectionless, we don't worry
+	   about the address to which the call was sent and from
+	   which the reply was sent, because there's no
+	   guarantee that the reply will come from the address
+	   to which the call was sent. */
+	if (pinfo->ptype == PT_TCP) {
+		conversation = find_conversation(&pinfo->src,
+		    &pinfo->dst, pinfo->ptype, pinfo->srcport,
+		    pinfo->destport, 0);
+	} else {
+		/*
+		 * XXX - can we just use NO_ADDR_B?  Unfortunately,
+		 * you currently still have to pass a non-null
+		 * pointer for the second address argument even
+		 * if you do that.
+		 */
+		conversation = find_conversation(&pinfo->src,
+		    &null_address, pinfo->ptype, pinfo->srcport,
+		    pinfo->destport, 0);
+	}
+	if (conversation == NULL) {
+		/* It's not part of any conversation - create a new
+		   one. */
+		if (pinfo->ptype == PT_TCP) {
+			conversation = conversation_new(&pinfo->src,
+			    &pinfo->dst, pinfo->ptype, pinfo->srcport,
+			    pinfo->destport, 0);
+		} else {
+			conversation = conversation_new(&pinfo->src,
+			    &null_address, pinfo->ptype, pinfo->srcport,
+			    pinfo->destport, 0);
+		}
+	}
+	
+	/* prepare the key data */
+	mgcp_call_key.transid = mi->transid;
+	mgcp_call_key.conversation = conversation;
+	
+	/* look up the request */
+	mgcp_call = g_hash_table_lookup(mgcp_calls, &mgcp_call_key);
+	if (mgcp_call != NULL) {
+		/* We've seen a request with this TRANSID, with the same
+		   source and destination, before - but was it
+		   *this* request? */
+		if (pinfo->fd->num != mgcp_call->req_num) {
+			/* No, so it's a duplicate request.
+			   Mark it as such. */
+			mi->is_duplicate = TRUE;
+			if (check_col(pinfo->cinfo, COL_INFO)) {
+				col_append_fstr(pinfo->cinfo, COL_INFO,
+					", Duplicate Request %ld",mi->transid);	
+				if (tree) {
+					proto_tree_add_uint_hidden(tree, 
+						hf_mgcp_dup, tvb, 0,0, mi->transid);
+					proto_tree_add_uint_hidden(tree, 
+						hf_mgcp_req_dup, tvb, 0,0, mi->transid);
+				}
+			}
+		}
+	}
+	else {
+		/* Prepare the value data.
+		   "req_num" and "rsp_num" are frame numbers;
+		   frame numbers are 1-origin, so we use 0
+		   to mean "we don't yet know in which frame
+		   the reply for this call appears". */
+		new_mgcp_call_key = g_mem_chunk_alloc(mgcp_call_info_key_chunk);
+		*new_mgcp_call_key = mgcp_call_key;
+		mgcp_call = g_mem_chunk_alloc(mgcp_call_info_value_chunk);
+		mgcp_call->req_num = pinfo->fd->num;
+		mgcp_call->rsp_num = 0;
+		mgcp_call->transid = mi->transid;
+		mgcp_call->responded = FALSE;
+		mgcp_call->req_time.secs=pinfo->fd->abs_secs;
+		mgcp_call->req_time.nsecs=pinfo->fd->abs_usecs*1000;
+		strcpy(mgcp_call->code,mi->code);
+		/* store it */
+		g_hash_table_insert(mgcp_calls, new_mgcp_call_key, mgcp_call);
+	}
+	if(mgcp_call && mgcp_call->rsp_num){
+		proto_tree_add_uint_format(tree, hf_mgcp_rsp_frame,
+		    tvb, 0, 0, mgcp_call->rsp_num,
+		    "The response to this request is in frame %u",
+		    mgcp_call->rsp_num);
+	}
       break;
     default:
       break;
     }
+    mi->mgcp_type = mgcp_type;
+    if(mgcp_call) {
+    	mi->req_time.secs=mgcp_call->req_time.secs;
+    	mi->req_time.nsecs=mgcp_call->req_time.nsecs;
+    }
   }
+  tap_queue_packet(mgcp_tap, pinfo, mi);
 }
 
 /*
Index: ethereal/plugins/plugin_api.c
===================================================================
RCS file: /cvsroot/ethereal/plugins/plugin_api.c,v
retrieving revision 1.43
diff -u -r1.43 plugin_api.c
--- ethereal/plugins/plugin_api.c	14 Nov 2002 18:54:53 -0000	1.43
+++ ethereal/plugins/plugin_api.c	5 Mar 2003 23:15:48 -0000
@@ -210,4 +210,7 @@
 	p_fragment_delete			= pat->p_fragment_delete;
 	p_show_fragment_tree			= pat->p_show_fragment_tree;
 	p_show_fragment_seq_tree		= pat->p_show_fragment_seq_tree;
+
+	p_register_tap				= pat->p_register_tap;
+	p_tap_queue_packet			= pat->p_tap_queue_packet;
 }
Index: ethereal/plugins/plugin_api.h
===================================================================
RCS file: /cvsroot/ethereal/plugins/plugin_api.h,v
retrieving revision 1.44
diff -u -r1.44 plugin_api.h
--- ethereal/plugins/plugin_api.h	14 Nov 2002 18:54:53 -0000	1.44
+++ ethereal/plugins/plugin_api.h	5 Mar 2003 23:15:48 -0000
@@ -240,7 +240,10 @@
 #define fragment_delete			(*p_fragment_delete)
 #define show_fragment_tree		(*p_show_fragment_tree)
 #define show_fragment_seq_tree		(*p_show_fragment_seq_tree)
-                                                
+
+#define register_tap			(*p_register_tap)
+#define tap_queue_packet		(*p_tap_queue_packet)
+
 #endif
 
 #include <epan/packet.h>
@@ -250,6 +253,7 @@
 #include "packet-giop.h"
 #include "packet-tpkt.h"
 #include "packet-tcp.h"
+#include "tap.h"
 
 #include "plugin_table.h"
 
Index: ethereal/plugins/plugin_api_decls.h
===================================================================
RCS file: /cvsroot/ethereal/plugins/plugin_api_decls.h,v
retrieving revision 1.6
diff -u -r1.6 plugin_api_decls.h
--- ethereal/plugins/plugin_api_decls.h	14 Nov 2002 18:54:53 -0000	1.6
+++ ethereal/plugins/plugin_api_decls.h	5 Mar 2003 23:15:49 -0000
@@ -250,3 +250,5 @@
 addr_show_fragment_tree			p_show_fragment_tree;
 addr_show_fragment_seq_tree		p_show_fragment_seq_tree;
 
+addr_register_tap			p_register_tap;
+addr_tap_queue_packet			p_tap_queue_packet;
Index: ethereal/plugins/plugin_table.h
===================================================================
RCS file: /cvsroot/ethereal/plugins/plugin_table.h,v
retrieving revision 1.56
diff -u -r1.56 plugin_table.h
--- ethereal/plugins/plugin_table.h	2 Dec 2002 23:34:40 -0000	1.56
+++ ethereal/plugins/plugin_table.h	5 Mar 2003 23:15:49 -0000
@@ -279,6 +279,9 @@
 typedef gboolean (*addr_show_fragment_tree)(fragment_data *, const fragment_items *, proto_tree *, packet_info *, tvbuff_t *);
 typedef gboolean (*addr_show_fragment_seq_tree)(fragment_data *, const fragment_items *, proto_tree *, packet_info *, tvbuff_t *);
 
+typedef int (*addr_register_tap)(char *);
+typedef void (*addr_tap_queue_packet)(int, packet_info *, void *);
+
 typedef struct  {
 
 #include "plugin_api_decls.h"
Index: ethereal/epan/plugins.c
===================================================================
RCS file: /cvsroot/ethereal/epan/plugins.c,v
retrieving revision 1.62
diff -u -r1.62 plugins.c
--- ethereal/epan/plugins.c	8 Dec 2002 22:22:03 -0000	1.62
+++ ethereal/epan/plugins.c	5 Mar 2003 23:15:47 -0000
@@ -65,6 +65,7 @@
 #include "packet-giop.h"
 #include "packet-tpkt.h"
 #include "packet-tcp.h"
+#include "tap.h"
 #include "plugins/plugin_table.h"
 static plugin_address_table_t	patable;
 #endif
@@ -487,7 +488,10 @@
 	patable.p_fragment_delete		= fragment_delete;
 	patable.p_show_fragment_tree		= show_fragment_tree;
 	patable.p_show_fragment_seq_tree	= show_fragment_seq_tree;
-
+	
+	patable.p_register_tap			= register_tap;
+	patable.p_tap_queue_packet		= tap_queue_packet;
+	
 #endif
 
 #ifdef WIN32
Index: ethereal/Makefile.nmake
===================================================================
RCS file: /cvsroot/ethereal/Makefile.nmake,v
retrieving revision 1.284
diff -u -r1.284 Makefile.nmake
--- ethereal/Makefile.nmake	2 Mar 2003 21:52:09 -0000	1.284
+++ ethereal/Makefile.nmake	5 Mar 2003 23:15:44 -0000
@@ -357,6 +357,7 @@
 	tap-dcerpcstat.c	\
 	tap-iostat.c		\
 	tap-iousers.c		\
+	tap-mgcpstat.c		\
 	tap-protocolinfo.c	\
 	tap-protohierstat.c	\
 	tap-rpcstat.c		\