diff options
Diffstat (limited to 'doc/README.tapping')
-rw-r--r-- | doc/README.tapping | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/doc/README.tapping b/doc/README.tapping new file mode 100644 index 00000000..4fcb6389 --- /dev/null +++ b/doc/README.tapping @@ -0,0 +1,244 @@ +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 +tap-rpcprogs.c (tshark version) +ui/qt/rpc_service_response_time_dialog.cpp (wireshark 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 +=== +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 = -1;' + +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 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), + 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-rpcstat.c use 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 + + 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 + + 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. |