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
|
The TAP system in Wireshark is a powerful and flexible mechanism to get event
driven notification on packets matching certain protocols and/or filters.
In order to use the tapping system, very little knowledge of Wireshark
internals is required.
As examples on how to use the tap system see the implementation of
ui/cli/tap-rpcprogs.c (tshark version)
If all you need is to keep some counters, there's the stats_tree API,
which offers a simple way to make a GUI and tshark tap-listener; see
README.stats_tree. However, keep reading, as you'll need much of what's
in this document.
The tap system consists of two parts:
1, code in the actual dissectors to allow tapping data from that particular
protocol dissector, and
2, event driven code in an extension such as tap-rpcstat.c that registers
a tap listener and processes received data.
So you want to hack together a tap application?
TAP
===
(see epan/dissectors/packet-dcerpc.c as an example)
First you must decide which protocol you are interested in writing a tap
application for and check if that protocol has already got a tap installed
in it.
If it already has a tap device installed then you don't have to do anything.
If not, then you have to add a tap but don't worry, this is extremely easy to
do and is done in four easy steps;
(see packet-rpc.c and search for tap for an example)
1, We need tap.h so just add '#include <epan/tap.h>' (preceded by packet.h) to
the includes.
2, We need a tap handler so just add 'static int <protocol>_tap;'
3, Down in proto_register_<protocol>() you need to add
'<protocol>_tap = register_tap("<protocol>");'
4, In the actual dissector for that protocol, after any child dissectors
have returned, just add 'tap_queue_packet(<protocol>_tap, pinfo, <pointer>);'
<pointer> is used if the tap has any special additional data to provide to the
tap listeners. What this points to is dependent on the protocol that is tapped,
or if there is no useful extra data to provide, just specify NULL. For
packet-rpc.c what we specify there is the persistent structure 'rpc_call' which
contains lots of useful information from the rpc layer that a listener might
need.
TAP LISTENER
============
(see ui/cli/tap-rpcprogs.c as an example)
Interfacing your application is not that much harder either.
Only 4 callbacks and two functions.
The two functions to start or stop tapping are
register_tap_listener(const char *tapname, void *tapdata, const char *fstring,
unsigned flags,
void (*reset)(void *tapdata),
tap_packet_status (*packet)(void *tapdata, packet_info *pinfo, epan_dissect_t *edt, const void *data, tap_flags_t flags),
void (*draw)(void *tapdata),
void (*finish)(void *tapdata));
This function is used to register an instance of a tap application
to the tap system.
remove_tap_listener(void *tapdata);
This function is used to deregister and stop a tap listener.
The parameters have the following meanings:
*tapname
is the name of the tap we want to listen to. I.e. the name used in
step 3 above.
*tapdata
is the instance identifier. The tap system uses the value of this
pointer to distinguish between different instances of a tap.
Just make sure that it is unique by letting it be the pointer to a struct
holding all state variables. If you want to allow multiple concurrent
instances, just put ALL state variables inside a struct allocated by
g_new() and use that pointer.
(tap-rpcprogs.c uses this technique to allow multiple simultaneous instances)
*fstring
is a pointer to a filter string.
If this is NULL, then the tap system will provide ALL packets passing the
tapped protocol to your listener.
If you specify a filter string here the tap system will first try
to apply this string to the packet and then only pass those packets that
matched the filter to your listener.
The syntax for the filter string is identical to normal display filters.
NOTE: Specifying filter strings will have a significant performance impact
on your application and Wireshark. If possible it is MUCH better to take
unfiltered data and just filter it yourself in the packet-callback than
to specify a filter string.
ONLY use a filter string if no other option exist.
flags
is a set of flags for the tap listener. The flags that can be set are:
TL_REQUIRES_PROTO_TREE
set if your tap listener "packet" routine requires a protocol
tree to be built. It will require a protocol tree to be
built if either
1) it looks at the protocol tree in edt->tree (N.B.: this
flag does *NOT* guarantee that the tree contains all fields
actually in the packet. Fields that are not referenced
[by a filter, by custom columns, etc.] will be "faked" and
not present. It is not necessary to include this flag if
the tap has a filter string, though, as filtering implies
needing the tree. So this case is rare. Actually making
all fields present in the tree requires a visible tree,
e.g. via epan_set_always_visible(), which hurts performance.)
or
2) the tap-specific data passed to it is constructed only if
the protocol tree is being built.
TL_REQUIRES_COLUMNS
set if your tap listener "packet" routine requires the column
strings to be constructed.
TL_REQUIRES_ERROR_PACKET
set if your tap listener should be updated even when pinfo->flags.in_error_pkt is set
e.g. if it is inside an ICMP unreachable packet.
TL_REQUIRES_PROTOCOLS
set if your tap listener requires that the protocol tree be built
and that all FT_PROTOCOL fields are actually present in the tree
and not faked. This is used by the protocol hierarchy statistics.
This implies needing a protocol tree, as the option doesn't make
sense otherwise.
TL_IGNORE_DISPLAY_FILTER
set if your tap listener wants to be updated with all packets, even ones
that do not pass the filter. Instead, packets that fail the filter will
have another flag set, TL_DISPLAY_FILTER_IGNORED, for the tap listener
to inspect.
TL_LIMIT_TO_DISPLAY_FILTER
set if your tap listener should only be updated with packets that pass
the main program display filter. (This can be in addition to setting a
per-tap filter.) This is useful if the tap should be automatically
updated when the main display filter changes.
If no flags are needed, use TL_REQUIRES_NOTHING.
void (*reset)(void *tapdata)
This callback is called whenever Wireshark wants to inform your
listener that it is about to start [re]reading a capture file or a new capture
from an interface and that your application should reset any state it has
in the *tapdata instance.
tap_packet_status (*packet)(void *tapdata, packet_info *pinfo, epan_dissect_t *edt, const void *data)
This callback is used whenever a new packet has arrived at the tap and that
it has passed the filter (if there was a filter).
The *data structure type is specific to each tap.
This function returns a tap_packet_status enum and it should return
TAP_PACKET_REDRAW, if the data in the packet caused state to be updated
(and thus a redraw of the window would later be required)
TAP_PACKET_DONT_REDRAW, if we don't need to redraw the window
TAP_PACKET_FAILED, if the tap failed and shouldn't be called again
in this pass (for example, if it's writing to a file and gets
an I/O error)
NOTE: that (*packet) should be as fast and efficient as possible. Use this
function ONLY to store data for later and do the CPU-intensive processing
or GUI updates down in (*draw) instead.
void (*draw)(void *tapdata)
This callback is used when Wireshark wants your application to redraw its
output. It will usually not be called unless your application has received
new data through the (*packet) callback.
On some ports of Wireshark (Qt) (*draw) will be called asynchronously
from a separate thread up to once every 2-3 seconds.
On other ports it might only be called once when the capture is finished
or the file has been [re]read completely.
void (*finish)(void *tapdata)
This callback is called when your listener is removed.
So, create four callbacks:
1, reset to reset the state variables in the structure passed to it.
2, packet to update these state variables.
3, draw to take these state variables and draw them on the screen.
4, finish to free all state variables.
then just make Wireshark call register_tap_listener() when you want to tap
and call remove_tap_listener() when you are finished.
WHEN DO TAP LISTENERS GET CALLED?
===================================
Tap listeners are only called when Wireshark reads a new capture for
the first time or whenever Wireshark needs to rescan/redissect
the capture.
Redissection occurs when you apply a new display filter or if you
change and Save/Apply a preference setting that might affect how
packets are dissected.
After each individual packet has been completely dissected and all
dissectors have returned, all the tap listeners that have been flagged
to receive tap data during the dissection of the frame will be called in
sequence.
The order in which the tap listeners will be called is not defined.
Not until all tap listeners for the frame has been called and returned
will Wireshark continue to dissect the next packet.
This is why it is important to make the *_packet() callbacks execute as
quickly as possible, else we create an extra delay until the next packet
is dissected.
Keep in mind though: for some protocols, such as IP, the protocol can
appear multiple times in different layers inside the same packet.
For example, IP encapsulated over IP which will call the ip dissector
twice for the same packet.
IF the tap is going to return private data using the last parameter to
tap_queue_packet() and IF the protocol can appear multiple times inside the
same packet, you will have to make sure that each instance of
tap_queue_packet() is using its own instance of private struct variable
so they don't overwrite each other.
See packet-ip.c which has a simple solution to the problem. It creates
a unique instance of the IP header using wmem_alloc().
Previous versions used a static struct of 4 instances of the IP header
struct and cycled through them each time the dissector was called. (4
was just a number taken out of the blue but it should be enough for most
cases.) This would fail if there were more than 4 IP headers in the same
packet, but that was unlikely.
TIPS
====
Of course, there is nothing that forces you to make (*draw) draw stuff
on the screen.
You can hand register_tap_listener() NULL for (*draw), (*reset) and (*finish)
(well also for (*packet) but that would be a very boring extension).
Perhaps you want an extension that will execute a certain command
every time it sees a certain packet?
Well, try this :
tap_packet_status packet(void *tapdata,...) {
...
system("mail ...");
return TAP_PACKET_DONT_REDRAW;
}
register_tap_listener("tcp", struct, "tcp.port==57", NULL, packet, NULL, NULL);
Let struct contain an email address?
Then you have something simple that will make Wireshark send an email
out automagically for each and every time it dissects
a packet containing TCP traffic to port 57.
Please put in some rate limitation if you do this.
Let struct contain a command line and make (*packet) execute it?
The possibilities are rather large.
See tap.c as well. It contains lots of comments and descriptions on the tap
system.
|