![Guy Harris](/assets/img/avatar_default.png)
adding to DISSECTOR_SRC instead, and update it to mention "Makefile.nmake" in the first instance. svn path=/trunk/; revision=8966
2337 lines
86 KiB
Text
2337 lines
86 KiB
Text
$Id: README.developer,v 1.86 2003/11/14 19:20:24 guy Exp $
|
|
|
|
This file is a HOWTO for Ethereal developers. It describes how to start coding
|
|
a Ethereal protocol dissector and the use some of the important functions and
|
|
variables.
|
|
|
|
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 Portability.
|
|
|
|
Ethereal runs on many platforms, and can be compiled with a number of
|
|
different compilers; here are some rules for writing code that will work
|
|
on multiple platforms.
|
|
|
|
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).
|
|
|
|
Don't use zero-length arrays; not all compilers support them. If an
|
|
array would have no members, just leave it out.
|
|
|
|
Don't declare variables in the middle of executable code; not all C
|
|
compilers support that. Variables should be declared outside a
|
|
function, or at the beginning of a function or compound statement.
|
|
|
|
Don't use "inline"; not all compilers support it. If you want to have a
|
|
function be an inline function if the compiler supports it, use
|
|
G_INLINE_FUNC, which is declared by <glib.h>. This may not work with
|
|
functions declared in header files; if it doesn't work, don't declare
|
|
the function in a header file, even if this requires that you not make
|
|
it inline on any platform.
|
|
|
|
Don't use "long long"; use "gint64" or "guint64", and only do so if
|
|
G_HAVE_GINT64 is defined. Make sure your code works even if
|
|
G_HAVE_GINT64 isn't defined, even if that means treating 64-bit integral
|
|
data types as opaque arrays of bytes on platforms where it's not
|
|
defined. Also, don't assume you can use "%lld", "%llu", "%llx", or
|
|
"%llo" to print 64-bit integral data types - not all platforms support
|
|
"%ll" for printing them.
|
|
|
|
Don't use "uint", "ulong" or "ushort"; they aren't defined on all
|
|
platforms. If you want an "int-sized" unsigned quantity, use "guint";
|
|
if you want a 32-bit unsigned quantity, use "guint32"; and if you want a
|
|
16-bit unsigned quantity, use "guint16". Use "%d", "%u", "%x", and "%o"
|
|
to print those types; don't use "%ld", "%lu", "%lx", or "%lo", as longs
|
|
are 64 bits long on many platforms, but "guint32" is 32 bits long.
|
|
|
|
Don't use "long" to mean "signed 32-bit integer", and don't use
|
|
"unsigned long" to mean "unsigned 32-bit integer"; "long"s are 64 bits
|
|
long on many platforms. Use "gint32" for signed 32-bit integers and use
|
|
"guint32" for unsigned 32-bit integers.
|
|
|
|
Don't use a label without a statement following it. For example,
|
|
something such as
|
|
|
|
if (...) {
|
|
|
|
...
|
|
|
|
done:
|
|
}
|
|
|
|
will not work with all compilers - you have to do
|
|
|
|
if (...) {
|
|
|
|
...
|
|
|
|
done:
|
|
;
|
|
}
|
|
|
|
with some statement, even if it's a null statement, after the label.
|
|
|
|
Don't use "bzero()", "bcopy()", or "bcmp()"; instead, use the ANSI C
|
|
routines
|
|
|
|
"memset()" (with zero as the second argument, so that it sets
|
|
all the bytes to zero);
|
|
|
|
"memcpy()" or "memmove()" (note that the first and second
|
|
arguments to "memcpy()" are in the reverse order to the
|
|
arguments to "bcopy()"; note also that "bcopy()" is typically
|
|
guaranteed to work on overlapping memory regions, while
|
|
"memcpy()" isn't, so if you may be copying from one region to a
|
|
region that overlaps it, use "memmove()", not "memcpy()" - but
|
|
"memcpy()" might be faster as a result of not guaranteeing
|
|
correct operation on overlapping memory regions);
|
|
|
|
and "memcmp()" (note that "memcmp()" returns 0, 1, or -1, doing
|
|
an ordered comparison, rather than just returning 0 for "equal"
|
|
and 1 for "not equal", as "bcmp()" does).
|
|
|
|
Not all platforms necessarily have "bzero()"/"bcopy()"/"bcmp()", and
|
|
those that do might not declare them in the header file on which they're
|
|
declared on your platform.
|
|
|
|
Don't use "index()" or "rindex()"; instead, use the ANSI C equivalents,
|
|
"strchr()" and "strrchr()". Not all platforms necessarily have
|
|
"index()" or "rindex()", and those that do might not declare them in the
|
|
header file on which they're declared on your platform.
|
|
|
|
Don't fetch data from packets by getting a pointer to data in the packet
|
|
with "tvb_get_ptr()", casting that pointer to a pointer to a structure,
|
|
and dereferencing that pointer. That point won't necessarily be aligned
|
|
on the proper boundary, which can cause crashes on some platforms (even
|
|
if it doesn't crash on an x86-based PC); furthermore, the data in a
|
|
packet is not necessarily in the byte order of the machine on which
|
|
Ethereal is running. Use the tvbuff routines to extract individual
|
|
items from the packet, or use "proto_tree_add_item()" and let it extract
|
|
the items for you.
|
|
|
|
Don't use "ntohs()", "ntohl()", "htons()", or "htonl()"; the header
|
|
files required to define or declare them differ between platforms, and
|
|
you might be able to get away with not including the appropriate header
|
|
file on your platform but that might not work on other platforms.
|
|
Instead, use "g_ntohs()", "g_ntohl()", "g_htons()", and "g_htonl()";
|
|
those are declared by <glib.h>, and you'll need to include that anyway,
|
|
as Ethereal header files that all dissectors must include use stuff from
|
|
<glib.h>.
|
|
|
|
Don't put a comma after the last element of an enum - some compilers may
|
|
either warn about it (producing extra noise) or refuse to accept it.
|
|
|
|
Don't include <unistd.h> without protecting it with
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
...
|
|
|
|
#endif
|
|
|
|
and, if you're including it to get routines such as "open()", "close()",
|
|
"read()", and "write()" declared, also include <io.h> if present:
|
|
|
|
#ifdef HAVE_IO_H
|
|
#include <io.h>
|
|
#endif
|
|
|
|
in order to declare the Windows C library routines "_open()",
|
|
"_close()", "_read()", and "_write()". Your file must include <glib.h>
|
|
- which many of the Ethereal header files include, so you might not have
|
|
to include it explicitly - in order to get "open()", "close()",
|
|
"read()", "write()", etc. mapped to "_open()", "_close()", "_read()",
|
|
"_write()", etc..
|
|
|
|
When opening a file with "fopen()", "freopen()", or "fdopen()", if the
|
|
file contains ASCII text, use "r", "w", "a", and so on as the open mode
|
|
- but if it contains binary data, use "rb", "wb", and so on. On
|
|
Windows, if a file is opened in a text mode, writing a byte with the
|
|
value of octal 12 (newline) to the file causes two bytes, one with the
|
|
value octal 15 (carriage return) and one with the value octal 12, to be
|
|
written to the file, and causes bytes with the value octal 15 to be
|
|
discarded when reading the file (to translate between C's UNIX-style
|
|
lines that end with newline and Windows' DEC-style lines that end with
|
|
carriage return/line feed).
|
|
|
|
In addition, that also means that when opening or creating a binary
|
|
file, you must use "open()" (with O_CREAT and possibly O_TRUNC if the
|
|
file is to be created if it doesn't exist), and OR in the O_BINARY flag.
|
|
That flag is not present on most, if not all, UNIX systems, so you must
|
|
also do
|
|
|
|
#ifndef O_BINARY
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
to properly define it for UNIX (it's not necessary on UNIX).
|
|
|
|
Don't use forward declarations of static arrays without a specified size
|
|
in a fashion such as this:
|
|
|
|
static const value_string foo_vals[];
|
|
|
|
...
|
|
|
|
static const value_string foo_vals[] = {
|
|
{ 0, "Red" },
|
|
{ 1, "Green" },
|
|
{ 2, "Blue" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
as some compilers will reject the first of those statements. Instead,
|
|
initialize the array at the point at which it's first declared, so that
|
|
the size is known.
|
|
|
|
For #define names and enum member names, prefix the names with a tag so
|
|
as to avoid collisions with other names - this might be more of an issue
|
|
on Windows, as it appears to #define names such as DELETE and
|
|
OPTIONAL.
|
|
|
|
Don't use the "numbered argument" feature that many UNIX printf's
|
|
implement, e.g.:
|
|
|
|
sprintf(add_string, " - (%1$d) (0x%1$04x)", value);
|
|
|
|
as not all UNIX printf's implement it, and Windows printf doesn't appear
|
|
to implement it. Use something like
|
|
|
|
sprintf(add_string, " - (%d) (0x%04x)", value, value);
|
|
|
|
instead.
|
|
|
|
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.
|
|
|
|
Dissectors that use the dissector registration to tell a lower level
|
|
dissector don't need to define a prototype in the .h file. For other
|
|
dissectors the main dissector routine should have a prototype 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.86 2003/11/14 19:20:24 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.86 2003/11/14 19:20:24 guy Exp $
|
|
*
|
|
* Ethereal - Network traffic analyzer
|
|
* By Gerald Combs <gerald@ethereal.com>
|
|
* 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 (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>
|
|
#include <string.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#ifdef NEED_SNPRINTF_H
|
|
# include "snprintf.h"
|
|
#endif
|
|
|
|
#include <epan/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 */
|
|
static void
|
|
dissect_PROTOABBREV(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
|
|
/* Set up structures needed 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(pinfo->cinfo, COL_PROTOCOL))
|
|
col_set_str(pinfo->cinfo, 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. See section 1.5
|
|
for more information.
|
|
|
|
If you are setting it 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 (check_col(pinfo->cinfo, COL_INFO))
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
*/
|
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
col_set_str(pinfo->cinfo, COL_INFO, "XXX Request");
|
|
|
|
/* In the interest of speed, if "tree" is NULL, avoid building a
|
|
protocol tree and adding stuff to it if possible. Note,
|
|
however, that you must call subdissectors regardless of whether
|
|
"tree" is NULL or not. */
|
|
if (tree) {
|
|
|
|
/* 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 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, FALSE);
|
|
|
|
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, FALSE)
|
|
|
|
|
|
/* Continue adding tree items to process the packet here */
|
|
|
|
|
|
}
|
|
|
|
/* If this protocol has a sub-dissector call it here, see section 1.8 */
|
|
}
|
|
|
|
|
|
/* Register the protocol with Ethereal */
|
|
|
|
/* 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)
|
|
{
|
|
|
|
/* Setup list of header fields See Section 1.6.1 for details*/
|
|
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",
|
|
"PROTOSHORTNAME", "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));
|
|
}
|
|
|
|
|
|
/* If this dissector uses sub-dissector registration add a registration routine.
|
|
This format is required because a script is used to find these routines and
|
|
create the code that calls these routines.
|
|
*/
|
|
void
|
|
proto_reg_handoff_PROTOABBREV(void)
|
|
{
|
|
dissector_handle_t PROTOABBREV_handle;
|
|
|
|
PROTOABBREV_handle = create_dissector_handle(dissect_PROTOABBREV,
|
|
proto_PROTOABBREV);
|
|
dissector_add("PARENT_SUBFIELD", ID_VALUE, PROTOABBREV_handle);
|
|
}
|
|
|
|
------------------------------------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; this is displayed in the
|
|
top-level protocol tree item for that protocol.
|
|
PROTOSHORTNAME An abbreviated name for the protocol; this is displayed
|
|
in the "Preferences" dialog box if your dissector has
|
|
any preferences, and in the dialog box for filter fields
|
|
when constructing a filter expression.
|
|
PROTOABBREV A name for the protocol for use in filter expressions;
|
|
it should contain only lower-case letters, digits, and
|
|
hyphens.
|
|
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_UINT64, FT_INT8, FT_INT16, FT_INT24, FT_INT32,
|
|
FT_INT64, FT_FLOAT, FT_DOUBLE, FT_ABSOLUTE_TIME,
|
|
FT_RELATIVE_TIME, FT_STRING, FT_STRINGZ, FT_UINT_STRING,
|
|
FT_ETHER, FT_BYTES, FT_IPv4, FT_IPv6, FT_IPXNET,
|
|
FT_FRAMENUM
|
|
FIELDBASE BASE_NONE, BASE_DEC, BASE_HEX, BASE_OCT
|
|
FIELDCONVERT VALS(x), TFS(x), NULL
|
|
BITMASK Usually 0x0 unless using the TFS(x) field conversion.
|
|
FIELDDESCR A brief description of the field.
|
|
PARENT_SUBFIELD Lower level protocol field used for lookup, i.e. "tcp.port"
|
|
ID_VALUE Lower level protocol field value that identifies this protocol
|
|
For example the TCP or UDP port number
|
|
|
|
If, for example, PROTONAME is "Internet Bogosity Discovery Protocol",
|
|
PROTOSHORTNAME would be "IBDP", and PROTOABBREV would be "ibdp". Try to
|
|
conform with IANA names.
|
|
|
|
1.4 The dissector and the data it receives.
|
|
|
|
|
|
1.4.1 Header file.
|
|
|
|
This is only needed if the dissector doesn't use self-registration to
|
|
register itself with the lower level dissector.
|
|
|
|
The dissector has the following header that must be placed into
|
|
packet-PROTOABBREV.h.
|
|
|
|
void
|
|
dissect_PROTOABBREV(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
|
|
|
|
|
|
1.4.2 Extracting data from packets.
|
|
|
|
NOTE: See the README.tvbuff for more details
|
|
|
|
The "tvb" argument to a dissector points to a buffer containing the raw
|
|
data to be analyzed by the dissector; for example, for a protocol
|
|
running atop UDP, it contains the UDP payload (but not the UDP header,
|
|
or any protocol headers above it). A tvbuffer is a opaque data
|
|
structure, the internal data structures are hidden and the data must be
|
|
access via the tvbuffer accessors.
|
|
|
|
The accessors are:
|
|
|
|
Single-byte accessor:
|
|
|
|
guint8 tvb_get_guint8(tvbuff_t*, gint offset);
|
|
|
|
Network-to-host-order access for 16-bit integers (guint16), 32-bit
|
|
integers (guint32), and 24-bit integers:
|
|
|
|
guint16 tvb_get_ntohs(tvbuff_t*, gint offset);
|
|
guint32 tvb_get_ntohl(tvbuff_t*, gint offset);
|
|
guint32 tvb_get_ntoh24(tvbuff_t*, gint offset);
|
|
|
|
Network-to-host-order access for single-precision and double-precision
|
|
IEEE floating-point numbers:
|
|
|
|
gfloat tvb_get_ntohieee_float(tvbuff_t*, gint offset);
|
|
gdouble tvb_get_ntohieee_double(tvbuff_t*, gint offset);
|
|
|
|
Little-Endian-to-host-order access for 16-bit integers (guint16), 32-bit
|
|
integers (guint32), and 24-bit integers:
|
|
|
|
guint16 tvb_get_letohs(tvbuff_t*, gint offset);
|
|
guint32 tvb_get_letohl(tvbuff_t*, gint offset);
|
|
guint32 tvb_get_letoh24(tvbuff_t*, gint offset);
|
|
|
|
Little-Endian-to-host-order access for single-precision and
|
|
double-precision IEEE floating-point numbers:
|
|
|
|
gfloat tvb_get_letohieee_float(tvbuff_t*, gint offset);
|
|
gdouble tvb_get_letohieee_double(tvbuff_t*, gint offset);
|
|
|
|
NOTE: IPv4 addresses are not to be converted to host byte order before
|
|
being passed to "proto_tree_add_ipv4()". You should use "tvb_memcpy()"
|
|
to fetch them, not "tvb_get_ntohl()" *OR* "tvb_get_letohl()" - don't,
|
|
for example, try to use "tvb_get_ntohl()", find that it gives you the
|
|
wrong answer on the PC on which you're doing development, and try
|
|
"tvb_get_letohl()" instead, as "tvb_get_letohl()" will give the wrong
|
|
answer on big-endian machines.
|
|
|
|
Copying memory:
|
|
guint8* tvb_memcpy(tvbuff_t*, guint8* target, gint offset, gint length);
|
|
guint8* tvb_memdup(tvbuff_t*, gint offset, gint length);
|
|
|
|
|
|
Pointer-retrieval:
|
|
/* WARNING! This function is possibly expensive, temporarily allocating
|
|
* another copy of the packet data. Furthermore, it's dangerous because once
|
|
* this pointer is given to the user, there's no guarantee that the user will
|
|
* honor the 'length' and not overstep the boundaries of the buffer.
|
|
*/
|
|
guint8* tvb_get_ptr(tvbuff_t*, gint offset, gint length);
|
|
|
|
The reason that tvb_get_ptr() have to allocate a copy of its data only
|
|
occurs with TVBUFF_COMPOSITES, data that spans multiple tvbuffers. If the
|
|
user request a pointer to a range of bytes that spans the member tvbuffs that
|
|
make up the TVBUFF_COMPOSITE, the data will have to be copied to another
|
|
memory region to assure that all the bytes are contiguous.
|
|
|
|
|
|
|
|
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
|
|
'check_col' 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_set_str function.
|
|
|
|
'col_set_str' takes a string as its third argument, and sets the value
|
|
for the column to that value. It assumes that the pointer passed to it
|
|
points to a string constant or a static "const" array, not to a
|
|
variable, as it doesn't copy the string, it merely saves the pointer
|
|
value; the argument can itself be a variable, as long as it always
|
|
points to a string constant or a static "const" array.
|
|
|
|
It is more efficient than 'col_add_str' or 'col_add_fstr'; however, if
|
|
the dissector will be using 'col_append_str' or 'col_append_fstr" to
|
|
append more information to the column, the string will have to be copied
|
|
anyway, so it's best to use 'col_add_str' rather than 'col_set_str' in
|
|
that case.
|
|
|
|
For example, to set the "Protocol" column
|
|
to "PROTOABBREV":
|
|
|
|
if (check_col(pinfo->cinfo, COL_PROTOCOL))
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "PROTOABBREV");
|
|
|
|
|
|
1.5.2 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. It takes the same arguments as
|
|
'col_set_str', but copies the string, so that if the string is, for
|
|
example, an automatic variable that won't remain in scope when the
|
|
dissector returns, it's safe to use.
|
|
|
|
|
|
1.5.3 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(pinfo->cinfo, COL_INFO))
|
|
col_add_fstr(pinfo->cinfo, COL_INFO, "%s request, %u bytes",
|
|
reqtype, n);
|
|
|
|
Don't use 'col_add_fstr' with a format argument of just "%s" -
|
|
'col_add_str', or possibly even 'col_set_str' if the string that matches
|
|
the "%s" is a static constant string, will do the same job more
|
|
efficiently.
|
|
|
|
|
|
1.5.4 The col_clear function.
|
|
|
|
If the Info column will be filled with information from the packet, that
|
|
means that some data will be fetched from the packet before the Info
|
|
column is filled in. If the packet is so small that the data in
|
|
question cannot be fetched, the routines to fetch the data will throw an
|
|
exception (see the comment at the beginning about tvbuffers improving
|
|
the handling of short packets - the tvbuffers keep track of how much
|
|
data is in the packet, and throw an exception on an attempt to fetch
|
|
data past the end of the packet, so that the dissector won't process
|
|
bogus data), causing the Info column not to be filled in.
|
|
|
|
This means that the Info column will have data for the previous
|
|
protocol, which would be confusing if, for example, the Protocol column
|
|
had data for this protocol.
|
|
|
|
Therefore, before a dissector fetches any data whatsoever from the
|
|
packet (unless it's a heuristic dissector fetching data to determine
|
|
whether the packet is one that it should dissect, in which case it
|
|
should check, before fetching the data, whether there's any data to
|
|
fetch; if there isn't, it should return FALSE), it should set the
|
|
Protocol column and the Info column.
|
|
|
|
If the Protocol column will ultimately be set to, for example, a value
|
|
containing a protocol version number, with the version number being a
|
|
field in the packet, the dissector should, before fetching the version
|
|
number field or any other field from the packet, set it to a value
|
|
without a version number, using 'col_set_str', and should later set it
|
|
to a value with the version number after it's fetched the version
|
|
number.
|
|
|
|
If the Info column will ultimately be set to a value containing
|
|
information from the packet, the dissector should, before fetching any
|
|
fields from the packet, clear the column using 'col_clear' (which is
|
|
more efficient than clearing it by calling 'col_set_str' or
|
|
'col_add_str' with a null string), and should later set it to the real
|
|
string after it's fetched the data to use when doing that.
|
|
|
|
|
|
1.5.5 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.6 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_SRC" in "Makefile.am" and "Makefile.nmake";
|
|
|
|
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",
|
|
/* short 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 fields that
|
|
aren't given a value, and that can only
|
|
be tested for presence or absence; a
|
|
field that represents a data structure,
|
|
with a subtree below it containing
|
|
fields for the members of the structure,
|
|
or that represents an array with a
|
|
subtree below it containing fields for
|
|
the members of the array, might be an
|
|
FT_NONE field.
|
|
FT_BOOLEAN 0 means "false", any other value means
|
|
"true".
|
|
FT_FRAMENUM A frame number; if this is used, the "Go
|
|
To Corresponding Frame" menu item can
|
|
work on that field.
|
|
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_UINT64 A 64-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_INT64 A 64-bit signed integer.
|
|
FT_FLOAT A single-precision floating point number.
|
|
FT_DOUBLE A double-precision floating point number.
|
|
FT_ABSOLUTE_TIME Seconds (4 bytes) and nanoseconds (4 bytes)
|
|
of time displayed as month name, month day,
|
|
year, hours, minutes, and seconds with 9
|
|
digits after the decimal point.
|
|
FT_RELATIVE_TIME Seconds (4 bytes) and nanoseconds (4 bytes)
|
|
of time displayed as seconds and 9 digits
|
|
after the decimal point.
|
|
FT_STRING A string of characters, not necessarily
|
|
NUL-terminated, but possibly NUL-padded.
|
|
This, and the other string-of-characters
|
|
types, are to be used for text strings,
|
|
not raw binary data.
|
|
FT_STRINGZ A NUL-terminated string of characters.
|
|
FT_UINT_STRING A counted string of characters, consisting
|
|
of a count (represented as an integral
|
|
value) followed immediately by the
|
|
specified number of characters.
|
|
FT_ETHER A six octet string displayed in
|
|
Ethernet-address format.
|
|
FT_BYTES A string of bytes with arbitrary values;
|
|
used for raw binary data.
|
|
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.
|
|
|
|
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_DEC, BASE_HEX, and BASE_OCT are decimal, hexadecimal, and octal,
|
|
respectively.
|
|
|
|
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. You may not use BASE_NONE for integers.
|
|
|
|
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, you 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");
|
|
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.
|
|
|
|
If you don't have any fields to register, do *NOT* create a zero-length
|
|
"hf" array; not all compilers used to compile Ethereal support them.
|
|
Just omit the "hf" array, and the "proto_register_field_array()" call,
|
|
entirely.
|
|
|
|
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() functions.
|
|
|
|
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, tvb, start, length, little_endian);
|
|
|
|
proto_item*
|
|
proto_tree_add_item_hidden(tree, id, tvb, start, length, little_endian);
|
|
|
|
proto_item*
|
|
proto_tree_add_none_format(tree, id, tvb, start, length, format, ...);
|
|
|
|
proto_item*
|
|
proto_tree_add_protocol_format(tree, id, tvb, start, length,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_bytes(tree, id, tvb, start, length, start_ptr);
|
|
|
|
proto_item *
|
|
proto_tree_add_bytes_hidden(tree, id, tvb, start, length, start_ptr);
|
|
|
|
proto_item *
|
|
proto_tree_add_bytes_format(tree, id, tvb, start, length, start_ptr,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_time(tree, id, tvb, start, length, value_ptr);
|
|
|
|
proto_item *
|
|
proto_tree_add_time_hidden(tree, id, tvb, start, length, value_ptr);
|
|
|
|
proto_item *
|
|
proto_tree_add_time_format(tree, id, tvb, start, length, value_ptr,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipxnet(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipxnet_hidden(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipxnet_format(tree, id, tvb, start, length, value,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipv4(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipv4_hidden(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipv4_format(tree, id, tvb, start, length, value,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipv6(tree, id, tvb, start, length, value_ptr);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipv6_hidden(tree, id, tvb, start, length, value_ptr);
|
|
|
|
proto_item *
|
|
proto_tree_add_ipv6_format(tree, id, tvb, start, length, value_ptr,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_ether(tree, id, tvb, start, length, value_ptr);
|
|
|
|
proto_item *
|
|
proto_tree_add_ether_hidden(tree, id, tvb, start, length, value_ptr);
|
|
|
|
proto_item *
|
|
proto_tree_add_ether_format(tree, id, tvb, start, length, value_ptr,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_string(tree, id, tvb, start, length, value_ptr);
|
|
|
|
proto_item *
|
|
proto_tree_add_string_hidden(tree, id, tvb, start, length, value_ptr);
|
|
|
|
proto_item *
|
|
proto_tree_add_string_format(tree, id, tvb, start, length, value_ptr,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_boolean(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_boolean_hidden(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_boolean_format(tree, id, tvb, start, length, value,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_float(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_float_hidden(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_float_format(tree, id, tvb, start, length, value,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_double(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_double_hidden(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_double_format(tree, id, tvb, start, length, value,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_uint(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_uint_hidden(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_uint_format(tree, id, tvb, start, length, value,
|
|
format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_int(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_int_hidden(tree, id, tvb, start, length, value);
|
|
|
|
proto_item *
|
|
proto_tree_add_int_format(tree, id, tvb, start, length, value,
|
|
format, ...);
|
|
|
|
proto_item*
|
|
proto_tree_add_text(tree, tvb, start, length, format, ...);
|
|
|
|
proto_item*
|
|
proto_tree_add_text_valist(tree, tvb, start, length, format, ap);
|
|
|
|
The 'tree' argument is the tree to which the item is to be added. The
|
|
'tvb' argument is the tvbuff from which the item's value is being
|
|
extracted; the 'start' argument is the offset from the beginning of that
|
|
tvbuff 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 -1, and, when the
|
|
dissection is complete, 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 a value. The value will be fetched
|
|
from the tvbuff by proto_tree_add_item(), based on the type of the field
|
|
and, for integral and Boolean fields, the byte order of the value; the
|
|
byte order is specified by the 'little_endian' argument, which is TRUE
|
|
if the value is little-endian and FALSE if it is big-endian.
|
|
|
|
Now that definitions of fields have 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;
|
|
|
|
proto_tree_add_item(bf_tree, hf_sna_th_fid, tvb, offset, 1, TRUE);
|
|
|
|
The definition of the field 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 create 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_none_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_none_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, ..., FALSE);
|
|
}
|
|
for(i = 0; i < num_rings - 1; i++) {
|
|
proto_tree_add_item_hidden(tree, hf_tr_rif_bridge, ..., FALSE);
|
|
}
|
|
|
|
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_none_format()
|
|
----------------------------
|
|
proto_tree_add_none_format is used to add an item of type FT_NONE.
|
|
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_bytes()
|
|
proto_tree_add_time()
|
|
proto_tree_add_ipxnet()
|
|
proto_tree_add_ipv4()
|
|
proto_tree_add_ipv6()
|
|
proto_tree_add_ether()
|
|
proto_tree_add_string()
|
|
proto_tree_add_boolean()
|
|
proto_tree_add_float()
|
|
proto_tree_add_double()
|
|
proto_tree_add_uint()
|
|
proto_tree_add_int()
|
|
----------------------------
|
|
These routines are used to add items to the protocol tree if either:
|
|
|
|
the value of the item to be added isn't just extracted from the
|
|
packet data, but is computed from data in the packet;
|
|
|
|
the value was fetched into a variable.
|
|
|
|
The 'value' argument has the value to be added to the tree.
|
|
|
|
NOTE: in all cases where the 'value' argument is a pointer, a copy is
|
|
made of the object pointed to; if you have dynamically allocated a
|
|
buffer for the object, that buffer will not be freed when the protocol
|
|
tree is freed - you must free the buffer yourself when you don't need it
|
|
any more.
|
|
|
|
For proto_tree_add_bytes(), the 'value_ptr' argument is a pointer to a
|
|
sequence of bytes.
|
|
|
|
For proto_tree_add_time(), the 'value_ptr' argument is a pointer to an
|
|
"nstime_t", which is a structure containing the time to be added; it has
|
|
'secs' and 'nsecs' members, giving the integral part and the fractional
|
|
part of a time in units of seconds, with 'nsecs' being the number of
|
|
nanoseconds. For absolute times, "secs" is a UNIX-style seconds since
|
|
January 1, 1970, 00:00:00 GMT value.
|
|
|
|
For proto_tree_add_ipxnet(), the 'value' argument is a 32-bit IPX
|
|
network address.
|
|
|
|
For proto_tree_add_ipv4(), the 'value' argument is a 32-bit IPv4
|
|
address, in network byte order.
|
|
|
|
For proto_tree_add_ipv6(), the 'value_ptr' argument is a pointer to a
|
|
128-bit IPv6 address.
|
|
|
|
For proto_tree_add_ether(), the 'value_ptr' argument is a pointer to a
|
|
48-bit MAC address.
|
|
|
|
For proto_tree_add_string(), the 'value_ptr' argument is a pointer to a
|
|
text string.
|
|
|
|
For proto_tree_add_boolean(), the 'value' argument is a 32-bit integer;
|
|
zero means "false", and non-zero means "true".
|
|
|
|
For proto_tree_add_float(), the 'value' argument is a 'float' in the
|
|
host's floating-point format.
|
|
|
|
For proto_tree_add_double(), the 'value' argument is a 'double' in the
|
|
host's floating-point format.
|
|
|
|
For proto_tree_add_uint(), the 'value' argument is a 32-bit unsigned
|
|
integer value, in host byte order. (This routine cannot be used to add
|
|
64-bit integers; they can only be added with proto_tree_add_item().)
|
|
|
|
For proto_tree_add_int(), the 'value' argument is a 32-bit signed
|
|
integer value, in host byte order. (This routine cannot be used to add
|
|
64-bit integers; they can only be added with proto_tree_add_item().)
|
|
|
|
proto_tree_add_bytes_hidden()
|
|
proto_tree_add_time_hidden()
|
|
proto_tree_add_ipxnet_hidden()
|
|
proto_tree_add_ipv4_hidden()
|
|
proto_tree_add_ipv6_hidden()
|
|
proto_tree_add_ether_hidden()
|
|
proto_tree_add_string_hidden()
|
|
proto_tree_add_boolean_hidden()
|
|
proto_tree_add_float_hidden()
|
|
proto_tree_add_double_hidden()
|
|
proto_tree_add_uint_hidden()
|
|
proto_tree_add_int_hidden()
|
|
----------------------------
|
|
These routines add fields and values to a tree, but don't show them in
|
|
the GUI tree. They are used for the same reason that
|
|
proto_tree_add_item() is used.
|
|
|
|
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_float_format()
|
|
proto_tree_add_double_format()
|
|
proto_tree_add_uint_format()
|
|
proto_tree_add_int_format()
|
|
----------------------------
|
|
These 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. The argument giving the value is
|
|
the same as the corresponding proto_tree_add_XXX() function; 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 the proto_tree_add_XXX() functions.
|
|
|
|
proto_tree_add_text()
|
|
---------------------
|
|
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.
|
|
|
|
For a subtree, the label on the subtree might reflect some of the items
|
|
in the subtree. This means the label can't be set until at least some
|
|
of the items in the subtree have been dissected. To do this, use
|
|
'proto_item_set_text()' or 'proto_item_append_text()':
|
|
|
|
void
|
|
proto_item_set_text(proto_item *ti, ...);
|
|
|
|
void
|
|
proto_item_append_text(proto_item *ti, ...);
|
|
|
|
'proto_item_set_text()' takes as an argument the value returned by
|
|
'proto_tree_add_text()', a 'printf'-style format string, and a set of
|
|
arguments corresponding to '%' format items in that string, and replaces
|
|
the text for the item created by 'proto_tree_add_text()' with the result
|
|
of applying the arguments to the format string.
|
|
|
|
'proto_item_append_text()' is similar, but it appends to the text for
|
|
the item the result of applying the arguments to the format string.
|
|
|
|
For example, early in the dissection, one might do:
|
|
|
|
ti = proto_tree_add_text(tree, tvb, offset, length, <label>);
|
|
|
|
and later do
|
|
|
|
proto_item_set_text(ti, "%s: %s", type, value);
|
|
|
|
after the "type" and "value" fields have been extracted and dissected.
|
|
<label> would be a label giving what information about the subtree is
|
|
available without dissecting any of the data in the subtree.
|
|
|
|
Note that an exception might thrown when trying to extract the values of
|
|
the items used to set the label, if not all the bytes of the item are
|
|
available. Thus, one should create the item with text that is as
|
|
meaningful as possible, and set it or append additional information to
|
|
it as the values needed to supply that information is extracted.
|
|
|
|
proto_tree_add_text_valist()
|
|
---------------------
|
|
This is like proto_tree_add_text(), but takes, as the last argument, a
|
|
'va_list'; it is used to allow routines that take a printf-like
|
|
variable-length list of arguments to add a text item to the protocol
|
|
tree.
|
|
|
|
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 Calling Other Dissector
|
|
|
|
NOTE: This is discussed in the README.tvbuff file. For more
|
|
information on tvbuffers consult that file.
|
|
|
|
As each dissector completes its portion of the protocol analysis, it
|
|
is expected to create a new tvbuff of type TVBUFF_SUBSET which
|
|
contains the payload portion of the protocol (that is, the bytes
|
|
that are relevant to the next dissector).
|
|
|
|
The syntax for creating a new TVBUFF_SUBSET is:
|
|
|
|
next_tvb = tvb_new_subset(tvb, offset, length, reported_length)
|
|
|
|
Where:
|
|
tvb is the tvbuff that the dissector has been working on. It
|
|
can be a tvbuff of any type.
|
|
|
|
next_tvb is the new TVBUFF_SUBSET.
|
|
|
|
offset is the byte offset of 'tvb' at which the new tvbuff
|
|
should start. The first byte is the 0th byte.
|
|
|
|
length is the number of bytes in the new TVBUFF_SUBSET. A length
|
|
argument of -1 says to use as many bytes as are available in
|
|
'tvb'.
|
|
|
|
reported_length is the number of bytes that the current protocol
|
|
says should be in the payload. A reported_length of -1 says that
|
|
the protocol doesn't say anything about the size of its payload.
|
|
|
|
|
|
An example from packet-ipx.c -
|
|
|
|
void
|
|
dissect_ipx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
tvbuff_t *next_tvb;
|
|
int reported_length, available_length;
|
|
|
|
|
|
/* Make the next tvbuff */
|
|
|
|
/* IPX does have a length value in the header, so calculate report_length */
|
|
Set this to -1 if there isn't any length information in the protocol
|
|
*/
|
|
reported_length = ipx_length - IPX_HEADER_LEN;
|
|
|
|
/* Calculate the available data in the packet,
|
|
set this to -1 to use all the data in the tv_buffer
|
|
*/
|
|
available_length = tvb_length(tvb) - IPX_HEADER_LEN;
|
|
|
|
/* Create the tvbuffer for the next dissector */
|
|
next_tvb = tvb_new_subset(tvb, IPX_HEADER_LEN,
|
|
MIN(available_length, reported_length),
|
|
reported_length);
|
|
|
|
/* call the next dissector */
|
|
dissector_next( next_tvb, pinfo, tree);
|
|
|
|
|
|
1.9 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_SRC' macro in the 'Makefile.am' and 'Makefile.nmake'
|
|
files 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.)
|
|
|
|
If your dissector also has its own header files, you must add them to
|
|
the 'noinst_HEADERS' macro in the 'Makefile.am' file in the top-level
|
|
directory, so that it's included when release source tarballs are built
|
|
(otherwise, the source in the release tarballs won't compile).
|
|
|
|
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.10 Using the CVS source code tree.
|
|
|
|
1.11 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 five routines that you will use to work with a conversation:
|
|
conversation_new, find_conversation, conversation_add_proto_data,
|
|
conversation_get_proto_data, and conversation_delete_proto_data.
|
|
|
|
|
|
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 are
|
|
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.7 for more information about the 'data' pointer.
|
|
|
|
|
|
2.2.2 The conversation_new function.
|
|
|
|
This routine will create a new conversation based upon two address/port
|
|
pairs. If you want to associate with the conversation a pointer to a
|
|
private data structure you must use the conversation_add_proto_data
|
|
function. The ptype variable is used to differentiate between
|
|
conversations over different protocols, i.e. TCP and UDP. The options
|
|
variable is used to define a conversation that will accept any destination
|
|
address and/or port. Set options = 0 if the destination port and address
|
|
are know when conversation_new is called. See section 2.4 for more
|
|
information on usage of the options parameter.
|
|
|
|
The conversation_new prototype:
|
|
conversation_t *conversation_new(address *addr1, address *addr2,
|
|
port_type ptype, guint32 port1, guint32 port2, guint options);
|
|
|
|
Where:
|
|
address* addr1 = first data packet address
|
|
address* addr2 = second data packet address
|
|
port_type ptype = port type, this is defined in packet.h
|
|
guint32 port1 = first data packet port
|
|
guint32 port2 = second data packet port
|
|
guint options = conversation options, NO_ADDR2 and/or NO_PORT2
|
|
|
|
"addr1" and "port1" are the first address/port pair; "addr2" and "port2"
|
|
are the second address/port pair. A conversation doesn't have source
|
|
and destination address/port pairs - packets in a conversation go in
|
|
both directions - so "addr1"/"port1" may be the source or destination
|
|
address/port pair; "addr2"/"port2" would be the other pair.
|
|
|
|
If NO_ADDR2 is specified, the conversation is set up so that a
|
|
conversation lookup will match only the "addr1" address; if NO_PORT2 is
|
|
specified, the conversation is set up so that a conversation lookup will
|
|
match only the "port1" port; if both are specified, i.e.
|
|
NO_ADDR2|NO_PORT2, the conversation is set up so that the lookup will
|
|
match only the "addr1"/"port1" address/port pair. This can be used if a
|
|
packet indicates that, later in the capture, a conversation will be
|
|
created using certain addresses and ports, in the case where the packet
|
|
doesn't specify the addresses and ports of both sides.
|
|
|
|
2.2.3 The find_conversation function.
|
|
|
|
Call this routine to look up a conversation. If no conversation is found,
|
|
the routine will return a NULL value.
|
|
|
|
The find_conversation prototype:
|
|
|
|
conversation_t *find_conversation(address *addr_a, address *addr_b,
|
|
port_type ptype, guint32 port_a, guint32 port_b, guint options);
|
|
|
|
Where:
|
|
address* addr_a = first address
|
|
address* addr_b = second address
|
|
port_type ptype = port type
|
|
guint32 port_a = first data packet port
|
|
guint32 port_b = second data packet port
|
|
guint options = conversation options, NO_ADDR_B and/or NO_PORT_B
|
|
|
|
"addr_a" and "port_a" are the first address/port pair; "addr_b" and
|
|
"port_b" are the second address/port pair. Again, as a conversation
|
|
doesn't have source and destination address/port pairs, so
|
|
"addr_a"/"port_a" may be the source or destination address/port pair;
|
|
"addr_b"/"port_b" would be the other pair. The search will match the
|
|
"a" address/port pair against both the "1" and "2" address/port pairs,
|
|
and match the "b" address/port pair against both the "2" and "1"
|
|
address/port pairs; you don't have to worry about which side the "a" or
|
|
"b" pairs correspond to.
|
|
|
|
If the NO_ADDR_B flag was specified to "find_conversation()", the
|
|
"addr_b" address will be treated as matching any "wildcarded" address;
|
|
if the NO_PORT_B flag was specified, the "port_b" port will be treated
|
|
as matching any "wildcarded" port. If both flags are specified, i.e.
|
|
NO_ADDR_B|NO_PORT_B, the "addr_b" address will be treated as matching
|
|
any "wildcarded" address and the "port_b" port will be treated as
|
|
matching any "wildcarded" port.
|
|
|
|
|
|
2.2.4 The conversation_add_proto_data function.
|
|
|
|
Once you have created a conversation with conversation_new, you can
|
|
associate data with it using this function.
|
|
|
|
The conversation_add_proto_data prototype:
|
|
|
|
void conversation_add_proto_data(conversation_t *conv, int proto,
|
|
void *proto_data);
|
|
|
|
Where:
|
|
conversation_t *conv = the conversation in question
|
|
int proto = registered protocol number
|
|
void *data = dissector data structure
|
|
|
|
"conversation" is the value returned by conversation_new. "proto" is a
|
|
unique protocol number created with proto_register_protocol. Protocols
|
|
are typically registered in the proto_register_XXXX section of your
|
|
dissector. "data" is a pointer to the data you wish to associate with the
|
|
conversation. Using the protocol number allows several dissectors to
|
|
associate data with a given conversation.
|
|
|
|
|
|
2.2.5 The conversation_get_proto_data function.
|
|
|
|
After you have located a conversation with find_conversation, you can use
|
|
this function to retrieve any data associated with it.
|
|
|
|
The conversation_get_proto_data prototype:
|
|
|
|
void *conversation_get_proto_data(conversation_t *conv, int proto);
|
|
|
|
Where:
|
|
conversation_t *conv = the conversation in question
|
|
int proto = registered protocol number
|
|
|
|
"conversation" is the conversation created with conversation_new. "proto"
|
|
is a unique protocol number acreated with proto_register_protocol,
|
|
typically in the proto_register_XXXX portion of a dissector. The function
|
|
returns a pointer to the data requested, or NULL if no data was found.
|
|
|
|
|
|
2.2.6 The conversation_delete_proto_data function.
|
|
|
|
After you are finished with a conversation, you can remove your assocation
|
|
with this function. Please note that ONLY the conversation entry is
|
|
removed. If you have allocated any memory for your data, you must free it
|
|
as well.
|
|
|
|
The conversation_delete_proto_data prototype:
|
|
|
|
void conversation_delete_proto_data(conversation_t *conv, int proto);
|
|
|
|
Where:
|
|
conversation_t *conv = the conversation in question
|
|
int proto = registered protocol number
|
|
|
|
"conversation" is the conversation created with conversation_new. "proto"
|
|
is a unique protocol number acreated with proto_register_protocol,
|
|
typically in the proto_register_XXXX portion of a dissector.
|
|
|
|
|
|
2.2.7 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;
|
|
|
|
/* Registered protocol number
|
|
static int my_proto = -1;
|
|
|
|
|
|
/********************* in the dissector routine *********************/
|
|
|
|
/* the local variables in the dissector */
|
|
|
|
conversation_t *conversation;
|
|
my_entry_t *data_ptr
|
|
|
|
|
|
/* look up the conversation */
|
|
|
|
conversation = find_conversation( &pinfo->src, &pinfo->dst, pinfo->ptype,
|
|
pinfo->srcport, pinfo->destport, 0);
|
|
|
|
/* if conversation found get the data pointer that you stored */
|
|
if ( conversation)
|
|
data_ptr = (my_entry_t*)conversation_get_proto_data(conversation,
|
|
my_proto);
|
|
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( &pinfo->src, &pinfo->dst, pinfo->ptype,
|
|
pinfo->srcport, pinfo->destport, 0);
|
|
conversation_add_proto_data(conversation, my_proto, (void *) data_ptr);
|
|
}
|
|
|
|
/* at this point the conversation data is ready */
|
|
|
|
|
|
/******************* in the dissector init routine *******************/
|
|
|
|
#define my_init_count 20
|
|
|
|
static void
|
|
my_dissector_init( void){
|
|
|
|
/* destroy 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);
|
|
|
|
my_proto = proto_register_protocol("My Protocol", "My Protocol", "my_proto");
|
|
|
|
|
|
2.2.8 The example conversation code using conversation index field
|
|
|
|
Sometimes the conversation isn't enough to define a unique data storage
|
|
value for the network traffic. For example if you are storing information
|
|
about requests carried in a conversation, the request may have an
|
|
identifier that is used to define the request. In this case the
|
|
conversation and the identifier are required to find the data storage
|
|
pointer. You can use the conversation data structure index value to
|
|
uniquely define the conversation.
|
|
|
|
See packet-afs.c for an example of how to use the conversation index. In
|
|
this dissector multiple requests are sent in the same conversation. To store
|
|
information for each request the dissector has an internal hash table based
|
|
upon the conversation index and values inside the request packets.
|
|
|
|
|
|
/* in the dissector routine */
|
|
|
|
/* to find a request value, first lookup conversation to get index */
|
|
/* then used the conversation index, and request data to find data */
|
|
/* in the local hash table */
|
|
|
|
conversation = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
|
|
pinfo->srcport, pinfo->destport, 0);
|
|
if (conversation == NULL) {
|
|
/* It's not part of any conversation - create a new one. */
|
|
conversation = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
|
|
pinfo->srcport, pinfo->destport, NULL, 0);
|
|
}
|
|
|
|
request_key.conversation = conversation->index;
|
|
request_key.service = pntohs(&rxh->serviceId);
|
|
request_key.callnumber = pntohl(&rxh->callNumber);
|
|
|
|
request_val = (struct afs_request_val *) g_hash_table_lookup(
|
|
afs_request_hash, &request_key);
|
|
|
|
/* only allocate a new hash element when it's a request */
|
|
opcode = 0;
|
|
if ( !request_val && !reply)
|
|
{
|
|
new_request_key = g_mem_chunk_alloc(afs_request_keys);
|
|
*new_request_key = request_key;
|
|
|
|
request_val = g_mem_chunk_alloc(afs_request_vals);
|
|
request_val -> opcode = pntohl(&afsh->opcode);
|
|
opcode = request_val->opcode;
|
|
|
|
g_hash_table_insert(afs_request_hash, new_request_key,
|
|
request_val);
|
|
}
|
|
|
|
|
|
|
|
2.3 Dynamic conversation dissector registration
|
|
|
|
|
|
NOTE: This sections assumes that all information is available to
|
|
create a complete conversation, source port/address and
|
|
destination port/address. If either the destination port or
|
|
address is know, see section 2.4 Dynamic server port dissector
|
|
registration.
|
|
|
|
For protocols that negotiate a secondary port connection, for example
|
|
packet-msproxy.c, a conversation can install a dissector to handle
|
|
the secondary protocol dissection. After the conversation is created
|
|
for the negotiated ports use the conversation_set_dissector to define
|
|
the dissection routine.
|
|
|
|
The second argument to conversation_set_dissector is a dissector handle,
|
|
which is created with a call to create_dissector_handle or
|
|
register_dissector.
|
|
|
|
create_dissector_handle takes as arguments a pointer to the dissector
|
|
function and a protocol ID as returned by proto_register_protocol;
|
|
register_dissector takes as arguments a string giving a name for the
|
|
dissector, a pointer to the dissector function, and a protocol ID.
|
|
|
|
The protocol ID is the ID for the protocol dissected by the function.
|
|
The function will not be called if the protocol has been disabled by the
|
|
user; instead, the data for the protocol will be dissected as raw data.
|
|
|
|
An example -
|
|
|
|
/* the handle for the dynamic dissector *
|
|
static dissector_handle_t sub_dissector_handle;
|
|
|
|
/* prototype for the dynamic dissector */
|
|
static void sub_dissector( tvbuff_t *tvb, packet_info *pinfo,
|
|
proto_tree *tree);
|
|
|
|
/* in the main protocol dissector, where the next dissector is setup */
|
|
|
|
/* if conversation has a data field, create it and load structure */
|
|
|
|
new_conv_info = g_mem_chunk_alloc( new_conv_vals);
|
|
new_conv_info->data1 = value1;
|
|
|
|
/* create the conversation for the dynamic port */
|
|
conversation = conversation_new( &pinfo->src, &pinfo->dst, protocol,
|
|
src_port, dst_port, new_conv_info, 0);
|
|
|
|
/* set the dissector for the new conversation */
|
|
conversation_set_dissector(conversation, sub_dissector_handle);
|
|
|
|
...
|
|
|
|
void
|
|
proto_register_PROTOABBREV(void)
|
|
{
|
|
...
|
|
|
|
sub_dissector_handle = create_dissector_handle(sub_dissector,
|
|
proto);
|
|
|
|
...
|
|
}
|
|
|
|
2.4 Dynamic server port dissector registration
|
|
|
|
NOTE: While this example used both NO_ADDR2 and NO_PORT2 to create a
|
|
conversation with only one port and address set, this isn't a
|
|
requirement. Either the second port or the second address can be set
|
|
when the conversation is created.
|
|
|
|
For protocols that define a server address and port for a secondary
|
|
protocol, a conversation can be used to link a protocol dissector to
|
|
the server port and address. The key is to create the new
|
|
conversation with the second address and port set to the "accept
|
|
any" values.
|
|
|
|
There are two support routines that will allow the second port and/or
|
|
address to be set latter.
|
|
|
|
conversation_set_port2( conversation_t *conv, guint32 port);
|
|
conversation_set_addr2( conversation_t *conv, address addr);
|
|
|
|
These routines will change the second address or port for the
|
|
conversation. So, the server port conversation will be converted into a
|
|
more complete conversation definition. Don't use these routines if you
|
|
want create a conversation between the server and client and retain the
|
|
server port definition, you must create a new conversation.
|
|
|
|
|
|
An example -
|
|
|
|
/* the handle for the dynamic dissector *
|
|
static dissector_handle_t sub_dissector_handle;
|
|
|
|
...
|
|
|
|
/* in the main protocol dissector, where the next dissector is setup */
|
|
|
|
/* if conversation has a data field, create it and load structure */
|
|
|
|
new_conv_info = g_mem_chunk_alloc( new_conv_vals);
|
|
new_conv_info->data1 = value1;
|
|
|
|
/* create the conversation for the dynamic server address and port */
|
|
/* NOTE: The second address and port values don't matter because the */
|
|
/* NO_ADDR2 and NO_PORT2 options are set. */
|
|
|
|
conversation = conversation_new( &server_src_addr, 0, protocol,
|
|
server_src_port, 0, new_conv_info, NO_ADDR2 | NO_PORT2);
|
|
|
|
/* set the dissector for the new conversation */
|
|
conversation_set_dissector(conversation, sub_dissector_handle);
|
|
|
|
|
|
2.5 Per packet information
|
|
|
|
Information can be stored for each data packet that is process by the dissector.
|
|
The information is added with the p_add_proto_data function and retreived with the
|
|
p_get_proto_data function. The data pointers passed into the p_add_proto_data are
|
|
not managed by the proto_data routines. If you use malloc or any other dynamic
|
|
memory allocation scheme, you must release the data when it isn't required.
|
|
|
|
void
|
|
p_add_proto_data(frame_data *fd, int proto, void *proto_data)
|
|
void *
|
|
p_get_proto_data(frame_data *fd, int proto)
|
|
|
|
Where:
|
|
fd - The fd pointer in the pinfo structure, pinfo->fd
|
|
proto - Protocol id returned by the proto_register_protocol call during initialization
|
|
proto_data - pointer to the dissector data.
|
|
|
|
|
|
2.6 User Preferences
|
|
|
|
If the dissector has user options, there is support for adding these preferences
|
|
to a configuration dialog.
|
|
|
|
You must register the module with the preferences routine with -
|
|
|
|
module_t *prefs_register_protocol(proto_id, void (*apply_cb)(void))
|
|
|
|
Where: proto_id - the value returned by "proto_register_protocol()" when
|
|
the protocol was registered
|
|
apply_cb - Callback routine that is call when preferences are applied
|
|
|
|
|
|
Then you can register the fields that can be configured by the user with these routines -
|
|
|
|
/* Register a preference with an unsigned integral value. */
|
|
void prefs_register_uint_preference(module_t *module, const char *name,
|
|
const char *title, const char *description, guint base, guint *var);
|
|
|
|
/* Register a preference with an Boolean value. */
|
|
void prefs_register_bool_preference(module_t *module, const char *name,
|
|
const char *title, const char *description, gboolean *var);
|
|
|
|
/* Register a preference with an enumerated value. */
|
|
void prefs_register_enum_preference(module_t *module, const char *name,
|
|
const char *title, const char *description, gint *var,
|
|
const enum_val *enumvals, gboolean radio_buttons)
|
|
|
|
/* Register a preference with a character-string value. */
|
|
void prefs_register_string_preference(module_t *module, const char *name,
|
|
const char *title, const char *description, char **var)
|
|
|
|
Where: module - Returned by the prefs_register_protocol routine
|
|
name - This is appended to the name of the protocol, with a
|
|
"." between them, to construct a name that
|
|
identifies the field in the preference file;
|
|
the name itself should not include the protocol
|
|
name, as the name in the preference file will
|
|
already have it
|
|
title - Field title in the preferences dialog
|
|
description - Comments added to the preference file above the
|
|
preference value
|
|
var - pointer to the storage location that is updated when the
|
|
field is changed in the preference dialog box
|
|
enumvals - an array of enum_val structures. This must be NULL terminated
|
|
radio_buttons - Is the enumvals a list of radio buttons?
|
|
|
|
|
|
An example from packet-beep.c -
|
|
|
|
proto_beep = proto_register_protocol("Blocks Extensible Exchange Protocol",
|
|
"BEEP", "beep");
|
|
|
|
...
|
|
|
|
/* Register our configuration options for BEEP, particularly our port */
|
|
|
|
beep_module = prefs_register_protocol(proto_beep, proto_reg_handoff_beep);
|
|
|
|
prefs_register_uint_preference(beep_module, "tcp.port", "BEEP TCP Port",
|
|
"Set the port for BEEP messages (if other"
|
|
" than the default of 10288)",
|
|
10, &global_beep_tcp_port);
|
|
|
|
prefs_register_bool_preference(beep_module, "strict_header_terminator",
|
|
"BEEP Header Requires CRLF",
|
|
"Specifies that BEEP requires CRLF as a "
|
|
"terminator, and not just CR or LF",
|
|
&global_beep_strict_term);
|
|
|
|
This will create preferences "beep.tcp.port" and
|
|
"beep.strict_header_terminator", the first of which is an unsigned
|
|
integer and the second of which is a Boolean.
|
|
|
|
2.7 Reassembly/desegmentation for protocols running atop TCP
|
|
|
|
There are two main ways of reassembling PDUs spanning across multiple
|
|
TCP segmentss. The first one is simpler, but assumes you are running
|
|
atop of TCP when this occurs (but your dissector might run atop of UDP,
|
|
too, for example), and that your PDUs consist of a fixed amount of data
|
|
that includes enough information to determine the PDU length, possibly
|
|
followed by additional data. The second one is more generic but
|
|
requires more code and is less efficient.
|
|
|
|
For the first method, you register two different dissection methods, on
|
|
for the TCP case, and one for the other cases. It is a good idea to
|
|
have a dissect_PROTO_common function which will parse the generic
|
|
content that you can find in all PDUs which is called from
|
|
dissect_PROTO_tcp when the reassembly is complete and from
|
|
dissect_PROTO_udp (or dissect_PROTO_other).
|
|
|
|
To register the distinct dissector functions, consider the following
|
|
example, stolen from packet-dns.c:
|
|
|
|
dissector_handle_t dns_udp_handle;
|
|
dissector_handle_t dns_tcp_handle;
|
|
dissector_handle_t mdns_udp_handle;
|
|
|
|
dns_udp_handle = create_dissector_handle(dissect_dns_udp,
|
|
proto_dns);
|
|
dns_tcp_handle = create_dissector_handle(dissect_dns_tcp,
|
|
proto_dns);
|
|
mdns_udp_handle = create_dissector_handle(dissect_mdns_udp,
|
|
proto_dns);
|
|
|
|
dissector_add("udp.port", UDP_PORT_DNS, dns_udp_handle);
|
|
dissector_add("tcp.port", TCP_PORT_DNS, dns_tcp_handle);
|
|
dissector_add("udp.port", UDP_PORT_MDNS, mdns_udp_handle);
|
|
dissector_add("tcp.port", TCP_PORT_MDNS, dns_tcp_handle);
|
|
|
|
The dissect_dns_udp function does very little work and calls
|
|
dissect_dns_common, while dissect_dns_tcp calls tcp_dissect_pdus with a
|
|
reference to a callback which will be called with reassembled data:
|
|
|
|
static void
|
|
dissect_dns_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
{
|
|
tcp_dissect_pdus(tvb, pinfo, tree, dns_desegment, 2,
|
|
get_dns_pdu_len, dissect_dns_tcp_pdu);
|
|
}
|
|
|
|
(The dissect_dns_tcp_pdu function acts similarly to dissect_dns_udp.)
|
|
The arguments to tcp_dissect_pdus are:
|
|
|
|
the tvbuff pointer, packet_info pointer, and proto_tree pointer
|
|
passed to the dissector;
|
|
|
|
a gboolean flag indicating whether desegmentation is enabled for
|
|
your protocol;
|
|
|
|
the number of bytes of PDU data required to determine the length
|
|
of the PDU;
|
|
|
|
a routine that takes as arguments a tvbuff pointer and an offset
|
|
value representing the offset into the tvbuff at which a PDU
|
|
begins and should return - *without* throwing an exception (it
|
|
is guaranteed that the number of bytes specified by the previous
|
|
argument to tcp_dissect_pdus is available, but more data might
|
|
not be available, so don't refer to any data past that) - the
|
|
total length of the PDU, in bytes;
|
|
|
|
a routine that's passed a tvbuff pointer, packet_info pointer,
|
|
and proto_tree pointer, with the tvbuff containing a
|
|
possibly-reassembled PDU, and that should dissect that PDU.
|
|
|
|
The second method is to return a modified pinfo structure when
|
|
dissect_PROTO is called. In this case, you have to check if you have
|
|
collected enough bytes: if you have enough, you parse the PDU, and if
|
|
don't have enough bytes, you return from the dissector supplying
|
|
information to the caller on how many bytes you need to proceed. This
|
|
is done by indicating the offset where you would like to start again and
|
|
the number of bytes that you need in pinfo->desegment_*:
|
|
|
|
if (i_miss_five_bytes) {
|
|
pinfo->desegment_offset = offset;
|
|
pinfo->desegment_len = 5;
|
|
}
|
|
|
|
You can repeat this procedure until you've got enough bytes; for
|
|
example, you can request one byte more until you've got the byte you're
|
|
searching for if the data to be dissected consists of a sequence of
|
|
bytes ending with a particular byte value.
|
|
|
|
3. Plugins
|
|
|
|
See the README.plugins for more information on how to "pluginize"
|
|
a dissector.
|
|
|
|
4.0 Extending Wiretap.
|
|
|
|
5.0 How the Display Filter Engine works
|
|
|
|
code:
|
|
epan/dfilter/* - the display filter engine, including
|
|
scanner, parser, syntax-tree semantics checker, DFVM bytecode
|
|
generator, and DFVM engine.
|
|
epan/ftypes/* - the definitions of the various FT_* field types.
|
|
epan/proto.c - proto_tree-related routines
|
|
|
|
5.1 Parsing text
|
|
|
|
The scanner/parser pair read the string representing the display filter
|
|
and convert it into a very simple syntax tree. The syntax tree is very
|
|
simple in that it is possible that many of the nodes contain unparsed
|
|
chunks of text from the display filter.
|
|
|
|
5.1 Enhancing the syntax tree.
|
|
|
|
The semantics of the simple syntax tree are checked to make sure that
|
|
the fields that are being compared are being compared to appropriate
|
|
values. For example, if a field is an integer, it can't be compared to
|
|
a string, unless a value_string has been defined for that field.
|
|
|
|
During the process of checking the semantics, the simple syntax tree is
|
|
fleshed out and no longer contains nodes with unparsed information. The
|
|
syntax tree is no longer in its simple form, but in its complete form.
|
|
|
|
5.2 Converting to DFVM bytecode
|
|
|
|
The syntax tree is analyzed to create a sequence of bytecodes in the
|
|
"DFVM" language. "DFVM" stands for Display Filter Virtual Machine. The
|
|
DFVM is similar in spirit, but not in definition, to the BPF VM that
|
|
libpcap uses to analyze packets.
|
|
|
|
A virtual bytecode is created and used so that the actual process of
|
|
filtering packets will be fast. That is, it should be faster to process
|
|
a list of VM bytecodes than to attempt to filter packets directly from
|
|
the syntax tree. (heh... no measurement has been made to support this
|
|
supposition)
|
|
|
|
5.3 Filtering
|
|
|
|
Once the DFVM bytecode has been produced, it's a simple matter of
|
|
running the DFVM engine against the proto_tree from the packet
|
|
dissection, using the DFVM bytecodes as instructions. If the DFVM
|
|
bytecode is known before packet dissection occurs, the
|
|
proto_tree-related code can be "primed" to store away pointers to
|
|
field_info structures that are interesting to the display filter. This
|
|
makes lookup of those field_info structures during the filtering process
|
|
faster.
|
|
|
|
|
|
6.0 Adding new capabilities.
|
|
|
|
|
|
|
|
|
|
James Coe <jammer@cin.net>
|
|
Gilbert Ramirez <gram@alumni.rice.edu>
|
|
Jeff Foster <jfoste@woodward.com>
|
|
Olivier Abad <oabad@cybercable.fr>
|
|
Laurent Deniel <laurent.deniel@free.fr>
|
|
Gerald Combs <gerald@ethereal.com>
|
|
Guy Harris <guy@alum.mit.edu>
|