WSDG: Edits to 'Packet dissection'

Change-Id: If4aa830561b5b49e0363c454a99206baba2fb9bc
Reviewed-on: https://code.wireshark.org/review/36723
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Moshe Kaplan 2020-04-06 00:19:52 -04:00 committed by Anders Broman
parent 8b7757811d
commit 2acbaf5849
1 changed files with 138 additions and 70 deletions

View File

@ -2,39 +2,38 @@
[[ChapterDissection]]
== Packet dissection
== Packet Dissection
[[ChDissectWorks]]
=== How it works
=== How packet dissection works
Each dissector decodes its part of the protocol, and then hands off
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 packet
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 will be decoded and
IP) and so on. At each stage, details of the packet are decoded and
displayed.
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 or DLL) that registers itself
to handle dissection.
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. On the Windows platform you have limited function access through the
ABI exposed by functions declared as WS_DLL_PUBLIC.
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
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 implementing
The file _doc/README.dissector_ contains detailed information about writing
a dissector. In many cases it is more up to date than this document.
====
@ -45,9 +44,9 @@ a dissector. In many cases it is more up to date than this document.
Lets 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 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 set of flags stored in 8 bits. 0x01 - start packet, 0x02 - end packet, 0x04 - priority packet.
* A sequence number - 16 bits.
@ -58,31 +57,28 @@ protocol. It consists of the following basic items.
==== Setting up the dissector
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.
built-in dissector and included in the main program, or a plugin.
Plugins are the easiest to write initially, 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.
Plugins are easier to write initially, so lets start with that.
With a little care, the plugin can be converted into a built-in dissector.
.Dissector Initialisation.
====
----
#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" /* abbrev */
"FOO Protocol", /* name */
"FOO", /* short name */
"foo" /* filter_name */
);
}
----
@ -91,22 +87,33 @@ proto_register_foo(void)
Lets go through this a bit at a time. First we have some boilerplate
include files. These will be pretty constant to start with.
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.
Its 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.
Then a `#define` for the UDP port that carries _foo_ traffic.
Now that we have the basics in place to interact with the main program, we'll
start with two protocol dissector setup functions.
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.
Its 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.
First we'll call `proto_register_protocol()` 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.
Next we need a handoff routine.
@ -124,17 +131,24 @@ proto_reg_handoff_foo(void)
----
====
Whats 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.
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.
The standard Wireshark dissector convention is to put `proto_register_foo()` and
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.
Now at last we get to write some dissecting code. For the moment we'll
leave it as a basic placeholder.
The next step is to write the dissecting function, `dissect_foo()`.
We'll start with a basic placeholder.
.Dissection.
====
@ -143,7 +157,7 @@ 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 out stuff in the info column */
/* Clear the info column */
col_clear(pinfo->cinfo,COL_INFO);
return tvb_captured_length(tvb);
@ -151,21 +165,65 @@ dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data
----
====
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
`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. In the first line we set the
text of this to our protocol, so everyone can see its being recognised. The
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 its being
recognised. The
only other thing we do is to clear out any data in the INFO column if its being
displayed.
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.
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:
In order to compile this dissector and create a plugin a couple of support files
.Complete _packet-foo.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.
@ -187,7 +245,7 @@ binary into the plugin directory of your Wireshark installation and run that.
[[ChDissectDetails]]
==== Dissecting the details of the protocol
==== Dissecting the protocol's details
Now that 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.
@ -202,7 +260,6 @@ This helps to keep things looking nice in the detailed display.
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);
@ -219,7 +276,7 @@ This subtree will hold all the details of this protocol and so not clutter
up the display when not required.
We are also marking the area of data that is being consumed by this
protocol. In our case its all that has been passed to us, as we're assuming
protocol. In this case its all of the passed data, as
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
@ -230,7 +287,7 @@ After this change, there should be a label in the detailed display for the proto
and selecting this will highlight the remaining contents of the packet.
Now lets 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
we'll need to construct a few tables to help with dissection. This needs
some additions to the `proto_register_foo()` function shown previously.
Two statically allocated arrays are added at the beginning of
@ -259,8 +316,8 @@ proto_register_foo(void)
proto_foo = proto_register_protocol (
"FOO Protocol", /* name */
"FOO", /* short name */
"foo" /* abbrev */
"FOO", /* short_name */
"foo" /* filter_name*/
);
proto_register_field_array(proto_foo, hf, array_length(hf));
@ -280,7 +337,7 @@ static gint ett_foo = -1;
----
====
Now we can enhance the protocol display with some detail.
Now we can enhance the protocol display with some packet detail.
.Dissector starting to dissect the packets.
====
@ -291,9 +348,9 @@ Now we can enhance the protocol display with some detail.
----
====
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.
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.
@ -312,14 +369,25 @@ 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.
* _hf_foo_pdu_type_ - The index for this node.
----
static hf_register_info hf[] = {
{ &hf_foo_pdu_type,
{ "FOO PDU Type", "foo.type",
FT_UINT8, BASE_DEC,
NULL, 0x0,
NULL, HFILL }
}
};
----
* _FOO PDU Type_ - The label for this item.
* _hf_foo_pdu_type_ - The node's index.
* _foo.type_ - This is the filter string. It enables us to type constructs such
as `foo.type=1` into the filter box.
* _FOO PDU Type_ - The item's label.
* _FT_UINT8_ - This specifies this item is an 8bit unsigned integer.
* _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