aboutsummaryrefslogtreecommitdiffstats
path: root/doc/README.request_response_tracking
blob: d22b53ca7576156eccd28f3e7d7d77981e61a566 (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
It is often useful to enhance dissectors for request/response style protocols
to match requests with responses.
This allows you to display useful information in the decode tree
such as which requests are matched to which response and the response time
for individual transactions.

This is also useful if you want to pass some data from the request onto the
dissection of the actual response. The RPC dissector for example does
something like this to pass the actual command opcode from the request
onto the response dissector since the opcode itself is not part of the
response packet  and without the opcode we would not know how to decode the
data.

It is also useful when you need to track information on a per conversation 
basis such as when some parameters are negotiatiod during a login phase of the 
protocol and when these parameters affect how future commands on that
session are to be decoded. The iSCSI dissector does something similar to that
to track which sessions that HeaderDigest is activated for and which ones
it is not.




The example below shows how simple this is to add to the dissector IF
1, there is something like a transaction id in the header
2, it is very unlikely that the transaction identifier is reused for the same conversation

The example is taken from the PANA dissector :


First we need to include the definitions for conversations and memory
magament

	#include <epan/conversation.h>
	#include <epan/emem.h>

Then we also need a few header fields to show the relations between request 
and response as well as the response time

	static int hf_pana_response_in = -1;
	static int hf_pana_response_to = -1;
	static int hf_pana_time = -1;

We need a structure that holds all the information we need to remember
between the request and the responses.
One such structure will be allocated for each unique transaction.
In the example we only keep the frame numbers of the request and the response
as well as the timestamp for the request.
But since this structure is persistent and also a unique one is allocated for 
each request/response pair, this is a good place to store other additional
data you may want to keep track of from a request to a response.

	typedef struct _pana_transaction_t {
	        guint32 req_frame;
	        guint32 rep_frame;
	        nstime_t req_time;
	} pana_transaction_t;

We also need a structure that holds persistent information for each 
conversation. ( a conversation is identified by SRC/DST address, protocol and SRC/DST port)
In this case we only want to have keep a binary tree to track the actual 
transactions that occur for this unique conversation.
Some protocols negotiate session parameters during a login phase and those
parameters may affect how later commands on the same session is to be decoded,
this would be a good place to store that additional info you may want to keep
around.

	typedef struct _pana_conv_info_t {
	        emem_tree_t *pdus;
	} pana_conv_info_t;



Finally for the meat of it,  add the conversation and tracking code to the 
actual dissector

	...
	guint32 seq_num;
	conversation_t *conversation;
	pana_conv_info_t *pana_info;
	pana_transaction_t *pana_trans;

	...
	/* Get the transaction identifier */
	seq_num = tvb_get_ntohl(tvb, 8);
	...

	/* 
	 * We need to track some state for this protocol on a per conversation
	 * basis so we can do neat things like request/response tracking
	 */
	/*
	 * Do we have a conversation for this connection?
	 */
	conversation = find_conversation(pinfo->fd->num, 
				&pinfo->src, &pinfo->dst,
				pinfo->ptype, 
				pinfo->srcport, pinfo->destport, 0);
	if (conversation == NULL) {
		/* We don't yet have a conversation, so create one. */
		conversation = conversation_new(pinfo->fd->num, 
					&pinfo->src, &pinfo->dst,
					pinfo->ptype,
					pinfo->srcport, pinfo->destport, 0);
	}
	/*
	 * Do we already have a state structure for this conv
	 */
	pana_info = conversation_get_proto_data(conversation, proto_pana);
	if (!pana_info) {
		/* No.  Attach that information to the conversation, and add
		 * it to the list of information structures.
		 */
		pana_info = se_alloc(sizeof(pana_conv_info_t));
		pana_info->pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "pana_pdus");

		conversation_add_proto_data(conversation, proto_pana, pana_info);
	}
	if(!pinfo->fd->flags.visited){
		if(flags&PANA_FLAG_R){
			/* This is a request */
			pana_trans=se_alloc(sizeof(pana_transaction_t));
			pana_trans->req_frame=pinfo->fd->num;
			pana_trans->rep_frame=0;
			pana_trans->req_time=pinfo->fd->abs_ts;
			se_tree_insert32(pana_info->pdus, seq_num, (void *)pana_trans);
		} else {
			pana_trans=se_tree_lookup32(pana_info->pdus, seq_num);
			if(pana_trans){
				pana_trans->rep_frame=pinfo->fd->num;
			}
		}
	} else {
		pana_trans=se_tree_lookup32(pana_info->pdus, seq_num);
	}
	if(!pana_trans){
		/* create a "fake" pana_trans structure */
		pana_trans=ep_alloc(sizeof(pana_transaction_t));
		pana_trans->req_frame=0;
		pana_trans->rep_frame=0;
		pana_trans->req_time=pinfo->fd->abs_ts;
	}

	/* print state tracking in the tree */
	if(flags&PANA_FLAG_R){
		/* This is a request */
		if(pana_trans->rep_frame){
			proto_item *it;

			it=proto_tree_add_uint(pana_tree, hf_pana_response_in, tvb, 0, 0, pana_trans->rep_frame);
			PROTO_ITEM_SET_GENERATED(it);
		}
	} else {
		/* This is a reply */
		if(pana_trans->req_frame){
			proto_item *it;
			nstime_t ns;

			it=proto_tree_add_uint(pana_tree, hf_pana_response_to, tvb, 0, 0, pana_trans->req_frame);
			PROTO_ITEM_SET_GENERATED(it);

			nstime_delta(&ns, &pinfo->fd->abs_ts, &pana_trans->req_time);
			it=proto_tree_add_time(pana_tree, hf_pana_time, tvb, 0, 0, &ns);
			PROTO_ITEM_SET_GENERATED(it);
		}
	}              






Then we just need to declare the hf fields we used

	{ &hf_pana_response_in,
		{ "Response In", "pana.response_in",
		FT_FRAMENUM, BASE_DEC, NULL, 0x0,
		"The response to this PANA request is in this frame", HFILL }
	},
	{ &hf_pana_response_to,
		{ "Request In", "pana.response_to",
		FT_FRAMENUM, BASE_DEC, NULL, 0x0,
		"This is a response to the PANA request in this frame", HFILL }
	},
	{ &hf_pana_time,
		{ "Time", "pana.time",
		FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
		"The time between the Call and the Reply", HFILL }
	},