forked from osmocom/wireshark
7ab0cfa8d6
svn path=/trunk/; revision=32496
1190 lines
42 KiB
XML
1190 lines
42 KiB
XML
<!-- WSDG Chapter Dissection -->
|
|
<!-- $Id$ -->
|
|
|
|
<chapter id="ChapterDissection">
|
|
<title>Packet dissection</title>
|
|
<!-- Julian Onions additions -->
|
|
<section id="ChDissectWorks">
|
|
<title>How it works</title>
|
|
<para>
|
|
Each dissector decodes its part of the protocol, and then hands off
|
|
decoding to subsequent dissectors for an encapsulated protocol.
|
|
</para>
|
|
<para>
|
|
So it might all start with a Frame dissector which dissects the packet details
|
|
of the capture file itself (e.g. timestamps), passes the data on to an
|
|
Ethernet frame dissector that decodes the Ethernet header,
|
|
and then passes the payload to the next dissector (e.g. IP) and so on.
|
|
At each stage, details of the packet will be decoded and displayed.
|
|
</para>
|
|
<para>
|
|
Dissection can be implemented in two possible ways. One is to have a dissector
|
|
module compiled into the main program, which means it's always available.
|
|
Another way is to make a plugin (a shared library/DLL) that registers itself
|
|
to handle dissection.
|
|
</para>
|
|
<para>
|
|
There is little difference in having your dissector as either a plugin
|
|
or built-in. On the Win32 platform you have limited function access
|
|
through what's listed in libwireshark.def, but that is mostly complete.
|
|
</para>
|
|
<para>
|
|
The big plus is that your rebuild cycle for a plugin is much shorter
|
|
than for a built-in one. So starting with a plugin makes initial development
|
|
simpler, while deployment of the finished code may well be done as built-in
|
|
dissector.
|
|
</para>
|
|
<note><title>See also README.developer</title>
|
|
<para>
|
|
The file <filename>doc/README.developer</filename> contains much detailed information about
|
|
implementing a dissector (and may, in some cases, be more up-to-date than this document).
|
|
</para>
|
|
</note>
|
|
</section>
|
|
|
|
<section id="ChDissectAdd">
|
|
<title>Adding a basic dissector</title>
|
|
<para>
|
|
Let's step through adding a basic dissector. We'll start with the made up
|
|
"foo" protocol. It consists of the following basic items.
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem><para>
|
|
A packet type - 8 bits, possible values: 1 - initialisation, 2 - terminate, 3 - data.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
A set of flags stored in 8 bits, 0x01 - start packet, 0x02 - end packet, 0x04 - priority packet.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
A sequence number - 16 bits.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
An IP address.
|
|
</para></listitem>
|
|
</itemizedlist>
|
|
<section id="ChDissectSetup">
|
|
<title>Setting up the dissector</title>
|
|
<para>
|
|
The first decision you need to make is if this dissector will be a
|
|
built-in dissector, included in the main program, or a plugin.
|
|
</para>
|
|
<para>
|
|
Plugins are the easiest to write initially, so let's start with that.
|
|
With a little care, the plugin can be made to run as a built-in
|
|
easily too - so we haven't lost anything.
|
|
</para>
|
|
<example><title>Dissector Initialisation.</title>
|
|
<programlisting>
|
|
<![CDATA[#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <epan/packet.h>
|
|
|
|
#define FOO_PORT 1234
|
|
|
|
static int proto_foo = -1;
|
|
|
|
|
|
void
|
|
proto_register_foo(void)
|
|
{
|
|
proto_foo = proto_register_protocol (
|
|
"FOO Protocol", /* name */
|
|
"FOO", /* short name */
|
|
"foo" /* abbrev */
|
|
);
|
|
}]]>
|
|
</programlisting></example>
|
|
<para>
|
|
Let's go through this a bit at a time. First we have some boilerplate
|
|
include files. These will be pretty constant to start with.
|
|
</para>
|
|
<para>
|
|
Next we have an int that is initialised to -1 that records our protocol.
|
|
This will get updated when we register this dissector with the main program.
|
|
It's good practice to make all variables and functions that aren't exported
|
|
static to keep name space pollution down. Normally this isn't a problem unless your
|
|
dissector gets so big it has to span multiple files.
|
|
</para>
|
|
<para>
|
|
Then a #define for the UDP port that we'll assume we are dissecting traffic for.
|
|
</para>
|
|
<para>
|
|
Now that we have the basics in place to interact with the main program, we'll
|
|
start with two protocol dissector setup functions.
|
|
|
|
</para>
|
|
<para>
|
|
First we'll call the proto_register_protocol function which registers the protocol.
|
|
We can give it three names that will be used for display in various places.
|
|
The full and short name are used in e.g. the "Preferences" and "Enabled protocols"
|
|
dialogs as well as the generated field name list in the documentation.
|
|
The abbreviation is used as the display filter name.
|
|
</para>
|
|
<para>
|
|
Next we need a handoff routine.
|
|
</para>
|
|
<example><title>Dissector Handoff.</title>
|
|
<programlisting>
|
|
<![CDATA[void
|
|
proto_reg_handoff_foo(void)
|
|
{
|
|
static dissector_handle_t foo_handle;
|
|
|
|
foo_handle = create_dissector_handle(dissect_foo, proto_foo);
|
|
dissector_add("udp.port", FOO_PORT, foo_handle);
|
|
}]]>
|
|
</programlisting></example>
|
|
<para>
|
|
What's happening here? We are initialising the dissector.
|
|
First we create a dissector handle; It is associated with the foo protocol and
|
|
with a routine to be called to do the actual dissecting.
|
|
Then we associate the handle with a UDP port number
|
|
so that the main program will know to call us when it gets UDP traffic on that port.
|
|
</para>
|
|
<para>
|
|
The stardard Wireshark dissector convention is to put proto_register_foo and
|
|
proto_reg_handoff_foo as the last two functions in the dissector source.
|
|
</para>
|
|
<para>
|
|
Now at last we get to write some dissecting code. For the moment we'll
|
|
leave it as a basic placeholder.
|
|
</para>
|
|
<example><title>Dissection.</title>
|
|
<programlisting>
|
|
<![CDATA[static void
|
|
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
|
|
/* Clear out stuff in the info column */
|
|
col_clear(pinfo->cinfo,COL_INFO);
|
|
}]]>
|
|
</programlisting></example>
|
|
<para>
|
|
This function is called to dissect the packets presented to it.
|
|
The packet data is held in a special buffer referenced here as tvb.
|
|
We shall become fairly familiar with this as we get deeper into the details
|
|
of the protocol.
|
|
The packet info structure contains general data about the protocol, and we
|
|
can update information here.
|
|
The tree parameter is where the detail dissection takes place.
|
|
</para>
|
|
<para>
|
|
For now we'll do the minimum we can get away with.
|
|
The first two lines check to see if the Protocol column is being displayed in
|
|
the UI. If it is, we set the text of this to our protocol, so everyone
|
|
can see it's been recognised.
|
|
The only other thing we do is to clear out any data in the INFO column
|
|
if it's being displayed.
|
|
</para>
|
|
<para>
|
|
At this point we should have a basic dissector ready to compile and install.
|
|
It doesn't do much at present, other than identify the protocol and label it.
|
|
</para>
|
|
<para>
|
|
In order to compile this dissector and create a plugin a couple of support files
|
|
are required, besides the dissector source in packet-foo.c:
|
|
<itemizedlist>
|
|
<listitem><para>
|
|
Makefile.am - This is the UNIX/Linux makefile template
|
|
</para></listitem>
|
|
<listitem><para>
|
|
Makefile.common - This contains the file names of this plugin
|
|
</para></listitem>
|
|
<listitem><para>
|
|
Makefile.nmake - This contains the Wireshark plugin makefile for Windows
|
|
</para></listitem>
|
|
<listitem><para>
|
|
moduleinfo.h - This contains plugin version info
|
|
</para></listitem>
|
|
<listitem><para>
|
|
moduleinfo.nmake - This contains DLL version info for Windows
|
|
</para></listitem>
|
|
<listitem><para>
|
|
packet-foo.c - This is your dissector source
|
|
</para></listitem>
|
|
<listitem><para>
|
|
plugin.rc.in - This contains the DLL resource template for Windows
|
|
</para></listitem>
|
|
</itemizedlist>
|
|
You can find a good example for these files in the agentx plugin directory. Makefile.common
|
|
and Makefile.am have to be modified to reflect the relevant files and dissector name.
|
|
moduldeinfo.h and moduleinfo.nmake have to be filled in with the version information.
|
|
Compile the dissector to a DLL or shared library and copy it into the plugin
|
|
directory of the installation.
|
|
</para>
|
|
</section>
|
|
|
|
<section id="ChDissectDetails">
|
|
<title>Dissecting the details of the protocol</title>
|
|
<para>
|
|
Now that we have our basic dissector up and running, let's do something with it.
|
|
The simplest thing to do to start with is to just label the payload.
|
|
This will allow us to set up some of the parts we will need.
|
|
</para>
|
|
<para>
|
|
The first thing we will do is to build a subtree to decode our results into.
|
|
This helps to keep things looking nice in the detailed display.
|
|
Now the dissector is called in two different cases. In one case
|
|
it is called to get a summary of the packet, in the other case it is
|
|
called to look into details of the packet. These two cases can be
|
|
distinguished by the tree pointer. If the tree pointer is NULL, then
|
|
we are being asked for a summary. If it is non null, we can pick apart
|
|
the protocol for display. So with that in mind, let's enhance our dissector.
|
|
</para>
|
|
<example><title>Plugin Packet Dissection.</title>
|
|
<programlisting>
|
|
<![CDATA[static void
|
|
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
|
|
/* Clear out stuff in the info column */
|
|
col_clear(pinfo->cinfo,COL_INFO);
|
|
|
|
if (tree) { /* we are being asked for details */
|
|
proto_item *ti = NULL;
|
|
ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, FALSE);
|
|
}
|
|
}]]>
|
|
</programlisting></example>
|
|
<para>
|
|
What we're doing here is adding a subtree to the dissection.
|
|
This subtree will hold all the details of this protocol and so not clutter
|
|
up the display when not required.
|
|
</para>
|
|
<para>
|
|
We are also marking the area of data that is being consumed by this
|
|
protocol. In our case it's all that has been passed to us, as we're assuming
|
|
this protocol does not encapsulate another.
|
|
Therefore, we add the new tree node with proto_tree_add_item, adding
|
|
it to the passed in tree, label it with the protocol, use the passed in
|
|
tvb buffer as the data, and consume from 0 to the end (-1) of this data.
|
|
The FALSE we'll ignore for now.
|
|
</para>
|
|
<para>
|
|
After this change, there should be a label in the detailed display for the protocol,
|
|
and selecting this will highlight the remaining contents of the packet.
|
|
</para>
|
|
<para>
|
|
Now let's go to the next step and add some protocol dissection.
|
|
For this step we'll need to construct a couple of tables that help with dissection.
|
|
This needs some additions to the proto_register_foo function shown previously.
|
|
</para>
|
|
<para>
|
|
Two statically allocated arrays are added at the beginning of proto_register_foo. The
|
|
arrays are then registered after the call to proto_register_protocol.
|
|
</para>
|
|
<example><title>Registering data structures.</title>
|
|
<programlisting>
|
|
<![CDATA[void
|
|
proto_register_foo(void)
|
|
{
|
|
static hf_register_info hf[] = {
|
|
{ &hf_foo_pdu_type,
|
|
{ "FOO PDU Type", "foo.type",
|
|
FT_UINT8, BASE_DEC,
|
|
NULL, 0x0,
|
|
NULL, HFILL }
|
|
}
|
|
};
|
|
|
|
/* Setup protocol subtree array */
|
|
static gint *ett[] = {
|
|
&ett_foo
|
|
};
|
|
|
|
proto_foo = proto_register_protocol (
|
|
"FOO Protocol", /* name */
|
|
"FOO", /* short name */
|
|
"foo" /* abbrev */
|
|
);
|
|
|
|
proto_register_field_array(proto_foo, hf, array_length(hf));
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
}]]>
|
|
</programlisting></example>
|
|
<para>
|
|
The variables hf_foo_pdu_type and ett_foo also need to be declared
|
|
somewhere near the top of the file.
|
|
</para>
|
|
<example><title>Dissector data structure globals.</title>
|
|
<programlisting>
|
|
<![CDATA[static int hf_foo_pdu_type = -1;
|
|
|
|
static gint ett_foo = -1;]]>
|
|
</programlisting></example>
|
|
<para>
|
|
Now we can enhance the protocol display with some detail.
|
|
</para>
|
|
<example><title>Dissector starting to dissect the packets.</title>
|
|
<programlisting>
|
|
<![CDATA[ if (tree) { /* we are being asked for details */
|
|
proto_item *ti = NULL;
|
|
proto_tree *foo_tree = NULL;
|
|
|
|
ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, FALSE);
|
|
foo_tree = proto_item_add_subtree(ti, ett_foo);
|
|
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, 0, 1, FALSE);
|
|
}]]>
|
|
</programlisting></example>
|
|
<para>
|
|
Now the dissection is starting to look more interesting. We have picked apart
|
|
our first bit of the protocol. One byte of data at the start of the packet
|
|
that defines the packet type for foo protocol.
|
|
</para>
|
|
<para>
|
|
The proto_item_add_subtree call has added a child node to the protocol tree
|
|
which is where we will do our detail dissection. The expansion of this
|
|
node is controlled by the ett_foo variable. This remembers if the node should
|
|
be expanded or not as you move between packets.
|
|
All subsequent dissection will be added to this tree, as you can see from the next call.
|
|
A call to proto_tree_add_item in the foo_tree, this time using the
|
|
hf_foo_pdu_type to control the formatting of the item. The pdu type
|
|
is one byte of data, starting at 0. We assume it is in network order,
|
|
so that is why we use FALSE. Although for 1 byte there is no order issue
|
|
it's best to keep this correct.
|
|
</para>
|
|
<para>
|
|
If we look in detail at the hf_foo_pdu_type declaration in the static array
|
|
we can see the details of the definition.
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem><para>
|
|
hf_foo_pdu_type - the index for this node.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
FOO PDU Type - the label for this item.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
foo.type - this is the filter string. It enables us to type constructs such
|
|
as foo.type=1 into the filter box.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
FT_UNIT8 - this specifies this item is an 8bit unsigned integer.
|
|
This tallies with our call above where we tell it to only look at one byte.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
BASE_DEC - for an integer type, this tells it to be printed as a decimal
|
|
number. It could be BASE_HEX or BASE_OCT if that made more sense.
|
|
</para></listitem>
|
|
</itemizedlist>
|
|
<para>
|
|
We'll ignore the rest of the structure for now.
|
|
</para>
|
|
<para>
|
|
If you install this plugin and try it out, you'll see something that begins to look
|
|
useful.
|
|
</para>
|
|
<para>
|
|
Now let's finish off dissecting the simple protocol. We need to add a few
|
|
more variables to the hf array, and a couple more procedure calls.
|
|
</para>
|
|
<example><title>Wrapping up the packet dissection.</title>
|
|
<programlisting>
|
|
<![CDATA[...
|
|
static int hf_foo_flags = -1;
|
|
static int hf_foo_sequenceno = -1;
|
|
static int hf_foo_initialip = -1;
|
|
...
|
|
|
|
static void
|
|
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
gint offset = 0;
|
|
|
|
...
|
|
|
|
if (tree) { /* we are being asked for details */
|
|
proto_item *ti = NULL;
|
|
proto_tree *foo_tree = NULL;
|
|
|
|
ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, FALSE);
|
|
foo_tree = proto_item_add_subtree(ti, ett_foo);
|
|
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, FALSE); offset += 1;
|
|
proto_tree_add_item(foo_tree, hf_foo_flags, tvb, offset, 1, FALSE); offset += 1;
|
|
proto_tree_add_item(foo_tree, hf_foo_sequenceno, tvb, offset, 2, FALSE); offset += 2;
|
|
proto_tree_add_item(foo_tree, hf_foo_initialip, tvb, offset, 4, FALSE); offset += 4;
|
|
}
|
|
...
|
|
}
|
|
|
|
void
|
|
proto_register_foo(void) {
|
|
...
|
|
...
|
|
{ &hf_foo_flags,
|
|
{ "FOO PDU Flags", "foo.flags",
|
|
FT_UINT8, BASE_HEX,
|
|
NULL, 0x0,
|
|
NULL, HFILL }
|
|
},
|
|
{ &hf_foo_sequenceno,
|
|
{ "FOO PDU Sequence Number", "foo.seqn",
|
|
FT_UINT16, BASE_DEC,
|
|
NULL, 0x0,
|
|
NULL, HFILL }
|
|
},
|
|
{ &hf_foo_initialip,
|
|
{ "FOO PDU Initial IP", "foo.initialip",
|
|
FT_IPv4, BASE_NONE,
|
|
NULL, 0x0,
|
|
NULL, HFILL }
|
|
},
|
|
...
|
|
...
|
|
}
|
|
...]]>
|
|
</programlisting></example>
|
|
<para>
|
|
This dissects all the bits of this simple hypothetical protocol. We've introduced a new
|
|
variable offset into the mix to help keep track of where we are in the packet dissection.
|
|
With these extra bits in place, the whole protocol is now dissected.
|
|
</para>
|
|
</section>
|
|
<section><title>Improving the dissection information</title>
|
|
<para>
|
|
We can certainly improve the display of the protocol with a bit of extra data.
|
|
The first step is to add some text labels. Let's start by labeling the packet types.
|
|
There is some useful support for this sort of thing by adding a couple of extra things.
|
|
First we add a simple table of type to name.
|
|
</para>
|
|
<example><title>Naming the packet types.</title>
|
|
<programlisting>
|
|
<![CDATA[static const value_string packettypenames[] = {
|
|
{ 1, "Initialise" },
|
|
{ 2, "Terminate" },
|
|
{ 3, "Data" },
|
|
{ 0, NULL }
|
|
};]]>
|
|
</programlisting></example>
|
|
<para>
|
|
This is a handy data structure that can be used to look up a name for a value.
|
|
There are routines to directly access this lookup table, but we don't need to
|
|
do that, as the support code already has that added in. We just have to give
|
|
these details to the appropriate part of the data, using the VALS macro.
|
|
</para>
|
|
<example><title>Adding Names to the protocol.</title>
|
|
<programlisting>
|
|
<![CDATA[ { &hf_foo_pdu_type,
|
|
{ "FOO PDU Type", "foo.type",
|
|
FT_UINT8, BASE_DEC,
|
|
VALS(packettypenames), 0x0,
|
|
NULL, HFILL }
|
|
}]]>
|
|
</programlisting></example>
|
|
<para>
|
|
This helps in deciphering the packets, and we can do a similar thing for the
|
|
flags structure. For this we need to add some more data to the table though.
|
|
</para>
|
|
<example><title>Adding Flags to the protocol.</title>
|
|
<programlisting>
|
|
<![CDATA[#define FOO_START_FLAG 0x01
|
|
#define FOO_END_FLAG 0x02
|
|
#define FOO_PRIORITY_FLAG 0x04
|
|
|
|
static int hf_foo_startflag = -1;
|
|
static int hf_foo_endflag = -1;
|
|
static int hf_foo_priorityflag = -1;
|
|
|
|
static void
|
|
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
...
|
|
...
|
|
proto_tree_add_item(foo_tree, hf_foo_flags, tvb, offset, 1, FALSE);
|
|
proto_tree_add_item(foo_tree, hf_foo_startflag, tvb, offset, 1, FALSE);
|
|
proto_tree_add_item(foo_tree, hf_foo_endflag, tvb, offset, 1, FALSE);
|
|
proto_tree_add_item(foo_tree, hf_foo_priorityflag, tvb, offset, 1, FALSE); offset += 1;
|
|
...
|
|
...
|
|
}
|
|
|
|
void
|
|
proto_register_foo(void) {
|
|
...
|
|
...
|
|
{ &hf_foo_startflag,
|
|
{ "FOO PDU Start Flags", "foo.flags.start",
|
|
FT_BOOLEAN, 8,
|
|
NULL, FOO_START_FLAG,
|
|
NULL, HFILL }
|
|
},
|
|
{ &hf_foo_endflag,
|
|
{ "FOO PDU End Flags", "foo.flags.end",
|
|
FT_BOOLEAN, 8,
|
|
NULL, FOO_END_FLAG,
|
|
NULL, HFILL }
|
|
},
|
|
{ &hf_foo_priorityflag,
|
|
{ "FOO PDU Priority Flags", "foo.flags.priority",
|
|
FT_BOOLEAN, 8,
|
|
NULL, FOO_PRIORITY_FLAG,
|
|
NULL, HFILL }
|
|
},
|
|
...
|
|
...
|
|
}
|
|
...]]>
|
|
</programlisting></example>
|
|
<para>
|
|
Some things to note here. For the flags, as each bit is a different flag, we use
|
|
the type FT_BOOLEAN, as the flag is either on or off. Second, we include the flag
|
|
mask in the 7th field of the data, which allows the system to mask the relevant bit.
|
|
We've also changed the 5th field to 8, to indicate that we are looking at an 8 bit
|
|
quantity when the flags are extracted. Then finally we add the extra constructs
|
|
to the dissection routine. Note we keep the same offset for each of the flags.
|
|
</para>
|
|
<para>
|
|
This is starting to look fairly full featured now, but there are a couple of other
|
|
things we can do to make things look even more pretty. At the moment our dissection
|
|
shows the packets as "Foo Protocol" which whilst correct is a little uninformative.
|
|
We can enhance this by adding a little more detail.
|
|
First, let's get hold of the actual value of the protocol type. We can use the handy
|
|
function tvb_get_guint8 to do this.
|
|
With this value in hand, there are a couple of things we can do.
|
|
First we can set the INFO column of the non-detailed view to show what sort of
|
|
PDU it is - which is extremely helpful when looking at protocol traces.
|
|
Second, we can also display this information in the dissection window.
|
|
</para>
|
|
<example><title>Enhancing the display.</title>
|
|
<programlisting>
|
|
<![CDATA[static void
|
|
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
guint8 packet_type = tvb_get_guint8(tvb, 0);
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
|
|
/* Clear out stuff in the info column */
|
|
col_clear(pinfo->cinfo,COL_INFO);
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "Type %s",
|
|
val_to_str(packet_type, packettypenames, "Unknown (0x%02x)"));
|
|
|
|
if (tree) { /* we are being asked for details */
|
|
proto_item *ti = NULL;
|
|
proto_tree *foo_tree = NULL;
|
|
gint offset = 0;
|
|
|
|
ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, FALSE);
|
|
proto_item_append_text(ti, ", Type %s",
|
|
val_to_str(packet_type, packettypenames, "Unknown (0x%02x)"));
|
|
foo_tree = proto_item_add_subtree(ti, ett_foo);
|
|
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, FALSE);
|
|
offset += 1;
|
|
}
|
|
}]]>
|
|
</programlisting></example>
|
|
<para>
|
|
So here, after grabbing the value of the first 8 bits, we use it with one of the
|
|
built-in utility routines val_to_str, to lookup the value. If the value isn't
|
|
found we provide a fallback which just prints the value in hex.
|
|
We use this twice, once in the INFO field of the columns - if it's displayed, and
|
|
similarly we append this data to the base of our dissecting tree.
|
|
</para>
|
|
|
|
</section>
|
|
</section>
|
|
|
|
<section id="ChDissectTransformed">
|
|
<title>How to handle transformed data</title>
|
|
<para>
|
|
Some protocols do clever things with data. They might possibly
|
|
encrypt the data, or compress data, or part of it. If you know
|
|
how these steps are taken it is possible to reverse them within the
|
|
dissector.
|
|
</para>
|
|
<para>
|
|
As encryption can be tricky, let's consider the case of compression.
|
|
These techniques can also work for other transformations of data,
|
|
where some step is required before the data can be examined.
|
|
</para>
|
|
<para>
|
|
What basically needs to happen here, is to identify the data that
|
|
needs conversion, take that data and transform it into a new stream,
|
|
and then call a dissector on it.
|
|
Often this needs to be done "on-the-fly" based on clues in the packet.
|
|
Sometimes this needs to be used in conjunction with other techniques,
|
|
such as packet reassembly. The following shows a technique to
|
|
achieve this effect.
|
|
</para>
|
|
<example><title>Decompressing data packets for dissection.</title>
|
|
<programlisting>
|
|
<![CDATA[
|
|
guint8 flags = tvb_get_guint8(tvb, offset);
|
|
offset ++;
|
|
if (flags & FLAG_COMPRESSED) { /* the remainder of the packet is compressed */
|
|
guint16 orig_size = tvb_get_ntohs(tvb, offset);
|
|
guchar *decompressed_buffer = (guchar*)g_malloc(orig_size);
|
|
offset += 2;
|
|
decompress_packet(tvb_get_ptr(tvb, offset, -1),
|
|
tvb_length_remaining(tvb, offset),
|
|
decompressed_buffer, orig_size);
|
|
/* Now re-setup the tvb buffer to have the new data */
|
|
next_tvb = tvb_new_real_data(decompressed_buffer, orig_size, orig_size);
|
|
tvb_set_child_real_data_tvbuff(tvb, next_tvb);
|
|
add_new_data_source(pinfo, next_tvb, "Decompressed Data");
|
|
} else {
|
|
next_tvb = tvb_new_subset(tvb, offset, -1, -1);
|
|
}
|
|
offset = 0;
|
|
/* process next_tvb from here on */
|
|
]]>
|
|
</programlisting></example>
|
|
<para>
|
|
The first steps here are to recognise the compression. In this case
|
|
a flag byte alerts us to the fact the remainder of the packet is compressed.
|
|
Next we retrieve the original size of the packet, which in this case
|
|
is conveniently within the protocol. If it's not, it may be part of the
|
|
compression routine to work it out for you, in which case the logic would
|
|
be different.
|
|
</para>
|
|
<para>
|
|
So armed with the size, a buffer is allocated to receive the uncompressed
|
|
data using g_malloc, and the packet is decompressed into it.
|
|
The tvb_get_ptr function is useful to get a pointer to the raw data of
|
|
the packet from the offset onwards. In this case the
|
|
decompression routine also needs to know the length, which is
|
|
given by the tvb_length_remaining function.
|
|
</para>
|
|
<para>
|
|
Next we build a new tvb buffer from this data, using the tvb_new_real_data
|
|
call. This data is a child of our original data, so we acknowledge that
|
|
in the next call to tvb_set_child_real_data_tvbuff.
|
|
Finally we add this data as a new data source, so that
|
|
the detailed display can show the decompressed bytes as well as the original.
|
|
One procedural step is to add a handler to free the data when it's no longer needed.
|
|
In this case as g_malloc was used to allocate the memory, g_free is the appropriate
|
|
function.
|
|
</para>
|
|
<para>
|
|
After this has been set up the remainder of the dissector can dissect the
|
|
buffer next_tvb, as it's a new buffer the offset needs to be 0 as we start
|
|
again from the beginning of this buffer. To make the rest of the dissector
|
|
work regardless of whether compression was involved or not, in the case that
|
|
compression was not signaled, we use the tvb_new_subset to deliver us
|
|
a new buffer based on the old one but starting at the current offset, and
|
|
extending to the end. This makes dissecting the packet from this point on
|
|
exactly the same regardless of compression.
|
|
</para>
|
|
</section>
|
|
<section id="ChDissectReassemble">
|
|
<title>How to reassemble split packets</title>
|
|
<para>
|
|
Some protocols have times when they have to split a large packet across
|
|
multiple other packets. In this case the dissection can't be carried out correctly
|
|
until you have all the data. The first packet doesn't have enough data,
|
|
and the subsequent packets don't have the expect format.
|
|
To dissect these packets you need to wait until all the parts have
|
|
arrived and then start the dissection.
|
|
</para>
|
|
<section id="ChDissectReassembleUdp">
|
|
<title>How to reassemble split UDP packets</title>
|
|
<para>
|
|
As an example, let's examine a protocol that is layered on
|
|
top of UDP that splits up its own data stream.
|
|
If a packet is bigger than some given size, it will be split into
|
|
chunks, and somehow signaled within its protocol.
|
|
</para>
|
|
<para>
|
|
To deal with such streams, we need several things to trigger
|
|
from. We need to know that this packet is part of a multi-packet
|
|
sequence. We need to know how many packets are in the sequence.
|
|
We also need to know when we have all the packets.
|
|
</para>
|
|
<para>
|
|
For this example we'll assume there is a simple in-protocol
|
|
signaling mechanism to give details. A flag byte that signals
|
|
the presence of a multi-packet sequence and also the last packet,
|
|
followed by an ID of the sequence and a packet sequence number.
|
|
</para>
|
|
<programlisting>
|
|
<![CDATA[msg_pkt ::= SEQUENCE {
|
|
.....
|
|
flags ::= SEQUENCE {
|
|
fragment BOOLEAN,
|
|
last_fragment BOOLEAN,
|
|
.....
|
|
}
|
|
msg_id INTEGER(0..65535),
|
|
frag_id INTEGER(0..65535),
|
|
.....
|
|
}]]>
|
|
</programlisting>
|
|
<example><title>Reassembling fragments - Part 1</title>
|
|
<programlisting>
|
|
<![CDATA[#include <epan/reassemble.h>
|
|
...
|
|
save_fragmented = pinfo->fragmented;
|
|
flags = tvb_get_guint8(tvb, offset); offset++;
|
|
if (flags & FL_FRAGMENT) { /* fragmented */
|
|
tvbuff_t* new_tvb = NULL;
|
|
fragment_data *frag_msg = NULL;
|
|
guint16 msg_seqid = tvb_get_ntohs(tvb, offset); offset += 2;
|
|
guint16 msg_num = tvb_get_ntohs(tvb, offset); offset += 2;
|
|
|
|
pinfo->fragmented = TRUE;
|
|
frag_msg = fragment_add_seq_check(tvb, offset, pinfo,
|
|
msg_seqid, /* ID for fragments belonging together */
|
|
msg_fragment_table, /* list of message fragments */
|
|
msg_reassembled_table, /* list of reassembled messages */
|
|
msg_num, /* fragment sequence number */
|
|
tvb_length_remaining(tvb, offset), /* fragment length - to the end */
|
|
flags & FL_FRAG_LAST); /* More fragments? */]]>
|
|
</programlisting></example>
|
|
<para>
|
|
We start by saving the fragmented state of this packet, so we can restore it later.
|
|
Next comes some protocol specific stuff, to dig the fragment data
|
|
out of the stream if it's present. Having decided it is present, we
|
|
let the function fragment_add_seq_check do its work.
|
|
We need to provide this with a certain amount of data.
|
|
</para>
|
|
<itemizedlist>
|
|
<listitem><para>
|
|
The tvb buffer we are dissecting.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
The offset where the partial packet starts.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
The provided packet info.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
The sequence number of the fragment stream. There may be several
|
|
streams of fragments in flight, and this is used to key the
|
|
relevant one to be used for reassembly.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
The msg_fragment_table and the msg_reassembled_table are variables
|
|
we need to declare. We'll consider these in detail later.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
msg_num is the packet number within the sequence.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
The length here is specified as the rest of the tvb as we want the rest of the packet data.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
Finally a parameter that signals if this is the last fragment or not.
|
|
This might be a flag as in this case, or there may be a counter in the
|
|
protocol.
|
|
</para></listitem>
|
|
</itemizedlist>
|
|
<example><title>Reassembling fragments part 2</title>
|
|
<programlisting>
|
|
<![CDATA[
|
|
new_tvb = process_reassembled_data(tvb, offset, pinfo,
|
|
"Reassembled Message", frag_msg, &msg_frag_items,
|
|
NULL, msg_tree);
|
|
|
|
if (frag_msg) { /* Reassembled */
|
|
col_append_str(pinfo->cinfo, COL_INFO,
|
|
" (Message Reassembled)");
|
|
} else { /* Not last packet of reassembled Short Message */
|
|
col_append_fstr(pinfo->cinfo, COL_INFO,
|
|
" (Message fragment %u)", msg_num);
|
|
}
|
|
|
|
if (new_tvb) { /* take it all */
|
|
next_tvb = new_tvb;
|
|
} else { /* make a new subset */
|
|
next_tvb = tvb_new_subset(tvb, offset, -1, -1);
|
|
}
|
|
}
|
|
else { /* Not fragmented */
|
|
next_tvb = tvb_new_subset(tvb, offset, -1, -1);
|
|
}
|
|
|
|
.....
|
|
pinfo->fragmented = save_fragmented;
|
|
]]>
|
|
</programlisting></example>
|
|
<para>
|
|
Having passed the fragment data to the reassembly handler, we can
|
|
now check if we have the whole message. If there is enough information,
|
|
this routine will return the newly reassembled data buffer.
|
|
</para>
|
|
<para>
|
|
After that, we add a couple of informative messages to the display
|
|
to show that this is part of a sequence. Then a bit of manipulation
|
|
of the buffers and the dissection can proceed.
|
|
Normally you will probably not bother dissecting further unless the
|
|
fragments have been reassembled as there won't be much to find. Sometimes
|
|
the first packet in the sequence can be partially decoded though if you wish.
|
|
</para>
|
|
<para>
|
|
Now the mysterious data we passed into the fragment_add_seq_check.
|
|
</para>
|
|
<example><title>Reassembling fragments - Initialisation</title>
|
|
<programlisting>
|
|
<![CDATA[static GHashTable *msg_fragment_table = NULL;
|
|
static GHashTable *msg_reassembled_table = NULL;
|
|
|
|
static void
|
|
msg_init_protocol(void)
|
|
{
|
|
fragment_table_init(&msg_fragment_table);
|
|
reassembled_table_init(&msg_reassembled_table);
|
|
}]]>
|
|
</programlisting></example>
|
|
<para>
|
|
First a couple of hash tables are declared, and these are initialised
|
|
in the protocol initialisation routine.
|
|
Following that, a fragment_items structure is allocated and filled
|
|
in with a series of ett items, hf data items, and a string tag.
|
|
The ett and hf values should be included in the relevant tables like
|
|
all the other variables your protocol may use. The hf variables
|
|
need to be placed in the structure something like the following.
|
|
Of course the names may need to be adjusted.
|
|
</para>
|
|
<example><title>Reassembling fragments - Data</title>
|
|
<programlisting>
|
|
<![CDATA[...
|
|
static int hf_msg_fragments = -1;
|
|
static int hf_msg_fragment = -1;
|
|
static int hf_msg_fragment_overlap = -1;
|
|
static int hf_msg_fragment_overlap_conflicts = -1;
|
|
static int hf_msg_fragment_multiple_tails = -1;
|
|
static int hf_msg_fragment_too_long_fragment = -1;
|
|
static int hf_msg_fragment_error = -1;
|
|
static int hf_msg_reassembled_in = -1;
|
|
static int hf_msg_reassembled_length = -1;
|
|
...
|
|
static gint ett_msg_fragment = -1;
|
|
static gint ett_msg_fragments = -1;
|
|
...
|
|
static const fragment_items msg_frag_items = {
|
|
/* Fragment subtrees */
|
|
&ett_msg_fragment,
|
|
&ett_msg_fragments,
|
|
/* Fragment fields */
|
|
&hf_msg_fragments,
|
|
&hf_msg_fragment,
|
|
&hf_msg_fragment_overlap,
|
|
&hf_msg_fragment_overlap_conflicts,
|
|
&hf_msg_fragment_multiple_tails,
|
|
&hf_msg_fragment_too_long_fragment,
|
|
&hf_msg_fragment_error,
|
|
/* Reassembled in field */
|
|
&hf_msg_reassembled_in,
|
|
/* Reassembled length field */
|
|
&hf_msg_reassembled_length,
|
|
/* Tag */
|
|
"Message fragments"
|
|
};
|
|
...
|
|
static hf_register_info hf[] =
|
|
{
|
|
...
|
|
{&hf_msg_fragments,
|
|
{"Message fragments", "msg.fragments",
|
|
FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
|
{&hf_msg_fragment,
|
|
{"Message fragment", "msg.fragment",
|
|
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
|
{&hf_msg_fragment_overlap,
|
|
{"Message fragment overlap", "msg.fragment.overlap",
|
|
FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
|
{&hf_msg_fragment_overlap_conflicts,
|
|
{"Message fragment overlapping with conflicting data",
|
|
"msg.fragment.overlap.conflicts",
|
|
FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
|
{&hf_msg_fragment_multiple_tails,
|
|
{"Message has multiple tail fragments",
|
|
"msg.fragment.multiple_tails",
|
|
FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
|
{&hf_msg_fragment_too_long_fragment,
|
|
{"Message fragment too long", "msg.fragment.too_long_fragment",
|
|
FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
|
{&hf_msg_fragment_error,
|
|
{"Message defragmentation error", "msg.fragment.error",
|
|
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
|
{&hf_msg_reassembled_in,
|
|
{"Reassembled in", "msg.reassembled.in",
|
|
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
|
{&hf_msg_reassembled_length,
|
|
{"Reassembled length", "msg.reassembled.length",
|
|
FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
|
|
...
|
|
static gint *ett[] =
|
|
{
|
|
...
|
|
&ett_msg_fragment,
|
|
&ett_msg_fragments
|
|
...]]>
|
|
</programlisting></example>
|
|
<para>
|
|
These hf variables are used internally within the reassembly routines
|
|
to make useful links, and to add data to the dissection. It produces
|
|
links from one packet to another - such as a partial packet having
|
|
a link to the fully reassembled packet. Likewise there are back pointers
|
|
to the individual packets from the reassembled one.
|
|
The other variables are used for flagging up errors.
|
|
</para>
|
|
</section>
|
|
<section id="TcpDissectPdus">
|
|
<title>How to reassemble split TCP Packets</title>
|
|
<para>
|
|
A dissector gets a tvbuff_t pointer which holds the payload
|
|
of a TCP packet. This payload contains the header and data
|
|
of your application layer protocol.
|
|
</para>
|
|
<para>
|
|
When dissecting an application layer protocol you cannot assume
|
|
that each TCP packet contains exactly one application layer message.
|
|
One application layer message can be split into several TCP packets.
|
|
</para>
|
|
<para>
|
|
You also cannot assume that a TCP packet contains only one application layer message
|
|
and that the message header is at the start of your TCP payload.
|
|
More than one messages can be transmitted in one TCP packet,
|
|
so that a message can start at an arbitrary position.
|
|
|
|
</para>
|
|
<para>
|
|
This sounds complicated, but there is a simple solution.
|
|
<methodname>tcp_dissect_pdus()</methodname> does all this tcp packet reassembling for you.
|
|
This function is implemented in <filename>epan/dissectors/packet-tcp.h</filename>.
|
|
</para>
|
|
<example>
|
|
<title>Reassembling TCP fragments</title>
|
|
<programlisting>
|
|
<![CDATA[#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <epan/packet.h>
|
|
#include <epan/prefs.h>
|
|
#include "packet-tcp.h"
|
|
|
|
...
|
|
|
|
#define FRAME_HEADER_LEN 8
|
|
|
|
/* The main dissecting routine */
|
|
static void dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN,
|
|
get_foo_message_len, dissect_foo_message);
|
|
}
|
|
|
|
/* This method dissects fully reassembled messages */
|
|
static void dissect_foo_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
/* TODO: implement your dissecting code */
|
|
}
|
|
|
|
/* determine PDU length of protocol foo */
|
|
static guint get_foo_message_len(packet_info *pinfo, tvbuff_t *tvb, int offset)
|
|
{
|
|
/* TODO: change this to your needs */
|
|
return (guint)tvb_get_ntohl(tvb, offset+4); /* e.g. length is at offset 4 */
|
|
}
|
|
|
|
...]]>
|
|
</programlisting>
|
|
</example>
|
|
<para>
|
|
As you can see this is really simple. Just call <function>tcp_dissect_pdus()</function> in
|
|
your main dissection routine and move you message parsing code into another function.
|
|
This function gets called whenever a message has been reassembled.
|
|
</para>
|
|
<para>
|
|
The parameters <parameter>tvb</parameter>, <parameter>pinfo</parameter> and <parameter>tree</parameter>
|
|
are just handed over to <function>tcp_dissect_pdus()</function>.
|
|
The 4th parameter is a flag to indicate if the data should be reassembled or not. This could be set
|
|
according to a dissector preference as well.
|
|
Parameter 5 indicates how much data has at least to be available to be able to determine the length
|
|
of the foo message.
|
|
Parameter 6 is a function pointer to a method that returns this length. It gets called when at least
|
|
the number of bytes given in the previous parameter is available.
|
|
Parameter 7 is a function pointer to your real message dissector.
|
|
</para>
|
|
</section>
|
|
</section>
|
|
<section id="ChDissectTap">
|
|
<title>How to tap protocols</title>
|
|
<para>
|
|
Adding a Tap interface to a protocol allows it to do some useful things.
|
|
In particular you can produce protocol statistics from the tap interface.
|
|
</para>
|
|
<para>
|
|
A tap is basically a way of allowing other items to see whats happening as
|
|
a protocol is dissected. A tap is registered with the main program, and
|
|
then called on each dissection. Some arbitrary protocol specific data
|
|
is provided with the routine that can be used.
|
|
</para>
|
|
<para>
|
|
To create a tap, you first need to register a tap.
|
|
A tap is registered with an integer handle, and registered
|
|
with the routine register_tap. This takes a string name
|
|
with which to find it again.
|
|
</para>
|
|
<example><title>Initialising a tap</title>
|
|
<programlisting>
|
|
<![CDATA[#include <epan/packet.h>
|
|
#include <epan/tap.h>
|
|
|
|
static int foo_tap = -1;
|
|
|
|
struct FooTap {
|
|
gint packet_type;
|
|
gint priority;
|
|
...
|
|
};
|
|
|
|
void proto_register_foo(void)
|
|
{
|
|
...
|
|
foo_tap = register_tap("foo");]]>
|
|
</programlisting></example>
|
|
<para>
|
|
Whilst you can program a tap without protocol specific data, it
|
|
is generally not very useful. Therefore it's a good idea
|
|
to declare a structure that can be passed through the tap.
|
|
This needs to be a static structure as it will be used after the
|
|
dissection routine has returned. It's generally best to pick out some
|
|
generic parts of the protocol you are dissecting into the tap data.
|
|
A packet type, a priority or a status code maybe.
|
|
The structure really needs to be included in a header file so
|
|
that it can be included by other components that want to listen in
|
|
to the tap.
|
|
</para>
|
|
<para>
|
|
Once you have these defined, it's simply a case of populating the
|
|
protocol specific structure and then calling tap_queue_packet, probably
|
|
as the last part of the dissector.
|
|
</para>
|
|
<example><title>Calling a protocol tap</title>
|
|
<programlisting>
|
|
<![CDATA[
|
|
void dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
...
|
|
fooinfo = ep_alloc(sizeof(struct FooTap));
|
|
fooinfo->packet_type = tvb_get_guint8(tvb, 0);
|
|
fooinfo->priority = tvb_get_ntohs(tvb, 8);
|
|
...
|
|
tap_queue_packet(foo_tap, pinfo, fooinfo);
|
|
}
|
|
]]>
|
|
</programlisting></example>
|
|
<para>
|
|
This now enables those interested parties to listen in on the details
|
|
of this protocol conversation.
|
|
</para>
|
|
</section>
|
|
<section id="ChDissectStats">
|
|
<title>How to produce protocol stats</title>
|
|
<para>
|
|
Given that you have a tap interface for the protocol, you can use this
|
|
to produce some interesting statistics (well presumably interesting!) from
|
|
protocol traces.
|
|
</para>
|
|
<para>
|
|
This can be done in a separate plugin, or in the same plugin that is
|
|
doing the dissection. The latter scheme is better, as the tap and stats
|
|
module typically rely on sharing protocol specific data, which might get out
|
|
of step between two different plugins.
|
|
</para>
|
|
<para>
|
|
Here is a mechanism to produce statistics from the above TAP interface.
|
|
</para>
|
|
<example><title>Initialising a stats interface</title>
|
|
<programlisting>
|
|
<![CDATA[/* register all http trees */
|
|
static void register_foo_stat_trees(void) {
|
|
stats_tree_register("foo", "foo", "Foo/Packet Types",
|
|
foo_stats_tree_packet, foo_stats_tree_init, NULL);
|
|
}
|
|
|
|
G_MODULE_EXPORT const gchar version[] = "0.0";
|
|
|
|
G_MODULE_EXPORT void plugin_register_tap_listener(void)
|
|
{
|
|
register_foo_stat_trees();
|
|
}
|
|
|
|
#endif]]>
|
|
</programlisting></example>
|
|
<para>
|
|
Working from the bottom up, first the plugin interface entry point is defined,
|
|
plugin_register_tap_listener. This simply calls the initialisation function
|
|
register_foo_stat_trees.
|
|
</para>
|
|
<para>
|
|
This in turn calls the stats_tree_register function, which takes
|
|
three strings, and three functions.
|
|
</para>
|
|
<orderedlist>
|
|
<listitem><para>
|
|
This is the tap name that is registered.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
An abbreviation of the stats name.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
The name of the stats module. A '/' character can be used to make sub menus.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
The function that will called to generate the stats.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
A function that can be called to initialise the stats data.
|
|
</para></listitem>
|
|
<listitem><para>
|
|
A function that will be called to clean up the stats data.
|
|
</para></listitem>
|
|
</orderedlist>
|
|
<para>
|
|
In this case we only need the first two functions, as there is nothing specific to clean up.
|
|
</para>
|
|
<example><title>Initialising a stats session</title>
|
|
<programlisting>
|
|
<![CDATA[static const guint8* st_str_packets = "Total Packets";
|
|
static const guint8* st_str_packet_types = "FOO Packet Types";
|
|
static int st_node_packets = -1;
|
|
static int st_node_packet_types = -1;
|
|
|
|
static void foo_stats_tree_init(stats_tree* st)
|
|
{
|
|
st_node_packets = stats_tree_create_node(st, st_str_packets, 0, TRUE);
|
|
st_node_packet_types = stats_tree_create_pivot(st, st_str_packet_types, st_node_packets);
|
|
}]]>
|
|
</programlisting></example>
|
|
<para>
|
|
In this case we create a new tree node, to handle the total packets,
|
|
and as a child of that we create a pivot table to handle the stats about
|
|
different packet types.
|
|
</para>
|
|
<example><title>Generating the stats</title>
|
|
<programlisting>
|
|
<![CDATA[static int foo_stats_tree_packet(stats_tree* st, packet_info* pinfo, epan_dissect_t* edt, const void* p)
|
|
{
|
|
struct FooTap *pi = (struct FooTap *)p;
|
|
tick_stat_node(st, st_str_packets, 0, FALSE);
|
|
stats_tree_tick_pivot(st, st_node_packet_types,
|
|
val_to_str(pi->packet_type, msgtypevalues, "Unknown packet type (%d)"));
|
|
return 1;
|
|
}]]>
|
|
</programlisting></example>
|
|
<para>
|
|
In this case the processing of the stats is quite simple.
|
|
First we call the tick_stat_node for the st_str_packets packet node, to count
|
|
packets.
|
|
Then a call to stats_tree_tick_pivot on the st_node_packet_types subtree
|
|
allows us to record statistics by packet type.
|
|
</para>
|
|
</section>
|
|
|
|
<section id="ChDissectConversation">
|
|
<title>How to use conversations</title>
|
|
<para>
|
|
Some info about how to use conversations in a dissector can be
|
|
found in the file doc/README.developer chapter 2.2.
|
|
</para>
|
|
</section>
|
|
|
|
</chapter>
|
|
<!-- End of WSDG Chapter Dissection -->
|