ANNOUNCEMENT: Live Wireshark University & Allegro Packets online APAC Wireshark Training Session
April 17th, 2024 | 14:30-16:00 SGT (UTC+8) | Online

Ethereal-dev: [Ethereal-dev] Fax t38 Analysis

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

From: Alejandro Vaquero <alejandrovaquero@xxxxxxxxx>
Date: Sun, 02 Oct 2005 13:48:23 -0600
Hi All,
    Find attached a new "Fax T38 Analysis" added to the "Statistics" menu to:

- Reassemble the HDLC t30 frames and dissect the header.
- Analyze the UPDTLPacket seq num for packet lost
- Stats of V.x Data:
    - Count the Data bytes
    - Duration
    - Wrong seq num
    - Max Burst of packet lost

TODO: dissect the complete t30 frames

Regards
Alejandro




Index: gtk/graph_analysis.c
===================================================================
--- gtk/graph_analysis.c	(revision 16065)
+++ gtk/graph_analysis.c	(working copy)
@@ -136,6 +136,8 @@
 
 	user_data->num_nodes = 0;
 	user_data->num_items = 0;
+	user_data->on_destroy_user_data = NULL;
+	user_data->data = NULL;
 	for (i=0; i<MAX_NUM_NODES; i++){
 		user_data->nodes[i].type = AT_NONE;
 		user_data->nodes[i].len = 0;
@@ -165,13 +167,14 @@
 	user_data->dlg.selected_item=0xFFFFFFFF;    /*not item selected */
 	user_data->dlg.window=NULL;
 	user_data->dlg.inverse = FALSE;
+	user_data->dlg.title=NULL;
 }
 
 /****************************************************************************/
 /* CALLBACKS */
 
 /****************************************************************************/
-/* close the dialog window and remove the tap listener */
+/* close the dialog window */
 static void on_destroy(GtkWidget *win _U_, graph_analysis_data_t *user_data _U_)
 {
 	int i;
@@ -183,6 +186,12 @@
 		user_data->nodes[i].data = NULL;
 	}
 	user_data->dlg.window = NULL;
+	g_free(user_data->dlg.title);
+	user_data->dlg.title = NULL;
+
+	if(user_data->on_destroy_user_data){
+        user_data->on_destroy_user_data(user_data->data);
+	}
 }
 
 #define RIGHT_ARROW 1
@@ -649,7 +658,7 @@
         top_y_border=TOP_Y_BORDER;	/* to display the node address */
         bottom_y_border=2;
 
-	draw_height=user_data->dlg.draw_area->allocation.height-top_y_border-bottom_y_border;
+	    draw_height=user_data->dlg.draw_area->allocation.height-top_y_border-bottom_y_border;
 
 	first_item = user_data->dlg.first_item;
 	display_items = draw_height/ITEM_HEIGHT;
@@ -734,7 +743,7 @@
         pango_layout_get_pixel_size(layout, &label_width, &label_height);
 #endif
 
-	/* resize the "time" draw area */
+		/* resize the "time" draw area */
 
         left_x_border=3;
 	user_data->dlg.left_x_border = left_x_border;
@@ -1359,9 +1368,9 @@
 	gdk_gc_set_rgb_fg_color(user_data->dlg.bg_gc[i], &col[i]);
 #endif
 	}
+		
+		dialog_graph_redraw(user_data);
 
-	dialog_graph_redraw(user_data);
-
         return TRUE;
 }
 
@@ -1429,6 +1438,7 @@
 
 	dialog_graph_redraw(user_data);
 
+
         return TRUE;
 }
 #if GTK_MAJOR_VERSION >= 2
@@ -1561,9 +1571,9 @@
 
         gtk_box_pack_start(GTK_BOX(hbox), user_data->dlg.draw_area_time, FALSE, FALSE, 0);
 
-	user_data->dlg.hpane = gtk_hpaned_new();
-	gtk_paned_pack1(GTK_PANED (user_data->dlg.hpane), user_data->dlg.scroll_window, FALSE, TRUE);
-	gtk_paned_pack2(GTK_PANED (user_data->dlg.hpane), scroll_window_comments, TRUE, TRUE);
+		user_data->dlg.hpane = gtk_hpaned_new();
+		gtk_paned_pack1(GTK_PANED (user_data->dlg.hpane), user_data->dlg.scroll_window, FALSE, TRUE);
+		gtk_paned_pack2(GTK_PANED (user_data->dlg.hpane), scroll_window_comments, TRUE, TRUE);
 #if GTK_MAJOR_VERSION >= 2
 	SIGNAL_CONNECT(user_data->dlg.hpane, "notify::position",  pane_callback, user_data);
 #endif
@@ -1596,7 +1606,10 @@
 	GtkTooltips *tooltips = gtk_tooltips_new();
 
         /* create the main window */
-        user_data->dlg.window=window_new(GTK_WINDOW_TOPLEVEL, "Graph Analysis");
+		if (user_data->dlg.title)
+	        user_data->dlg.window=window_new(GTK_WINDOW_TOPLEVEL, user_data->dlg.title);
+		else
+	        user_data->dlg.window=window_new(GTK_WINDOW_TOPLEVEL, "Graph Analysis");
 
 
         vbox=gtk_vbox_new(FALSE, 0);
@@ -1787,3 +1800,24 @@
 	return;
 }
 
+
+/****************************************************************************/
+void graph_analysis_redraw(graph_analysis_data_t* user_data)
+{
+	/* get nodes (each node is an address) */
+	get_nodes(user_data);
+
+	user_data->dlg.pixmap_width = user_data->num_nodes * NODE_WIDTH;
+    WIDGET_SET_SIZE(user_data->dlg.draw_area, user_data->dlg.pixmap_width, user_data->dlg.pixmap_height);
+	if ( user_data->num_nodes < 6)  
+			WIDGET_SET_SIZE(user_data->dlg.scroll_window, NODE_WIDTH*user_data->num_nodes, user_data->dlg.pixmap_height);
+		else
+			WIDGET_SET_SIZE(user_data->dlg.scroll_window, NODE_WIDTH*5, user_data->dlg.pixmap_height);
+
+
+	/* redraw the graph */
+	dialog_graph_redraw(user_data); 
+
+    window_present(user_data->dlg.window);
+	return;
+}
Index: gtk/graph_analysis.h
===================================================================
--- gtk/graph_analysis.h	(revision 16065)
+++ gtk/graph_analysis.h	(working copy)
@@ -104,10 +104,11 @@
 	display_items_t items[NUM_DISPLAY_ITEMS];
     guint32 left_x_border;
     char *save_file;
+	char *title; 				/* Graph analysis window's title */
 } dialog_data_t;
 
+typedef void (*destroy_user_data_cb)(void *data);
 
-
 /* structure that holds general information and the dialog */
 typedef struct _graph_analysis_data_t {
 	/* graphic data */
@@ -118,11 +119,14 @@
 	address nodes[MAX_NUM_NODES];
 	guint32 num_nodes;
 	guint32 num_items;
+	destroy_user_data_cb on_destroy_user_data;  /* callback info for destroy */
+	void *data; /* data to be passes when on destroy */
 } graph_analysis_data_t;
 
 graph_analysis_data_t* graph_analysis_init(void);
 void graph_analysis_create(graph_analysis_data_t* user_data);
 void graph_analysis_update(graph_analysis_data_t* user_data);
+void graph_analysis_redraw(graph_analysis_data_t* user_data);
 
 
 #endif /*GRAPH_ANALYSIS_H_INCLUDED*/
Index: gtk/Makefile.common
===================================================================
--- gtk/Makefile.common	(revision 16065)
+++ gtk/Makefile.common	(working copy)
@@ -157,6 +157,7 @@
 	sctp_stat_dlg.c	\
 	sip_stat.c	\
 	smb_stat.c	\
+	t38_analysis.c  \
 	tcp_graph.c	\
 	voip_calls_dlg.c \
 	wsp_stat.c
Index: gtk/voip_calls.c
===================================================================
--- gtk/voip_calls.c	(revision 16065)
+++ gtk/voip_calls.c	(working copy)
@@ -168,6 +168,8 @@
 		graph_item = list->data;
 		g_free(graph_item->frame_label);
 		g_free(graph_item->comment);
+		g_free((void *)graph_item->src_addr.data);
+		g_free((void *)graph_item->dst_addr.data);
 		g_free(list->data);
 		list = g_list_next(list);
 	}
Index: gtk/t38_analysis.c
===================================================================
--- gtk/t38_analysis.c	(revision 0)
+++ gtk/t38_analysis.c	(revision 0)
@@ -0,0 +1,738 @@
+/* t38_analysis.c
+ * t38 fax analysis for ethereal
+ *
+ * $Id: voip_calls.c 14867 2005-07-07 04:03:35Z guy $
+ *
+ * Copyright 2005 Verso Technologies Inc.
+ * By Alejandro Vaquero <alejandro.vaquero@xxxxxxxxx>
+ *
+ *
+ * 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 <string.h>
+
+#include "graph_analysis.h"
+
+#include "globals.h"
+
+#include <epan/tap.h>
+#include <epan/epan_dissect.h>
+#include <epan/dissectors/packet-t38.h>
+#include <epan/dissectors/packet-rtp.h>
+#include <epan/conversation.h>
+#include "../stat_menu.h"
+
+#include "alert_box.h"
+#include "simple_dialog.h"
+#include "graph_analysis.h"
+
+#define MAX_HDLC_FRAME 1024
+
+typedef enum {
+	UNKNOWN,
+    CORRECT, 
+    EARLY,	/* seq_num > than expected, we assume all previous as lost */
+    LATE    /* seq_num < than expected, late packet we drop it */
+} SEQ_STATUS;
+
+typedef struct _tap_t38_stat_t {
+	gboolean first_packet;
+	gint32 seq_num;	/* UDPTLPacket sequence number */
+	gint32 wrong_seq_num;	/* count UDPTLPacket wron sequence number */
+	guint8 hdlc_data[MAX_HDLC_FRAME];  /* V21 HDLC data */
+	guint16 hdlc_data_index;			/* V21 HDLC index */
+	gboolean valid_hdlc_data;
+	guint32 other_data_num_bytes;		/* num of bytes of other data (non hdlc) */
+	guint32 other_data_lost;		/* num of packet lost (wrong seq num) of other data (non hdlc) */
+	guint32 other_data_max_burst_lost;	/* max burst num of packet lost (wrong seq num) of other data (non hdlc) */
+	guint32 other_data_burst_lost;	/* burst num of packet lost (wrong seq num) of other data (non hdlc) */
+	gint32 start_frame_other_data; /* start frame of other_data */
+	double start_time_other_data; /* start time of other_data */
+	SEQ_STATUS prev_seq_status;   /*previous seq num status used to calclate the busrt error */
+
+} tap_t38_stat_t;
+
+/* structure that holds general information about the connection 
+* and structures for both directions */
+typedef struct _user_data_t {
+	/* tap associated data*/
+	address ip_src_fwd;
+	guint16 port_src_fwd;
+	address ip_dst_fwd;
+	guint16 port_dst_fwd;
+	address ip_src_rev;
+	guint16 port_src_rev;
+	address ip_dst_rev;
+	guint16 port_dst_rev;
+
+	tap_t38_stat_t forward;
+	tap_t38_stat_t reverse;
+
+	graph_analysis_data_t *graph_analysis_data;
+} user_data_t;
+
+
+
+
+
+/****************************************************************************/
+/* Add a new item into the graph */
+int add_to_graph_t38(user_data_t *user_data, packet_info *pinfo, const gchar *frame_label, gchar *comment, gint line_style)
+{
+	graph_analysis_item_t *gai;
+
+	gai = g_malloc(sizeof(graph_analysis_item_t));
+	gai->frame_num = pinfo->fd->num;
+	gai->time= nstime_to_sec(&pinfo->fd->rel_ts);
+	COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
+	COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
+
+	gai->port_src=pinfo->srcport;
+	gai->port_dst=pinfo->destport;
+	if (frame_label != NULL)
+		gai->frame_label = g_strdup(frame_label);
+	else
+		gai->frame_label = g_strdup("");
+
+	if (comment != NULL)
+		gai->comment = g_strdup(comment);
+	else
+		gai->comment = g_strdup("");
+	gai->conv_num=1;
+	gai->line_style=line_style; /* 1=single line   2=dual line */
+	gai->display=TRUE;
+
+	user_data->graph_analysis_data->graph_info->list = g_list_append(user_data->graph_analysis_data->graph_info->list, gai);
+
+	return 1;
+}
+
+/****************************************************************************/
+/* Change the frame_label and comment in a graph item if not NULL*/
+/* return 0 if the frame_num is not in the graph list */
+int change_frame_graph_t38(user_data_t *user_data, gint32 frame_num, const gchar *new_frame_label, const gchar *new_comment)
+{
+	graph_analysis_item_t *gai;
+	GList* list;
+	gchar *tmp_str = NULL;
+	gchar *tmp_str2 = NULL;
+
+	if (frame_num == -1) return 0;
+
+	list = g_list_first(user_data->graph_analysis_data->graph_info->list);
+	while (list)
+	{
+		gai = list->data;
+		if (gai->frame_num == (guint32) frame_num){
+			tmp_str = gai->frame_label;
+			tmp_str2 = gai->comment;
+
+			if (new_frame_label != NULL){
+				gai->frame_label = g_strdup(new_frame_label);
+				g_free(tmp_str);
+			}
+
+			if (new_comment != NULL){
+				gai->comment = g_strdup(new_comment);
+				g_free(tmp_str2);
+			}
+			break;
+		}
+		list = g_list_next (list);
+	}
+	if (tmp_str == NULL) return 0;		/* it is not in the list */
+	return 1;
+}
+
+/* TODO: Dissect the complete t30 HDLC packets */
+#if 0
+#define MAX_DESC 1024 
+void dissect_t30_DIS_DTC(guint8 *data, guint len)
+{
+	guint8 octet;
+	int offset;
+	gchar  buf[MAX_DESC];
+
+	offset = 3;
+
+	if (len == 0)
+		return;
+	octet = data[offset];
+
+	g_snprintf(buf, MAX_DESC, "%sStore and forward Internet fax- Simple mode (ITU-T T.37)", octet&0x80?"":"No ");
+
+	g_snprintf(buf, MAX_DESC, "%sReal-time Internet fax (ITU T T.38)", octet&0x20?"":"No ");
+
+	g_snprintf(buf, MAX_DESC, "%s3rd Generation Mobile Network ", octet&0x10?"":"No ")
+}
+#endif
+
+/****************************************************************************/
+static const value_string t30_facsimile_control_field_vals_short[] = {
+	{ 0x01, "DIS" },
+	{ 0x02, "CSI" },
+	{ 0x04, "NSF" },
+	{ 0x81, "DTC" },
+	{ 0x82, "CIG" },
+	{ 0x84, "NSC" },
+	{ 0x83, "PWD" },
+	{ 0x85, "SEP" },
+	{ 0x86, "PSA" },
+	{ 0x87, "CIA" },
+	{ 0x88, "ISP" },
+	{ 0x41, "DCS" },
+	{ 0x42, "TSI" },
+	{ 0x44, "NSS" },
+	{ 0x43, "SUB" },
+	{ 0x45, "SID" },
+	{ 0x46, "TSA" },
+	{ 0x47, "IRA" },
+	{ 0x21, "CFR" },
+	{ 0x22, "FTT" },
+	{ 0x24, "CSA" },
+	{ 0x71, "EOM" },
+	{ 0x72, "MPS" },
+	{ 0x74, "EOP" },
+	{ 0x79, "PRI-EOM" },
+	{ 0x7A, "PRI-MPS" },
+	{ 0x7C, "PRI-EOP" },
+	{ 0x78, "PRI-EOP" },
+	{ 0x31, "MCF" },
+	{ 0x33, "RTP" },
+	{ 0x32, "RTN" },
+	{ 0x35, "PIP" },
+	{ 0x34, "PIN" },
+	{ 0x3F, "FDM" },
+	{ 0x5F, "DCN" },
+	{ 0x58, "CRP" },
+	{ 0x53, "FNV" },
+	{ 0x57, "TNR" },
+	{ 0x56, "TR" }
+};
+
+static const value_string t30_facsimile_control_field_vals[] = {
+	{ 0x01, "Digital Identification Signal" },
+	{ 0x02, "Called Subscriber Identification" },
+	{ 0x04, "Non-Standard Facilities" },
+	{ 0x81, "Digital Transmit Command" },
+	{ 0x82, "Calling Subscriber Identification" },
+	{ 0x84, "Non-Standard facilities Command" },
+	{ 0x83, "Password" },
+	{ 0x85, "Selective Polling" },
+	{ 0x86, "Polled Subaddress" },
+	{ 0x87, "Calling subscriber Internet Address" },
+	{ 0x88, "Internet Selective Polling Address" },
+	{ 0x41, "Digital Command Signal" },
+	{ 0x42, "Transmitting Subscriber Identification" },
+	{ 0x44, "Non-Standard facilities Set-up" },
+	{ 0x43, "Subaddress" },
+	{ 0x45, "Sender Identification" },
+	{ 0x46, "Transmitting Subscriber Internet address" },
+	{ 0x47, "Internet Routing Address" },
+	{ 0x21, "Confirmation To Receive" },
+	{ 0x22, "Failure To Train" },
+	{ 0x24, "Called Subscriber Internet Address" },
+	{ 0x71, "End Of Message" },
+	{ 0x72, "MultiPage Signal" },
+	{ 0x74, "End Of Procedure" },
+	{ 0x79, "Procedure Interrupt-End Of Message" },
+	{ 0x7A, "Procedure Interrupt-MultiPage Signal" },
+	{ 0x7C, "Procedure Interrupt-End Of Procedure" },
+	{ 0x78, "Procedure Interrupt-End Of Procedure" },
+	{ 0x31, "Message Confirmation" },
+	{ 0x33, "Retrain Positive" },
+	{ 0x32, "Retrain Negative" },
+	{ 0x35, "Procedure Interrupt Positive" },
+	{ 0x34, "Procedure Interrupt Negative" },
+	{ 0x3F, "File Diagnostics Message" },
+	{ 0x5F, "Disconnect" },
+	{ 0x58, "Command Repeat" },
+	{ 0x53, "Field Not Valid" },
+	{ 0x57, "Transmit not ready" },
+	{ 0x56, "Transmit ready" }
+
+};
+
+static const value_string data_vals[] = {
+	{ 0, "v21" },
+	{ 1, "v27-2400" },
+	{ 2, "v27-4800" },
+	{ 3, "v29-7200" },
+	{ 4, "v29-9600" },
+	{ 5, "v17-7200" },
+	{ 6, "v17-9600" },
+	{ 7, "v17-12000" },
+	{ 8, "v17-14400" },
+	{ 9, "v8" },
+	{ 10, "v34-pri-rate" },
+	{ 11, "v34-CC-1200" },
+	{ 12, "v34-pri-ch" },
+	{ 13, "v33-12000" },
+	{ 14, "v33-14400" },
+	{ 0, NULL },
+};
+
+void dissect_t30(tap_t38_stat_t *statinfo,
+ 							  user_data_t *user_data _U_,
+                              packet_info *pinfo,
+							  guint32 data_value
+)
+{
+	gchar *frame_label = NULL;
+	gchar *comment = NULL;
+	guint8 octet;
+
+	octet = statinfo->hdlc_data[2];
+
+/* TODO: Dissect the complete t30 HDLC packets */
+#if 0
+	/* Facsimile Control Field (FCF) */
+	if ( ((octet&0xF0) == 0x00) || ((octet&0xF0) == 0x80) ) { /* Initial identification  or Command to send */
+		frame_label = g_strdup_printf("%s:hdlc:%s", val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet, t30_facsimile_control_field_vals_short, "Ukn (0x%02X)"));
+		comment = g_strdup_printf("%s:HDLC:%s",val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet, t30_facsimile_control_field_vals, "Ukn (0x%02X)"));
+		if ( (octet == 0x01) || (octet == 0x81) ) {
+			dissect_t30_DIS_DTC(statinfo->hdlc_data, statinfo->hdlc_data_index);
+		}
+	} else { /* all other values */
+		frame_label = g_strdup_printf("%s:hdlc:%s", val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet&0x7F, t30_facsimile_control_field_vals_short, "Ukn (0x%02X)"));
+		comment = g_strdup_printf("%s:HDLC:%s",val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet&0x7F, t30_facsimile_control_field_vals, "Ukn (0x%02X)"));
+	}
+#else
+	frame_label = g_strdup_printf("%s:hdlc:%s", val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet&0x7F, t30_facsimile_control_field_vals_short, "Ukn (0x%02X)"));
+	comment = g_strdup_printf("%s:HDLC:%s",val_to_str(data_value, data_vals, "Ukn (0x%02X)"), val_to_str(octet&0x7F, t30_facsimile_control_field_vals, "Ukn (0x%02X)"));
+#endif
+	add_to_graph_t38(user_data, pinfo, frame_label, comment, 2);
+}
+
+/****************************************************************************/
+int t38_packet_analyse(tap_t38_stat_t *statinfo,
+							  user_data_t *user_data _U_,
+                              packet_info *pinfo,
+                              const t38_packet_info *t38_info)
+{
+	gchar *frame_label = NULL;
+	gchar *comment = NULL;
+	SEQ_STATUS seq_status = UNKNOWN;
+	
+	/* if it is duplicated, just return */
+	if (statinfo->seq_num == t38_info->seq_num) return 0;
+
+	/* if it is the correct seq or first packet */
+	if ( (statinfo->seq_num+1 == t38_info->seq_num) || (statinfo->seq_num == -1) ) seq_status = CORRECT;
+
+	/* EARLY: seq_num > than expexted */
+	else if (t38_info->seq_num > statinfo->seq_num+1 ) seq_status = EARLY;
+
+	/* LATE: seq_num < than expexted */
+	else if (t38_info->seq_num < statinfo->seq_num+1 ) seq_status = LATE;
+
+		
+	if (t38_info->type_msg == 0) {	/*  t30-indicator */
+		frame_label = g_strdup_printf("t30 Ind:%s",val_to_str(t38_info->t30ind_value, t30_indicator_vals, "Ukn (0x%02X)") );
+		comment = g_strdup_printf("t30 Ind:%s",val_to_str(t38_info->t30ind_value, t30_indicator_vals, "Ukn (0x%02X)") );
+		add_to_graph_t38(user_data, pinfo, frame_label, comment, 1);
+
+		/* reset other_data stats in case we never got the previos t4-non-ecm-sig-end */
+		statinfo->other_data_num_bytes = 0;
+		statinfo->other_data_lost = 0;
+		statinfo->start_frame_other_data = -1;
+		statinfo->prev_seq_status = CORRECT;
+		statinfo->other_data_max_burst_lost = 0;
+		statinfo->other_data_burst_lost = 0;
+		statinfo->start_time_other_data = 0;
+	} else if (t38_info->type_msg == 1) {	/*  data */
+		int i;
+		for (i=0; i<t38_info->t38_info_data_item_index; i++) {
+			switch(t38_info->data_type[i]){
+			case 0: /* hdlc-data */
+				/* if it is hdlc-data add it to the array */
+				/* check we'll not excede the array */
+				if (statinfo->hdlc_data_index+t38_info->data_len[i] < MAX_HDLC_FRAME) {
+					g_memmove(&statinfo->hdlc_data[statinfo->hdlc_data_index], t38_info->data[i],t38_info->data_len[i]);
+					statinfo->hdlc_data_index += t38_info->data_len[i];
+				}
+				if (seq_status != CORRECT) statinfo->valid_hdlc_data = FALSE;
+				break;
+			case 2: /* hdlc-fcs-OK */
+			case 4: /* hdlc-fcs-OK-sig-end */
+				if (statinfo->valid_hdlc_data)
+					dissect_t30(statinfo, user_data, pinfo, t38_info->data_value);
+				else {
+					frame_label = g_strdup_printf("%s:hdlc:not decoded",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)"));
+					comment = g_strdup_printf("%s:HDLC:ERROR: wrong seq number in HDLC packet(s)",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)"));
+					add_to_graph_t38(user_data, pinfo, frame_label, comment, 2);
+				}
+				statinfo->hdlc_data_index = 0;
+				statinfo->valid_hdlc_data = TRUE;
+				break;
+			case 1: /* hdlc-sig-end */
+				if (statinfo->hdlc_data_index != 0) { /* if there was no fcs-OK, this is an error */
+					frame_label = g_strdup_printf("%s:hdlc:hdlc-sig-end",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)"));
+					comment = g_strdup_printf("%s:HDLC:ERROR: received hdlc-sig-end without received fcs-OK or fcs-BAD",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)"));
+					add_to_graph_t38(user_data, pinfo, frame_label, comment, 2);
+					statinfo->hdlc_data_index = 0;
+				}
+				break;
+			case 3: /* hdlc-fcs-BAD */
+			case 5: /* hdlc-fcs-BAD-sig-end */
+				frame_label = g_strdup_printf("%s:hdlc:%s",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)"),t38_info->data_type[i] == 3 ? "fcs-BAD" : "fcs-BAD-sig-end" );
+				comment = g_strdup_printf("WARNING: received %s:hdlc:%s", val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)"), t38_info->data_type[i] == 3 ? "fcs-BAD" : "fcs-BAD-sig-end");
+				add_to_graph_t38(user_data, pinfo, frame_label, comment, 2);
+				statinfo->hdlc_data_index = 0;
+				break;
+			} 
+			if ( (t38_info->data_type[i] == 6) || (t38_info->data_type[i] == 7) ) { /* t4-non-ecm-data or t4-non-ecm-sig-end */
+				statinfo->other_data_num_bytes += t38_info->data_len[i];
+				if (seq_status != CORRECT) { 
+					statinfo->other_data_lost++;
+					statinfo->other_data_burst_lost++;
+				} else {
+					if (statinfo->other_data_burst_lost > statinfo->other_data_max_burst_lost) 
+						statinfo->other_data_max_burst_lost = statinfo->other_data_burst_lost;
+						statinfo->other_data_burst_lost = 0;
+				}
+				if (statinfo->start_frame_other_data == -1) {
+					statinfo->start_frame_other_data = pinfo->fd->num;
+					statinfo->start_time_other_data = nstime_to_sec(&pinfo->fd->rel_ts);
+				}
+
+				frame_label = g_strdup_printf("data:%s",val_to_str(t38_info->data_value, data_vals, "Ukn (0x%02X)")  );
+				comment = g_strdup_printf("Num of bytes: %d  Duration: %.2fs Wrong seq num: %d  Burst pack lost: %d", 
+					statinfo->other_data_num_bytes, 
+					nstime_to_sec(&pinfo->fd->rel_ts) - statinfo->start_time_other_data,
+					statinfo->other_data_lost, 
+					statinfo->other_data_max_burst_lost);
+				if ( !change_frame_graph_t38(user_data, statinfo->start_frame_other_data, frame_label, comment) )
+					add_to_graph_t38(user_data, pinfo, frame_label, comment, 2);
+
+				if (t38_info->data_type[i] == 7) { /* t4-non-ecm-sig-end reset values */
+					statinfo->other_data_num_bytes = 0;
+					statinfo->other_data_lost = 0;
+					statinfo->start_frame_other_data = -1;
+					statinfo->prev_seq_status = CORRECT;
+					statinfo->other_data_max_burst_lost = 0;
+					statinfo->other_data_burst_lost = 0;
+				}
+			}
+		}
+	}
+	if (seq_status != LATE) statinfo->seq_num = t38_info->seq_num;
+	g_free(frame_label);
+	g_free(comment);
+	return 0;
+}
+
+/****************************************************************************/
+/* whenever a T38 packet is seen by the tap listener */
+static int t38_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *t38_info_arg)
+{
+	user_data_t *user_data = user_data_arg;
+	const t38_packet_info *t38_info = t38_info_arg;
+	/* we ignore packets that are not displayed */
+	if (pinfo->fd->flags.passed_dfilter == 0)
+		return 0;
+	/* is it the forward direction?  */
+	else if (CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
+		&& user_data->port_src_fwd == pinfo->srcport
+		&& CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
+		&& user_data->port_dst_fwd == pinfo->destport)  {
+		t38_packet_analyse(&(user_data->forward),user_data, pinfo, t38_info);
+	}
+	/* is it the reversed direction? */
+	else if (CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
+		&& user_data->port_src_rev == pinfo->srcport
+		&& CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
+		&& user_data->port_dst_rev == pinfo->destport)  {
+		t38_packet_analyse(&(user_data->reverse),user_data, pinfo, t38_info);
+	}
+
+	return 1;
+}
+
+/****************************************************************************/
+/* reset user_data valueas and clean graph info */
+static void
+t38_clean(user_data_t *user_data)
+{
+	graph_analysis_item_t *gai;
+	GList* list;
+
+	user_data->forward.hdlc_data_index = 0;
+	user_data->reverse.hdlc_data_index = 0;
+
+	user_data->forward.wrong_seq_num = 0;
+	user_data->reverse.wrong_seq_num = 0;
+
+	user_data->forward.seq_num = -1;
+	user_data->reverse.seq_num = -1;
+
+	user_data->forward.valid_hdlc_data = TRUE;
+	user_data->reverse.valid_hdlc_data = TRUE;
+
+	user_data->forward.other_data_num_bytes = 0;
+	user_data->reverse.other_data_num_bytes = 0;
+
+	user_data->forward.other_data_lost = 0;
+	user_data->reverse.other_data_lost = 0;
+
+	user_data->forward.other_data_max_burst_lost = 0;
+	user_data->reverse.other_data_max_burst_lost = 0;
+
+	user_data->forward.other_data_burst_lost = 0;
+	user_data->reverse.other_data_burst_lost = 0;
+
+	user_data->forward.start_frame_other_data = -1;
+	user_data->reverse.start_frame_other_data = -1;
+
+	user_data->forward.start_time_other_data = 0;
+	user_data->reverse.start_time_other_data = 0;
+
+	user_data->forward.prev_seq_status = CORRECT;
+	user_data->reverse.prev_seq_status = CORRECT;
+	
+	/* free the graph list */
+	list = g_list_first(user_data->graph_analysis_data->graph_info->list);
+	while (list)
+	{
+		gai = list->data;
+		g_free(gai->frame_label);
+		g_free(gai->comment);
+		g_free((void *)gai->src_addr.data);
+		g_free((void *)gai->dst_addr.data);
+		g_free(list->data);
+		list = g_list_next (list);
+	}
+	g_list_free(user_data->graph_analysis_data->graph_info->list);
+	user_data->graph_analysis_data->graph_info->nconv = 0;
+	user_data->graph_analysis_data->graph_info->list = NULL;
+
+	return;
+}
+
+
+/****************************************************************************/
+/* when there is a [re]reading of packet's */
+static void
+t38_reset(void *user_data_arg)
+{
+	user_data_t *user_data = user_data_arg;
+
+	t38_clean(user_data);
+	
+	/* create or refresh the graph windows */
+	if (user_data->graph_analysis_data->dlg.window == NULL)	/* create the window */
+		graph_analysis_create(user_data->graph_analysis_data);
+	else
+		graph_analysis_update(user_data->graph_analysis_data);		/* refresh it */
+
+	return;
+}
+
+/****************************************************************************/
+static void
+t38_draw(void *user_data_arg)
+{
+	user_data_t *user_data = user_data_arg;
+
+	graph_analysis_redraw(user_data->graph_analysis_data);
+
+	return;
+}
+
+/****************************************************************************/
+/* called when the graph windows is destroyed */
+static void
+t38_on_destroy(void *user_data_arg)
+{
+	user_data_t *user_data = user_data_arg;
+
+	/* remove tap listener */
+	protect_thread_critical_region();
+	remove_tap_listener(user_data);
+	unprotect_thread_critical_region();
+
+	/* free the address */
+	g_free((void *)user_data->ip_src_fwd.data);
+	g_free((void *)user_data->ip_dst_fwd.data);
+	g_free((void *)user_data->ip_src_rev.data);
+	g_free((void *)user_data->ip_dst_rev.data);
+
+	/* clean graph info */
+	t38_clean(user_data);
+
+	g_free(user_data->graph_analysis_data->graph_info);
+
+}
+
+/****************************************************************************/
+void t38_analysis(
+		address *ip_src_fwd,
+		guint16 port_src_fwd,
+		address *ip_dst_fwd,
+		guint16 port_dst_fwd,
+		address *ip_src_rev,
+		guint16 port_src_rev,
+		address *ip_dst_rev,
+		guint16 port_dst_rev
+		)
+{
+	user_data_t *user_data;
+	GString *error_string;
+
+	/* init */
+	user_data = g_malloc(sizeof(user_data_t));
+
+	user_data->graph_analysis_data = graph_analysis_init();
+	user_data->graph_analysis_data->graph_info = g_malloc(sizeof(graph_analysis_info_t));
+	user_data->graph_analysis_data->graph_info->nconv = 0;
+	user_data->graph_analysis_data->graph_info->list = NULL;
+
+	user_data->graph_analysis_data->dlg.title = g_strdup("Fax T38 analysis");
+
+	user_data->graph_analysis_data->dlg.inverse = TRUE;  /* to display "calling ----> called" fax call */
+
+	user_data->graph_analysis_data->on_destroy_user_data = t38_on_destroy;
+	user_data->graph_analysis_data->data = user_data;
+
+	COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
+	user_data->port_src_fwd = port_src_fwd;
+	COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
+	user_data->port_dst_fwd = port_dst_fwd;
+	COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
+	user_data->port_src_rev = port_src_rev;
+	COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
+	user_data->port_dst_rev = port_dst_rev;
+	
+	/* register tap listener */
+	error_string = register_tap_listener("t38", user_data, NULL,
+		t38_reset, t38_packet, t38_draw);
+	if (error_string != NULL) {
+		simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
+			g_string_free(error_string, TRUE);
+		return;
+	}
+
+	/* retap all packets */
+	cf_retap_packets(&cfile, FALSE);	
+}
+
+/****************************************************************************/
+/* entry point from main menu */
+void t38_analysis_cb(GtkWidget *w _U_, gpointer data _U_) 
+{
+	address ip_src_fwd;
+	guint16 port_src_fwd;
+	address ip_dst_fwd;
+	guint16 port_dst_fwd;
+	address ip_src_rev;
+	guint16 port_src_rev;
+	address ip_dst_rev;
+	guint16 port_dst_rev;
+
+	gchar filter_text[256];
+	dfilter_t *sfcode;
+	capture_file *cf;
+	epan_dissect_t *edt;
+	gint err;
+	gchar *err_info;
+	gboolean frame_matched;
+	frame_data *fdata;
+
+	/* Try to compile the filter. */
+	strcpy(filter_text,"t38 && (ip || ipv6)");
+	if (!dfilter_compile(filter_text, &sfcode)) {
+		simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, dfilter_error_msg);
+		return;
+	}
+	/* we load the current file into cf variable */
+	cf = &cfile;
+	fdata = cf->current_frame;
+	
+	/* we are on the selected frame now */
+	if (fdata == NULL)
+		return; /* if we exit here it's an error */
+
+	/* dissect the current frame */
+	if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
+	    cf->pd, fdata->cap_len, &err, &err_info)) {
+		simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+			cf_read_error_message(err, err_info), cf->filename);
+		return;
+	}
+	edt = epan_dissect_new(TRUE, FALSE);
+	epan_dissect_prime_dfilter(edt, sfcode);
+	epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
+	frame_matched = dfilter_apply_edt(sfcode, edt);
+	
+	/* check if it is a t38 frame */
+	frame_matched = dfilter_apply_edt(sfcode, edt);
+	if (frame_matched != 1) {
+		epan_dissect_free(edt);
+		simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+		    "You didn't choose a T38 packet!");
+		return;
+	}
+
+	/* ok, it is a T38 frame, so let's get the ip and port values */
+	COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
+	COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
+	port_src_fwd = edt->pi.srcport;
+	port_dst_fwd = edt->pi.destport;
+
+	/* assume the inverse ip/port combination for the reverse direction */
+	COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
+	COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
+	port_src_rev = edt->pi.destport;
+	port_dst_rev = edt->pi.srcport;
+	
+	t38_analysis(
+		&ip_src_fwd,
+		port_src_fwd,
+		&ip_dst_fwd,
+		port_dst_fwd,
+		&ip_src_rev,
+		port_src_rev,
+		&ip_dst_rev,
+		port_dst_rev
+	);
+	
+}
+
+/****************************************************************************/
+static void
+t38_analysis_init(char *dummy _U_)
+{
+	t38_analysis_cb(NULL, NULL);
+}
+
+/****************************************************************************/
+void
+register_tap_listener_t38_analysis(void)
+{
+	register_stat_cmd_arg("t38", t38_analysis_init);
+
+
+	register_stat_menu_item("Fax T38 Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
+	    t38_analysis_cb, NULL, NULL, NULL);
+}
Index: epan/libethereal.def
===================================================================
--- epan/libethereal.def	(revision 16065)
+++ epan/libethereal.def	(working copy)
@@ -557,6 +557,7 @@
 stats_tree_tick_pivot
 stats_tree_tick_range
 string_to_name_resolve
+t30_indicator_vals		DATA
 T_h323_message_body_vals	DATA
 tap_push_tapped_queue
 tap_queue_init
Index: epan/dissectors/packet-t38.c
===================================================================
--- epan/dissectors/packet-t38.c	(revision 16065)
+++ epan/dissectors/packet-t38.c	(working copy)
@@ -51,6 +51,7 @@
 #include <glib.h>
 #include <epan/packet.h>
 #include <epan/conversation.h>
+#include <epan/tap.h>
 
 #include <stdio.h>
 #include <string.h>
@@ -67,6 +68,8 @@
 static guint global_t38_tcp_port = PORT_T38;
 static guint global_t38_udp_port = PORT_T38;
 
+static int t38_tap = -1;
+
 /*
 * Variables to allow for proper deletion of dissector registration when
 * the user changes port from the gui.
@@ -171,6 +174,13 @@
 /* Preferences bool to control whether or not setup info should be shown */
 static gboolean global_t38_show_setup_info = TRUE;
 
+/* Can tap up to 4 T38 packets within same packet */
+/* We only tap the primary part, not the redundancy */
+#define MAX_T38_MESSAGES_IN_PACKET 4
+static t38_packet_info t38_info_arr[MAX_T38_MESSAGES_IN_PACKET];
+static int t38_info_current=0;
+static t38_packet_info *t38_info=NULL;
+
 /* Set up an T38 conversation */
 void t38_add_address(packet_info *pinfo,
                      address *addr, int port,
@@ -292,7 +302,7 @@
 	{ 0, NULL, 0, NULL }
 };
 
-static const value_string t30_indicator_vals[] = {
+const value_string t30_indicator_vals[] = {
 	{ 0, "no-signal" },
 	{ 1, "cng" },
 	{ 2, "ced" },
@@ -330,6 +340,11 @@
         col_append_fstr(pinfo->cinfo, COL_INFO, " t30ind: %s",
          val_to_str(T30ind_value,t30_indicator_vals,"<unknown>"));
 	}
+
+	/* info for tap */
+	if (primary_part)
+		t38_info->t30ind_value = T30ind_value;
+
 	return offset;
 }
 
@@ -397,6 +412,12 @@
         col_append_fstr(pinfo->cinfo, COL_INFO, " data:%s:",
          val_to_str(Data_value,data_vals,"<unknown>"));
 	}
+
+	
+	/* info for tap */
+	if (primary_part)
+		t38_info->data_value = Data_value;
+
 	return offset;
 }
 
@@ -419,6 +440,10 @@
                               ett_t38_Type_of_msg, Type_of_msg_choice,
                               &Type_of_msg_value);
 
+  /* info for tap */
+  if (primary_part)
+    t38_info->type_msg = Type_of_msg_value;
+
   return offset;
 }
 
@@ -511,6 +536,16 @@
          val_to_str(Data_Field_field_type_value,Data_Field_field_type_vals,"<unknown>"));
 	}
 
+	/* info for tap */
+	if (primary_part) {
+		if ( (t38_info->t38_info_data_item_index < MAX_T38_DATA_ITEMS) && (t38_info->t38_info_data_item_index >= 0) ){ /*sanity check */
+			t38_info->data_type[t38_info->t38_info_data_item_index] = Data_Field_field_type_value;
+
+			if (t38_info->t38_info_data_item_index++ == MAX_T38_DATA_ITEMS-1) t38_info->t38_info_data_item_index = 1;
+		}
+	}
+
+
     return offset;
 }
 
@@ -535,6 +570,16 @@
                tvb_bytes_to_str(value_tvb,0,7));
         }
 	}
+
+
+	/* info for tap */
+	if (primary_part) {
+		if ( (t38_info->t38_info_data_item_index <= MAX_T38_DATA_ITEMS) && (t38_info->t38_info_data_item_index > 0) ){ /*sanity check */
+			t38_info->data_len[t38_info->t38_info_data_item_index-1] = value_len;
+			t38_info->data[t38_info->t38_info_data_item_index-1] = tvb_memdup(value_tvb,0,value_len);
+		}
+	}
+
 	return offset;
 }
 
@@ -591,6 +636,10 @@
 	offset=dissect_per_constrained_integer(tvb, offset, pinfo,
 		tree, hf_t38_seq_number, 0, 65535,
 		&seq_number, NULL, FALSE);
+	
+	/* info for tap */
+	if (primary_part)
+		t38_info->seq_num = seq_number;
 
       if (check_col(pinfo->cinfo, COL_INFO)){
         col_append_fstr(pinfo->cinfo, COL_INFO, "Seq=%05u ",seq_number);
@@ -630,7 +679,8 @@
 {
     /* When the field-data is not present, we MUST offset 1 byte*/
     if((Data_Field_field_type_value != 0) &&
-       (Data_Field_field_type_value != 6))
+       (Data_Field_field_type_value != 6) &&
+	   (Data_Field_field_type_value != 7))
     {
         offset=offset+8;
     }
@@ -745,6 +795,7 @@
 	proto_item *it;
 	proto_tree *tr;
 	guint32 offset=0;
+	int i;
 
 	/*
 	 * XXX - heuristic to check for misidentified packets.
@@ -757,6 +808,25 @@
 		}
 	}
 
+	/* tap info */
+	t38_info_current++;
+	if (t38_info_current==MAX_T38_MESSAGES_IN_PACKET) {
+		t38_info_current=0;
+	}
+	t38_info = &t38_info_arr[t38_info_current];
+
+	t38_info->seq_num = 0;
+	t38_info->type_msg = 0;
+	t38_info->data_value = 0;
+	t38_info->t30ind_value =0;
+
+	t38_info->t38_info_data_item_index = 0;
+	for (i=0; i<MAX_T38_DATA_ITEMS; i++) {
+		t38_info->data_type[i] = 0;
+		t38_info->data[i] = NULL;
+		t38_info->data_len[i] = 0;
+	}
+
 	if (check_col(pinfo->cinfo, COL_PROTOCOL)){
 		col_set_str(pinfo->cinfo, COL_PROTOCOL, "T.38");
 	}
@@ -794,6 +864,18 @@
 			col_append_fstr(pinfo->cinfo, COL_INFO, " [Malformed?]");
 		}
 	}
+	
+	/* if is a valid t38 packet, add to tap */
+	if (!pinfo->in_error_pkt)
+		tap_queue_packet(t38_tap, pinfo, t38_info);
+	else { /* if not, free the data */
+		for (i=0; i<MAX_T38_DATA_ITEMS; i++) {
+			t38_info->data_type[i] = 0;
+			g_free(t38_info->data[i]);
+			t38_info->data[i] = NULL;
+			t38_info->data_len[i] = 0;
+		}
+	}
 }
 
 static void
@@ -1037,6 +1119,8 @@
 	proto_register_subtree_array(ett, array_length(ett));
 	register_dissector("t38", dissect_t38, proto_t38);
 
+	t38_tap = register_tap("t38");
+
 	t38_module = prefs_register_protocol(proto_t38, proto_reg_handoff_t38);
 	prefs_register_bool_preference(t38_module, "use_pre_corrigendum_asn1_specification",
 	    "Use the Pre-Corrigendum ASN.1 specification",
Index: epan/dissectors/packet-t38.h
===================================================================
--- epan/dissectors/packet-t38.h	(revision 16065)
+++ epan/dissectors/packet-t38.h	(working copy)
@@ -25,6 +25,20 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#define MAX_T38_DATA_ITEMS 4
+typedef struct _t38_packet_info {
+	guint16 seq_num;	/* UDPTLPacket sequence number */
+	guint32 type_msg;	/* 0=t30-indicator    1=data */
+	guint32 t30ind_value;
+	guint32 data_value;	/* standard and speed */
+
+	int t38_info_data_item_index; /* this will have the number of Data Items in the packet and is used as the index when decoding the packet */
+	guint32 data_type[MAX_T38_DATA_ITEMS];
+	guint8 *data[MAX_T38_DATA_ITEMS];
+	gint data_len[MAX_T38_DATA_ITEMS];
+} t38_packet_info;
+
+
 /* Info to save in T38 conversation / packet-info */
 #define MAX_T38_SETUP_METHOD_SIZE 7
 struct _t38_conversation_info
@@ -37,4 +51,6 @@
 void t38_add_address(packet_info *pinfo,
                      address *addr, int port,
                      int other_port,
-                     const gchar *setup_method, guint32 setup_frame_number);
+                     gchar *setup_method, guint32 setup_frame_number);
+
+ETH_VAR_IMPORT const value_string t30_indicator_vals[];