Write our NSIS installer to the build directory.
[metze/wireshark/wip.git] / doc / README.request_response_tracking
1 1. Introduction
2
3 It is often useful to enhance dissectors for request/response style protocols
4 to match requests with responses.
5 This allows you to display useful information in the decode tree such as which
6 requests are matched to which response and the response time for individual
7 transactions.
8
9 This is also useful if you want to pass some data from the request onto the
10 dissection of the actual response. The RPC dissector for example does
11 something like this to pass the actual command opcode from the request onto
12 the response dissector since the opcode itself is not part of the response
13 packet and without the opcode we would not know how to decode the data.
14
15 It is also useful when you need to track information on a per conversation
16 basis such as when some parameters are negotiated during a login phase of the
17 protocol and when these parameters affect how future commands on that session
18 are to be decoded. The iSCSI dissector does something similar to that to track
19 which sessions that HeaderDigest is activated for and which ones it is not.
20
21 2. Implementation
22
23 The example below shows how simple this is to add to the dissector IF:
24 1. there is something like a transaction id in the header,
25 2. it is very unlikely that the transaction identifier is reused for the
26    same conversation.
27
28 The example is taken from the PANA dissector:
29
30 First we need to include the definitions for conversations and memory
31 management.
32
33         #include <epan/conversation.h>
34         #include <epan/wmem/wmem.h>
35
36 Then we also need a few header fields to show the relations between request
37 and response as well as the response time.
38
39         static int hf_pana_response_in = -1;
40         static int hf_pana_response_to = -1;
41         static int hf_pana_response_time = -1;
42
43 We need a structure that holds all the information we need to remember
44 between the request and the responses. One such structure will be allocated
45 for each unique transaction.
46 In the example we only keep the frame numbers of the request and the response
47 as well as the timestamp for the request.
48 But since this structure is persistent and also a unique one is allocated for
49 each request/response pair, this is a good place to store other additional
50 data you may want to keep track of from a request to a response.
51
52         typedef struct _pana_transaction_t {
53                 guint32 req_frame;
54                 guint32 rep_frame;
55                 nstime_t req_time;
56         } pana_transaction_t;
57
58 We also need a structure that holds persistent information for each
59 conversation. A conversation is identified by SRC/DST address, protocol and
60 SRC/DST port, see README.dissector, section 2.2.
61 In this case we only want to have a hash table to track the actual
62 transactions that occur for this unique conversation.
63 Some protocols negotiate session parameters during a login phase and those
64 parameters may affect how later commands on the same session is to be decoded,
65 this would be a good place to store that additional info you may want to keep
66 around.
67
68         typedef struct _pana_conv_info_t {
69                 wmem_map_t *pdus;
70         } pana_conv_info_t;
71
72 Finally for the meat of it, add the conversation and tracking code to the
73 actual dissector.
74
75         ...
76         guint32 seq_num;
77         conversation_t *conversation;
78         pana_conv_info_t *pana_info;
79         pana_transaction_t *pana_trans;
80
81         ...
82         /* Get the transaction identifier */
83         seq_num = tvb_get_ntohl(tvb, 8);
84         ...
85
86         /*
87          * We need to track some state for this protocol on a per conversation
88          * basis so we can do neat things like request/response tracking
89          */
90         conversation = find_or_create_conversation(pinfo);
91
92         /*
93          * Do we already have a state structure for this conv
94          */
95         pana_info = (pana_conv_info_t *)conversation_get_proto_data(conversation, proto_pana);
96         if (!pana_info) {
97                 /*
98                  * No.  Attach that information to the conversation, and add
99                  * it to the list of information structures.
100                  */
101                 pana_info = wmem_new(wmem_file_scope(), pana_conv_info_t);
102                 pana_info->pdus=wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
103
104                 conversation_add_proto_data(conversation, proto_pana, pana_info);
105         }
106         if (!PINFO_FD_VISITED(pinfo)) {
107                 if (flags&PANA_FLAG_R) {
108                         /* This is a request */
109                         pana_trans=wmem_new(wmem_file_scope(), pana_transaction_t);
110                         pana_trans->req_frame = pinfo->num;
111                         pana_trans->rep_frame = 0;
112                         pana_trans->req_time = pinfo->fd->abs_ts;
113                         wmem_map_insert(pana_info->pdus, GUINT_TO_POINTER(seq_num), (void *)pana_trans);
114                 } else {
115                         pana_trans=(pana_transaction_t *)wmem_map_lookup(pana_info->pdus, GUINT_TO_POINTER(seq_num));
116                         if (pana_trans) {
117                                 pana_trans->rep_frame = pinfo->num;
118                         }
119                 }
120         } else {
121                 pana_trans=(pana_transaction_t *)wmem_map_lookup(pana_info->pdus, GUINT_TO_POINTER(seq_num));
122         }
123         if (!pana_trans) {
124                 /* create a "fake" pana_trans structure */
125                 pana_trans=wmem_new(wmem_packet_scope(), pana_transaction_t);
126                 pana_trans->req_frame = 0;
127                 pana_trans->rep_frame = 0;
128                 pana_trans->req_time = pinfo->fd->abs_ts;
129         }
130
131         /* print state tracking in the tree */
132         if (flags&PANA_FLAG_R) {
133                 /* This is a request */
134                 if (pana_trans->rep_frame) {
135                         proto_item *it;
136
137                         it = proto_tree_add_uint(pana_tree, hf_pana_response_in,
138                                         tvb, 0, 0, pana_trans->rep_frame);
139                         PROTO_ITEM_SET_GENERATED(it);
140                 }
141         } else {
142                 /* This is a reply */
143                 if (pana_trans->req_frame) {
144                         proto_item *it;
145                         nstime_t ns;
146
147                         it = proto_tree_add_uint(pana_tree, hf_pana_response_to,
148                                         tvb, 0, 0, pana_trans->req_frame);
149                         PROTO_ITEM_SET_GENERATED(it);
150
151                         nstime_delta(&ns, &pinfo->fd->abs_ts, &pana_trans->req_time);
152                         it = proto_tree_add_time(pana_tree, hf_pana_response_time, tvb, 0, 0, &ns);
153                         PROTO_ITEM_SET_GENERATED(it);
154                 }
155         }
156
157 Then we just need to declare the hf fields we used.
158
159         { &hf_pana_response_in,
160                 { "Response In", "pana.response_in",
161                 FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
162                 "The response to this PANA request is in this frame", HFILL }
163         },
164         { &hf_pana_response_to,
165                 { "Request In", "pana.response_to",
166                 FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
167                 "This is a response to the PANA request in this frame", HFILL }
168         },
169         { &hf_pana_response_time,
170                 { "Response Time", "pana.response_time",
171                 FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
172                 "The time between the Call and the Reply", HFILL }
173         },