nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | The TAP system in Wireshark is a powerful and flexible mechanism to get event |
2 | driven notification on packets matching certain protocols and/or filters. |
||
3 | In order to use the tapping system, very little knowledge of Wireshark |
||
4 | internals are required. |
||
5 | |||
6 | As examples on how to use the tap system see the implementation of |
||
7 | tap-rpcstat.c (tshark version) |
||
8 | gtk/rpc_stat.c (gtk-wireshark version) |
||
9 | |||
10 | If all you need is to keep some counters, there's the stats_tree API, |
||
11 | which offers a simple way to make a GUI and tshark tap-listener; see |
||
12 | README.stats_tree. However, keep reading, as you'll need much of what's |
||
13 | in this document. |
||
14 | |||
15 | The tap system consists of two parts: |
||
16 | 1, code in the actual dissectors to allow tapping data from that particular |
||
17 | protocol dissector, and |
||
18 | 2, event driven code in an extension such as tap-rpcstat.c that registers |
||
19 | a tap listener and processes received data. |
||
20 | |||
21 | |||
22 | |||
23 | So you want to hack together a tap application? |
||
24 | |||
25 | |||
26 | TAP |
||
27 | === |
||
28 | First you must decide which protocol you are interested in writing a tap |
||
29 | application for and check if that protocol has already got a tap installed |
||
30 | in it. |
||
31 | If it already has a tap device installed then you don't have to do anything. |
||
32 | If not, then you have to add a tap but don't worry, this is extremely easy to |
||
33 | do and is done in four easy steps; |
||
34 | (see packet-rpc.c and search for tap for an example) |
||
35 | |||
36 | 1, We need tap.h so just add '#include "tap.h"' (preceded by packet.h) to |
||
37 | the includes. |
||
38 | |||
39 | 2, We need a tap handler so just add 'static int <protocol>_tap = -1;' |
||
40 | |||
41 | 3, Down in proto_register_<protocol>() you need to add |
||
42 | '<protocol>_tap = register_tap("<protocol>");' |
||
43 | |||
44 | 4, In the actual dissector for that protocol, after any child dissectors |
||
45 | have returned, just add 'tap_queue_packet(<protocol>_tap, pinfo, <pointer>);' |
||
46 | |||
47 | <pointer> is used if the tap has any special additional data to provide to the |
||
48 | tap listeners. What this points to is dependent on the protocol that is tapped, |
||
49 | or if there are no useful extra data to provide just specify NULL. For |
||
50 | packet-rpc.c what we specify there is the persistent structure 'rpc_call' which |
||
51 | contains lots of useful information from the rpc layer that a listener might |
||
52 | need. |
||
53 | |||
54 | |||
55 | |||
56 | TAP LISTENER |
||
57 | ============ |
||
58 | (see tap-rpcstat.c as an example) |
||
59 | Interfacing your application is not that much harder either. |
||
60 | Only 3 callbacks and two functions. |
||
61 | |||
62 | |||
63 | The two functions to start or stop tapping are |
||
64 | |||
65 | register_tap_listener(const char *tapname, void *tapdata, const char *fstring, |
||
66 | guint flags, |
||
67 | void (*reset)(void *tapdata), |
||
68 | gboolean (*packet)(void *tapdata, packet_info *pinfo, epan_dissect_t *edt, const void *data), |
||
69 | void (*draw)(void *tapdata)); |
||
70 | |||
71 | remove_tap_listener(void *tapdata); |
||
72 | |||
73 | |||
74 | remove_tap_listener(void *tapdata) |
||
75 | This function is used to deregister and stop a tap listener. |
||
76 | |||
77 | register_tap_listener() is used to register an instance of a tap application |
||
78 | to the tap system. |
||
79 | |||
80 | *tapname |
||
81 | is the name of the tap we want to listen to. I.e. the name used in |
||
82 | step 3 above. |
||
83 | |||
84 | *tapdata |
||
85 | is the instance identifier. The tap system uses the value of this |
||
86 | pointer to distinguish between different instances of a tap. |
||
87 | Just make sure that it is unique by letting it be the pointer to a struct |
||
88 | holding all state variables. If you want to allow multiple concurrent |
||
89 | instances, just put ALL state variables inside a struct allocated by |
||
90 | g_malloc() and use that pointer. |
||
91 | (tap-rpcstat.c use this technique to allow multiple simultaneous instances) |
||
92 | |||
93 | *fstring |
||
94 | is a pointer to a filter string. |
||
95 | If this is NULL, then the tap system will provide ALL packets passing the |
||
96 | tapped protocol to your listener. |
||
97 | If you specify a filter string here the tap system will first try |
||
98 | to apply this string to the packet and then only pass those packets that |
||
99 | matched the filter to your listener. |
||
100 | The syntax for the filter string is identical to normal display filters. |
||
101 | |||
102 | NOTE: Specifying filter strings will have a significant performance impact |
||
103 | on your application and Wireshark. If possible it is MUCH better to take |
||
104 | unfiltered data and just filter it yourself in the packet-callback than |
||
105 | to specify a filter string. |
||
106 | ONLY use a filter string if no other option exist. |
||
107 | |||
108 | flags |
||
109 | is a set of flags for the tap listener. The flags that can be set are: |
||
110 | |||
111 | TL_REQUIRES_PROTO_TREE |
||
112 | |||
113 | set if your tap listener "packet" routine requires a protocol |
||
114 | tree to be built. It will require a protocol tree to be |
||
115 | built if either |
||
116 | |||
117 | 1) it looks at the protocol tree in edt->tree |
||
118 | |||
119 | or |
||
120 | |||
121 | 2) the tap-specific data passed to it is constructed only if |
||
122 | the protocol tree is being built. |
||
123 | |||
124 | TL_REQUIRES_COLUMNS |
||
125 | |||
126 | set if your tap listener "packet" routine requires the column |
||
127 | strings to be constructed. |
||
128 | |||
129 | If no flags are needed, use TL_REQUIRES_NOTHING. |
||
130 | |||
131 | void (*reset)(void *tapdata) |
||
132 | This callback is called whenever Wireshark wants to inform your |
||
133 | listener that it is about to start [re]reading a capture file or a new capture |
||
134 | from an interface and that your application should reset any state it has |
||
135 | in the *tapdata instance. |
||
136 | |||
137 | gboolean (*packet)(void *tapdata, packet_info *pinfo, epan_dissect_t *edt, const void *data) |
||
138 | This callback is used whenever a new packet has arrived at the tap and that |
||
139 | it has passed the filter (if there were a filter). |
||
140 | The *data structure type is specific to each tap. |
||
141 | This function returns an gboolean and it should return |
||
142 | TRUE, if the data in the packet caused state to be updated |
||
143 | (and thus a redraw of the window would later be required) |
||
144 | FALSE, if we don't need to redraw the window. |
||
145 | NOTE: that (*packet) should be as fast and efficient as possible. Use this |
||
146 | function ONLY to store data for later and do the CPU-intensive processing |
||
147 | or GUI updates down in (*draw) instead. |
||
148 | |||
149 | |||
150 | void (*draw)(void *tapdata) |
||
151 | This callback is used when Wireshark wants your application to redraw its |
||
152 | output. It will usually not be called unless your application has received |
||
153 | new data through the (*packet) callback. |
||
154 | On some ports of Wireshark (gtk2) (*draw) will be called asynchronously |
||
155 | from a separate thread up to once every 2-3 seconds. |
||
156 | On other ports it might only be called once when the capture is finished |
||
157 | or the file has been [re]read completely. |
||
158 | |||
159 | |||
160 | |||
161 | So, create three callbacks: |
||
162 | 1, reset to reset the state variables in the structure passed to it. |
||
163 | 2, packet to update these state variables. |
||
164 | 3, draw to take these state variables and draw them on the screen. |
||
165 | |||
166 | then just make Wireshark call register_tap_listener() when you want to tap |
||
167 | and call remove_tap_listener() when you are finished. |
||
168 | |||
169 | |||
170 | WHEN DO TAP LISTENERS GET CALLED? |
||
171 | =================================== |
||
172 | Tap listeners are only called when Wireshark reads a new capture for |
||
173 | the first time or whenever Wireshark needs to rescan/redissect |
||
174 | the capture. |
||
175 | Redissection occurs when you apply a new display filter or if you |
||
176 | change and Save/Apply a preference setting that might affect how |
||
177 | packets are dissected. |
||
178 | After each individual packet has been completely dissected and all |
||
179 | dissectors have returned, all the tap listeners that have been flagged |
||
180 | to receive tap data during the dissection of the frame will be called in |
||
181 | sequence. |
||
182 | The order in which the tap listeners will be called is not defined. |
||
183 | Not until all tap listeners for the frame has been called and returned |
||
184 | will Wireshark continue to dissect the next packet. |
||
185 | This is why it is important to make the *_packet() callbacks execute as |
||
186 | quickly as possible, else we create an extra delay until the next packet |
||
187 | is dissected. |
||
188 | |||
189 | Keep in mind though: for some protocols, such as IP, the protocol can |
||
190 | appear multiple times in different layers inside the same packet. |
||
191 | For example, IP encapsulated over IP which will call the ip dissector |
||
192 | twice for the same packet. |
||
193 | IF the tap is going to return private data using the last parameter to |
||
194 | tap_queue_packet() and IF the protocol can appear multiple times inside the |
||
195 | same packet, you will have to make sure that each instance of |
||
196 | tap_queue_packet() is using its own instance of private struct variable |
||
197 | so they don't overwrite each other. |
||
198 | |||
199 | See packet-ip.c which has a simple solution to the problem. It creates |
||
200 | a unique instance of the IP header using wmem_alloc(). |
||
201 | Previous versions used a static struct of 4 instances of the IP header |
||
202 | struct and cycled through them each time the dissector was called. (4 |
||
203 | was just a number taken out of the blue but it should be enough for most |
||
204 | cases.) This would fail if there were more than 4 IP headers in the same |
||
205 | packet, but that was unlikely. |
||
206 | |||
207 | |||
208 | TIPS |
||
209 | ==== |
||
210 | Of course, there is nothing that forces you to make (*draw) draw stuff |
||
211 | on the screen. |
||
212 | You can hand register_tap_listener() NULL for both (*draw) and (*reset) |
||
213 | (well also for (*packet) but that would be a very boring extension). |
||
214 | |||
215 | Perhaps you want an extension that will execute a certain command |
||
216 | every time it sees a certain packet? |
||
217 | Well, try this : |
||
218 | gboolean packet(void *tapdata,...) { |
||
219 | ... |
||
220 | system("mail ..."); |
||
221 | return FALSE; |
||
222 | } |
||
223 | |||
224 | register_tap_listener("tcp", struct, "tcp.port==57", NULL, packet, NULL); |
||
225 | |||
226 | Let struct contain an email address? |
||
227 | Then you have something simple that will make Wireshark send an email |
||
228 | out automagically for each and every time it dissects |
||
229 | a packet containing TCP traffic to port 57. |
||
230 | Please put in some rate limitation if you do this. |
||
231 | |||
232 | Let struct contain a command line and make (*packet) execute it? |
||
233 | The possibilities are rather large. |
||
234 | |||
235 | |||
236 | |||
237 | See tap-rpcstat.c for an example |
||
238 | See tap.c as well. It contains lots of comments and descriptions on the tap |
||
239 | system. |