forked from osmocom/wireshark
from Julian Onions: add a devel section about start writing a dissector
some editing by me ... svn path=/trunk/; revision=14570
This commit is contained in:
parent
fa91e9559d
commit
3acf278bf5
|
@ -7,15 +7,650 @@
|
|||
<section id="ChDissectWorks">
|
||||
<title>How it works</title>
|
||||
<para>
|
||||
XXX - well, how does it work?
|
||||
Each dissector decodes it's part of the protocol, and then hand 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 its always available.
|
||||
Another way is to make a plugin (a shared library/DLL) that registers itself
|
||||
to handle dissection. - XXX add a special explanation section for this?
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ChDissectAdd">
|
||||
<title>Adding a basic dissector</title>
|
||||
<para>
|
||||
The steps to add a new dissector (including some skeleton code) can be
|
||||
found in the file doc/README.developer.
|
||||
Lets 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 as they don't need
|
||||
write permission on the main code base. So lets 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>Basic Plugin setup.</title>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gmodule.h>
|
||||
#include <epan/packet.h>
|
||||
#include <epan/prefs.h>
|
||||
|
||||
/* forward reference */
|
||||
void proto_register_foo();
|
||||
void proto_reg_handoff_foo();
|
||||
void dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
|
||||
|
||||
/* Define version if we are not building ethereal statically */
|
||||
#ifndef ENABLE_STATIC
|
||||
G_MODULE_EXPORT const gchar version[] = "0.0";
|
||||
#endif
|
||||
|
||||
static int proto_foo = -1;
|
||||
static int global_foo_port = 1234;
|
||||
static dissector_handle_t foo_handle;
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
G_MODULE_EXPORT void
|
||||
plugin_register(void)
|
||||
{
|
||||
/* register the new protocol, protocol fields, and subtrees */
|
||||
if (proto_foo == -1) { /* execute protocol initialization only once */
|
||||
proto_register_foo();
|
||||
}
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
plugin_reg_handoff(void){
|
||||
proto_reg_handoff_foo();
|
||||
}
|
||||
#endif]]>
|
||||
</programlisting>
|
||||
</example>
|
||||
<para>
|
||||
Lets go through this a bit at a time. First we have some boiler plate
|
||||
include files. These will be pretty constant to start with. Here we also
|
||||
pre-declare some functions that we'll be writing shortly.
|
||||
</para>
|
||||
<para>
|
||||
Next we have a section surrounded by #ifdef ENABLE_STATIC. This is what
|
||||
makes this a plugin rather than a built in dissector.
|
||||
</para>
|
||||
<para>
|
||||
The version is a simple string that is used to report on the version of this
|
||||
dissector. You should increase this number each time you make changes that you
|
||||
need to keep track of.
|
||||
</para>
|
||||
<para>
|
||||
Next we have an int that is initialised to -1 that records our protocol.
|
||||
This will get updated when we register this plugin with the main program.
|
||||
We can use this as a handy way to detect if we've been initalised yet.
|
||||
Its good practice to make all variables and functions that aren't exported
|
||||
static to keep name space pollution. Normally this isn't a problem unless your
|
||||
dissector gets so big it has to span multiple files.
|
||||
</para>
|
||||
<para>
|
||||
Then a global variable which contains the UDP port that we'll assume we are dissecting traffic for.
|
||||
</para>
|
||||
<para>
|
||||
Next a dissector reference that we'll initialise later.
|
||||
</para>
|
||||
<para>
|
||||
Next, the first plugin entry point. The function plugin_register() is called
|
||||
when the plugin is loaded and allows you to do some initialisation stuff,
|
||||
which will include communicating with the main program what you're plugins
|
||||
capabilities are.
|
||||
</para>
|
||||
<para>
|
||||
The plugin_reg_handoff routine is used when dissecting sub protocols. As our
|
||||
hyperthetical protocol will be hyperthetically carried over UDP then we will
|
||||
need to do this.
|
||||
</para>
|
||||
<para>
|
||||
Now we have the basics in place to interact with the main program, we had
|
||||
better fill in those missing functions. Lets start with register function.
|
||||
</para>
|
||||
<example><title>Plugin Initialisation.</title>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
void
|
||||
proto_register_foo(void)
|
||||
{
|
||||
module_t *foo_module;
|
||||
|
||||
if (proto_foo == -1) {
|
||||
proto_foo = proto_register_protocol (
|
||||
"FOO Protocol", /* name */
|
||||
"FOO", /* short name */
|
||||
"foo" /* abbrev */
|
||||
);
|
||||
}
|
||||
foo_module = prefs_register_protocol(proto_foo, proto_reg_handoff_foo);
|
||||
}]]>
|
||||
</programlisting></example>
|
||||
<para>
|
||||
First a call to proto_register_protocol that
|
||||
registers the protocol. We can give it three names that
|
||||
will be used in various places to display it.
|
||||
- XXX explain where, this can be confusing
|
||||
</para>
|
||||
<para>
|
||||
Then we call the preference register function. At the moment we have
|
||||
no specific protocol preferences so this will be all that we need.
|
||||
This takes a function parameter which is our handoff function.
|
||||
I guess we'd better write that next.
|
||||
</para>
|
||||
<example><title>Plugin Handoff.</title>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
void
|
||||
proto_reg_handoff_foo(void)
|
||||
{
|
||||
static int Initialized=FALSE;
|
||||
|
||||
if (!Initialized) {
|
||||
foo_handle = create_dissector_handle(dissect_foo, proto_foo);
|
||||
dissector_add("udp.port", global_foo_port, foo_handle);
|
||||
}
|
||||
}]]>
|
||||
</programlisting></example>
|
||||
<para>
|
||||
Whats happening here? We are initialising the dissector if it hasn't
|
||||
been initialised yet.
|
||||
First we create the dissector. This registers a routine
|
||||
to be called to do the actual dissecting.
|
||||
Then we associate it 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>
|
||||
Now at last we finaly get to write some dissecting code. For the moment we'll
|
||||
leave it as a basic placeholder.
|
||||
</para>
|
||||
<example><title>Plugin Dissection.</title>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
static void
|
||||
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
||||
{
|
||||
|
||||
if (check_col(pinfo->cinfo, COL_PROTOCOL))
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
|
||||
/* Clear out stuff in the info column */
|
||||
if(check_col(pinfo->cinfo,COL_INFO)){
|
||||
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 its been recognised.
|
||||
The only other thing we do is to clear out any data in the INFO column
|
||||
if its 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, than identify the protcol and label it.
|
||||
Compile the dissector to a dll or shared library, and copy it into the plugin
|
||||
directory of the installation. To finish this off a Makefile of some sort will be
|
||||
required. A Makefile.nmake for Windows platforms and a Makefile.am for unix/linux
|
||||
types.
|
||||
</para>
|
||||
|
||||
<example><title>Makefile.nmake for Windows.</title>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
include ..\..\config.nmake
|
||||
|
||||
############### no need to modify below this line #########
|
||||
|
||||
CFLAGS=/DHAVE_CONFIG_H /I../.. /I../../wiretap $(GLIB_CFLAGS) \
|
||||
/I$(PCAP_DIR)\include -D_U_="" $(LOCAL_CFLAGS)
|
||||
|
||||
LDFLAGS = /NOLOGO /INCREMENTAL:no /MACHINE:I386 $(LOCAL_LDFLAGS)
|
||||
|
||||
!IFDEF ENABLE_LIBETHEREAL
|
||||
LINK_PLUGIN_WITH=..\..\epan\libethereal.lib
|
||||
CFLAGS=/DHAVE_WIN32_LIBETHEREAL_LIB /D_NEED_VAR_IMPORT_ $(CFLAGS)
|
||||
|
||||
OBJECTS=foo.obj
|
||||
|
||||
foo.dll foo.exp foo.lib : $(OBJECTS) $(LINK_PLUGIN_WITH)
|
||||
link -dll /out:foo.dll $(LDFLAGS) $(OBJECTS) $(LINK_PLUGIN_WITH) \
|
||||
$(GLIB_LIBS)
|
||||
|
||||
!ENDIF
|
||||
|
||||
clean:
|
||||
rm -f $(OBJECTS) foo.dll foo.exp foo.lib *.pdb
|
||||
|
||||
distclean: clean
|
||||
|
||||
maintainer-clean: distclean]]>
|
||||
</programlisting></example>
|
||||
<example><title>Makefile.am for unix/linux.</title>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
INCLUDES = -I$(top_srcdir)
|
||||
|
||||
plugindir = @plugindir@
|
||||
|
||||
plugin_LTLIBRARIES = foo.la
|
||||
foo_la_SOURCES = foo.c moduleinfo.h
|
||||
foo_la_LDFLAGS = -module -avoid-version
|
||||
foo_la_LIBADD = @PLUGIN_LIBS@
|
||||
|
||||
# Libs must be cleared, or else libtool won't create a shared module.
|
||||
# If your module needs to be linked against any particular libraries,
|
||||
# add them here.
|
||||
LIBS =
|
||||
|
||||
CLEANFILES = \
|
||||
foo \
|
||||
*~
|
||||
|
||||
EXTRA_DIST = \
|
||||
Makefile.nmake
|
||||
]]>
|
||||
</programlisting></example>
|
||||
</section>
|
||||
|
||||
<section id="ChDissectDetails">
|
||||
<title>Dissecting the details of the protocol</title>
|
||||
<para>
|
||||
Now we have our basic dissector up and running, lets 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 thig 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, lets 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)
|
||||
{
|
||||
|
||||
if (check_col(pinfo->cinfo, COL_PROTOCOL))
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
|
||||
/* Clear out stuff in the info column */
|
||||
if(check_col(pinfo->cinfo,COL_INFO)){
|
||||
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 cases its 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 lets go to the next step and add some protocol disection.
|
||||
For this step we'll need to construct a couple of tables that help with dissection.
|
||||
This needs some changes to proto_register_foo. First a couple of statically
|
||||
declare arrays.
|
||||
</para>
|
||||
<example><title>Plugin Registering data structures.</title>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
static hf_register_info hf[] = {
|
||||
{ &hf_foo_pdu_type,
|
||||
{ "FOO PDU Type", "foo.type",
|
||||
FT_UINT8, BASE_DEC, NULL, 0x0,
|
||||
"", HFILL }
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* Setup protocol subtree array */
|
||||
static gint *ett[] = {
|
||||
&ett_foo,
|
||||
};
|
||||
]]>
|
||||
</programlisting></example>
|
||||
<para>
|
||||
Then, after the registration code, we register these arrays.
|
||||
</para>
|
||||
<example><title>Plugin Registering data structures.</title>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
|
||||
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>Plugin 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>Plugin 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 issues
|
||||
its best to keep right.
|
||||
</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 lets 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>Plugin 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;
|
||||
...
|
||||
{ &hf_foo_flags,
|
||||
{ "FOO PDU Flags", "foo.flags",
|
||||
FT_UINT8, BASE_HEX, NULL, 0x0,
|
||||
"", HFILL }
|
||||
},
|
||||
{ &hf_foo_sequenceno,
|
||||
{ "FOO PDU Sequence Number", "foo.seqn",
|
||||
FT_UINT16, BASE_DEC, NULL, 0x0,
|
||||
"", HFILL }
|
||||
},
|
||||
{ &hf_foo_initialip,
|
||||
{ "FOO PDU Initial IP", "foo.initialip",
|
||||
FT_IPv4, BASE_NONE, NULL, 0x0,
|
||||
"", HFILL }
|
||||
},
|
||||
...
|
||||
gint offset = 0;
|
||||
|
||||
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;
|
||||
|
||||
]]>
|
||||
</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 protcol with a bit of extra data.
|
||||
The first step is to add some text labels. Lets start by labelling 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 value to names.
|
||||
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 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,
|
||||
"", 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;
|
||||
...
|
||||
{ &hf_foo_startflag,
|
||||
{ "FOO PDU Start Flags", "foo.flags.start",
|
||||
FT_BOOLEAN, 8, NULL, FOO_START_FLAG,
|
||||
"", HFILL }
|
||||
},
|
||||
{ &hf_foo_endflag,
|
||||
{ "FOO PDU End Flags", "foo.flags.end",
|
||||
FT_BOOLEAN, 8, NULL, FOO_END_FLAG,
|
||||
"", HFILL }
|
||||
},
|
||||
{ &hf_foo_priorityflag,
|
||||
{ "FOO PDU Priority Flags", "foo.flags.priority",
|
||||
FT_BOOLEAN, 8, NULL, FOO_PRIORITY_FLAG,
|
||||
"", HFILL }
|
||||
},
|
||||
...
|
||||
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;
|
||||
]]>
|
||||
</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 whislt correct is a little uninformative.
|
||||
We can enhance this by adding a little more detail.
|
||||
First, lets 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>
|
||||
static void
|
||||
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
||||
{
|
||||
guint8 packet_type = tvb_get_guint8(tvb, 0);
|
||||
|
||||
if (check_col(pinfo->cinfo, COL_PROTOCOL))
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
|
||||
/* Clear out stuff in the info column */
|
||||
if(check_col(pinfo->cinfo,COL_INFO)){
|
||||
col_clear(pinfo->cinfo,COL_INFO);
|
||||
}
|
||||
if (check_col(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 its displayed, and
|
||||
similarly we append this data to the base of our dissecting tree.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
<section id="ChDissectTap">
|
||||
<title>How to tap protocols</title>
|
||||
<para>
|
||||
Some info about how to use conversations in a dissector can be
|
||||
found in the file doc/README.tapping.
|
||||
</para>
|
||||
</section>
|
||||
<section id="ChDissectStats">
|
||||
<title>How to produce protocol stats</title>
|
||||
<para>
|
||||
Some info about how to use conversations in a dissector can be
|
||||
found in the file doc/README.stats_tree.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
|
Loading…
Reference in New Issue