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