forked from osmocom/wireshark
1499 lines
49 KiB
Plaintext
1499 lines
49 KiB
Plaintext
// WSDG Chapter Dissection
|
||
|
||
[#ChapterDissection]
|
||
|
||
== Packet Dissection
|
||
|
||
[#ChDissectWorks]
|
||
|
||
=== How packet dissection works
|
||
|
||
Each dissector decodes its part of the protocol and then hands off
|
||
decoding to subsequent dissectors for an encapsulated protocol.
|
||
|
||
Every dissection starts with the Frame dissector which dissects the
|
||
details of the capture file itself (e.g. timestamps). From there it passes the
|
||
data on to the lowest-level data dissector, e.g. the Ethernet dissector for
|
||
the Ethernet header. The payload is then passed on to the next dissector (e.g.
|
||
IP) and so on. At each stage, details of the packet are decoded and
|
||
displayed.
|
||
|
||
Dissectors can either be built-in to Wireshark or written as a self-registering
|
||
plugin (a shared library or DLL).
|
||
There is little difference in having your dissector as either a plugin
|
||
or built-in. You have limited function access through the ABI exposed
|
||
by functions declared as WS_DLL_PUBLIC.
|
||
|
||
The big benefit of writing a dissector as a plugin is that rebuilding
|
||
a plugin is much faster than rebuilding wireshark after editing a built-in
|
||
dissector.
|
||
As such, starting with a plugin often makes initial development quicker, while
|
||
the finished code may make more sense as a built-in dissector.
|
||
|
||
[NOTE]
|
||
.Read README.dissector
|
||
====
|
||
The file _doc/README.dissector_ contains detailed information about writing
|
||
a dissector. In many cases it is more up to date than this document.
|
||
====
|
||
|
||
[#ChDissectAdd]
|
||
|
||
=== Adding a basic dissector
|
||
|
||
Let’s step through adding a basic dissector. We'll start with the made up "foo"
|
||
protocol. It consists of the following basic items.
|
||
|
||
* A packet type - 8 bits. Possible values: 1 - initialisation, 2 - terminate, 3 - data.
|
||
|
||
* A set of flags stored in 8 bits. 0x01 - start packet, 0x02 - end packet, 0x04 - priority packet.
|
||
|
||
* A sequence number - 16 bits.
|
||
|
||
* An IPv4 address.
|
||
|
||
[#ChDissectSetup]
|
||
|
||
==== Setting up the dissector
|
||
|
||
The first decision you need to make is if this dissector will be a
|
||
built-in dissector and included in the main program, or a plugin.
|
||
|
||
Plugins are easier to write initially, so let’s start with that.
|
||
With a little care, the plugin can be converted into a built-in dissector.
|
||
|
||
.Dissector Initialisation.
|
||
[source,c]
|
||
----
|
||
#include "config.h"
|
||
#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" /* filter_name */
|
||
);
|
||
}
|
||
----
|
||
|
||
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.
|
||
|
||
Then a `#define` for the UDP port that carries _foo_ traffic.
|
||
|
||
Next we have `proto_foo`, an int that stores our protocol handle and is
|
||
initialised to `-1`.
|
||
This handle will be set when the dissector is registered within the main program.
|
||
It’s good practice to make all variables and functions that aren't exported
|
||
static to minimize name space pollution. This normally isn't a problem unless your
|
||
dissector gets so big that it spans multiple files.
|
||
|
||
Now that we have the basics in place to interact with the main program, we'll
|
||
start with two protocol dissector setup functions: `proto_register_XXX` and
|
||
`proto_reg_handoff_XXX`.
|
||
|
||
Each protocol must have a register function with the form "proto_register_XXX".
|
||
This function is used to register the protocol in Wireshark.
|
||
The code to call the register routines is generated automatically and is
|
||
called when Wireshark starts. In this example, the function is named
|
||
`proto_register_foo`.
|
||
|
||
`proto_register_foo` calls `proto_register_protocol()`, which takes a `name`,
|
||
`short name`, and `filter_name`. The
|
||
name and short name are used in the "Preferences" and "Enabled protocols"
|
||
dialogs and the documentation's generated field name list. The
|
||
`filter_name` is used as the display filter name. `proto_register_protocol()`
|
||
returns a protocol handle, which can be used to refer to the protocol and
|
||
obtain a handle to the protocol's dissector.
|
||
|
||
|
||
Next we need a handoff routine.
|
||
|
||
.Dissector Handoff.
|
||
[source,c]
|
||
----
|
||
void
|
||
proto_reg_handoff_foo(void)
|
||
{
|
||
static dissector_handle_t foo_handle;
|
||
|
||
foo_handle = create_dissector_handle(dissect_foo, proto_foo);
|
||
dissector_add_uint("udp.port", FOO_PORT, foo_handle);
|
||
}
|
||
----
|
||
|
||
A handoff routine associates a protocol handler with the protocol's
|
||
traffic. It consists of two major steps: The first step is to create a
|
||
dissector handle, which is a handle associated with the protocol and the
|
||
function called to do the actual dissecting.
|
||
The second step is to register the dissector handle so that traffic
|
||
associated with the protocol calls the dissector.
|
||
|
||
In this example, `proto_reg_handoff_foo()` calls `create_dissector_handle()`
|
||
to obtain a dissector handle for the foo protocol. It then uses
|
||
`dissector_add_uint()` to associate traffic on UDP port FOO_PORT (1234)
|
||
with the foo protocol, so that Wireshark will call `dissect_foo()` when
|
||
it receives UDP traffic on port 1234.
|
||
|
||
Wireshark's dissector convention is to put `proto_register_foo()` and
|
||
`proto_reg_handoff_foo()` as the last two functions in the dissector source.
|
||
|
||
The next step is to write the dissecting function, `dissect_foo()`.
|
||
We'll start with a basic placeholder.
|
||
|
||
.Dissection.
|
||
[source,c]
|
||
----
|
||
static int
|
||
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_)
|
||
{
|
||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
|
||
/* Clear the info column */
|
||
col_clear(pinfo->cinfo,COL_INFO);
|
||
|
||
return tvb_captured_length(tvb);
|
||
}
|
||
----
|
||
|
||
`dissect_foo()` is called to dissect the packets presented to it. The packet data
|
||
is held in a special buffer referenced here as `tvb`. 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.
|
||
Note that the `\_U_` following `tree` and `data` signals to the compiler that the
|
||
parameters are unused, so that the compiler does not print a warning.
|
||
|
||
For now we'll do the minimum we can get away with. `col_set_str()` is used to set
|
||
Wireshark's Protocol column to "FOO" so everyone can see it’s being
|
||
recognised. The
|
||
only other thing we do is to clear out any data in the INFO column if it’s being
|
||
displayed.
|
||
|
||
At this point we have a basic dissector ready to compile and install.
|
||
The dissector doesn't do anything other than identify the protocol and label it.
|
||
Here is the dissector's complete code:
|
||
|
||
.Complete _packet-foo.c_:.
|
||
[source,c]
|
||
----
|
||
#include "config.h"
|
||
#include <epan/packet.h>
|
||
|
||
#define FOO_PORT 1234
|
||
|
||
static int proto_foo = -1;
|
||
|
||
static int
|
||
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_)
|
||
{
|
||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
|
||
/* Clear the info column */
|
||
col_clear(pinfo->cinfo,COL_INFO);
|
||
|
||
return tvb_captured_length(tvb);
|
||
}
|
||
|
||
void
|
||
proto_register_foo(void)
|
||
{
|
||
proto_foo = proto_register_protocol (
|
||
"FOO Protocol", /* name */
|
||
"FOO", /* short_name */
|
||
"foo" /* filter_name */
|
||
);
|
||
}
|
||
|
||
void
|
||
proto_reg_handoff_foo(void)
|
||
{
|
||
static dissector_handle_t foo_handle;
|
||
|
||
foo_handle = create_dissector_handle(dissect_foo, proto_foo);
|
||
dissector_add_uint("udp.port", FOO_PORT, foo_handle);
|
||
}
|
||
|
||
----
|
||
|
||
To compile this dissector and create a plugin a few support files
|
||
are required, besides the dissector source in _packet-foo.c_:
|
||
|
||
* _CMakeLists.txt_ - Contains the CMake file and version info for this plugin.
|
||
|
||
* _packet-foo.c_ - Your dissector source.
|
||
|
||
* _plugin.rc.in_ - Contains the DLL resource template for Windows. (optional)
|
||
|
||
Samples of these files are available in the gryphon plugin directory
|
||
(plugins/epan/gryphon).
|
||
If you copy the files from the gryphon plugin, _CMakeLists.txt_ will need
|
||
to be updated with the correct plugin name, version
|
||
info, and the relevant files to compile.
|
||
|
||
In the main top-level source directory, copy _CMakeListsCustom.txt.example_ to
|
||
_CMakeListsCustom.txt_ and add the path of your plugin to the list in
|
||
`CUSTOM_PLUGIN_SRC_DIR`.
|
||
|
||
Compile the dissector to a DLL or shared library and either run Wireshark from
|
||
the build directory as detailed in <<ChSrcRunFirstTime>> or copy the plugin
|
||
binary into the plugin directory of your Wireshark installation and run that.
|
||
|
||
[#ChDissectDetails]
|
||
|
||
==== Dissecting the protocol's details
|
||
|
||
Now that we have our basic dissector up and running, let’s do something with it.
|
||
The simplest thing to start with is labeling the payload. We can label the
|
||
payload by building a subtree to decode our results into.
|
||
This subtree will hold all the protocol's details and
|
||
helps keep things looking nice in the detailed display.
|
||
|
||
We add the new subtree with `proto_tree_add_item()`, as is depicted below:
|
||
|
||
.Plugin Packet Dissection.
|
||
[source,c]
|
||
----
|
||
static int
|
||
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
||
{
|
||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
|
||
/* Clear out stuff in the info column */
|
||
col_clear(pinfo->cinfo,COL_INFO);
|
||
|
||
proto_item *ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
|
||
|
||
return tvb_captured_length(tvb);
|
||
}
|
||
----
|
||
|
||
As the `FOO` protocol does not encapsulate another protocol, we
|
||
consume all of the tvb's data, from `0` to the end (`-1`).
|
||
|
||
The final parameter specifies the "encoding" and is set to
|
||
`ENC_NA` ("not applicable"), as the protocol doesn't specifically
|
||
use big endian (`ENC_BIG_ENDIAN`) or little endian (`ENC_LITTLE_ENDIAN`).
|
||
|
||
After adding the call to
|
||
`proto_tree_add_item()`
|
||
, there should be a label `FOO` in the protocol's detailed display.
|
||
Selecting this label will highlight the remaining contents of the packet.
|
||
|
||
Now let’s go to the next step and add some protocol dissection. To do this
|
||
we'll need to construct tables to define which fields will be present in the
|
||
packet and to store the opened/closed state of the subtree. We'll
|
||
add these statically allocated arrays to the beginning of the file
|
||
and name them
|
||
`hf_register_info` ('hf' is short for 'header field') and `ett`.
|
||
The arrays wil then registered after the call to
|
||
`proto_register_protocol()` by calling `proto_register_field_array()`
|
||
and `proto_register_subtree_array()`:
|
||
|
||
.Registering data structures.
|
||
[source,c]
|
||
----
|
||
static int hf_foo_pdu_type = -1;
|
||
static gint ett_foo = -1;
|
||
|
||
/* ... */
|
||
|
||
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" /* filter_name*/
|
||
);
|
||
|
||
proto_register_field_array(proto_foo, hf, array_length(hf));
|
||
proto_register_subtree_array(ett, array_length(ett));
|
||
}
|
||
----
|
||
|
||
As you can see, a field `foo.type` was defined inside the array of
|
||
header fields.
|
||
|
||
Now we can dissect the `FOO PDU Type` (referenced as `foo.type`)
|
||
field in `dissect_foo()` by adding
|
||
the FOO Protocol's subtree with `proto_item_add_subtree()` and
|
||
then calling `proto_tree_add_item()` to add the field:
|
||
|
||
.Dissector starting to dissect the packets.
|
||
[source,c]
|
||
----
|
||
proto_item *ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
|
||
proto_tree *foo_tree = proto_item_add_subtree(ti, ett_foo);
|
||
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, 0, 1, ENC_BIG_ENDIAN);
|
||
----
|
||
|
||
As mentioned earlier, the foo protocol begins with an 8-bit `packet type`
|
||
which can have three possible values: 1 - initialisation, 2 - terminate, 3 - data.
|
||
Here's how we can add the packet details:
|
||
|
||
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 (also called big endian), so that is why we use `ENC_BIG_ENDIAN`.
|
||
For a 1-byte quantity, there is no order issue, but it is good practice to
|
||
make this the same as any multibyte fields that may be present, and as we will
|
||
see in the next section, this particular protocol uses network order.
|
||
|
||
If we look in detail at the `hf_foo_pdu_type` declaration in
|
||
the static array we can see the details of the definition.
|
||
|
||
----
|
||
static hf_register_info hf[] = {
|
||
{ &hf_foo_pdu_type,
|
||
{ "FOO PDU Type", "foo.type",
|
||
FT_UINT8, BASE_DEC,
|
||
NULL, 0x0,
|
||
NULL, HFILL }
|
||
}
|
||
};
|
||
----
|
||
|
||
* _hf_foo_pdu_type_ - The node's index.
|
||
|
||
* _FOO PDU Type_ - The item's label.
|
||
|
||
* _foo.type_ - The item's abbreviated name, for use in the display filter
|
||
(e.g., `foo.type=1`).
|
||
|
||
* _FT_UINT8_ - The item's type: An 8bit unsigned integer.
|
||
This tallies with our call above where we tell it to only look at one byte.
|
||
|
||
* _BASE_DEC_ - For an integer type, this tells it to be printed as a decimal
|
||
number. It could be hexadecimal (BASE_HEX) or octal (BASE_OCT) if that made more sense.
|
||
|
||
We'll ignore the rest of the structure for now.
|
||
|
||
If you install this plugin and try it out, you'll see something that begins to look
|
||
useful.
|
||
|
||
Now let’s finish off dissecting the simple protocol. We need to add a few
|
||
more variables to the hfarray, and a couple more procedure calls.
|
||
|
||
.Wrapping up the packet dissection.
|
||
[source,c]
|
||
----
|
||
...
|
||
static int hf_foo_flags = -1;
|
||
static int hf_foo_sequenceno = -1;
|
||
static int hf_foo_initialip = -1;
|
||
...
|
||
|
||
static int
|
||
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
||
{
|
||
gint offset = 0;
|
||
|
||
...
|
||
proto_item *ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
|
||
proto_tree *foo_tree = proto_item_add_subtree(ti, ett_foo);
|
||
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, ENC_BIG_ENDIAN);
|
||
offset += 1;
|
||
proto_tree_add_item(foo_tree, hf_foo_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
|
||
offset += 1;
|
||
proto_tree_add_item(foo_tree, hf_foo_sequenceno, tvb, offset, 2, ENC_BIG_ENDIAN);
|
||
offset += 2;
|
||
proto_tree_add_item(foo_tree, hf_foo_initialip, tvb, offset, 4, ENC_BIG_ENDIAN);
|
||
offset += 4;
|
||
...
|
||
|
||
return tvb_captured_length(tvb);
|
||
}
|
||
|
||
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 }
|
||
},
|
||
...
|
||
...
|
||
}
|
||
...
|
||
----
|
||
|
||
This dissects all the bits of this simple hypothetical protocol. We've
|
||
introduced a new variable offsetinto 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.
|
||
|
||
==== Improving the dissection information
|
||
|
||
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.
|
||
|
||
|
||
.Naming the packet types.
|
||
[source,c]
|
||
----
|
||
static const value_string packettypenames[] = {
|
||
{ 1, "Initialise" },
|
||
{ 2, "Terminate" },
|
||
{ 3, "Data" },
|
||
{ 0, NULL }
|
||
};
|
||
----
|
||
|
||
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.
|
||
|
||
.Adding Names to the protocol.
|
||
[source,c]
|
||
----
|
||
{ &hf_foo_pdu_type,
|
||
{ "FOO PDU Type", "foo.type",
|
||
FT_UINT8, BASE_DEC,
|
||
VALS(packettypenames), 0x0,
|
||
NULL, HFILL }
|
||
}
|
||
----
|
||
|
||
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.
|
||
|
||
.Adding Flags to the protocol.
|
||
[source,c]
|
||
----
|
||
#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 int
|
||
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
||
{
|
||
...
|
||
...
|
||
static int* const bits[] = {
|
||
&hf_foo_startflag,
|
||
&hf_foo_endflag,
|
||
&hf_foo_priorityflag,
|
||
NULL
|
||
};
|
||
|
||
proto_tree_add_bitmask(foo_tree, tvb, offset, hf_foo_flags, ett_foo, bits, ENC_BIG_ENDIAN);
|
||
offset += 1;
|
||
...
|
||
...
|
||
return tvb_captured_length(tvb);
|
||
}
|
||
|
||
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 }
|
||
},
|
||
...
|
||
...
|
||
}
|
||
...
|
||
----
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
.Enhancing the display.
|
||
[source,c]
|
||
----
|
||
static int
|
||
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
||
{
|
||
gint offset = 0;
|
||
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)"));
|
||
|
||
proto_item *ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
|
||
proto_item_append_text(ti, ", Type %s",
|
||
val_to_str(packet_type, packettypenames, "Unknown (0x%02x)"));
|
||
proto_tree *foo_tree = proto_item_add_subtree(ti, ett_foo);
|
||
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, ENC_BIG_ENDIAN);
|
||
offset += 1;
|
||
|
||
return tvb_captured_length(tvb);
|
||
}
|
||
----
|
||
|
||
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.
|
||
|
||
[#ChDissectExpertInfo]
|
||
|
||
=== How to add an expert item
|
||
|
||
A dissector showing the protocol fields and interpretation of their values is
|
||
very informative. It can be even more helpful if the dissector can draw your
|
||
attention to fields where something noteworthy can be seen. This can be something
|
||
as simple as the start flag of a session, or something more severe as an invalid
|
||
value.
|
||
|
||
Here we take our dissector for `FOO` and add an expert item for the sequence
|
||
number being zero (assuming that's a noteworthy thing for this protocol).
|
||
|
||
.Expert item setup.
|
||
[source,c]
|
||
----
|
||
#include <epan/expert.h>
|
||
|
||
static expert_field ei_foo_seqn_zero = EI_INIT;
|
||
|
||
/* ... */
|
||
|
||
void
|
||
proto_register_foo(void)
|
||
{
|
||
/* ... */
|
||
expert_module_t* expert_foo;
|
||
|
||
/* ... */
|
||
static ei_register_info ei[] = {
|
||
{
|
||
&ei_foo_seqn_zero,
|
||
{ "foo.seqn_zero", PI_SEQUENCE, PI_CHAT,
|
||
"Sequence number is zero", EXPFILL }
|
||
}
|
||
};
|
||
|
||
/* ... */
|
||
expert_foo = expert_register_protocol(proto_foo);
|
||
expert_register_field_array(expert_foo, ei, array_length(ei));
|
||
}
|
||
----
|
||
|
||
Let's go through this step by step. The data structures and functions needed for
|
||
expert items are found in epan/expert.h, so we have to include that file.
|
||
|
||
Next we have to allocate an `expert_field` structure for every type of expert item
|
||
we would like to add to the dissection. This structure is initialised with `EI_INIT`.
|
||
|
||
Now we have to register with the protocol we are providing expert info for. Since
|
||
we already have a function to register our protocol, we add the expert info
|
||
registration there too. This is done by calling `expert_register_protocol()` with
|
||
the handle for the protocol we received earlier in this function.
|
||
|
||
Next we need to register an array of definitions of expert items that we would
|
||
like to add to the dissection. This array, not unlike the array of header fields
|
||
before, contains all the data the dissection engine needs to create and handle
|
||
the expert items.
|
||
|
||
The expert item definition consists of a pointer to the `expert_field` structure
|
||
we defined before and a structure with data elements of the expert item itself.
|
||
|
||
* _"foo.seqn_zero"_ - The expert items display filter
|
||
|
||
* _PI_SEQUENCE_ - The group to which the expert item belongs
|
||
|
||
* _PI_CHAT_ - The severity of the expert item
|
||
|
||
* _"Sequence number is zero"_ - The text string added to the dissection
|
||
|
||
We'll ignore the rest of the structure for now.
|
||
|
||
To keep an overview of lots of expert items it helps to categorize them into groups.
|
||
Currently there are several types of groups defined, e.g. `checksum`, `sequence`,
|
||
`protocol`, etc. All these are defined in the epan/proto.h header file.
|
||
|
||
Not every noteworthy field value is of equal severity. The start of a session
|
||
is nice to know, while an invalid value may be significant error in the protocol.
|
||
To differentiate between these severties the expert item is assigned one of them:
|
||
`comment`, `chat`, `note`, `warn` or `error`. Try to choose the lowest one which
|
||
is suitable. The topic you're currently working on seems probably more important
|
||
than it will look like in a few weeks.
|
||
|
||
With the expert item array setup, we add this to the dissection engine with a
|
||
call to `expert_register_field_array()`.
|
||
|
||
Now that all information of the expert item is defined and registered it's time
|
||
to actually add the expert item to the dissection.
|
||
|
||
.Expert item use.
|
||
[source,c]
|
||
----
|
||
static int
|
||
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
||
{
|
||
guint32 sequenceno = 0xFFFF;
|
||
|
||
/* ... */
|
||
|
||
ti = proto_tree_add_item_ret_uint(foo_tree, hf_foo_sequenceno,
|
||
tvb, offset, 2, ENC_BIG_ENDIAN, &sequenceno);
|
||
if (sequenceno == 0) {
|
||
expert_add_info(pinfo, ti, &ei_foo_seqn_zero);
|
||
}
|
||
|
||
/* ... */
|
||
}
|
||
----
|
||
|
||
There's been a slight alteration to the function used to add the sequence number
|
||
dissection. First the proto_item created by the function is saved in previously
|
||
defined variable `ti`, and the actual value of the field is stored in the variable
|
||
`sequenceno`. We can now use the value of this field to determine wether to add
|
||
the expert item.
|
||
|
||
Adding the expert item is simply done by calling `expert_add_info()` with reference
|
||
to the `packet_info` structure, the proto item `ti` to add the expert item to and
|
||
the previously defined and registered expert item information.
|
||
|
||
[#ChDissectTransformed]
|
||
|
||
=== How to handle transformed data
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
.Decompressing data packets for dissection.
|
||
[source,c]
|
||
----
|
||
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*)wmem_alloc(pinfo->pool, orig_size);
|
||
offset += 2;
|
||
decompress_packet(tvb_get_ptr(tvb, offset, -1),
|
||
tvb_captured_length_remaining(tvb, offset),
|
||
decompressed_buffer, orig_size);
|
||
/* Now re-setup the tvb buffer to have the new data */
|
||
next_tvb = tvb_new_child_real_data(tvb, decompressed_buffer, orig_size, orig_size);
|
||
add_new_data_source(pinfo, next_tvb, "Decompressed Data");
|
||
} else {
|
||
next_tvb = tvb_new_subset_remaining(tvb, offset);
|
||
}
|
||
offset = 0;
|
||
/* process next_tvb from here on */
|
||
----
|
||
|
||
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.
|
||
|
||
So armed with the size, a buffer is allocated to receive the uncompressed data
|
||
using `wmem_alloc()` in pinfo->pool memory, 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_captured_length_remaining()` function.
|
||
|
||
Next we build a new tvb buffer from this data, using the
|
||
`tvb_new_child_real_data()` call. This data is a child of our original data, so
|
||
calling this function also acknowledges that. No need to call
|
||
`tvb_set_free_cb()` as the pinfo->pool was used (the memory block will be
|
||
automatically freed when the pinfo pool lifetime expires). Finally we add this
|
||
tvb as a new data source, so that the detailed display can show the
|
||
decompressed bytes as well as the original.
|
||
|
||
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 `tvb_new_subset_remaining()` 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.
|
||
|
||
[#ChDissectReassemble]
|
||
|
||
=== How to reassemble split packets
|
||
|
||
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.
|
||
|
||
The following sections will guide you through two common cases. For a
|
||
description of all possible functions, structures and parameters, see
|
||
_epan/reassemble.h_.
|
||
|
||
[#ChDissectReassembleUdp]
|
||
|
||
==== How to reassemble split UDP packets
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
----
|
||
msg_pkt ::= SEQUENCE {
|
||
.....
|
||
flags ::= SEQUENCE {
|
||
fragment BOOLEAN,
|
||
last_fragment BOOLEAN,
|
||
.....
|
||
}
|
||
msg_id INTEGER(0..65535),
|
||
frag_id INTEGER(0..65535),
|
||
.....
|
||
}
|
||
----
|
||
|
||
.Reassembling fragments - Part 1
|
||
[source,c]
|
||
----
|
||
#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(msg_reassembly_table,
|
||
tvb, offset, pinfo,
|
||
msg_seqid, NULL, /* ID for fragments belonging together */
|
||
msg_num, /* fragment sequence number */
|
||
tvb_captured_length_remaining(tvb, offset), /* fragment length - to the end */
|
||
flags & FL_FRAG_LAST); /* More fragments? */
|
||
----
|
||
|
||
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 parameters:
|
||
|
||
* The `msg_reassembly_table` table is for bookkeeping and is described later.
|
||
|
||
* The tvb buffer we are dissecting.
|
||
|
||
* The offset where the partial packet starts.
|
||
|
||
* The provided packet info.
|
||
|
||
* 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.
|
||
|
||
* Optional additional data for identifying the fragment. Can be set to `NULL`
|
||
(as is done in the example) for most dissectors.
|
||
|
||
* msg_num is the packet number within the sequence.
|
||
|
||
* The length here is specified as the rest of the tvb as we want the rest of the packet data.
|
||
|
||
* 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.
|
||
|
||
.Reassembling fragments part 2
|
||
[source,c]
|
||
----
|
||
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_remaining(tvb, offset);
|
||
}
|
||
}
|
||
else { /* Not fragmented */
|
||
next_tvb = tvb_new_subset_remaining(tvb, offset);
|
||
}
|
||
|
||
.....
|
||
pinfo->fragmented = save_fragmented;
|
||
----
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
Now the mysterious data we passed into the `fragment_add_seq_check()`.
|
||
|
||
.Reassembling fragments - Initialisation
|
||
[source,c]
|
||
----
|
||
static reassembly_table reassembly_table;
|
||
|
||
static void
|
||
proto_register_msg(void)
|
||
{
|
||
reassembly_table_register(&msg_reassemble_table,
|
||
&addresses_ports_reassembly_table_functions);
|
||
}
|
||
----
|
||
|
||
First a `reassembly_table` structure is declared and initialised in the protocol
|
||
initialisation routine. The second parameter specifies the functions that should
|
||
be used for identifying fragments. We will use
|
||
`addresses_ports_reassembly_table_functions` in order to identify fragments by
|
||
the given sequence number (`msg_seqid`), the source and destination addresses
|
||
and ports from the packet.
|
||
|
||
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.
|
||
|
||
.Reassembling fragments - Data
|
||
[source,c]
|
||
----
|
||
...
|
||
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_fragment_count = -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,
|
||
&hf_msg_fragment_count,
|
||
/* 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, 0, NULL, 0x00, NULL, HFILL } },
|
||
{&hf_msg_fragment_overlap_conflicts,
|
||
{"Message fragment overlapping with conflicting data",
|
||
"msg.fragment.overlap.conflicts",
|
||
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
|
||
{&hf_msg_fragment_multiple_tails,
|
||
{"Message has multiple tail fragments",
|
||
"msg.fragment.multiple_tails",
|
||
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
|
||
{&hf_msg_fragment_too_long_fragment,
|
||
{"Message fragment too long", "msg.fragment.too_long_fragment",
|
||
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
|
||
{&hf_msg_fragment_error,
|
||
{"Message defragmentation error", "msg.fragment.error",
|
||
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
||
{&hf_msg_fragment_count,
|
||
{"Message fragment count", "msg.fragment.count",
|
||
FT_UINT32, BASE_DEC, 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
|
||
...
|
||
----
|
||
|
||
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.
|
||
|
||
[#TcpDissectPdus]
|
||
|
||
==== How to reassemble split TCP Packets
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
This sounds complicated, but there is a simple solution.
|
||
`tcp_dissect_pdus()` does all this tcp packet reassembling for you.
|
||
This function is implemented in _epan/dissectors/packet-tcp.h_.
|
||
|
||
.Reassembling TCP fragments
|
||
[source,c]
|
||
----
|
||
#include "config.h"
|
||
|
||
#include <epan/packet.h>
|
||
#include <epan/prefs.h>
|
||
#include "packet-tcp.h"
|
||
|
||
...
|
||
|
||
#define FRAME_HEADER_LEN 8
|
||
|
||
/* This method dissects fully reassembled messages */
|
||
static int
|
||
dissect_foo_message(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_)
|
||
{
|
||
/* TODO: implement your dissecting code */
|
||
return tvb_captured_length(tvb);
|
||
}
|
||
|
||
/* determine PDU length of protocol foo */
|
||
static guint
|
||
get_foo_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
|
||
{
|
||
/* TODO: change this to your needs */
|
||
return (guint)tvb_get_ntohl(tvb, offset+4); /* e.g. length is at offset 4 */
|
||
}
|
||
|
||
/* The main dissecting routine */
|
||
static int
|
||
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
|
||
{
|
||
tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN,
|
||
get_foo_message_len, dissect_foo_message, data);
|
||
return tvb_captured_length(tvb);
|
||
}
|
||
|
||
...
|
||
----
|
||
|
||
As you can see this is really simple. Just call `tcp_dissect_pdus()` in your
|
||
main dissection routine and move you message parsing code into another function.
|
||
This function gets called whenever a message has been reassembled.
|
||
|
||
The parameters tvb, pinfo, tree and data are just handed over to
|
||
`tcp_dissect_pdus()`. 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. Parameter 8 is the data
|
||
passed in from parent dissector.
|
||
|
||
Protocols which need more data before the message length can be determined can
|
||
return zero. Other values smaller than the fixed length will result in an
|
||
exception.
|
||
|
||
[#ChDissectTap]
|
||
|
||
=== How to tap protocols
|
||
|
||
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.
|
||
|
||
A tap is basically a way of allowing other items to see what’s 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.
|
||
|
||
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.
|
||
|
||
.Initialising a tap
|
||
[source,c]
|
||
----
|
||
#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");
|
||
----
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
.Calling a protocol tap
|
||
[source,c]
|
||
----
|
||
static int
|
||
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
||
{
|
||
...
|
||
fooinfo = wmem_alloc(pinfo->pool, 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);
|
||
|
||
return tvb_captured_length(tvb);
|
||
}
|
||
----
|
||
|
||
This now enables those interested parties to listen in on the details
|
||
of this protocol conversation.
|
||
|
||
[#ChDissectStats]
|
||
|
||
=== How to produce protocol stats
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
Here is a mechanism to produce statistics from the above TAP interface.
|
||
|
||
.Initialising a stats interface
|
||
[source,c]
|
||
----
|
||
#include <epan/stats_tree.h>
|
||
|
||
/* register all http trees */
|
||
static void register_foo_stat_trees(void) {
|
||
stats_tree_register_plugin("foo", "foo", "Foo/Packet Types", 0,
|
||
foo_stats_tree_packet, foo_stats_tree_init, NULL);
|
||
}
|
||
|
||
WS_DLL_PUBLIC_DEF void plugin_register_tap_listener(void)
|
||
{
|
||
register_foo_stat_trees();
|
||
}
|
||
----
|
||
|
||
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()`.
|
||
|
||
This in turn calls the `stats_tree_register_plugin()` function, which takes three
|
||
strings, an integer, and three callback functions.
|
||
|
||
. This is the tap name that is registered.
|
||
|
||
. An abbreviation of the stats name.
|
||
|
||
. The name of the stats module. A “/” character can be used to make sub menus.
|
||
|
||
. Flags for per-packet callback
|
||
|
||
. The function that will called to generate the stats.
|
||
|
||
. A function that can be called to initialise the stats data.
|
||
|
||
. A function that will be called to clean up the stats data.
|
||
|
||
In this case we only need the first two functions, as there is nothing specific to clean up.
|
||
|
||
.Initialising a stats session
|
||
[source,c]
|
||
----
|
||
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, STAT_DT_INT, TRUE);
|
||
st_node_packet_types = stats_tree_create_pivot(st, st_str_packet_types, st_node_packets);
|
||
}
|
||
----
|
||
|
||
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.
|
||
|
||
|
||
.Generating the stats
|
||
[source,c]
|
||
----
|
||
static tap_packet_status foo_stats_tree_packet(stats_tree* st, packet_info* pinfo, epan_dissect_t* edt, const void* p, tap_flags_t flags)
|
||
{
|
||
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, packettypenames, "Unknown packet type (%d)"));
|
||
return TAP_PACKET_REDRAW;
|
||
}
|
||
----
|
||
|
||
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.
|
||
|
||
[#ChDissectConversation]
|
||
|
||
=== How to use conversations
|
||
|
||
Some info about how to use conversations in a dissector can be found in the file
|
||
_doc/README.dissector_, chapter 2.2.
|
||
|
||
[#ChDissectIdl2wrs]
|
||
|
||
=== __idl2wrs__: Creating dissectors from CORBA IDL files
|
||
|
||
Many of Wireshark’s dissectors are automatically generated. This section shows
|
||
how to generate one from a CORBA IDL file.
|
||
|
||
==== What is it?
|
||
|
||
As you have probably guessed from the name, `idl2wrs` takes a user specified IDL
|
||
file and attempts to build a dissector that can decode the IDL traffic over
|
||
GIOP. The resulting file is “C” code, that should compile okay as a Wireshark
|
||
dissector.
|
||
|
||
`idl2wrs` parses the data struct given to it by the `omniidl` compiler,
|
||
and using the GIOP API available in packet-giop.[ch], generates get_CDR_xxx
|
||
calls to decode the CORBA traffic on the wire.
|
||
|
||
It consists of 4 main files.
|
||
|
||
README.idl2wrs::
|
||
This document
|
||
|
||
wireshark_be.py::
|
||
The main compiler backend
|
||
|
||
wireshark_gen.py::
|
||
A helper class, that generates the C code.
|
||
|
||
idl2wrs::
|
||
A simple shell script wrapper that the end user should use to generate the
|
||
dissector from the IDL file(s).
|
||
|
||
==== Why do this?
|
||
|
||
It is important to understand what CORBA traffic looks like over GIOP/IIOP, and
|
||
to help build a tool that can assist in troubleshooting CORBA interworking. This
|
||
was especially the case after seeing a lot of discussions about how particular
|
||
IDL types are represented inside an octet stream.
|
||
|
||
I have also had comments/feedback that this tool would be good for say a CORBA
|
||
class when teaching students what CORBA traffic looks like “on the wire”.
|
||
|
||
It is also COOL to work on a great Open Source project such as the case with
|
||
“Wireshark” ({wireshark-main-url}).
|
||
|
||
|
||
==== How to use idl2wrs
|
||
|
||
To use the idl2wrs to generate Wireshark dissectors, you need the following:
|
||
|
||
* Python must be installed. See link:https://python.org/[]
|
||
|
||
* `omniidl` from the omniORB package must be available. See link:http://omniorb.sourceforge.net/[]
|
||
|
||
* Of course you need Wireshark installed to compile the code and tweak it if
|
||
required. idl2wrs is part of the standard Wireshark distribution
|
||
|
||
To use idl2wrs to generate an Wireshark dissector from an idl file use the following procedure:
|
||
|
||
* To write the C code to stdout.
|
||
+
|
||
--
|
||
----
|
||
$ idl2wrs <your_file.idl>
|
||
----
|
||
|
||
e.g.:
|
||
|
||
----
|
||
$ idl2wrs echo.idl
|
||
----
|
||
--
|
||
|
||
* To write to a file, just redirect the output.
|
||
+
|
||
--
|
||
----
|
||
$ idl2wrs echo.idl > packet-test-idl.c
|
||
----
|
||
|
||
You may wish to comment out the register_giop_user_module() code and that will
|
||
leave you with heuristic dissection.
|
||
|
||
If you don't want to use the shell script wrapper, then try steps 3 or 4 instead.
|
||
--
|
||
|
||
* To write the C code to stdout.
|
||
+
|
||
--
|
||
----
|
||
$ omniidl -p ./ -b wireshark_be <your file.idl>
|
||
----
|
||
|
||
e.g.:
|
||
|
||
----
|
||
$ omniidl -p ./ -b wireshark_be echo.idl
|
||
----
|
||
--
|
||
|
||
* To write to a file, just redirect the output.
|
||
+
|
||
--
|
||
----
|
||
$ omniidl -p ./ -b wireshark_be echo.idl > packet-test-idl.c
|
||
----
|
||
|
||
You may wish to comment out the register_giop_user_module() code and that will
|
||
leave you with heuristic dissection.
|
||
--
|
||
|
||
* Copy the resulting C code to subdirectory epan/dissectors/ inside your
|
||
Wireshark source directory.
|
||
+
|
||
--
|
||
----
|
||
$ cp packet-test-idl.c /dir/where/wireshark/lives/epan/dissectors/
|
||
----
|
||
|
||
The new dissector has to be added to CMakeLists.txt in the same directory. Look
|
||
for the declaration DISSECTOR_SRC and add the new dissector there. For
|
||
example,
|
||
|
||
----
|
||
DISSECTOR_SRC = \
|
||
${CMAKE_CURRENT_SOURCE_DIR}/packet-2dparityfec.c
|
||
${CMAKE_CURRENT_SOURCE_DIR}/packet-3com-njack.c
|
||
...
|
||
----
|
||
|
||
becomes
|
||
|
||
----
|
||
DISSECTOR_SRC = \
|
||
${CMAKE_CURRENT_SOURCE_DIR}/packet-test-idl.c \
|
||
${CMAKE_CURRENT_SOURCE_DIR}/packet-2dparityfec.c \
|
||
${CMAKE_CURRENT_SOURCE_DIR}/packet-3com-njack.c \
|
||
...
|
||
----
|
||
--
|
||
|
||
For the next steps, go up to the top of your Wireshark source directory.
|
||
|
||
* Create a build dir
|
||
+
|
||
--
|
||
----
|
||
$ mkdir build && cd build
|
||
----
|
||
--
|
||
|
||
* Run cmake
|
||
+
|
||
--
|
||
----
|
||
$ cmake ..
|
||
----
|
||
--
|
||
|
||
* Build the code
|
||
+
|
||
--
|
||
----
|
||
$ make
|
||
----
|
||
--
|
||
|
||
* Good Luck !!
|
||
|
||
==== TODO
|
||
|
||
* Exception code not generated (yet), but can be added manually.
|
||
|
||
* Enums not converted to symbolic values (yet), but can be added manually.
|
||
|
||
* Add command line options etc
|
||
|
||
* More I am sure :-)
|
||
|
||
==== Limitations
|
||
|
||
See the TODO list inside _packet-giop.c_
|
||
|
||
==== Notes
|
||
|
||
The `-p ./` option passed to omniidl indicates that the wireshark_be.py and
|
||
wireshark_gen.py are residing in the current directory. This may need tweaking
|
||
if you place these files somewhere else.
|
||
|
||
If it complains about being unable to find some modules (e.g. tempfile.py), you
|
||
may want to check if PYTHONPATH is set correctly.
|
||
|
||
|
||
// End of WSDG Chapter Dissection
|