7a7314da0ed09a529cd5cfd29bc66508bc26ef14
[obnox/wireshark/wip.git] / tap-diameter-avp.c
1 /* tap-diameter-avp.c
2  * Copyright 2010 Andrej Kuehnal <andrejk@freenet.de>
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 /*
26  * This TAP enables extraction of most important diameter fields in text format.
27  * - much more performance than -T text and -T pdml
28  * - more powerfull than -T field and -z proto,colinfo
29  * - exacltly one text line per diameter message
30  * - multiple diameter messages in one frame supported
31  *   E.g. one device watchdog answer and two credit control answers
32  *        in one TCP packet produces 3 text lines.
33  * - several fields with same name within one diameter message supported
34  *   E.g. Multiple AVP(444) Subscription-Id-Data once with IMSI once with MSISDN
35  * - several grouped AVPs supported
36  *   E.g. Zero or more Multiple-Services-Credit-Control AVPs(456)
37  */
38
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42
43 #include <stdio.h>
44
45 #ifdef HAVE_SYS_TYPES_H
46 # include <sys/types.h>
47 #endif
48
49 #include <string.h>
50 #include "epan/packet_info.h"
51 #include <epan/tap.h>
52 #include <epan/epan_dissect.h>
53 #include <epan/stat_cmd_args.h>
54 #include "epan/value_string.h"
55 #include "epan/nstime.h"
56 #include "epan/ftypes/ftypes.h"
57 #include "epan/to_str.h"
58 #include "epan/dissectors/packet-diameter.h"
59
60
61 /* used to keep track of the statistics for an entire program interface */
62 typedef struct _diameteravp_t {
63         guint32 frame;
64         guint32 diammsg_toprocess;
65         guint32 cmd_code;
66         guint32 req_count;
67         guint32 ans_count;
68         guint32 paired_ans_count;
69         gchar* filter;
70 } diameteravp_t;
71
72 /* Copied from proto.c */
73 static gboolean
74 tree_traverse_pre_order(proto_tree *tree, proto_tree_traverse_func func, gpointer data)
75 {
76         proto_node *pnode = tree;
77         proto_node *child;
78         proto_node *current;
79
80         if (func(pnode, data))
81                 return TRUE;
82
83         child = pnode->first_child;
84         while (child != NULL) {
85                 current = child;
86                 child = current->next;
87                 if (tree_traverse_pre_order((proto_tree *)current, func, data))
88                         return TRUE;
89         }
90         return FALSE;
91 }
92
93 static gboolean
94 diam_tree_to_csv(proto_node* node, gpointer data)
95 {
96         char* val_str=NULL;
97         char* val_tmp=NULL;
98         ftenum_t ftype;
99         field_info* fi;
100         header_field_info       *hfi;
101         if(!node) {
102                 fprintf(stderr,"traverse end: empty node. node='%p' data='%p'\n",node,data);
103                 return FALSE;
104         }
105         fi=node->finfo;
106         hfi=fi ? fi->hfinfo : NULL;
107         if(!hfi) {
108                 fprintf(stderr,"traverse end: hfi not found. node='%p'\n",node);
109                 return FALSE;
110         }
111         ftype=fi->value.ftype->ftype;
112         if (ftype!=FT_NONE&&ftype!=FT_PROTOCOL) {
113                 /* convert value to string */
114                 if(fi->value.ftype->val_to_string_repr)
115                 {
116                         val_tmp=fvalue_to_string_repr(&fi->value,FTREPR_DISPLAY,NULL);
117                         if(val_tmp)
118                         {
119                                 val_str=ep_strdup(val_tmp);
120                                 g_free(val_tmp);
121                         }
122                 }
123                 if(!val_str)
124                         val_str=ep_strdup_printf("unsupported type: %s",ftype_name(ftype));
125
126                 /*printf("traverse: name='%s', abbrev='%s',desc='%s', val='%s'\n",hfi->name,hfi->abbrev,ftype_name(hfi->type),val_str);*/
127                 printf("%s='%s' ",hfi->name,val_str);
128         }
129         return FALSE;
130 }
131
132 static int
133 diameteravp_packet(void *pds, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pdi)
134 {
135         int ret = 0;
136         double resp_time=0.;
137         gboolean is_request=TRUE;
138         guint32 cmd_code=0;
139         guint32 result_code=0;
140         guint32 req_frame=0;
141         guint32 ans_frame=0;
142         guint32 diam_child_node=0;
143         proto_node* current=NULL;
144         proto_node* node = NULL;
145         header_field_info* hfi=NULL;
146         field_info* finfo=NULL;
147         const diameter_req_ans_pair_t* dp=pdi;
148         diameteravp_t *ds=NULL;
149
150         /* Validate paramerers. */
151         if(!dp || !edt || !edt->tree)
152                 return ret;
153
154         /* Several diameter messages within one frame are possible.                    *
155          * Check if we processing the message in same frame like befor or in new frame.*/
156         ds=(diameteravp_t *)pds;
157         if(pinfo->fd->num > ds->frame) {
158                 ds->frame=pinfo->fd->num;
159                 ds->diammsg_toprocess=0;
160         } else {
161                         ds->diammsg_toprocess+=1;
162         }
163
164         /* Extract data from request/answer pair provided by diameter dissector.*/
165         is_request=dp->processing_request;
166         cmd_code=dp->cmd_code;
167         result_code=dp->result_code;
168         req_frame=dp->req_frame;
169         ans_frame=dp->ans_frame;
170         if(!is_request) {
171                 nstime_t ns;
172                 nstime_delta(&ns, &pinfo->fd->abs_ts, &dp->req_time);
173                 resp_time=nstime_to_sec(&ns);
174                 resp_time=resp_time<0?0.:resp_time;
175         }
176
177         /* Check command code provided by command line option.*/
178         if (ds->cmd_code && ds->cmd_code!=cmd_code)
179                 return ret;
180
181         /* Loop over top level nodes */
182         node = edt->tree->first_child;
183         while (node != NULL) {
184                 current = node;
185                 node = current->next;
186                 finfo=current->finfo;
187                 hfi=finfo ? finfo->hfinfo : NULL;
188                 /*fprintf(stderr,"DEBUG: diameteravp_packet %d %p %p node=%p abbrev=%s\n",cmd_code,edt,edt->tree,current,hfi->abbrev);*/
189                 /* process current diameter subtree in the current frame. */
190                 if(hfi && hfi->abbrev && strcmp(hfi->abbrev,"diameter")==0) {
191                         /* Process current diameter message in the frame */
192                         if (ds->diammsg_toprocess==diam_child_node) {
193                                 if(is_request) {
194                                         ds->req_count++;
195                                 } else {
196                                         ds->ans_count++;
197                                         if (req_frame>0)
198                                                 ds->paired_ans_count++;
199                                 }
200                                 /* Output frame data.*/
201                                 printf("frame='%d' time='%f' src='%s' srcport='%d' dst='%s' dstport='%d' proto='diameter' msgnr='%d' is_request='%d' cmd='%d' req_frame='%d' ans_frame='%d' resp_time='%f' ",
202                                                                 pinfo->fd->num,nstime_to_sec(&pinfo->fd->abs_ts),ep_address_to_str(&pinfo->src),pinfo->srcport,ep_address_to_str(&pinfo->dst), pinfo->destport,ds->diammsg_toprocess,is_request,cmd_code,req_frame,ans_frame,resp_time);
203                                 /* Visit selected nodes of one diameter message.*/
204                                 tree_traverse_pre_order(current, diam_tree_to_csv, &ds);
205                                 /* End of message.*/
206                                 printf("\n");
207                                 /*printf("hfi: name='%s', msg_curr='%d' abbrev='%s',type='%s'\n",hfi->name,diam_child_node,hfi->abbrev,ftype_name(hfi->type));*/
208                         }
209                         diam_child_node++;
210                 }
211         }
212         return ret;
213 }
214
215 static void
216 diameteravp_draw(void* pds)
217 {
218         diameteravp_t *ds=(diameteravp_t *)pds;
219         /* printing results */
220         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);
221 }
222
223
224 static void
225 diameteravp_init(const char *optarg, void* userdata _U_)
226 {
227         diameteravp_t *ds;
228         gchar* field=NULL;
229         gchar** tokens;
230         guint opt_count=0;
231         guint opt_idx=0;
232         GString* filter=NULL;
233         GString* error_string=NULL;
234
235         ds=g_malloc(sizeof(diameteravp_t));
236         ds->frame=0;
237         ds->diammsg_toprocess=0;
238         ds->cmd_code=0;
239         ds->req_count=0;
240         ds->ans_count=0;
241         ds->paired_ans_count=0;
242         ds->filter=NULL;
243
244         filter=g_string_new("diameter");
245
246         /* Split command line options. */
247         tokens = g_strsplit(optarg,",", 1024);
248         opt_count=0;
249         while (tokens[opt_count])
250                 opt_count++;
251         if (opt_count>2)
252                 ds->cmd_code=(guint32)atoi(tokens[2]);
253
254         /* Loop over diameter field names. */
255         for(opt_idx=3;opt_idx<opt_count;opt_idx++)
256         {
257                 /* Current field from command line arguments. */
258                 field=tokens[opt_idx];
259                 /* Connect all requested fields with logical OR. */
260                 g_string_append(filter,"||");
261                 /* Prefix field name with "diameter." by default. */
262                 if(!strchr(field,'.'))
263                         g_string_append(filter, "diameter.");
264                 /* Append field name to the filter. */
265                 g_string_append(filter, field);
266         }
267         g_strfreev(tokens);
268         ds->filter=g_string_free(filter,FALSE);
269
270         error_string=register_tap_listener("diameter", ds, ds->filter, 0, NULL, diameteravp_packet, diameteravp_draw);
271         if(error_string){
272                 /* error, we failed to attach to the tap. clean up */
273                 g_free(ds);
274
275                 fprintf(stderr, "tshark: Couldn't register diam,csv tap: %s\n",
276                                 error_string->str);
277                 g_string_free(error_string, TRUE);
278                 exit(1);
279         }
280 }
281
282
283 void
284 register_tap_listener_diameteravp(void)
285 {
286         register_stat_cmd_arg("diameter,avp", diameteravp_init, NULL);
287 }
288