aboutsummaryrefslogtreecommitdiffstats
path: root/tap-diameter-avp.c
blob: 002167c31fc5796f80bf5e803d583bb2c3dca29f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/* tap-diameter-avp.c
 * Copyright 2010 Andrej Kuehnal <andrejk@freenet.de>
 *
 * $Id$
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * 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 <stdio.h>

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

#include <string.h>
#include "epan/packet_info.h"
#include <epan/tap.h>
#include <epan/epan_dissect.h>
#include <epan/stat_cmd_args.h>
#include "epan/value_string.h"
#include "epan/nstime.h"
#include "epan/ftypes/ftypes.h"
#include "epan/to_str.h"
#include "epan/dissectors/packet-diameter.h"


/* used to keep track of the statistics for an entire program interface */
typedef struct _diameteravp_t {
	guint32 frame;
	guint32 diammsg_toprocess;
	guint32 cmd_code;
	guint32 req_count;
	guint32 ans_count;
	guint32 paired_ans_count;
	gchar* 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: empty node. node='%p' data='%p'\n",node,data);
		return FALSE;
	}
	fi=node->finfo;
	hfi=fi ? fi->hfinfo : NULL;
	if(!hfi) {
		fprintf(stderr,"traverse end: 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("unsupported 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 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;
	diameteravp_t *ds=NULL;

	/* Validate paramerers. */
	if(!dp || !edt || !edt->tree)
		return ret;

	/* Several diameter messages within one frame are possible.                    *
	 * Check if we processing the message in same frame like befor or in new frame.*/
	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.*/
	is_request=dp->processing_request;
	cmd_code=dp->cmd_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;
	}

	/* Check command code provided by command line option.*/
	if (ds->cmd_code && ds->cmd_code!=cmd_code)
		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,"DEBUG: 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' 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' ",
								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);
				/* 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;
	gchar* field=NULL;
	gchar** tokens;
	guint opt_count=0;
	guint opt_idx=0;
	GString* filter=NULL;
	GString* error_string=NULL;

	ds=g_malloc(sizeof(diameteravp_t));
	ds->frame=0;
	ds->diammsg_toprocess=0;
	ds->cmd_code=0;
	ds->req_count=0;
	ds->ans_count=0;
	ds->paired_ans_count=0;
	ds->filter=NULL;

	filter=g_string_new("diameter");

	/* Split command line options. */
	tokens = g_strsplit(optarg,",", 1024);
	opt_count=0;
	while (tokens[opt_count])
		opt_count++;
	if (opt_count>2)
		ds->cmd_code=(guint32)atoi(tokens[2]);

	/* Loop over diameter field names. */
	for(opt_idx=3;opt_idx<opt_count;opt_idx++)
	{
		/* Current field from command line arguments. */
		field=tokens[opt_idx];
		/* Connect all requested fields with logical OR. */
		g_string_append(filter,"||");
		/* Prefix field name with "diameter." by default. */
		if(!strchr(field,'.'))
			g_string_append(filter, "diameter.");
		/* Append field name to the filter. */
		g_string_append(filter, field);
	}
	g_strfreev(tokens);
	ds->filter=g_string_free(filter,FALSE);

	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);
}