From 1a855bfde2e1c984628aacac8e418b92c9856427 Mon Sep 17 00:00:00 2001 From: Anders Broman Date: Sat, 6 Mar 2010 20:54:58 +0000 Subject: [PATCH] From Andrej Kuehnal: New feature: extract specified diameter AVPs from large capture files. https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=4560 svn path=/trunk/; revision=32132 --- CMakeLists.txt | 1 + Makefile.common | 1 + epan/dissectors/packet-diameter.c | 17 +- epan/dissectors/packet-diameter.h | 1 + gtk/diameter_stat.c | 5 + tap-diameter-avp.c | 275 ++++++++++++++++++++++++++++++ 6 files changed, 297 insertions(+), 3 deletions(-) create mode 100644 tap-diameter-avp.c diff --git a/CMakeLists.txt b/CMakeLists.txt index e2bb100969..9489e21ca0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -489,6 +489,7 @@ set(TSHARK_TAP_SRC tap-camelsrt.c tap-comparestat.c tap-dcerpcstat.c + tap-diameter-avp.c tap-funnel.c tap-gsm_astat.c tap-h225counter.c diff --git a/Makefile.common b/Makefile.common index 0676bce0a9..1a13fc5101 100644 --- a/Makefile.common +++ b/Makefile.common @@ -107,6 +107,7 @@ TSHARK_TAP_SRC = \ tap-camelsrt.c \ tap-comparestat.c \ tap-dcerpcstat.c \ + tap-diameter-avp.c \ tap-funnel.c \ tap-gsm_astat.c \ tap-h225counter.c \ diff --git a/epan/dissectors/packet-diameter.c b/epan/dissectors/packet-diameter.c index af7fe3ad9a..6dbf11cd58 100644 --- a/epan/dissectors/packet-diameter.c +++ b/epan/dissectors/packet-diameter.c @@ -796,6 +796,7 @@ dissect_diameter_common(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree) diameter_pair = se_alloc(sizeof(diameter_req_ans_pair_t)); diameter_pair->hop_by_hop_id = hop_by_hop_id; diameter_pair->cmd_code = cmd; + diameter_pair->result_code = 0; diameter_pair->cmd_str = cmd_str; diameter_pair->req_frame = pinfo->fd->num; diameter_pair->ans_frame = 0; @@ -814,10 +815,15 @@ dissect_diameter_common(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree) if (!diameter_pair) { /* create a "fake" diameter_pair structure */ diameter_pair = ep_alloc(sizeof(diameter_req_ans_pair_t)); + diameter_pair->hop_by_hop_id = hop_by_hop_id; + diameter_pair->cmd_code = cmd; + diameter_pair->result_code = 0; + diameter_pair->cmd_str = cmd_str; diameter_pair->req_frame = 0; diameter_pair->ans_frame = 0; diameter_pair->req_time = pinfo->fd->abs_ts; } + diameter_pair->processing_request=(flags_bits & 0x80)!=0; if (!tree) return; @@ -840,10 +846,7 @@ dissect_diameter_common(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree) diameter_pair->srt_time = ns; it = proto_tree_add_time(diam_tree, hf_diameter_answer_time, tvb, 0, 0, &ns); PROTO_ITEM_SET_GENERATED(it); - /* TODO: Populate result_code in tap record from AVP 268 */ - /* Also TODO: See how to handle requests for which no answers were found */ - tap_queue_packet(diameter_tap, pinfo, diameter_pair); } } @@ -857,6 +860,14 @@ dissect_diameter_common(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree) offset += (offset % 4) ? 4 - (offset % 4) : 0 ; } + + /* Handle requests for which no answers were found and + * anawers for which no requests were found in the tap listener. + * In case if you don't need unpaired requests/answers use: + * if(diameter_pair->processing_request || !diameter_pair->req_frame) + * return; + */ + tap_queue_packet(diameter_tap, pinfo, diameter_pair); } static guint diff --git a/epan/dissectors/packet-diameter.h b/epan/dissectors/packet-diameter.h index cecba8b4cd..4444224b7a 100644 --- a/epan/dissectors/packet-diameter.h +++ b/epan/dissectors/packet-diameter.h @@ -34,6 +34,7 @@ typedef struct _diameter_req_ans_pair_t guint32 ans_frame; /* frame number in which answer was seen */ nstime_t req_time; nstime_t srt_time; + gboolean processing_request; /* TRUE if processing request, FALSE if processing answer. */ } diameter_req_ans_pair_t; /* Conversation Info */ diff --git a/gtk/diameter_stat.c b/gtk/diameter_stat.c index 11e380dbd3..b188da7ccc 100644 --- a/gtk/diameter_stat.c +++ b/gtk/diameter_stat.c @@ -93,6 +93,11 @@ diameterstat_packet(void *pdiameter, packet_info *pinfo, epan_dissect_t *edt _U_ diameterstat_t *fs=(diameterstat_t *)pdiameter; int* idx = NULL; + /* Process only answers where corresponding request is found. + * Unpaired daimeter messages are currently not supported by statistics. + * Return 0, since redraw is not needed. */ + if(!diameter || diameter->processing_request || !diameter->req_frame) + return 0; idx = (int*) g_hash_table_lookup(cmd_str_hash, diameter->cmd_str); if (idx == NULL) { diff --git a/tap-diameter-avp.c b/tap-diameter-avp.c new file mode 100644 index 0000000000..26d1f0e10d --- /dev/null +++ b/tap-diameter-avp.c @@ -0,0 +1,275 @@ +/* tap-diameter-avp.c + * Copyright 2010 Andrej Kuehnal + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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. + */ + +/* + * This TAP enables extraction of most important diameter fields in text format. + * - much more performance than -T text and -T pdml + * - more powerfull than -T field and -z proto,colinfo + * - exacltly one text line per diameter message + * - multiple diameter messages in one frame supported + * E.g. one device watchdog answer and two credit control answers + * in one TCP packet produces 3 text lines. + * - several fields with same name within one diameter message supported + * E.g. Multiple AVP(444) Subscription-Id-Data once with IMSI once with MSISDN + * - several grouped AVPs supported + * E.g. Zero or more Multiple-Services-Credit-Control AVPs(456) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#include +#include "epan/packet_info.h" +#include +#include +#include +#include "epan/value_string.h" +#include "epan/nstime.h" +#include "epan/ftypes/ftypes.h" +#include "register.h" +#include + + +/* used to keep track of the statistics for an entire program interface */ +typedef struct _diameteravp_t { + guint32 frame; + guint32 diammsg_toprocess; + guint32 req_count; + guint32 ans_count; + guint32 paired_ans_count; + char* filter; +} diameteravp_t; + +/* Copied from proto.c */ +static gboolean +tree_traverse_pre_order(proto_tree *tree, proto_tree_traverse_func func, gpointer data) +{ + proto_node *pnode = tree; + proto_node *child; + proto_node *current; + + if (func(pnode, data)) + return TRUE; + + child = pnode->first_child; + while (child != NULL) { + current = child; + child = current->next; + if (tree_traverse_pre_order((proto_tree *)current, func, data)) + return TRUE; + } + return FALSE; +} + +static gboolean +diam_tree_to_csv(proto_node* node, gpointer data) +{ + char* val_str=NULL; + char* val_tmp=NULL; + ftenum_t ftype; + field_info* fi; + header_field_info *hfi; + if(!node) { + fprintf(stderr,"traverse end: node='%p' data='%p'\n",node,data); + return FALSE; + } + fi=node->finfo; + hfi=fi ? fi->hfinfo : NULL; + if(!hfi) { + fprintf(stderr,"traverse end2: Hfi not found node='%p'\n",node); + return FALSE; + } + ftype=fi->value.ftype->ftype; + if (ftype!=FT_NONE&&ftype!=FT_PROTOCOL) { + /* convert value to string */ + if(fi->value.ftype->val_to_string_repr) + { + val_tmp=fvalue_to_string_repr(&fi->value,FTREPR_DISPLAY,NULL); + if(val_tmp) + { + val_str=ep_strdup(val_tmp); + g_free(val_tmp); + } + } + if(!val_str) + val_str=ep_strdup_printf("unsuprorted type: %s",ftype_name(ftype)); + + /*printf("traverse: name='%s', abbrev='%s',desc='%s', val='%s'\n",hfi->name,hfi->abbrev,ftype_name(hfi->type),val_str);*/ + printf("%s='%s' ",hfi->name,val_str); + } + return FALSE; +} + +static int +diameteravp_packet(void *pds, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pdi) +{ + int ret = 0; + double resp_time=0.; + gboolean is_request=TRUE; + guint32 cmd_code=0; + guint32 result_code=0; + guint32 req_frame=0; + guint32 ans_frame=0; + guint32 diam_child_node=0; + proto_node* current=NULL; + proto_node* node = NULL; + header_field_info* hfi=NULL; + field_info* finfo=NULL; + const diameter_req_ans_pair_t* dp=pdi; + + /* Several diameter messages within one frame are possible. * + * Check if we processing the message in same frame like befor or in new frame.*/ + diameteravp_t *ds=(diameteravp_t *)pds; + if(pinfo->fd->num > ds->frame) { + ds->frame=pinfo->fd->num; + ds->diammsg_toprocess=0; + } else { + ds->diammsg_toprocess+=1; + } + /* Extract data from request/answer pair provided by diameter dissector.*/ + if(dp) { + is_request=dp->processing_request; + cmd_code=dp->cmd_code; + result_code=dp->result_code; + req_frame=dp->req_frame; + ans_frame=dp->ans_frame; + if(!is_request) { + nstime_t ns; + nstime_delta(&ns, &pinfo->fd->abs_ts, &dp->req_time); + resp_time=nstime_to_sec(&ns); + resp_time=resp_time<0?0.:resp_time; + } + } + if (!edt || !edt->tree || cmd_code!=272) + return ret; + /* Loop over top level nodes */ + node = edt->tree->first_child; + while (node != NULL) { + current = node; + node = current->next; + finfo=current->finfo; + hfi=finfo ? finfo->hfinfo : NULL; + /*fprintf(stderr,"diameteravp_packet %d %p %p node=%p abbrev=%s\n",cmd_code,edt,edt->tree,current,hfi->abbrev);*/ + /* process current diameter subtree in the current frame. */ + if(hfi && hfi->abbrev && strcmp(hfi->abbrev,"diameter")==0) { + /* Process current diameter message in the frame */ + if (ds->diammsg_toprocess==diam_child_node) { + if(is_request) { + ds->req_count++; + } else { + ds->ans_count++; + if (req_frame>0) + ds->paired_ans_count++; + } + /* Output frame data.*/ + printf("frame='%d' proto='diameter' msgnr='%d' is_request='%d' cmd='%d' req_frame='%d' ans_frame='%d' resp_time='%f' ",pinfo->fd->num,ds->diammsg_toprocess,is_request,cmd_code,req_frame,ans_frame,resp_time); + /* Visit selected nodes of one diameter message.*/ + tree_traverse_pre_order(current, diam_tree_to_csv, &ds); + /* End of message.*/ + printf("\n"); + /*printf("hfi: name='%s', msg_curr='%d' abbrev='%s',type='%s'\n",hfi->name,diam_child_node,hfi->abbrev,ftype_name(hfi->type));*/ + } + diam_child_node++; + } + } + return ret; +} + +static void +diameteravp_draw(void* pds) +{ + diameteravp_t *ds=(diameteravp_t *)pds; + /* printing results */ + printf("=== Diameter Summary ===\nrequset count:\t%d\nanswer count:\t%d\nreq/ans pairs:\t%d\n",ds->req_count,ds->ans_count,ds->paired_ans_count); +} + + +static void +diameteravp_init(const char *optarg, void* userdata _U_) +{ + diameteravp_t *ds; + char* options=NULL; + char* saveptr=NULL; + char* str=NULL; + int field_count=0; + int filter_len=0; + GString *error_string; + + ds=g_malloc(sizeof(diameteravp_t)); + ds->frame=0; + ds->diammsg_toprocess=0; + ds->req_count=0; + ds->ans_count=0; + ds->paired_ans_count=0; + str=NULL; + ds->filter=NULL; + + options=g_strdup(optarg); + for(str=options;*str;str++) + { + if(*str==',') + field_count++; + } + filter_len=strlen(optarg)+sizeof("diameter")+field_count*sizeof("||diameter."); + ds->filter=g_malloc0(filter_len); + strcat(ds->filter,"diameter"); + + for(str=strtok_r(options+sizeof("diameter,avp"),",",&saveptr);str;str=strtok_r(NULL,",",&saveptr)) + { + /* Connect all requested fields with logical OR. */ + strcat(ds->filter,"||"); + /* Prefix field name with "diameter." by default. */ + if(!strchr(str,'.')) + strcat(ds->filter,"diameter."); + /* Append field name to the filter. */ + strcat(ds->filter,str); + } + g_free(options); + + error_string=register_tap_listener("diameter", ds, ds->filter, 0, NULL, diameteravp_packet, diameteravp_draw); + if(error_string){ + /* error, we failed to attach to the tap. clean up */ + g_free(ds); + + fprintf(stderr, "tshark: Couldn't register diam,csv tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } +} + + +void +register_tap_listener_diameteravp(void) +{ + register_stat_cmd_arg("diameter,avp", diameteravp_init, NULL); +} +