1250 lines
44 KiB
Text
1250 lines
44 KiB
Text
$Id: README.developer,v 1.12 2000/04/29 07:57:43 guy Exp $
|
|
|
|
This file is a HOWTO for Ethereal developers. It describes how to start coding
|
|
a protocol dissector and the use some of the important functions and variables
|
|
in Ethereal.
|
|
|
|
See also the "proto_tree" file for a more detailed description of the
|
|
protocol tree construction functions.
|
|
|
|
1. Setting up your protocol dissector code.
|
|
|
|
This section provides skeleton code for a protocol dissector. It also explains
|
|
the basic functions needed to enter values in the traffic summary columns,
|
|
add to the protocol tree, and work with registered header fields.
|
|
|
|
1.1 Code style.
|
|
|
|
1.1.1 Comments.
|
|
|
|
Don't use C++-style comments (comments beginning with "//" and running to the
|
|
end of the line); Ethereal's dissectors are written in C, and thus run through C
|
|
rather than C++ compilers, and not all C compilers support C++-style comments
|
|
(GCC does, but IBM's C compiler for AIX, for example, doesn't do so by default).
|
|
|
|
1.1.2 Name convention.
|
|
|
|
Ethereal uses the underscore_convention rather than the InterCapConvention for
|
|
function names, so new code should probably use underscores rather than
|
|
intercaps for functions and variable names. This is especially important if you
|
|
are writing code that will be called from outside your code. We are just
|
|
trying to keep thing consistent for other users.
|
|
|
|
1.2 Skeleton code.
|
|
|
|
Ethereal requires certain things when setting up a protocol dissector.
|
|
Below is skeleton code for a dissector that you can copy to a file and
|
|
fill in. Your dissector should follow the naming convention of packet-
|
|
followed by the abbreviated name for the protocol. It is recommended
|
|
that where possible you keep to the IANA abbreviated name for the
|
|
protocol, if there is one, or a commonly-used abbreviation for the
|
|
protocol, if any.
|
|
|
|
You should declare the main dissector routine in a header file whose
|
|
name is "packet-", followed by the abbreviated name for the protocol,
|
|
followed by ".h"; any dissector file that calls your dissector should be
|
|
changed to include that file.
|
|
|
|
You may not need to include all the headers listed in the skeleton
|
|
below, and you may need to include additional headers. For example, the
|
|
code inside
|
|
|
|
#ifdef NEED_SNPRINTF_H
|
|
|
|
...
|
|
|
|
#endif
|
|
|
|
is needed only if you are using the "snprintf()" function.
|
|
|
|
The "$Id: README.developer,v 1.12 2000/04/29 07:57:43 guy Exp $" in the comment will be updated by CVS when the file is
|
|
checked in; it will allow the RCS "ident" command to report which
|
|
version of the file is currently checked out.
|
|
|
|
------------------------------------Cut here------------------------------------
|
|
/* packet-PROTOABBREV.c
|
|
* Routines for PROTONAME dissection
|
|
* Copyright 2000, YOUR_NAME <YOUR_EMAIL_ADDRESS>
|
|
*
|
|
* $Id: README.developer,v 1.12 2000/04/29 07:57:43 guy Exp $
|
|
*
|
|
* Ethereal - Network traffic analyzer
|
|
* By Gerald Combs <gerald@unicom.net>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* Copied from WHATEVER_FILE_YOU_USED
|
|
*
|
|
* 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 (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
# include <sys/types.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINET_IN_H
|
|
# include <netinet/in.h>
|
|
#endif
|
|
|
|
#ifdef NEED_SNPRINTF_H
|
|
# ifdef HAVE_STDARG_H
|
|
# include <stdarg.h>
|
|
# else
|
|
# include <varargs.h>
|
|
# endif
|
|
# include "snprintf.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <glib.h>
|
|
#include "packet.h"
|
|
#include "packet-PROTOABBREV.h"
|
|
|
|
/* Initialize the protocol and registered fields */
|
|
static int proto_PROTOABBREV = -1;
|
|
static int hf_PROTOABBREV_FIELDABBREV = -1;
|
|
|
|
/* Initialize the subtree pointers */
|
|
static gint ett_PROTOABBREV = -1;
|
|
|
|
/* Code to actually dissect the packets */
|
|
void
|
|
dissect_PROTOABBREV(cont u_char *pd, int offset, frame_data *fd, proto_tree *tree)
|
|
{
|
|
|
|
/* Set up structures we will need to add the protocol subtree and manage it */
|
|
proto_item *ti;
|
|
proto_tree *PROTOABBREV_tree;
|
|
|
|
/* Make entries in Protocol column and Info column on summary display */
|
|
if (check_col(fd, COL_PROTOCOL))
|
|
col_add_str(fd, COL_PROTOCOL, "PROTOABBREV");
|
|
|
|
/* This field shows up as the "Info" column in the display; you should make
|
|
it, if possible, summarize what's in the packet, so that a user looking
|
|
at the list of packets can tell what type of packet it is.
|
|
|
|
"col_add_fstr()" can be used instead of "col_add_str()"; it takes
|
|
"printf()"-like arguments. */
|
|
if (check_col(fd, COL_INFO))
|
|
col_add_str(fd, COL_INFO, "XXX Request");
|
|
|
|
/* In the interest of speed, if "tree" is NULL, don't do any work not
|
|
necessary to generate protocol tree items. */
|
|
if (tree) {
|
|
/* NOTE: The offset and length values in the previous 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.
|
|
|
|
END_OF_FRAME is a handy way to highlight all data from the offset to
|
|
the end of the packet. */
|
|
ti = proto_tree_add_item(tree, proto_PROTOABBREV, offset, END_OF_FRAME, NULL);
|
|
PROTOABBREV_tree = proto_item_add_subtree(ti, ett_PROTOABBREV);
|
|
|
|
/* Code to process the packet goes here */
|
|
|
|
|
|
}
|
|
}
|
|
|
|
/* Register the protocol with Ethereal */
|
|
proto_register_PROTOABBREV(void)
|
|
{
|
|
|
|
/* Setup list of header fields */
|
|
static hf_register_info hf[] = {
|
|
{ &hf_PROTOABBREV_FIELDABBREV,
|
|
{ "FIELDNAME", "PROTOABBREV.FIELDABBREV",
|
|
FIELDTYPE, FIELDBASE, FIELDCONVERT, BITMASK,
|
|
"FIELDDESCR" }
|
|
},
|
|
};
|
|
|
|
/* Setup protocol subtree array */
|
|
static gint *ett[] = {
|
|
&ett_PROTOABBREV,
|
|
};
|
|
|
|
/* Register the protocol name and description */
|
|
proto_PROTOABBREV = proto_register_protocol("PROTONAME", "PROTOABBREV");
|
|
|
|
/* Required function calls to register the header fields and subtrees used */
|
|
proto_register_field_array(proto_PROTOABBREV, hf, array_length(hf));
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
};
|
|
------------------------------------Cut here------------------------------------
|
|
|
|
1.3 Explanation of needed substitutions in code skeleton.
|
|
|
|
In the above code block the following strings should be substituted with
|
|
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.
|
|
PROTONAME The name of the protocol.
|
|
PROTOABBREV An abbreviated name for the protocol. (NO SPACES) (rec.
|
|
a-z, 0-9 only and try to conform with IANA names)
|
|
FIELDNAME The displayed name for the header field.
|
|
FIELDABBREV The abbreviated name for the header field. (NO SPACES)
|
|
FIELDTYPE FT_NONE, FT_BOOLEAN, FT_UINT8, FT_UINT16, FT_UINT24,
|
|
FT_UINT32, FT_INT_8, FT_INT_16, FT_INT_24, FT_INT_32,
|
|
FT_DOUBLE, FT_ABSOLUTE_TIME, FT_RELATIVE_TIME, FT_STRING,
|
|
FT_ETHER, FT_BYTES, FT_IPv4, FT_IPv6, FT_IPXNET,
|
|
FT_TEXT_ONLY
|
|
FIELDBASE BASE_NONE, BASE_DEC, BASE_HEX, BASE_OCT, BASE_BIN
|
|
FIELDCONVERT VALS(x), TFS(x), NULL
|
|
BITMASK Usually 0x0 unless using the TFS(x) field conversion.
|
|
FIELDDESCR A brief description of the field.
|
|
|
|
1.4 The dissector and the data it receives.
|
|
|
|
1.4.1 Header file.
|
|
|
|
The dissector has the following header which must be placed into
|
|
packet-PROTOABBREV.h.
|
|
|
|
void
|
|
dissect_PROTOABBREV(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
|
|
|
|
1.4.2 Extracting data from packets.
|
|
|
|
The "pd" argument to a dissector points to a buffer containing the raw
|
|
data for the frame; the "offset" argument is the offset into that buffer
|
|
of the first byte belonging to the protocol to be dissected by that
|
|
dissector.
|
|
|
|
One must not assume that the data to be dissected is aligned on a
|
|
particular boundary in memory, and therefore one must not, for example,
|
|
extract a 4-byte unsigned integer by doing
|
|
|
|
foo = *(guint32 *)pd[offset + 8];
|
|
|
|
as, if "pd + offset" is not aligned on a 4-byte boundary in memory
|
|
(because, for example, "pd" is so aligned but "offset" is not a multiple
|
|
of 4), that line of code may, when executed, cause Ethereal to crash.
|
|
|
|
In addition, one must not assume that Ethereal is running on a machine
|
|
with a particular byte order (big-endian or little-endian); if the field
|
|
one is dissecting is, for example, big-endian, code such as the example
|
|
above, even if "pd + offset" is aligned on a 4-byte boundary or if the
|
|
processor on which Ethereal is running doesn't require addresses to be
|
|
aligned on appropriate boundaries, will not work correctly on
|
|
little-endian systems such as IBM-compatible PCs or most systems with
|
|
Alpha processors - and the same problem would exist if the field were
|
|
little-endian and the code were running on big-endian systems such as
|
|
SPARC machines.
|
|
|
|
One should, instead, use the "pntohs()", "pntohl()", "pletohs()", or
|
|
"pletohl()" macros:
|
|
|
|
foo = pntohl(&pd[offset + 8]);
|
|
|
|
which will extract a 4-byte big-endian value from the field starting at
|
|
an offset of "offset + 8" from the beginning of the frame.
|
|
|
|
The macros in question extract:
|
|
|
|
pntohs() 2-byte, big-endian quantity
|
|
|
|
pntohl() 4-byte, big-endian quantity
|
|
|
|
pletohs() 2-byte, little-endian quantity
|
|
|
|
pletohl() 4-byte, little-endian quantity
|
|
|
|
1.5 Functions to handle columns in the traffic summary window.
|
|
|
|
The topmost pane of the main window is a list of the packets in the
|
|
capture, possibly filtered by a display filter.
|
|
|
|
Each line corresponds to a packet, and has one or more columns, as
|
|
configured by the user.
|
|
|
|
Many of the columns are handled by code outside individual dissectors;
|
|
most dissectors need only specify the value to put in the "Protocol" and
|
|
"Info" columns.
|
|
|
|
Columns are specified by COL_ values; the COL_ value for the "Protocol"
|
|
field, typically giving an abbreviated name for the protocol (but not
|
|
the all-lower-case abbreviation used elsewhere) is COL_PROTOCOL, and the
|
|
COL_ value for the "Info" field, giving a summary of the contents of the
|
|
packet for that protocol, is COL_INFO.
|
|
|
|
A value for a column should only be added if the user specified that it
|
|
be displayed; to check whether a given column is to be displayed, call
|
|
'col_info' with the COL_ value for that field as an argument - it will
|
|
return TRUE if the column is to be displayed and FALSE if it is not to
|
|
be displayed.
|
|
|
|
The value for a column can be specified with one of several functions,
|
|
all of which take the 'fd' argument to the dissector as their first
|
|
argument, and the COL_ value for the column as their second argument.
|
|
|
|
1.5.1 The col_add_str function.
|
|
|
|
'col_add_str' takes a string as its third argument, and sets the value
|
|
for the column to that value. For example, to set the "Protocol" column
|
|
to "PROTOABBREV":
|
|
|
|
if (check_col(fd, COL_PROTOCOL))
|
|
col_add_str(fd, COL_PROTOCOL, "PROTOABBREV");
|
|
|
|
1.5.2 The col_add_fstr function.
|
|
|
|
'col_add_fstr' takes a 'printf'-style format string as its third
|
|
argument, and 'printf'-style arguments corresponding to '%' format
|
|
items in that string as its subsequent arguments. For example, to set
|
|
the "Info" field to "<XXX> request, <N> bytes", where "reqtype" is a
|
|
string containing the type of the request in the packet and "n" is an
|
|
unsigned integer containing the number of bytes in the request:
|
|
|
|
if (check_col(fd, COL_INFO))
|
|
col_add_fstr(fd, COL_INFO, "%s request, %u bytes",
|
|
reqtype, n);
|
|
|
|
1.5.3 The col_append_str function.
|
|
|
|
Sometimes the value of a column, especially the "Info" column, can't be
|
|
conveniently constructed at a single point in the dissection process;
|
|
for example, it might contain small bits of information from many of the
|
|
fields in the packet. 'col_append_str' takes, as arguments, the same
|
|
arguments as 'col_add_str', but the string is appended to the end of the
|
|
current value for the column, rather than replacing the value for that
|
|
column. (Note that no blank separates the appended string from the
|
|
string to which it is appended; if you want a blank there, you must add
|
|
it yourself as part of the string being appended.)
|
|
|
|
1.5.4 The col_append_fstr function.
|
|
|
|
'col_append_fstr' is to 'col_add_fstr' as 'col_append_str' is to
|
|
'col_add_str' - it takes, as arguments, the same arguments as
|
|
'col_add_fstr', but the formatted string is appended to the end of the
|
|
current value for the column, rather than replacing the value for that
|
|
column.
|
|
|
|
1.6 Constructing the protocol tree.
|
|
|
|
The middle pane of the main window, and the topmost pane of a packet
|
|
popup window, are constructed from the "protocol tree" for a packet.
|
|
|
|
The protocol tree, or proto_tree, is a GNode, the N-way tree structure
|
|
available within GLIB. Of course the protocol dissectors don't care
|
|
what a proto_tree really is; they just pass the proto_tree pointer as an
|
|
argument to the routines which allow them to add items and new branches
|
|
to the tree.
|
|
|
|
When a packet is selected in the packet-list pane, or a packet popup
|
|
window is created, a new logical protocol tree (proto_tree) is created.
|
|
The pointer to the proto_tree (in this case, 'protocol tree'), is passed
|
|
to the top-level protocol dissector, and then to all subsequent protocol
|
|
dissectors for that packet, and then the GUI tree is drawn via
|
|
proto_tree_draw().
|
|
The logical proto_tree needs to know detailed information about the
|
|
protocols and fields about which information will be collected from the
|
|
dissection routines. By strictly defining (or "typing") the data that can
|
|
be attached to a proto tree, searching and filtering becomes possible.
|
|
This means that the for every protocol and field (which I also call
|
|
"header fields", since they are fields in the protocol headers) which
|
|
might be attached to a tree, some information is needed.
|
|
|
|
Every dissector routine will need to register its protocols and fields
|
|
with the central protocol routines (in proto.c). At first I thought I
|
|
might keep all the protocol and field information about all the
|
|
dissectors in one file, but decentralization seemed like a better idea.
|
|
That one file would have gotten very large; one small change would have
|
|
required a re-compilation of the entire file. Also, by allowing
|
|
registration of protocols and fields at run-time, loadable modules of
|
|
protocol dissectors (perhaps even user-supplied) is feasible.
|
|
|
|
To do this, each protocol should have a register routine, which will be
|
|
called when Ethereal starts. The code to call the register routines is
|
|
generated automatically; to arrange that a protocol's register routine
|
|
be called at startup:
|
|
|
|
the file containing a dissector's "register" routine must be
|
|
added to "DISSECTOR_SOURCES" in "Makefile.am";
|
|
|
|
the "register" routine must have a name of the form
|
|
"proto_register_XXX";
|
|
|
|
the "register" routine must take no argument, and return no
|
|
value;
|
|
|
|
the "register" routine's name must appear in the source file
|
|
either at the beginning of the line, or preceded only by "void "
|
|
at the beginning of the line (that'd typically be the
|
|
definition) - other white space shouldn't cause a problem, e.g.:
|
|
|
|
void proto_register_XXX(void) {
|
|
|
|
...
|
|
|
|
}
|
|
|
|
and
|
|
|
|
void
|
|
proto_register_XXX( void )
|
|
{
|
|
|
|
...
|
|
|
|
}
|
|
|
|
and so on should work.
|
|
|
|
For every protocol or field that a dissector wants to register, a variable of
|
|
type int needs to be used to keep track of the protocol. The IDs are
|
|
needed for establishing parent/child relationships between protocols and
|
|
fields, as well as associating data with a particular field so that it
|
|
can be stored in the logical tree and displayed in the GUI protocol
|
|
tree.
|
|
|
|
Some dissectors will need to create branches within their tree to help
|
|
organize header fields. These branches should be registered as header
|
|
fields. Only true protocols should be registered as protocols. This is
|
|
so that a display filter user interface knows how to distinguish
|
|
protocols from fields.
|
|
|
|
A protocol is registered with the name of the protocol and its
|
|
abbreviation.
|
|
|
|
Here is how the frame "protocol" is registered.
|
|
|
|
int proto_frame;
|
|
|
|
proto_frame = proto_register_protocol (
|
|
/* name */ "Frame",
|
|
/* abbrev */ "frame" );
|
|
|
|
A header field is also registered with its name and abbreviation, but
|
|
information about the its data type is needed. It helps to look at
|
|
the header_field_info struct to see what information is expected:
|
|
|
|
struct header_field_info {
|
|
char *name;
|
|
char *abbrev;
|
|
enum ftenum type;
|
|
int display;
|
|
void *strings;
|
|
guint bitmask;
|
|
char *blurb;
|
|
|
|
int id; /* calculated */
|
|
int parent;
|
|
int bitshift; /* calculated */
|
|
};
|
|
|
|
name
|
|
----
|
|
A string representing the name of the field. This is the name
|
|
that will appear in the graphical protocol tree.
|
|
|
|
abbrev
|
|
------
|
|
A string with an abbreviation of the field. We concatenate the
|
|
abbreviation of the parent protocol with an abbreviation for the field,
|
|
using a period as a separator. For example, the "src" field in an IP packet
|
|
would have "ip.addr" as an abbreviation. It is acceptable to have
|
|
multiple levels of periods if, for example, you have fields in your
|
|
protocol that are then subdivided into subfields. For example, TRMAC
|
|
has multiple error fields, so the abbreviations follow this pattern:
|
|
"trmac.errors.iso", "trmac.errors.noniso", etc.
|
|
|
|
The abbreviation is the identifier used in a display filter.
|
|
|
|
type
|
|
----
|
|
The type of value this field holds. The current field types are:
|
|
|
|
FT_NONE No field type. Used for protocol labels.
|
|
FT_BOOLEAN 0 means "false", any other value means
|
|
"true".
|
|
FT_UINT8 An 8-bit unsigned integer.
|
|
FT_UINT16 A 16-bit unsigned integer.
|
|
FT_UINT24 A 24-bit unsigned integer.
|
|
FT_UINT32 A 32-bit unsigned integer.
|
|
FT_INT8 An 8-bit signed integer.
|
|
FT_INT16 A 16-bit signed integer.
|
|
FT_INT24 A 24-bit signed integer.
|
|
FT_INT32 A 32-bit signed integer.
|
|
FT_DOUBLE A floating point number.
|
|
FT_ABSOLUTE_TIME Seconds (4 bytes) and microseconds (4 bytes)
|
|
of time displayed as month name, month day,
|
|
year, hours, minutes, and seconds with 4
|
|
digits after the decimal point.
|
|
FT_RELATIVE_TIME Seconds (4 bytes) and microseconds (4 bytes)
|
|
of time displayed as seconds and 6 digits
|
|
after the decimal point.
|
|
FT_STRING A string of characters.
|
|
FT_ETHER A six octet string displayed in
|
|
Ethernet-address format.
|
|
FT_BYTES A string of bytes.
|
|
FT_IPv4 A version 4 IP address (4 bytes) displayed
|
|
in dotted-quad IP address format (4
|
|
decimal numbers separated by dots).
|
|
FT_IPv6 A version 6 IP address (16 bytes) displayed
|
|
in standard IPv6 address format.
|
|
FT_IPXNET An IPX address displayed in hex as a 6-byte
|
|
network number followed by a 6-byte station
|
|
address.
|
|
FT_TEXT_ONLY A reserved, non-filterable type for
|
|
converting old style trees. You shouldn't
|
|
be using this.
|
|
|
|
Some of these field types are still not handled in the display filter
|
|
routines, but the most common ones are. The FT_UINT* variables all
|
|
represent unsigned integers, and the FT_INT* variables all represent
|
|
signed integers; the number on the end represent how many bits are used
|
|
to represent the number.
|
|
|
|
display
|
|
-------
|
|
The display field has a couple of overloaded uses. This is unfortunate,
|
|
but since we're C as an application programming language, this sometimes
|
|
makes for cleaner programs. Right now I still think that overloading
|
|
this variable was okay.
|
|
|
|
For integer fields (FT_UINT* and FT_INT*), this variable represents the
|
|
base in which you would like the value displayed. The acceptable bases
|
|
are:
|
|
|
|
BASE_DEC,
|
|
BASE_HEX,
|
|
BASE_OCT,
|
|
BASE_BIN
|
|
|
|
For FT_BOOLEAN fields that are also bitfields, 'display' is used to tell
|
|
the proto_tree how wide the parent bitfield is. With integers this is
|
|
not needed since the type of integer itself (FT_UINT8, FT_UINT16,
|
|
FT_UINT24, FT_UINT32, etc.) tells the proto_tree how wide the parent
|
|
bitfield is.
|
|
|
|
Additionally, BASE_NONE is used for 'display' as a NULL-value. That is,
|
|
for non-integers and non-bitfield FT_BOOLEANs, you'll want to use BASE_NONE
|
|
in the 'display' field.
|
|
|
|
It is possible that in the future we will record the endianness of
|
|
integers. If so, it is likely that we'll use a bitmask on the display field
|
|
so that integers would be represented as BEND|BASE_DEC or LEND|BASE_HEX.
|
|
But that has not happened yet.
|
|
|
|
strings
|
|
-------
|
|
Some integer fields, of type FT_UINT*, need labels to represent the true
|
|
value of a field. You could think of those fields as having an
|
|
enumerated data type, rather than an integral data type.
|
|
|
|
A 'value_string' structure is a way to map values to strings.
|
|
|
|
typedef struct _value_string {
|
|
guint32 value;
|
|
gchar *strptr;
|
|
} value_string;
|
|
|
|
For fields of that type, yoou would declare an array of "value_string"s:
|
|
|
|
static const value_string valstringname[] = {
|
|
{ INTVAL1, "Descriptive String 1" },
|
|
{ INTVAL2, "Descriptive String 2" },
|
|
{ 0, NULL },
|
|
};
|
|
|
|
(the last entry in the array must have a NULL 'strptr' value, to
|
|
indicate the end of the array). The 'strings' field would be set to
|
|
'VALS(valstringname)'.
|
|
|
|
(Note: before Ethereal 0.7.6, we had separate field types like
|
|
FT_VALS_UINT8 which denoted the use of value_strings. Now, the
|
|
non-NULLness of the pointer lets the proto_tree know that a value_string
|
|
is meant for this field).
|
|
|
|
If the field has a numeric rather than an enumerated type, the 'strings'
|
|
field would be set to NULL.
|
|
|
|
FT_BOOLEANS have a default map of 0 = "False", 1 (or anything else) = "True".
|
|
Sometimes it is useful to change the labels for boolean values (e.g.,
|
|
to "Yes"/"No", "Fast"/"Slow", etc.). For these mappings, a struct called
|
|
true_false_string is used. (This struct is new as of Ethereal 0.7.6).
|
|
|
|
typedef struct true_false_string {
|
|
char *true_string;
|
|
char *false_string;
|
|
} true_false_string;
|
|
|
|
For Boolean fields for which "False" and "True" aren't the desired
|
|
labels, you would declare a "true_false_string"s:
|
|
|
|
static const true_false_string boolstringname = {
|
|
"String for True",
|
|
"String for False"
|
|
};
|
|
|
|
Its two fields are pointers to the string representing truth, and the
|
|
string representing falsehood. For FT_BOOLEAN fields that need a
|
|
'true_false_string' struct, the 'strings' field would be set to
|
|
'TFS(&boolstringname)'.
|
|
|
|
If the Boolean field is to be displayed as "False" or "True", the
|
|
'strings' field would be set to NULL.
|
|
|
|
bitmask
|
|
-------
|
|
If the field is a bitfield, then the bitmask is the mask which will
|
|
leave only the bits needed to make the field when ANDed with a value.
|
|
The proto_tree routines will calculate 'bitshift' automatically
|
|
from 'bitmask', by finding the rightmost set bit in the bitmask.
|
|
If the field is not a bitfield, then bitmask should be set to 0.
|
|
|
|
blurb
|
|
-----
|
|
This is a string giving a proper description of the field.
|
|
It should be at least one grammatically complete sentence.
|
|
It is meant to provide a more detailed description of the field than the
|
|
name alone provides. This information will be used in the man page, and
|
|
in a future GUI display-filter creation tool. We might also add tooltips
|
|
to the labels in the GUI protocol tree, in which case the blurb would
|
|
be used as the tooltip text.
|
|
|
|
1.6.1 Field Registration.
|
|
|
|
Protocol registration is handled by creating an instance of the
|
|
header_field_info struct (or an array of such structs), and
|
|
calling the registration function along with the registration ID of
|
|
the protocol that is the parent of the fields. Here is a complete example:
|
|
|
|
static int proto_eg = -1;
|
|
static int hf_field_a = -1;
|
|
static int hf_field_b = -1;
|
|
|
|
static hf_register_info hf[] = {
|
|
|
|
{ &hf_field_a,
|
|
{ "Field A", "proto.field_a", FT_UINT8, BASE_HEX, NULL,
|
|
0xf0, "Field A represents Apples" }},
|
|
|
|
{ &hf_field_b,
|
|
{ "Field B", "proto.field_b", FT_UINT16, BASE_DEC, VALS(vs),
|
|
0x0, "Field B represents Bananas" }}
|
|
};
|
|
|
|
proto_eg = proto_register_protocol("Example Protocol", "proto");
|
|
proto_register_field_array(proto_eg, hf, array_length(hf));
|
|
|
|
Be sure that your array of hf_register_info structs is declared 'static',
|
|
since the proto_register_field_array() function does not create a copy
|
|
of the information in the array... it uses that static copy of the
|
|
information that the compiler created inside your array. Here's the
|
|
layout of the hf_register_info struct:
|
|
|
|
typedef struct hf_register_info {
|
|
int *p_id; /* pointer to parent variable */
|
|
header_field_info hfinfo;
|
|
} hf_register_info;
|
|
|
|
Also be sure to use the handy array_length() macro found in packet.h
|
|
to have the compiler compute the array length for you at compile time.
|
|
|
|
1.6.2 Adding Items and Values to the Protocol Tree.
|
|
|
|
A protocol item is added to an existing protocol tree with one of a
|
|
handful of proto_tree_add_XXX() funtions.
|
|
|
|
Subtrees can be made with the proto_item_add_subtree() function:
|
|
|
|
item = proto_tree_add_item(....);
|
|
new_tree = proto_item_add_subtree(item, tree_type);
|
|
|
|
This will add a subtree under the item in question; a subtree can be
|
|
created under an item made by any of the "proto_tree_add_XXX" functions,
|
|
so that the tree can be given an arbitrary depth.
|
|
|
|
Subtree types are integers, assigned by
|
|
"proto_register_subtree_array()". To register subtree types, pass an
|
|
array of pointers to "gint" variables to hold the subtree type values to
|
|
"proto_register_subtree_array()":
|
|
|
|
static gint ett_eg = -1;
|
|
static gint ett_field_a = -1;
|
|
|
|
static gint *ett[] = {
|
|
&ett_eg,
|
|
&ett_field_a,
|
|
};
|
|
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
|
|
in your "register" routine, just as you register the protocol and the
|
|
fields for that protocol.
|
|
|
|
There are several functions that the programmer can use to add either
|
|
protocol or field labels to the proto_tree:
|
|
|
|
proto_item*
|
|
proto_tree_add_item(tree, id, start, length, value);
|
|
|
|
proto_item*
|
|
proto_tree_add_item_hidden(tree, id, start, length, value);
|
|
|
|
proto_item*
|
|
proto_tree_add_protocol_format(tree, id, start, length, format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_bytes_format(tree, id, start, length, start_ptr,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_time_format(tree, id, start, length, value_ptr,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipxnet_format(tree, id, start, length, value,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipv4_format(tree, id, start, length, value,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipv6_format(tree, id, start, length, value_ptr,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_ether_format(tree, id, start, length, value_ptr,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_string_format(tree, id, start, length, value_ptr,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_boolean_format(tree, id, start, length, value,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_uint_format(tree, id, start, length, value,
|
|
format, ...);
|
|
|
|
proto_item*
|
|
proto_tree_add_text(tree, start, length, format, ...);
|
|
|
|
proto_item*
|
|
proto_tree_add_notext(tree, start, length);
|
|
|
|
The 'tree' argument is the tree to which the item is to be added. The
|
|
'start' argument is the offset from the beginning of the frame (not the
|
|
offset from the beginning of the part of the packet belonging to this
|
|
protocol, but the offset from the beginning of the frame as a whole) of
|
|
the item being added, and the 'length' argument is the length, in bytes,
|
|
of the item.
|
|
|
|
The length of some items cannot be determined until the item has been
|
|
dissected; to add such an item, add it with a length of 0, and, when the
|
|
dissection is complete (or fails because the packet is too short), set
|
|
the length with 'proto_item_set_len()':
|
|
|
|
void
|
|
proto_item_set_len(ti, length);
|
|
|
|
The "ti" argument is the value returned by the call that added the item
|
|
to the tree, and the "length" argument is the length of the item.
|
|
|
|
proto_tree_add_item()
|
|
---------------------
|
|
proto_tree_add_item is used when you wish to do no special formatting.
|
|
The item added to the GUI tree will contain the name (as passed in the
|
|
proto_register_*() function) and any value. If your field does have a
|
|
value, it is passed after the length variable (notice the ellipsis in
|
|
the function prototype).
|
|
|
|
Now that the proto_tree has detailed information about bitfield fields,
|
|
you can use proto_tree_add_item() with no extra processing to add bitfield
|
|
values to your tree. Here's an example. Take the Format Identifer (FID)
|
|
field in the Transmission Header (TH) portion of the SNA protocol. The
|
|
FID is the high nibble of the first byte of the TH. The FID would be
|
|
registered like this:
|
|
|
|
name = "Format Identifer"
|
|
abbrev = "sna.th.fid"
|
|
type = FT_UINT8
|
|
display = BASE_HEX
|
|
strings = sna_th_fid_vals
|
|
bitmask = 0xf0
|
|
|
|
The bitmask contains the value which would leave only the FID if bitwise-ANDed
|
|
against the parent field, the first byte of the TH.
|
|
|
|
The code to add the FID to the tree would be;
|
|
|
|
guint8 th_0 = pd[offset];
|
|
proto_tree_add_item(bf_tree, hf_sna_th_fid, offset, 1, th_0);
|
|
|
|
Note: we do not do *any* manipulation of th_0 in order to ge the FID value.
|
|
We just pass it to proto_tree_add_item(). The proto_tree already has
|
|
the information about bitmasking and bitshifting, so it does the work
|
|
of masking and shifting for us! This also means that you no longer
|
|
have to crate value_string structs with the values bitshifted. The
|
|
value_string for FID looks like this, even though the FID value is
|
|
actually contained in the high nibble. (You'd expect the values to be
|
|
0x0, 0x10, 0x20, etc.)
|
|
|
|
/* Format Identifier */
|
|
static const value_string sna_th_fid_vals[] = {
|
|
{ 0x0, "SNA device <--> Non-SNA Device" },
|
|
{ 0x1, "Subarea Node <--> Subarea Node" },
|
|
{ 0x2, "Subarea Node <--> PU2" },
|
|
{ 0x3, "Subarea Node or SNA host <--> Subarea Node" },
|
|
{ 0x4, "?" },
|
|
{ 0x5, "?" },
|
|
{ 0xf, "Adjaced Subarea Nodes" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
The final implication of this is that display filters work the way you'd
|
|
naturally expect them to. You'd type "sna.th.fid == 0xf" to find Adjacent
|
|
Subarea Nodes. The user does not have to shift the value of the FID to
|
|
the high nibble of the byte ("sna.th.fid == 0xf0") as was necessary
|
|
before Ethereal 0.7.6.
|
|
|
|
proto_tree_add_item_hidden()
|
|
----------------------------
|
|
proto_tree_add_item_hidden is used to add fields and values to a tree,
|
|
but not show them on a GUI tree. The caller may want a value to be
|
|
included in a tree so that the packet can be filtered on this field, but
|
|
the representation of that field in the tree is not appropriate. An
|
|
example is the token-ring routing information field (RIF). The best way
|
|
to show the RIF in a GUI is by a sequence of ring and bridge numbers.
|
|
Rings are 3-digit hex numbers, and bridges are single hex digits:
|
|
|
|
RIF: 001-A-013-9-C0F-B-555
|
|
|
|
In the case of RIF, the programmer should use a field with no value and
|
|
use proto_tree_add_item_format() to build the above representation. The
|
|
programmer can then add the ring and bridge values, one-by-one, with
|
|
proto_tree_add_item_hidden() so that the user can then filter on or
|
|
search for a particular ring or bridge. Here's a skeleton of how the
|
|
programmer might code this.
|
|
|
|
char *rif;
|
|
rif = create_rif_string(...);
|
|
|
|
proto_tree_add_item_format(tree, hf_tr_rif_label,..., "RIF: %s", rif);
|
|
|
|
for(i = 0; i < num_rings; i++) {
|
|
proto_tree_add_item_hidden(tree, hf_tr_rif_ring, ..., ring[i]);
|
|
}
|
|
for(i = 0; i < num_rings - 1; i++) {
|
|
proto_tree_add_item_hidden(tree, hf_tr_rif_ring, ..., bridge[i]);
|
|
}
|
|
|
|
The logical tree has these items:
|
|
|
|
hf_tr_rif_label, text="RIF: 001-A-013-9-C0F-B-555", value = NONE
|
|
hf_tr_rif_ring, hidden, value=0x001
|
|
hf_tr_rif_bridge, hidden, value=0xA
|
|
hf_tr_rif_ring, hidden, value=0x013
|
|
hf_tr_rif_bridge, hidden, value=0x9
|
|
hf_tr_rif_ring, hidden, value=0xC0F
|
|
hf_tr_rif_bridge, hidden, value=0xB
|
|
hf_tr_rif_ring, hidden, value=0x555
|
|
|
|
GUI or print code will not display the hidden fields, but a display
|
|
filter or "packet grep" routine will still see the values. The possible
|
|
filter is then possible:
|
|
|
|
tr.rif_ring eq 0x013
|
|
|
|
proto_tree_add_protocol_format()
|
|
----------------------------
|
|
proto_tree_add_protocol_format is used to add the top-level item for the
|
|
protocol when the dissector routines wants complete control over how the
|
|
field and value will be represented on the GUI tree. The ID value for
|
|
the protocol is passed in as the "id" argument; the rest of the
|
|
arguments are a "printf"-style format and any arguments for that format.
|
|
The caller must include the name of the protocol in the format; it is
|
|
not added automatically as in proto_tree_add_item().
|
|
|
|
proto_tree_add_bytes_format()
|
|
proto_tree_add_time_format()
|
|
proto_tree_add_ipxnet_format()
|
|
proto_tree_add_ipv4_format()
|
|
proto_tree_add_ipv6_format()
|
|
proto_tree_add_ether_format()
|
|
proto_tree_add_string_format()
|
|
proto_tree_add_boolean_format()
|
|
proto_tree_add_uint_format()
|
|
----------------------------
|
|
|
|
The other "proto_tree_add_XXX_format" routines are used to add items to
|
|
the protocol tree when the dissector routines wants complete control
|
|
over how the field and value will be represented on the GUI tree.
|
|
|
|
For "proto_tree_add_time_format", the "value_ptr" argument is a pointer
|
|
to a "struct timeval" containing the time to be added; for those other
|
|
functions that take a "value_ptr" argument, that argument is a pointer
|
|
to the first byte of the value to be added.
|
|
|
|
For the other functions, the "value" argument is a 32-bit integral value
|
|
to be added.
|
|
|
|
The rest of the arguments are a "printf"-style format and any arguments
|
|
for that format. The caller must include the name of the field in the
|
|
format; it is not added automatically as in proto_tree_add_item().
|
|
|
|
proto_tree_add_text()
|
|
---------------------
|
|
The fourth function, proto_tree_add_text(), is used to add a label to
|
|
the GUI tree. It will contain no value, so it is not searchable in the
|
|
display filter process. This function was needed in the transition from
|
|
the old-style proto_tree to this new-style proto_tree so that Ethereal
|
|
would still decode all protocols w/o being able to filter on all
|
|
protocols and fields. Otherwise we would have had to cripple Ethereal's
|
|
functionality while we converted all the old-style proto_tree calls to
|
|
the new-style proto_tree calls.
|
|
|
|
This can also be used for items with subtrees, which may not have values
|
|
themselves - the items in the subtree are the ones with values.
|
|
|
|
proto_tree_add_notext()
|
|
-----------------------
|
|
The fifth function, proto_tree_add_notext(), is used to add an item to
|
|
the logical tree that will have only a label, and no value (so it is not
|
|
searchable in the display filter process), but that doesn't yet have a
|
|
label, either. This is for items where the value is to be filled in
|
|
later. This is typically used for an item with a subtree, where the
|
|
label is to contain a summary of the subtree, with the values of some of
|
|
the fields in the subtree shown in the label of the item for the subtree
|
|
as a whole; the item can be created as a placeholder, with the label
|
|
added when the dissection is complete - and, if the dissection doesn't
|
|
complete because the packet is too short and not all the required fields
|
|
are present, the label could be set to something indicating this.
|
|
|
|
The text is set by 'proto_item_set_text()':
|
|
|
|
void
|
|
proto_tree_set_text(proto_item *ti, ...);
|
|
|
|
which takes as an argument the value returned by
|
|
'proto_tree_add_notext()', a 'printf'-style format string, and a set of
|
|
arguments corresponding to '%' format items in that string. For
|
|
example, early in the dissection, one might do:
|
|
|
|
ti = proto_tree_add_notext(tree, offset, length);
|
|
|
|
and later do
|
|
|
|
proto_item_set_text(ti, "%s: %s", type, value);
|
|
|
|
after the "type" and "value" fields have been extracted and dissected.
|
|
|
|
1.7 Utility routines
|
|
|
|
1.7.1 match_strval and val_to_str
|
|
|
|
A dissector may need to convert a value to a string, using a
|
|
'value_string' structure, by hand, rather than by declaring a field with
|
|
an associated 'value_string' structure; this might be used, for example,
|
|
to generate a COL_INFO line for a frame.
|
|
|
|
'match_strval()' will do that:
|
|
|
|
gchar*
|
|
match_strval(guint32 val, const value_string *vs)
|
|
|
|
It will look up the value 'val' in the 'value_string' table pointed to
|
|
by 'vs', and return either the corresponding string, or NULL if the
|
|
value could not be found in the table.
|
|
|
|
'val_to_str()' can be used to generate a string for values not found in
|
|
the table:
|
|
|
|
gchar*
|
|
val_to_str(guint32 val, const value_string *vs, const char *fmt)
|
|
|
|
If the value 'val' is found in the 'value_string' table pointed to by
|
|
'vs', 'val_to_str' will return the corresponding string; otherwise, it
|
|
will use 'fmt' as an 'sprintf'-style format, with 'val' as an argument,
|
|
to generate a string, and will return a pointer to that string.
|
|
(Currently, it has three 64-byte static buffers, and cycles through
|
|
them; this permits the results of up to three calls to 'val_to_str' to
|
|
be passed as arguments to a routine using those strings.)
|
|
|
|
1.8 Editing Makefile.am and Makefile.nmake to add your dissector.
|
|
|
|
To arrange that your dissector will be built as part of Ethereal, you
|
|
must add the name of the source file for your dissector, and the header
|
|
file that declares your main dissector routine, to the
|
|
'DISSECTOR_SOURCES' macro in the 'Makefile.am' file in the top-level
|
|
directory, and must add the name the object file for the dissector will
|
|
have when built on Windows - if your dissector source file is
|
|
'packet-PROTOABBREV.c', the object file for it will be
|
|
'packet-PROTOABBREV.obj' - to the 'DISSECTOR_OBJECTS' macro in the
|
|
'Makefile.nmake' file in the top-level directory. (Note that this is
|
|
for modern versions of UNIX, so there is no 14-character limitation on
|
|
file names, and for modern versions of Windows, so there is no
|
|
8.3-character limitation on file names.)
|
|
|
|
Please remember to update both files; it may not be necessary to do so
|
|
in order for you to build Ethereal on your machine, but both changes
|
|
will need to be checked in to the Ethereal source code, to allow it to
|
|
build on all platforms.
|
|
|
|
1.9 Using the CVS source code tree.
|
|
|
|
1.10 Submitting code for your new dissector.
|
|
|
|
2. Advanced dissector topics.
|
|
|
|
2.1 ??
|
|
|
|
2.2 Following "conversations."
|
|
|
|
In ethereal a conversation is defined as a series of data packet between two
|
|
address:port combinations. A conversation is not sensitive to the direction of
|
|
the packet. The same conversation will be returned for a packet bound from
|
|
ServerA:1000 to ClientA:2000 and the packet from ClientA:2000 to ServerA:1000.
|
|
There are two routine that you will use to work with a conversation:
|
|
conversation_new and find_conversation.
|
|
|
|
|
|
2.2.1 The conversation_init function.
|
|
|
|
This is an internal routine for the conversation code. As such the you will not
|
|
have to call this routine. Just be aware that this routine is called at the
|
|
start of each capture and before the packets are filtered with a display filter.
|
|
The routine will destroy all stored conversations. This routine does NOT clean
|
|
up any data pointers that is passed in the conversation_new 'data' variable.
|
|
You are responsible for this clean up if you pass a malloc'ed pointer in this
|
|
variable.
|
|
|
|
See item 2.2.4 for more information about the 'data' pointer.
|
|
|
|
|
|
2.2.2 The conversation_new function.
|
|
|
|
This routine will create a new conversation based upon the source address:port
|
|
and destination address:port. If you want store a pointer to memory structure it
|
|
should be passed in the conversation_new 'data' variable. The ptype variable is
|
|
used to differentiate between conversations over different protocols, ie. TCP
|
|
and UDP.
|
|
|
|
See packet.h for information on the port_type.
|
|
|
|
|
|
2.2.3 The find_conversation function.
|
|
|
|
Call this routine to lookup a conversation. If no conversation is found the
|
|
routine will return a NULL value. You don't have to worry about interchanging
|
|
the source and destination values. The conversation routine will automatically
|
|
return the same conversation for packets traveling in both directions.
|
|
|
|
|
|
2.2.4 The example conversation code with GMemChunk's
|
|
|
|
For a conversation between two IP addresses and ports you can use this as an
|
|
example. This example uses the GMemChunk to allocate memory and stores the data
|
|
pointer in the conversation 'data' variable.
|
|
|
|
NOTE: Remember to register the init routine (my_dissector_init) in the
|
|
protocol_register routine.
|
|
|
|
|
|
/************************ Globals values ************************/
|
|
|
|
/* the number of entries in the memory chunk array */
|
|
#define my_init_count 10
|
|
|
|
/* define your structure here */
|
|
typedef struct {
|
|
|
|
}my_entry_t;
|
|
|
|
/* the GMemChunk base structure */
|
|
static GMemChunk *my_vals = NULL;
|
|
|
|
|
|
/********************* in the dissector routine *********************/
|
|
|
|
/* the local variables in the dissector */
|
|
|
|
conversation_t *conversation;
|
|
my_entry_t *data_ptr
|
|
|
|
|
|
/* look up the conversation */
|
|
/* pi is a global variable of type packet_info, see packet.h */
|
|
|
|
conversation = find_conversation( &pi.src, &pi.dst, pi.ptype,
|
|
pi.srcport, pi.destport);
|
|
|
|
/* if conversation found get the data pointer that you stored */
|
|
if ( conversation)
|
|
data_ptr = (my_entry_t*)conversation->data;
|
|
else {
|
|
|
|
/* new conversation create local data structure */
|
|
|
|
data_ptr = g_mem_chunk_alloc(my_protocol_vals);
|
|
|
|
/*** add your code here to setup the new data structure ***/
|
|
|
|
/* create the conversation with your data pointer */
|
|
|
|
conversation_new( &pi.src, &pi.dst, pi.ptype,
|
|
pi.srcport, pi.destport, (void*)data_ptr);
|
|
}
|
|
|
|
/* at this point the conversation data is ready */
|
|
|
|
|
|
/******************* in the dissector init routine *******************/
|
|
|
|
#define rlogin_hash_init_count 20
|
|
|
|
static void
|
|
my_dissector_init( void){
|
|
|
|
/* destory memory chunks if needed */
|
|
|
|
if ( my_vals)
|
|
g_mem_chunk_destroy(my_vals);
|
|
|
|
/* now create memory chunks */
|
|
|
|
my_vals = g_mem_chunk_new( "my_proto_vals",
|
|
sizeof( _entry_t),
|
|
my_init_count * sizeof( my_entry_t),
|
|
G_ALLOC_AND_FREE);
|
|
}
|
|
|
|
/***************** in the protocol register routine *****************/
|
|
|
|
/* register re-init routine */
|
|
|
|
register_init_routine( &my_dissector_init);
|
|
|
|
|
|
2.2.4 The example conversation code using conversation index field
|
|
|
|
Add this latter ............
|
|
|
|
|
|
2.3 ??
|
|
|
|
3. Plugins
|
|
|
|
Writing a "plugin" dissector is not very different from writing a standard one.
|
|
All the functions described in the first part of this file can be used in
|
|
plugins exactly as they are used in standard dissectors.
|
|
|
|
However, there are a few things you need to do (you can look at the gryphon
|
|
plugin for an example) :
|
|
|
|
3.1 Needed headers
|
|
|
|
#include "plugins/plugin_api.h"
|
|
|
|
Some OSes (Win32) have DLLs that cannot reference symbols in the parent
|
|
executable. So, the executable needs to provide a table of pointers for the DLL
|
|
plugin to use. The plugin_api.h header provides definitions for this (or empty
|
|
definitions on OSes which don't need this).
|
|
|
|
#include "moduleinfo.h"
|
|
|
|
This header is optional. It is used by the gryphon plugin to provide a VERSION
|
|
macro (different from the ethereal VERSION).
|
|
|
|
Ex :
|
|
$ cat moduleinfo.h
|
|
/* Included *after* config.h, in order to re-define these macros */
|
|
#ifdef VERSION
|
|
#undef VERSION
|
|
#endif
|
|
|
|
/* Plugin version number */
|
|
#define VERSION "0.1.2"
|
|
|
|
3.2 Exported constants
|
|
|
|
Plugins need to provide the following exported constants (the DLLEXPORT macro is
|
|
defined in plugin_api.h) :
|
|
|
|
DLLEXPORT const gchar version[] = VERSION;
|
|
DLLEXPORT const gchar desc[] = "DG Gryphon Protocol";
|
|
DLLEXPORT const gchar protocol[] = "tcp";
|
|
DLLEXPORT const gchar filter_string[] = "tcp.port == 7000";
|
|
|
|
version : a version number associated with the plugin.
|
|
desc : description of the dissector (displayed in the plugin selection
|
|
window).
|
|
protocol : name of the underlying protocol (e.g. if protocol == "xxx", the
|
|
plugin will be called from dissect_xxx). Only "tcp" and "udp"
|
|
are supported for now.
|
|
filter_string : display filter which is applied to a frame to determine if it
|
|
should be dissected by this plugin.
|
|
|
|
The above definitions, taken from the gryphon plugin, show that the gryphon
|
|
plugin will be called in dissect_tcp() if the TCP source or destination port is
|
|
7000.
|
|
|
|
3.3 Exported functions
|
|
|
|
The following two functions need to be exported by the plugin :
|
|
|
|
DLLEXPORT void dissector(const u_char *pd, int offset, frame_data *fd,
|
|
proto_tree *tree)
|
|
|
|
This function should be similar to any other dissect_xxx() function, except for
|
|
its name.
|
|
|
|
DLLEXPORT void plugin_init(plugin_address_table_t *pat)
|
|
|
|
This function is called by ethereal when the plugin is initialized. Here is a
|
|
sample code for the function :
|
|
|
|
/* initialise the table of pointers needed in Win32 DLLs */
|
|
plugin_address_table_init(pat);
|
|
/* destroy the dfilter tree */
|
|
dfilter_cleanup();
|
|
/* register the new protocol, protocol fields, and subtrees */
|
|
proto_xxx = proto_register_protocol("XXX Protocol", "xxx");
|
|
proto_register_field_array(proto_xxx, hf, array_length(hf));
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
/* initialize the dfilter tree with all the header field and protocol
|
|
* abbrevs defined, including xxx */
|
|
dfilter_init();
|
|
|
|
4.0 Extending Wiretap.
|
|
|
|
5.0 Adding new capabilities.
|
|
|
|
|
|
|
|
|
|
James Coe <jammer@cin.net>
|
|
Gilbert Ramirez <gram@xiexie.org>
|
|
Jeff Foster <jfoste@woodward.com>
|
|
Olivier Abad <abad@daba.dhis.net>
|