Major cleanup of skeleton dissector and related bits of README.developer.

Changes of note:
- Removed the 'Copied from' notice, it's only relevant if they're *not* using
  the skeleton code. Added a paragraph to README.developer instead.
- Exorcised all references to if (tree) and placed them in their own section
  at the bottom as an optimization. Hopefully this will be less confusing.

svn path=/trunk/; revision=48861
This commit is contained in:
Evan Huus 2013-04-15 21:43:40 +00:00
parent bb2820deb0
commit 4e3c836273
2 changed files with 215 additions and 196 deletions

View File

@ -734,6 +734,10 @@ one, or a commonly-used abbreviation for the protocol, if any.
The skeleton code lives in the file "packet-PROTOABBREV.c" in the same source
directory as this README.
If instead of using the skeleton you base your dissector on an existing real
dissector, please put a little note in the copyright header indicating which
dissector you started with.
Usually, you will put your newly created dissector file into the directory
epan/dissectors/, just like all the other packet-*.c files already in there.
@ -761,9 +765,7 @@ your information.
YOUR_NAME Your name, of course. You do want credit, don't you?
It's the only payment you will receive....
YOUR_EMAIL_ADDRESS Keep those cards and letters coming.
WHATEVER_FILE_YOU_USED Add this line if you are using another file as a
starting point.
YOUR_EMAIL_ADDRESS Keep those cards and letters coming.
PROTONAME The name of the protocol; this is displayed in the
top-level protocol tree item for that protocol.
PROTOSHORTNAME An abbreviated name for the protocol; this is displayed
@ -1723,11 +1725,6 @@ for internally used fields.
A protocol item is added to an existing protocol tree with one of a
handful of proto_XXX_DO_YYY() functions.
Remember that it only makes sense to add items to a protocol tree if its
proto_tree pointer is not null. Should you add an item to a NULL tree, then
the proto_XXX_DO_YYY() function will immediately return. The cost of this
function call can be avoided by checking for the tree pointer.
Subtrees can be made with the proto_item_add_subtree() function:
item = proto_tree_add_item(....);
@ -3793,6 +3790,43 @@ ptvcursor_set_subtree(ptvcursor_t* ptvc, proto_item* it, gint ett_subtree);
Creates a subtree and adds it to the cursor as the working tree but does
not save the old working tree.
2.9 Optimizations
A protocol dissector may be called in 2 different ways - with, or
without a non-null "tree" argument.
If the proto_tree argument is null, Wireshark does not need to use
the protocol tree information from your dissector, and therefore is
passing the dissector a null "tree" argument so that it doesn't
need to do work necessary to build the protocol tree.
In the interest of speed, if "tree" is NULL, avoid building a
protocol tree and adding stuff to it, or even looking at any packet
data needed only if you're building the protocol tree, if possible.
Note, however, that you must fill in column information, create
conversations, reassemble packets, do calls to "expert" functions,
build any other persistent state needed for dissection, and call
subdissectors regardless of whether "tree" is NULL or not.
This might be inconvenient to do without doing most of the
dissection work; the routines for adding items to the protocol tree
can be passed a null protocol tree pointer, in which case they'll
return a null item pointer, and "proto_item_add_subtree()" returns
a null tree pointer if passed a null item pointer, so, if you're
careful not to dereference any null tree or item pointers, you can
accomplish this by doing all the dissection work. This might not
be as efficient as skipping that work if you're not building a
protocol tree, but if the code would have a lot of tests whether
"tree" is null if you skipped that work, you might still be better
off just doing all that work regardless of whether "tree" is null
or not.
Note also that there is no guarantee, the first time the dissector is
called, whether "tree" will be null or not; your dissector must work
correctly, building or updating whatever state information is
necessary, in either case.
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*

View File

@ -8,12 +8,6 @@
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* Copied from WHATEVER_FILE_YOU_USED (where "WHATEVER_FILE_YOU_USED"
* is a dissector file; if you just copied this from README.developer,
* don't bother with the "Copied from" - you don't even need to put
* in a "Copied from" if you copied an existing dissector, especially
* if the bulk of the code in the new dissector is your code)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@ -43,12 +37,18 @@
#include <epan/packet.h>
#include <epan/prefs.h>
/* IF PROTO exposes code to other dissectors, then it must be exported
in a header file. If not, a header file is not needed at all. */
#if 0
/* IF AND ONLY IF your protocol dissector exposes code to other dissectors
* (which most dissectors don't need to do) then the 'public' prototypes and
* data structures can go in the header file packet-PROTOABBREV.h. If not, then
* a header file is not needed at all and this #include statement can be
* removed. */
#include "packet-PROTOABBREV.h"
#endif
/* Forward declaration we need below (if using proto_reg_handoff...
as a prefs callback) */
/* Forward declaration that is needed below if using the
* proto_reg_handoff_PROTOABBREV function as a callback for when protocol
* preferences get changed. */
void proto_reg_handoff_PROTOABBREV(void);
/* Initialize the protocol and registered fields */
@ -57,215 +57,209 @@ static int hf_PROTOABBREV_FIELDABBREV = -1;
/* Global sample preference ("controls" display of numbers) */
static gboolean gPREF_HEX = FALSE;
/* Global sample port pref */
/* Global sample port preference - real port preferences should generally
* default to 0 unless there is an IANA-registered (or equivalent) port for your
* protocol. */
static guint gPORT_PREF = 1234;
/* Initialize the subtree pointers */
static gint ett_PROTOABBREV = -1;
/* A sample #define of the minimum length (in bytes) of the protocol data.
* If data is received with fewer than this many bytes it is rejected by
* the current dissector. */
#define PROTOABBREV_MIN_LENGTH 8
/* Code to actually dissect the packets */
static int
dissect_PROTOABBREV(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
dissect_PROTOABBREV(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_)
{
/* Set up structures needed to add the protocol subtree and manage it */
/* Set up structures needed to add the protocol subtree and manage it */
proto_item *ti;
proto_tree *PROTOABBREV_tree;
/* Other misc. local variables. */
guint offset = 0;
/*** HEURISTICS ***/
/* First, if at all possible, do some heuristics to check if the packet
* cannot possibly belong to your protocol. This is especially important
* for protocols directly on top of TCP or UDP where port collisions are
* common place (e.g., even though your protocol uses a well known port,
* someone else may set up, for example, a web server on that port which,
* if someone analyzed that web server's traffic in Wireshark, would result
* in Wireshark handing an HTTP packet to your dissector).
*
* For example:
*/
/* First, if at all possible, do some heuristics to check if the packet cannot
* possibly belong to your protocol. This is especially important for
* protocols directly on top of TCP or UDP where port collisions are
* common place (e.g., even though your protocol uses a well known port,
* someone else may set up, for example, a web server on that port which,
* if someone analyzed that web server's traffic in Wireshark, would result
* in Wireshark handing an HTTP packet to your dissector). For example:
*/
/* Check that there's enough data */
if (tvb_length(tvb) < /* your protocol's smallest packet size */)
if (tvb_length(tvb) < PROTOABBREV_MIN_LENGTH)
return 0;
/* Get some values from the packet header, probably using tvb_get_*() */
/* Fetch some values from the packet header using tvb_get_*(). If these
* values are not valid/possible in your protocol then return 0 to give
* some other dissector a chance to dissect it.
*/
if ( /* these values are not possible in PROTONAME */ )
/* This packet does not appear to belong to PROTONAME.
* Return 0 to give another dissector a chance to dissect it.
*/
return 0;
/* Make entries in Protocol column and Info column on summary display */
/*** COLUMN DATA ***/
/* There are two normal columns to fill in: the 'Protocol' column which
* is narrow and generally just contains the constant string 'PROTOABBREV',
* and the 'Info' column which can be much wider and contain misc. summary
* information (for example, the port number for TCP packets).
*
* If you are setting the column to a constant string, use "col_set_str()",
* as it's more efficient than the other "col_set_XXX()" calls.
*
* If
* - you may be appending to the column later OR
* - you have constructed the string locally OR
* - the string was returned from a call to val_to_str()
* then use "col_add_str()" instead, as that takes a copy of the string.
*
* The function "col_add_fstr()" can be used instead of "col_add_str()"; it
* takes "printf()"-like arguments. Don't use "col_add_fstr()" with a format
* string of "%s" - just use "col_add_str()" or "col_set_str()", as it's
* more efficient than "col_add_fstr()".
*
* For full details see section 1.5 of README.developer.
*/
/* Set the Protocol column to the constant string of PROTOABBREV */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "PROTOABBREV");
/* This field shows up as the "Info" column in the display; you should use
it, if possible, to summarize what's in the packet, so that a user looking
at the list of packets can tell what type of packet it is. See section 1.5
for more information.
If you are setting the column to a constant string, use "col_set_str()",
as it's more efficient than the other "col_set_XXX()" calls.
If you're setting it to a string you've constructed, or will be
appending to the column later, use "col_add_str()".
"col_add_fstr()" can be used instead of "col_add_str()"; it takes
"printf()"-like arguments. Don't use "col_add_fstr()" with a format
string of "%s" - just use "col_add_str()" or "col_set_str()", as it's
more efficient than "col_add_fstr()".
If you will be fetching any data from the packet before filling in
the Info column, clear that column first, in case the calls to fetch
data from the packet throw an exception because they're fetching data
past the end of the packet, so that the Info column doesn't have data
left over from the previous dissector; do
#if 0
/* If you will be fetching any data from the packet before filling in
* the Info column, clear that column first in case the calls to fetch
* data from the packet throw an exception so that the Info column doesn't
* contain data left over from the previous dissector: */
col_clear(pinfo->cinfo, COL_INFO);
*/
#endif
col_set_str(pinfo->cinfo, COL_INFO, "XXX Request");
/* A protocol dissector may be called in 2 different ways - with, or
without a non-null "tree" argument.
/*** PROTOCOL TREE ***/
If the proto_tree argument is null, Wireshark does not need to use
the protocol tree information from your dissector, and therefore is
passing the dissector a null "tree" argument so that it doesn't
need to do work necessary to build the protocol tree.
/* Now we will create a sub-tree for our protocol and start adding fields
* to display under that sub-tree. Most of the time the only functions you
* will need are proto_tree_add_item() and proto_item_add_subtree().
*
* NOTE: The offset and length values in the call to proto_tree_add_item()
* define what data bytes to highlight in the hex display window when the
* line in the protocol tree display corresponding to that item is selected.
*
* Supplying a length of -1 tells Wireshark to highlight all data from the
* offset to the end of the packet.
*/
In the interest of speed, if "tree" is NULL, avoid building a
protocol tree and adding stuff to it, or even looking at any packet
data needed only if you're building the protocol tree, if possible.
/* create display subtree for the protocol */
ti = proto_tree_add_item(tree, proto_PROTOABBREV, tvb, 0, -1, ENC_NA);
Note, however, that you must fill in column information, create
conversations, reassemble packets, do calls to "expert" functions,
build any other persistent state needed for dissection, and call
subdissectors regardless of whether "tree" is NULL or not.
PROTOABBREV_tree = proto_item_add_subtree(ti, ett_PROTOABBREV);
This might be inconvenient to do without doing most of the
dissection work; the routines for adding items to the protocol tree
can be passed a null protocol tree pointer, in which case they'll
return a null item pointer, and "proto_item_add_subtree()" returns
a null tree pointer if passed a null item pointer, so, if you're
careful not to dereference any null tree or item pointers, you can
accomplish this by doing all the dissection work. This might not
be as efficient as skipping that work if you're not building a
protocol tree, but if the code would have a lot of tests whether
"tree" is null if you skipped that work, you might still be better
off just doing all that work regardless of whether "tree" is null
or not.
/* Add an item to the subtree, see section 1.6 of README.developer for more
* information. */
proto_tree_add_item(PROTOABBREV_tree, hf_PROTOABBREV_FIELDABBREV, tvb,
offset, len, ENC_xxx);
offset += len;
Note also that there is no guarantee, the first time the dissector is
called, whether "tree" will be null or not; your dissector must work
correctly, building or updating whatever state information is
necessary, in either case. */
if (tree) {
/* Continue adding tree items to process the packet here... */
/* NOTE: The offset and length values in the call to
"proto_tree_add_item()" define what data bytes to highlight in the hex
display window when the line in the protocol tree display
corresponding to that item is selected.
/* If this protocol has a sub-dissector call it here, see section 1.8 of
* README.developer for more information. */
Supplying a length of -1 is the way to highlight all data from the
offset to the end of the packet. */
/* create display subtree for the protocol */
ti = proto_tree_add_item(tree, proto_PROTOABBREV, tvb, 0, -1, ENC_NA);
PROTOABBREV_tree = proto_item_add_subtree(ti, ett_PROTOABBREV);
/* add an item to the subtree, see section 1.6 for more information */
proto_tree_add_item(PROTOABBREV_tree,
hf_PROTOABBREV_FIELDABBREV, tvb, offset, len, ENC_xxx);
/* Continue adding tree items to process the packet here */
}
/* If this protocol has a sub-dissector call it here, see section 1.8 */
/* Return the amount of data this dissector was able to dissect */
/* Return the amount of data this dissector was able to dissect (which may
* or may not be the entire packet as we return here). */
return tvb_length(tvb);
}
/* Register the protocol with Wireshark */
/* This format is require because a script is used to build the C function
that calls all the protocol registration.
*/
/* Register the protocol with Wireshark.
*
* This format is require because a script is used to build the C function that
* calls all the protocol registration.
*/
void
proto_register_PROTOABBREV(void)
{
module_t *PROTOABBREV_module;
/* Setup list of header fields See Section 1.6.1 for details*/
/* Setup list of header fields See Section 1.6.1 of README.developer for
* details. */
static hf_register_info hf[] = {
{ &hf_PROTOABBREV_FIELDABBREV,
{ "FIELDNAME", "PROTOABBREV.FIELDABBREV",
FIELDTYPE, FIELDDISPLAY, FIELDCONVERT, BITMASK,
"FIELDDESCR", HFILL }
FIELDTYPE, FIELDDISPLAY, FIELDCONVERT, BITMASK,
"FIELDDESCR", HFILL }
}
};
/* Setup protocol subtree array */
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_PROTOABBREV
};
/* Register the protocol name and description */
/* Register the protocol name and description */
proto_PROTOABBREV = proto_register_protocol("PROTONAME",
"PROTOSHORTNAME", "PROTOABBREV");
"PROTOSHORTNAME", "PROTOABBREV");
/* Required function calls to register the header fields and subtrees used */
/* Required function calls to register the header fields and subtrees */
proto_register_field_array(proto_PROTOABBREV, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
/* Register preferences module (See Section 2.6 for more on preferences) */
/* (Registration of a prefs callback is not required if there are no */
/* prefs-dependent registration functions (eg: a port pref). */
/* See proto_reg_handoff below. */
/* If a prefs callback is not needed, use NULL instead of */
/* proto_reg_handoff_PROTOABBREV in the following). */
/* Register a preferences module (see section 2.6 of README.developer
* for more details). Registration of a prefs callback is not required
* if there are no preferences that affect protocol registration (an example
* of a preference that would affect registration is a port preference).
* If the prefs callback is not needed, use NULL instead of
* proto_reg_handoff_PROTOABBREV in the following.
*/
PROTOABBREV_module = prefs_register_protocol(proto_PROTOABBREV,
proto_reg_handoff_PROTOABBREV);
proto_reg_handoff_PROTOABBREV);
/* Register preferences module under preferences subtree.
Use this function instead of prefs_register_protocol if you want to group
preferences of several protocols under one preferences subtree.
Argument subtree identifies grouping tree node name, several subnodes can be
specified using slash '/' (e.g. "OSI/X.500" - protocol preferences will be
accessible under Protocols->OSI->X.500-><PROTOSHORTNAME> preferences node.
*/
PROTOABBREV_module = prefs_register_protocol_subtree(const char *subtree,
proto_PROTOABBREV, proto_reg_handoff_PROTOABBREV);
/* Register a preferences module under the preferences subtree.
* Only use this function instead of prefs_register_protocol (above) if you
* want to group preferences of several protocols under one preferences
* subtree.
*
* Argument subtree identifies grouping tree node name, several subnodes can
* be specified using slash '/' (e.g. "OSI/X.500" - protocol preferences
* will be accessible under Protocols->OSI->X.500-><PROTOSHORTNAME>
* preferences node.
*/
PROTOABBREV_module = prefs_register_protocol_subtree(const char *subtree,
proto_PROTOABBREV, proto_reg_handoff_PROTOABBREV);
/* Register a sample preference */
/* Register a simple example preference */
prefs_register_bool_preference(PROTOABBREV_module, "show_hex",
"Display numbers in Hex",
"Enable to display numerical values in hexadecimal.",
&gPREF_HEX);
"Display numbers in Hex",
"Enable to display numerical values in hexadecimal.",
&gPREF_HEX);
/* Register a sample port preference */
/* Register an example port preference */
prefs_register_uint_preference(PROTOABBREV_module, "tcp.port", "PROTOABBREV TCP Port",
" PROTOABBREV TCP port if other than the default",
10, &gPORT_PREF);
" PROTOABBREV TCP port if other than the default",
10, &gPORT_PREF);
}
/* If this dissector uses sub-dissector registration add a registration routine.
This exact format is required because a script is used to find these
routines and create the code that calls these routines.
If this function is registered as a prefs callback (see prefs_register_protocol
above) this function is also called by preferences whenever "Apply" is pressed;
In that case, it should accommodate being called more than once.
This form of the reg_handoff function is used if if you perform
registration functions which are dependent upon prefs. See below
for a simpler form which can be used if there are no
prefs-dependent registration functions.
*/
* This exact format is required because a script is used to find these
* routines and create the code that calls these routines.
*
* If this function is registered as a prefs callback (see
* prefs_register_protocol above) this function is also called by Wireshark's
* preferences manager whenever "Apply" or "OK" are pressed. In that case, it
* should accommodate being called more than once by use of the static
* 'initialized' variable included below.
*
* This form of the reg_handoff function is used if if you perform registration
* functions which are dependent upon prefs. See below this function for a
* simpler form which can be used if there are no prefs-dependent registration
* functions.
*/
void
proto_reg_handoff_PROTOABBREV(void)
{
@ -274,57 +268,50 @@ proto_reg_handoff_PROTOABBREV(void)
static int currentPort;
if (!initialized) {
/* Use new_create_dissector_handle() to indicate that dissect_PROTOABBREV()
* returns the number of bytes it dissected (or 0 if it thinks the packet
* does not belong to PROTONAME).
*/
/* Use new_create_dissector_handle() to indicate that
* dissect_PROTOABBREV() returns the number of bytes it dissected (or 0
* if it thinks the packet does not belong to PROTONAME).
*/
PROTOABBREV_handle = new_create_dissector_handle(dissect_PROTOABBREV,
proto_PROTOABBREV);
proto_PROTOABBREV);
initialized = TRUE;
} else {
/*
If you perform registration functions which are dependent upon
prefs the you should de-register everything which was associated
with the previous settings and re-register using the new prefs
settings here. In general this means you need to keep track of
the PROTOABBREV_handle and the value the preference had at the time
you registered. The PROTOABBREV_handle value and the value of the
preference can be saved using local statics in this
function (proto_reg_handoff).
*/
} else {
/* If you perform registration functions which are dependent upon
* prefs then you should de-register everything which was associated
* with the previous settings and re-register using the new prefs
* settings here. In general this means you need to keep track of
* the PROTOABBREV_handle and the value the preference had at the time
* you registered. The PROTOABBREV_handle value and the value of the
* preference can be saved using local statics in this
* function (proto_reg_handoff).
*/
dissector_delete_uint("tcp.port", currentPort, PROTOABBREV_handle);
}
currentPort = gPORT_PREF;
dissector_add_uint("tcp.port", currentPort, PROTOABBREV_handle);
}
#if 0
/* Simple form of proto_reg_handoff_PROTOABBREV which can be used if there are
no prefs-dependent registration function calls.
*/
/* Simpler form of proto_reg_handoff_PROTOABBREV which can be used if there are
* no prefs-dependent registration function calls. */
void
proto_reg_handoff_PROTOABBREV(void)
{
dissector_handle_t PROTOABBREV_handle;
/* Use new_create_dissector_handle() to indicate that dissect_PROTOABBREV()
* returns the number of bytes it dissected (or 0 if it thinks the packet
* does not belong to PROTONAME).
*/
/* Use new_create_dissector_handle() to indicate that dissect_PROTOABBREV()
* returns the number of bytes it dissected (or 0 if it thinks the packet
* does not belong to PROTONAME).
*/
PROTOABBREV_handle = new_create_dissector_handle(dissect_PROTOABBREV,
proto_PROTOABBREV);
proto_PROTOABBREV);
dissector_add_uint("PARENT_SUBFIELD", ID_VALUE, PROTOABBREV_handle);
}
#endif
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
@ -337,5 +324,3 @@ proto_reg_handoff_PROTOABBREV(void)
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/