forked from osmocom/wireshark
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3702 lines
156 KiB
3702 lines
156 KiB
This file is a HOWTO for Wireshark developers interested in writing or working
|
|
on Wireshark protocol dissectors. It describes expected code patterns and the
|
|
use of some of the important functions and variables.
|
|
|
|
This file is compiled to give in depth information on Wireshark.
|
|
It is by no means all inclusive and complete. Please feel free to send
|
|
remarks and patches to the developer mailing list.
|
|
|
|
If you haven't read README.developer, read that first!
|
|
|
|
0. Prerequisites.
|
|
|
|
Before starting to develop a new dissector, a "running" Wireshark build
|
|
environment is required - there's no such thing as a standalone "dissector
|
|
build toolkit".
|
|
|
|
How to setup such an environment is platform dependent; detailed
|
|
information about these steps can be found in the "Developer's Guide"
|
|
(available from: https://www.wireshark.org) and in the INSTALL and
|
|
README.md files of the sources root dir.
|
|
|
|
0.1. Dissector related README files.
|
|
|
|
You'll find additional dissector related information in the following README
|
|
files:
|
|
|
|
- README.heuristic - what are heuristic dissectors and how to write them
|
|
- README.plugins - how to "pluginize" a dissector
|
|
- README.request_response_tracking - how to track req./resp. times and such
|
|
- README.wmem - how to obtain "memory leak free" memory
|
|
|
|
0.2 Contributors
|
|
|
|
James Coe <jammer[AT]cin.net>
|
|
Gilbert Ramirez <gram[AT]alumni.rice.edu>
|
|
Jeff Foster <jfoste[AT]woodward.com>
|
|
Olivier Abad <oabad[AT]cybercable.fr>
|
|
Laurent Deniel <laurent.deniel[AT]free.fr>
|
|
Gerald Combs <gerald[AT]wireshark.org>
|
|
Guy Harris <guy[AT]alum.mit.edu>
|
|
Ulf Lamping <ulf.lamping[AT]web.de>
|
|
Barbu Paul - Gheorghe <barbu.paul.gheorghe[AT]gmail.com>
|
|
|
|
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 Skeleton code.
|
|
|
|
Wireshark requires certain things when setting up a protocol dissector.
|
|
We provide basic skeleton code for a dissector that you can copy to a new 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.
|
|
|
|
The skeleton code lives in the file "packet-PROTOABBREV.c" in the same source
|
|
directory as this README.
|
|
|
|
If instead of using the skeleton you base your dissector on an existing real
|
|
dissector, please put a little note in the copyright header indicating which
|
|
dissector you started with.
|
|
|
|
Usually, you will put your newly created dissector file into the directory
|
|
epan/dissectors/, just like all the other packet-*.c files already in there.
|
|
|
|
Also, please add your dissector file to the corresponding makefiles,
|
|
described in section "1.8 Editing CMakeLists.txt to add your dissector" below.
|
|
|
|
Dissectors that use the dissector registration API to register with a lower
|
|
level protocol (this is the vast majority) don't need to define a prototype in
|
|
their .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, and you may
|
|
need to include additional headers.
|
|
|
|
1.2 Explanation of needed substitutions in code skeleton.
|
|
|
|
In the skeleton sample code 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.
|
|
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, in the dialog box of enabled protocols,
|
|
and in the dialog box for filter fields when constructing
|
|
a filter expression.
|
|
PROTOFILTERNAME A name for the protocol for use in filter expressions;
|
|
it may contain only letters, digits, hyphens, underscores and
|
|
periods. Names should use lower case only. (Support for
|
|
upper/mixed case may be removed in the future.)
|
|
PROTOABBREV An abbreviation for the protocol; this is used in code and
|
|
must be a valid C identifier. Additionally it should follow
|
|
any applicable C style guidelines. It is usually the same as
|
|
PROTOFILTERNAME with all lower-case letters and
|
|
non-alphanumerics replaced with underscores.
|
|
LICENSE The license this dissector is under. Please use a SPDX License
|
|
identifier.
|
|
YEARS The years the above license is valid for.
|
|
FIELDNAME The displayed name for the header field.
|
|
FIELDFILTERNAME A name for the header field for use in filter expressions;
|
|
it may contain only letters, digits, hyphens, underscores and
|
|
periods. It must start with PROTOFILTERNAME followed by a dot.
|
|
Names should use lower case only. (Support for upper/mixed case
|
|
may be removed in the future.)
|
|
FIELDABBREV An abbreviation for the header field; this is used in code and
|
|
must be a valid C identifier. Additionally it should follow
|
|
any applicable C style guidelines. It is usually the same as
|
|
FIELDFILTERNAME with all lower-case letters and
|
|
non-alphanumerics replaced with underscores.
|
|
FIELDTYPE FT_NONE, FT_BOOLEAN, FT_CHAR, FT_UINT8, FT_UINT16, FT_UINT24,
|
|
FT_UINT32, FT_UINT40, FT_UINT48, FT_UINT56, FT_UINT64,
|
|
FT_INT8, FT_INT16, FT_INT24, FT_INT32, FT_INT40, FT_INT48,
|
|
FT_INT56, FT_INT64, FT_FLOAT, FT_DOUBLE, FT_ABSOLUTE_TIME,
|
|
FT_RELATIVE_TIME, FT_STRING, FT_STRINGZ, FT_EUI64,
|
|
FT_UINT_STRING, FT_ETHER, FT_BYTES, FT_UINT_BYTES, FT_IPv4,
|
|
FT_IPv6, FT_IPXNET, FT_FRAMENUM, FT_PROTOCOL, FT_GUID, FT_OID,
|
|
FT_REL_OID, FT_AX25, FT_VINES, FT_SYSTEM_ID, FT_FC, FT_FCWWN
|
|
FIELDDISPLAY --For FT_UINT{8,16,24,32,40,48,56,64} and
|
|
FT_INT{8,16,24,32,40,48,56,64):
|
|
|
|
BASE_DEC, BASE_HEX, BASE_OCT, BASE_DEC_HEX, BASE_HEX_DEC,
|
|
BASE_CUSTOM, or BASE_NONE, possibly ORed with
|
|
BASE_RANGE_STRING, BASE_EXT_STRING, BASE_VAL64_STRING,
|
|
BASE_ALLOW_ZERO, BASE_UNIT_STRING, BASE_SPECIAL_VALS,
|
|
BASE_NO_DISPLAY_VALUE, BASE_SHOW_ASCII_PRINTABLE, or
|
|
BASE_SHOW_UTF_8_PRINTABLE
|
|
|
|
BASE_NONE may be used with a non-NULL FIELDCONVERT when the
|
|
numeric value of the field itself is not of significance to
|
|
the user (for example, the number is a generated field).
|
|
When this is the case the numeric value is not shown to the
|
|
user in the protocol decode nor is it used when preparing
|
|
filters for the field in question.
|
|
|
|
BASE_NO_DISPLAY_VALUE will just display the field name with
|
|
no value. It is intended for byte arrays (FT_BYTES or
|
|
FT_UINT_BYTES) or header fields above a subtree. The
|
|
value will still be filterable, just not displayed.
|
|
|
|
--For FT_UINT16:
|
|
|
|
BASE_PT_UDP, BASE_PT_TCP, BASE_PT_DCCP or BASE_PT_SCTP
|
|
|
|
--For FT_UINT24:
|
|
|
|
BASE_OUI
|
|
|
|
--For FT_CHAR:
|
|
BASE_HEX, BASE_OCT, BASE_CUSTOM, or BASE_NONE, possibly
|
|
ORed with BASE_RANGE_STRING, BASE_EXT_STRING or
|
|
BASE_VAL64_STRING.
|
|
|
|
BASE_NONE can be used in the same way as with FT_UINT8.
|
|
|
|
--For FT_FLOAT, FT_DOUBLE:
|
|
BASE_NONE, BASE_DEC, BASE_HEX, BASE_EXP or BASE_CUSTOM.
|
|
|
|
BASE_NONE uses BASE_DEC or BASE_EXP, similarly to the
|
|
%g double format for the printf() function.
|
|
|
|
--For FT_ABSOLUTE_TIME:
|
|
|
|
ABSOLUTE_TIME_LOCAL, ABSOLUTE_TIME_UTC, or
|
|
ABSOLUTE_TIME_DOY_UTC
|
|
|
|
--For FT_BOOLEAN:
|
|
|
|
if BITMASK is non-zero:
|
|
Number of bits in the field containing the FT_BOOLEAN
|
|
bitfield.
|
|
otherwise:
|
|
(must be) BASE_NONE
|
|
|
|
--For FT_STRING, FT_STRINGZ and FT_UINT_STRING:
|
|
|
|
(must be) BASE_NONE
|
|
|
|
--For FT_BYTES and FT_UINT_BYTES:
|
|
|
|
SEP_DOT, SEP_DASH, SEP_COLON, or SEP_SPACE to provide
|
|
a separator between bytes; BASE_NONE has no separator
|
|
between bytes. These can be ORed with BASE_ALLOW_ZERO,
|
|
BASE_SHOW_ASCII_PRINTABLE, or BASE_SHOW_UTF_8_PRINTABLE.
|
|
|
|
BASE_ALLOW_ZERO displays <none> instead of <MISSING>
|
|
for a zero-sized byte array.
|
|
BASE_SHOW_ASCII_PRINTABLE will check whether the
|
|
field's value consists entirely of printable ASCII
|
|
characters and, if so, will display the field's value
|
|
as a string, in quotes. The value will still be
|
|
filterable as a byte value.
|
|
BASE_SHOW_UTF_8_PRINTABLE will check whether the
|
|
field's value is valid UTF-8 consisting entirely of
|
|
printable characters and, if so, will display the field's
|
|
value as a string, in quotes. The value will still be
|
|
filterable as a byte value.
|
|
|
|
--For FT_IPv4:
|
|
|
|
BASE_NETMASK - Used for IPv4 address that should never
|
|
attempted to be resolved (like netmasks)
|
|
otherwise:
|
|
(must be) BASE_NONE
|
|
|
|
--For all other types:
|
|
|
|
BASE_NONE
|
|
FIELDCONVERT VALS(x), VALS64(x), RVALS(x), TFS(x), CF_FUNC(x), NULL
|
|
BITMASK Used to mask a field not 8-bit aligned or with a size other
|
|
than a multiple of 8 bits
|
|
FIELDDESCR A brief description of the field, or NULL. [Please do not use ""].
|
|
|
|
If, for example, PROTONAME is "Internet Bogosity Discovery Protocol",
|
|
PROTOSHORTNAME would be "IBDP", and PROTOFILTERNAME would be "ibdp". Try to
|
|
conform with IANA names.
|
|
|
|
1.2.1 Automatic substitution in code skeleton
|
|
|
|
Instead of manual substitutions in the code skeleton, a tool to automate it can
|
|
be found under the tools directory. The script is called tools/generate-dissector.py
|
|
and takes all the needed options to generate a compilable dissector. Look at the
|
|
above fields to know how to set them. Some assumptions have been made in the
|
|
generation to shorten the list of required options. The script patches the
|
|
CMakeLists.txt file adding the new dissector in the proper list, alphabetically
|
|
sorted.
|
|
|
|
1.3 The dissector and the data it receives.
|
|
|
|
|
|
1.3.1 Header file.
|
|
|
|
This is only needed if the dissector doesn't use self-registration to
|
|
register itself with the lower level dissector, or if the protocol dissector
|
|
wants/needs to expose code to other subdissectors.
|
|
|
|
The dissector must be declared exactly as follows in the file
|
|
packet-PROTOABBREV.h:
|
|
|
|
int
|
|
dissect_PROTOABBREV(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
|
|
|
|
|
|
1.3.2 Extracting data from packets.
|
|
|
|
NOTE: See the file /epan/tvbuff.h 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 an opaque data
|
|
structure, the internal data structures are hidden and the data must be
|
|
accessed via the tvbuffer accessors.
|
|
|
|
The accessors are:
|
|
|
|
Bit accessors for a maximum of 8-bits, 16-bits 32-bits and 64-bits:
|
|
|
|
guint8 tvb_get_bits8(tvbuff_t *tvb, gint bit_offset, const gint no_of_bits);
|
|
guint16 tvb_get_bits16(tvbuff_t *tvb, guint bit_offset, const gint no_of_bits, const guint encoding);
|
|
guint32 tvb_get_bits32(tvbuff_t *tvb, guint bit_offset, const gint no_of_bits, const guint encoding);
|
|
guint64 tvb_get_bits64(tvbuff_t *tvb, guint bit_offset, const gint no_of_bits, const guint encoding);
|
|
|
|
Single-byte accessors for 8-bit unsigned integers (guint8) and 8-bit
|
|
signed integers (gint8):
|
|
|
|
guint8 tvb_get_guint8(tvbuff_t *tvb, const gint offset);
|
|
gint8 tvb_get_gint8(tvbuff_t *tvb, const gint offset);
|
|
|
|
Network-to-host-order accessors:
|
|
|
|
16-bit unsigned (guint16) and signed (gint16) integers:
|
|
|
|
guint16 tvb_get_ntohs(tvbuff_t *tvb, const gint offset);
|
|
gint16 tvb_get_ntohis(tvbuff_t *tvb, const gint offset);
|
|
|
|
24-bit unsigned and signed integers:
|
|
|
|
guint32 tvb_get_ntoh24(tvbuff_t *tvb, const gint offset);
|
|
gint32 tvb_get_ntohi24(tvbuff_t *tvb, const gint offset);
|
|
|
|
32-bit unsigned (guint32) and signed (gint32) integers:
|
|
|
|
guint32 tvb_get_ntohl(tvbuff_t *tvb, const gint offset);
|
|
gint32 tvb_get_ntohil(tvbuff_t *tvb, const gint offset);
|
|
|
|
40-bit unsigned and signed integers:
|
|
|
|
guint64 tvb_get_ntoh40(tvbuff_t *tvb, const gint offset);
|
|
gint64 tvb_get_ntohi40(tvbuff_t *tvb, const gint offset);
|
|
|
|
48-bit unsigned and signed integers:
|
|
|
|
guint64 tvb_get_ntoh48(tvbuff_t *tvb, const gint offset);
|
|
gint64 tvb_get_ntohi48(tvbuff_t *tvb, const gint offset);
|
|
|
|
56-bit unsigned and signed integers:
|
|
|
|
guint64 tvb_get_ntoh56(tvbuff_t *tvb, const gint offset);
|
|
gint64 tvb_get_ntohi56(tvbuff_t *tvb, const gint offset);
|
|
|
|
64-bit unsigned (guint64) and signed (gint64) integers:
|
|
|
|
guint64 tvb_get_ntoh64(tvbuff_t *tvb, const gint offset);
|
|
gint64 tvb_get_ntohi64(tvbuff_t *tvb, const gint offset);
|
|
|
|
Single-precision and double-precision IEEE floating-point numbers:
|
|
|
|
gfloat tvb_get_ntohieee_float(tvbuff_t *tvb, const gint offset);
|
|
gdouble tvb_get_ntohieee_double(tvbuff_t *tvb, const gint offset);
|
|
|
|
Little-Endian-to-host-order accessors:
|
|
|
|
16-bit unsigned (guint16) and signed (gint16) integers:
|
|
|
|
guint16 tvb_get_letohs(tvbuff_t *tvb, const gint offset);
|
|
gint16 tvb_get_letohis(tvbuff_t *tvb, const gint offset);
|
|
|
|
24-bit unsigned and signed integers:
|
|
|
|
guint32 tvb_get_letoh24(tvbuff_t *tvb, const gint offset);
|
|
gint32 tvb_get_letohi24(tvbuff_t *tvb, const gint offset);
|
|
|
|
32-bit unsigned (guint32) and signed (gint32) integers:
|
|
|
|
guint32 tvb_get_letohl(tvbuff_t *tvb, const gint offset);
|
|
gint32 tvb_get_letohil(tvbuff_t *tvb, const gint offset);
|
|
|
|
40-bit unsigned and signed integers:
|
|
|
|
guint64 tvb_get_letoh40(tvbuff_t *tvb, const gint offset);
|
|
gint64 tvb_get_letohi40(tvbuff_t *tvb, const gint offset);
|
|
|
|
48-bit unsigned and signed integers:
|
|
|
|
guint64 tvb_get_letoh48(tvbuff_t *tvb, const gint offset);
|
|
gint64 tvb_get_letohi48(tvbuff_t *tvb, const gint offset);
|
|
|
|
56-bit unsigned and signed integers:
|
|
|
|
guint64 tvb_get_letoh56(tvbuff_t *tvb, const gint offset);
|
|
gint64 tvb_get_letohi56(tvbuff_t *tvb, const gint offset);
|
|
|
|
64-bit unsigned (guint64) and signed (gint64) integers:
|
|
|
|
guint64 tvb_get_letoh64(tvbuff_t *tvb, const gint offset);
|
|
gint64 tvb_get_letohi64(tvbuff_t *tvb, const gint offset);
|
|
|
|
NOTE: Although each of the integer accessors above return types with
|
|
specific sizes, the returned values are subject to C's integer promotion
|
|
rules. It's often safer and more useful to use int or guint for 32-bit
|
|
and smaller types, and gint64 or guint64 for 40-bit and larger types.
|
|
Just because a value occupied 16 bits on the wire or over the air
|
|
doesn't mean it will within Wireshark.
|
|
|
|
Single-precision and double-precision IEEE floating-point numbers:
|
|
|
|
gfloat tvb_get_letohieee_float(tvbuff_t *tvb, const gint offset);
|
|
gdouble tvb_get_letohieee_double(tvbuff_t *tvb, const gint offset);
|
|
|
|
Encoding-to_host-order accessors:
|
|
|
|
16-bit unsigned (guint16) and signed (gint16) integers:
|
|
|
|
guint16 tvb_get_guint16(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
gint16 tvb_get_gint16(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
|
|
24-bit unsigned and signed integers:
|
|
|
|
guint32 tvb_get_guint24(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
gint32 tvb_get_gint24(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
|
|
32-bit unsigned (guint32) and signed (gint32) integers:
|
|
|
|
guint32 tvb_get_guint32(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
gint32 tvb_get_gint32(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
|
|
40-bit unsigned and signed integers:
|
|
|
|
guint64 tvb_get_guint40(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
gint64 tvb_get_gint40(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
|
|
48-bit unsigned and signed integers:
|
|
|
|
guint64 tvb_get_guint48(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
gint64 tvb_get_gint48(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
|
|
56-bit unsigned and signed integers:
|
|
|
|
guint64 tvb_get_guint56(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
gint64 tvb_get_gint56(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
|
|
64-bit unsigned (guint64) and signed (gint64) integers:
|
|
|
|
guint64 tvb_get_guint64(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
gint64 tvb_get_gint64(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
|
|
Single-precision and double-precision IEEE floating-point numbers:
|
|
|
|
gfloat tvb_get_ieee_float(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
gdouble tvb_get_ieee_double(tvbuff_t *tvb, const gint offset, const guint encoding);
|
|
|
|
"encoding" should be ENC_BIG_ENDIAN for Network-to-host-order,
|
|
ENC_LITTLE_ENDIAN for Little-Endian-to-host-order, or ENC_HOST_ENDIAN
|
|
for host order.
|
|
|
|
Accessors for IPv4 and IPv6 addresses:
|
|
|
|
guint32 tvb_get_ipv4(tvbuff_t *tvb, const gint offset);
|
|
void tvb_get_ipv6(tvbuff_t *tvb, const gint offset, ws_in6_addr *addr);
|
|
|
|
NOTE: IPv4 addresses are not to be converted to host byte order before
|
|
being passed to "proto_tree_add_ipv4()". You should use "tvb_get_ipv4()"
|
|
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.
|
|
|
|
gchar *tvb_ip_to_str(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset)
|
|
gchar *tvb_ip6_to_str(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset)
|
|
|
|
Returns a null-terminated buffer containing a string with IPv4 or IPv6 Address
|
|
from the specified tvbuff, starting at the specified offset.
|
|
|
|
Accessors for GUID:
|
|
|
|
void tvb_get_ntohguid(tvbuff_t *tvb, const gint offset, e_guid_t *guid);
|
|
void tvb_get_letohguid(tvbuff_t *tvb, const gint offset, e_guid_t *guid);
|
|
void tvb_get_guid(tvbuff_t *tvb, const gint offset, e_guid_t *guid, const guint encoding);
|
|
|
|
String accessors:
|
|
|
|
guint8 *tvb_get_string_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint length, const guint encoding);
|
|
|
|
Returns a null-terminated buffer allocated from the specified scope, containing
|
|
data from the specified tvbuff, starting at the specified offset, and containing
|
|
the specified length worth of characters. Reads data in the specified encoding
|
|
and produces UTF-8 in the buffer. See below for a list of input encoding values.
|
|
|
|
The buffer is allocated in the given wmem scope (see README.wmem for more
|
|
information).
|
|
|
|
guint8 *tvb_get_stringz_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp, const guint encoding);
|
|
|
|
Returns a null-terminated buffer allocated from the specified scope,
|
|
containing data from the specified tvbuff, starting at the specified
|
|
offset, and containing all characters from the tvbuff up to and
|
|
including a terminating null character in the tvbuff. Reads data in the
|
|
specified encoding and produces UTF-8 in the buffer. See below for a
|
|
list of input encoding values. "*lengthp" will be set to the length of
|
|
the string, including the terminating null.
|
|
|
|
The buffer is allocated in the given wmem scope (see README.wmem for more
|
|
information).
|
|
|
|
gint tvb_get_nstringz(tvbuff_t *tvb, const gint offset, const guint bufsize, guint8* buffer);
|
|
gint tvb_get_nstringz0(tvbuff_t *tvb, const gint offset, const guint bufsize, guint8* buffer);
|
|
|
|
Copies bufsize bytes, including the terminating NULL, to buffer. If a NULL
|
|
terminator is found before reaching bufsize, only the bytes up to and including
|
|
the NULL are copied. Returns the number of bytes copied (not including
|
|
terminating NULL), or -1 if the string was truncated in the buffer due to
|
|
not having reached the terminating NULL. In this case, the resulting
|
|
buffer is not NULL-terminated.
|
|
tvb_get_nstringz0() works like tvb_get_nstringz(), but never returns -1 since
|
|
the string is guaranteed to have a terminating NULL. If the string was truncated
|
|
when copied into buffer, a NULL is placed at the end of buffer to terminate it.
|
|
|
|
gchar *tvb_get_ts_23_038_7bits_string(wmem_allocator_t *scope, tvbuff_t *tvb,
|
|
const gint bit_offset, gint no_of_chars);
|
|
|
|
tvb_get_ts_23_038_7bits_string() returns a string of a given number of
|
|
characters and encoded according to 3GPP TS 23.038 7 bits alphabet.
|
|
|
|
The buffer is allocated in the given wmem scope (see README.wmem for more
|
|
information).
|
|
|
|
Byte Array Accessors:
|
|
|
|
gchar *tvb_bytes_to_str(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint len);
|
|
|
|
Formats a bunch of data from a tvbuff as bytes, returning a pointer
|
|
to the string with the data formatted as two hex digits for each byte.
|
|
The string pointed to is stored in an "wmem_alloc'd" buffer which will be freed
|
|
depending on its scope (typically wmem_packet_scope which is freed after the frame).
|
|
The formatted string will contain the hex digits for at most the first 16 bytes of
|
|
the data. If len is greater than 16 bytes, a trailing "..." will be added to the string.
|
|
|
|
gchar *tvb_bytes_to_str_punct(wmem_allocator_t *scope, tvbuff_t *tvb,
|
|
const gint offset, const gint len, const gchar punct);
|
|
|
|
This function is similar to tvb_bytes_to_str(...) except that 'punct' is inserted
|
|
between the hex representation of each byte.
|
|
|
|
GByteArray *tvb_get_string_bytes(tvbuff_t *tvb, const gint offset, const gint length,
|
|
const guint encoding, GByteArray* bytes, gint *endoff)
|
|
|
|
Given a tvbuff, an offset into the tvbuff, and a length that starts
|
|
at that offset (which may be -1 for "all the way to the end of the
|
|
tvbuff"), fetch the hex-decoded byte values of the tvbuff into the
|
|
passed-in 'bytes' array, based on the passed-in encoding. In other
|
|
words, convert from a hex-ascii string in tvbuff, into the supplied
|
|
GByteArray.
|
|
|
|
gchar *tvb_bcd_dig_to_wmem_packet_str(tvbuff_t *tvb, const gint offset, const gint len, dgt_set_t *dgt, gboolean skip_first);
|
|
|
|
Given a tvbuff, an offset into the tvbuff, and a length that starts
|
|
at that offset (which may be -1 for "all the way to the end of the
|
|
tvbuff"), fetch BCD encoded digits from a tvbuff starting from either
|
|
the low or high half byte, formatting the digits according to an input digit set,
|
|
if NUll a default digit set of 0-9 returning "?" for overdecadic digits will be used.
|
|
A pointer to the packet scope allocated string will be returned.
|
|
Note: a tvbuff content of 0xf is considered a 'filler' and will end the conversion.
|
|
|
|
Copying memory:
|
|
void* tvb_memcpy(tvbuff_t *tvb, void* target, const gint offset, size_t length);
|
|
|
|
Copies into the specified target the specified length's worth of data
|
|
from the specified tvbuff, starting at the specified offset.
|
|
|
|
void *tvb_memdup(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, size_t length);
|
|
|
|
Returns a buffer containing a copy of the given TVB bytes. The buffer is
|
|
allocated in the given wmem scope (see README.wmem for more information).
|
|
|
|
Pointer-retrieval:
|
|
/* WARNING! Don't use this function. There is almost always a better way.
|
|
* 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. Also see the warning in the Portability section.
|
|
*/
|
|
const guint8* tvb_get_ptr(tvbuff_t *tvb, const gint offset, const gint length);
|
|
|
|
Length query:
|
|
Get amount of captured data in the buffer (which is *NOT* necessarily the
|
|
length of the packet). You probably want tvb_reported_length instead:
|
|
|
|
guint tvb_captured_length(const tvbuff_t *tvb);
|
|
|
|
Get reported length of buffer:
|
|
|
|
guint tvb_reported_length(const tvbuff_t *tvb);
|
|
|
|
|
|
1.4 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.
|
|
|
|
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.4.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 "PROTOFILTERNAME":
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "PROTOFILTERNAME");
|
|
|
|
|
|
1.4.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.4.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:
|
|
|
|
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.4.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.4.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.4.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.4.7 The col_append_sep_str and col_append_sep_fstr functions.
|
|
|
|
In specific situations the developer knows that a column's value will be
|
|
created in a stepwise manner, where the appended values are listed. Both
|
|
'col_append_sep_str' and 'col_append_sep_fstr' functions will add an item
|
|
separator between two consecutive items, and will not add the separator at the
|
|
beginning of the column. The remainder of the work both functions do is
|
|
identical to what 'col_append_str' and 'col_append_fstr' do.
|
|
|
|
1.4.8 The col_set_fence and col_prepend_fence_fstr functions.
|
|
|
|
Sometimes a dissector may be called multiple times for different PDUs in the
|
|
same frame (for example in the case of SCTP chunk bundling: several upper
|
|
layer data packets may be contained in one SCTP packet). If the upper layer
|
|
dissector calls 'col_set_str()' or 'col_clear()' on the Info column when it
|
|
begins dissecting each of those PDUs then when the frame is fully dissected
|
|
the Info column would contain only the string from the last PDU in the frame.
|
|
The 'col_set_fence' function erects a "fence" in the column that prevents
|
|
subsequent 'col_...' calls from clearing the data currently in that column.
|
|
For example, the SCTP dissector calls 'col_set_fence' on the Info column
|
|
after it has called any subdissectors for that chunk so that subdissectors
|
|
of any subsequent chunks may only append to the Info column.
|
|
'col_prepend_fence_fstr' prepends data before a fence (moving it if
|
|
necessary). It will create a fence at the end of the prepended data if the
|
|
fence does not already exist.
|
|
|
|
|
|
1.4.9 The col_set_time function.
|
|
|
|
The 'col_set_time' function takes an nstime value as its third argument.
|
|
This nstime value is a relative value and will be added as such to the
|
|
column. The fourth argument is the filtername holding this value. This
|
|
way, rightclicking on the column makes it possible to build a filter
|
|
based on the time-value.
|
|
|
|
For example:
|
|
|
|
col_set_time(pinfo->cinfo, COL_REL_TIME, &ts, "s4607.ploc.time");
|
|
|
|
|
|
1.5 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 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 Wireshark 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 "epan/dissectors/CMakeLists.txt";
|
|
|
|
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 would 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 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 {
|
|
const char *name;
|
|
const char *abbrev;
|
|
enum ftenum type;
|
|
int display;
|
|
const void *strings;
|
|
guint64 bitmask;
|
|
const char *blurb;
|
|
.....
|
|
};
|
|
|
|
name (FIELDNAME)
|
|
----------------
|
|
A string representing the name of the field. This is the name
|
|
that will appear in the graphical protocol tree. It must be a non-empty
|
|
string.
|
|
|
|
abbrev (FIELDFILTERNAME)
|
|
--------------------
|
|
A string with a filter name for the field. The name should start
|
|
with the filter name of the parent protocol followed by a period as a
|
|
separator. For example, the "src" field in an IP packet would have "ip.src"
|
|
as a filter name. 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 names
|
|
follow this pattern: "trmac.errors.iso", "trmac.errors.noniso", etc.
|
|
It must be a non-empty string.
|
|
|
|
type (FIELDTYPE)
|
|
----------------
|
|
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_PROTOCOL Used for protocols which will be placing
|
|
themselves as top-level items in the
|
|
"Packet Details" pane of the UI.
|
|
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_CHAR An 8-bit ASCII character. It's treated similarly to an
|
|
FT_UINT8, but is displayed as a C-style character
|
|
constant.
|
|
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_UINT40 A 40-bit unsigned integer.
|
|
FT_UINT48 A 48-bit unsigned integer.
|
|
FT_UINT56 A 56-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_INT40 A 40-bit signed integer.
|
|
FT_INT48 A 48-bit signed integer.
|
|
FT_INT56 A 56-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 An absolute time from some fixed point in time,
|
|
displayed as the date, followed by the time, as
|
|
hours, minutes, and seconds with 9 digits after
|
|
the decimal point.
|
|
FT_RELATIVE_TIME Seconds (4 bytes) and nanoseconds (4 bytes)
|
|
of time relative to an arbitrary time.
|
|
displayed as seconds and 9 digits
|
|
after the decimal point.
|
|
FT_STRING A string of characters, not necessarily
|
|
NULL-terminated, but possibly NULL-padded.
|
|
This, and the other string-of-characters
|
|
types, are to be used for text strings,
|
|
not raw binary data.
|
|
FT_STRINGZ A NULL-terminated string of characters.
|
|
The string length is normally the length
|
|
given in the proto_tree_add_item() call.
|
|
However if the length given in the call
|
|
is -1, then the length used is that
|
|
returned by calling tvb_strsize().
|
|
This should only be used if the string,
|
|
in the packet, is always terminated with
|
|
a NULL character, either because the length
|
|
isn't otherwise specified or because a
|
|
character count *and* a NULL terminator are
|
|
both used.
|
|
FT_STRINGZPAD A NULL-padded string of characters.
|
|
The length is given in the proto_tree_add_item()
|
|
call, but may be larger than the length of
|
|
the string, with extra bytes being NULL padding.
|
|
This is typically used for fixed-length fields
|
|
that contain a string value that might be shorter
|
|
than the fixed length.
|
|
FT_STRINGZTRUNC A NULL-truncated string of characters.
|
|
The length is given in the proto_tree_add_item()
|
|
call, but may be larger than the length of
|
|
the string, with a NULL character after the last
|
|
character of the string, and the remaining bytes
|
|
being padding with unspecified contents. This is
|
|
typically used for fixed-length fields that contain
|
|
a string value that might be shorter than the fixed
|
|
length.
|
|
FT_UINT_STRING A counted string of characters, consisting
|
|
of a count (represented as an integral value,
|
|
of width given in the proto_tree_add_item()
|
|
call) followed immediately by that 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_UINT_BYTES A counted string of bytes, consisting
|
|
of a count (represented as an integral value,
|
|
of width given in the proto_tree_add_item()
|
|
call) followed immediately by that number of
|
|
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.
|
|
FT_GUID A Globally Unique Identifier
|
|
FT_OID An ASN.1 Object Identifier
|
|
FT_REL_OID An ASN.1 Relative Object Identifier
|
|
FT_EUI64 A EUI-64 Address
|
|
FT_AX25 A AX-25 Address
|
|
FT_VINES A Vines Address
|
|
FT_SYSTEM_ID An OSI System-ID
|
|
FT_FCWWN A Fibre Channel WWN 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.
|
|
|
|
Some constraints are imposed on the header fields depending on the type
|
|
(e.g. FT_BYTES) of the field. Fields of type FT_ABSOLUTE_TIME must use
|
|
'ABSOLUTE_TIME_{LOCAL,UTC,DOY_UTC}, NULL, 0x0' as values for the
|
|
'display, 'strings', and 'bitmask' fields, and all other non-integral
|
|
types (i.e.. types that are _not_ FT_INT* and FT_UINT*) must use
|
|
'BASE_NONE, NULL, 0x0' as values for the 'display', 'strings', 'bitmask'
|
|
fields. The reason is simply that the type itself implicitly defines the
|
|
nature of 'display', 'strings', 'bitmask'.
|
|
|
|
display (FIELDDISPLAY)
|
|
----------------------
|
|
The display field has a couple of overloaded uses. This is unfortunate,
|
|
but since we're using 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_HEX,
|
|
BASE_HEX_DEC,
|
|
BASE_CUSTOM
|
|
|
|
BASE_DEC, BASE_HEX, and BASE_OCT are decimal, hexadecimal, and octal,
|
|
respectively. BASE_DEC_HEX and BASE_HEX_DEC display value in two bases
|
|
(the 1st representation followed by the 2nd in parenthesis).
|
|
|
|
BASE_CUSTOM allows one to specify a callback function pointer that will
|
|
format the value.
|
|
|
|
For 32-bit and smaller values, custom_fmt_func_t can be used to declare
|
|
the callback function pointer. Specifically, this is defined as:
|
|
|
|
void func(gchar *, guint32);
|
|
|
|
For values larger than 32-bits, custom_fmt_func_64_t can be used to declare
|
|
the callback function pointer. Specifically, this is defined as:
|
|
|
|
void func(gchar *, guint64);
|
|
|
|
The first argument is a pointer to a buffer of the ITEM_LABEL_LENGTH size
|
|
and the second argument is the value to be formatted.
|
|
|
|
Both custom_fmt_func_t and custom_fmt_func_64_t are defined in epan/proto.h.
|
|
|
|
For FT_UINT16 'display' can be used to select a transport layer protocol using one
|
|
of BASE_PT_UDP, BASE_PT_TCP, BASE_PT_DCCP or BASE_PT_SCTP. If transport name
|
|
resolution is enabled the port field label is displayed in decimal and as a well-known
|
|
service name (if one is available).
|
|
|
|
For FT_BOOLEAN fields that are also bitfields (i.e., 'bitmask' is non-zero),
|
|
'display' is used specify a "field-width" (i.e., tell the proto_tree how
|
|
wide the parent bitfield is). (If the FT_BOOLEAN 'bitmask' is zero, then
|
|
'display' must be BASE_NONE).
|
|
|
|
For integer fields a "field-width" is not needed since the type of
|
|
integer itself (FT_UINT8, FT_UINT16, FT_UINT24, FT_UINT32, FT_UINT40,
|
|
FT_UINT48, FT_UINT56, FT_UINT64, etc) tells the proto_tree how wide the
|
|
parent bitfield is. The same is true of FT_CHAR, as it's an 8-bit
|
|
character.
|
|
|
|
For FT_ABSOLUTE_TIME fields, 'display' is used to indicate whether the
|
|
time is to be displayed as a time in the time zone for the machine on
|
|
which Wireshark/TShark is running or as UTC and, for UTC, whether the
|
|
date should be displayed as "{monthname} {day_of_month}, {year}" or as
|
|
"{year/day_of_year}".
|
|
|
|
Additionally, BASE_NONE is used for 'display' as a NULL-value. That is, for
|
|
non-integers other than FT_ABSOLUTE_TIME fields, 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; note that there are protocols for which
|
|
no endianness is specified, such as the X11 protocol and the DCE RPC
|
|
protocol, so it would not be possible to record the endianness of all
|
|
integral fields.
|
|
|
|
strings (FIELDCONVERT)
|
|
----------------------
|
|
-- value_string
|
|
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)'.
|
|
|
|
If the field has a numeric rather than an enumerated type, the 'strings'
|
|
field would be set to NULL.
|
|
|
|
If BASE_SPECIAL_VALS is also applied to the display bitmask, then if the
|
|
numeric value of a field doesn't match any values in the value_string
|
|
then just the numeric value is displayed (i.e. no "Unknown"). This is
|
|
intended for use when the value_string only gives special names for
|
|
certain field values and values not in the value_string are expected.
|
|
|
|
-- Extended value strings
|
|
You can also use an extended version of the value_string for faster lookups.
|
|
It requires a value_string array as input.
|
|
If all of a contiguous range of values from min to max are present in the array
|
|
in ascending order the value will be used as a direct index into a value_string array.
|
|
|
|
If the values in the array are not contiguous (ie: there are "gaps"), but are
|
|
in ascending order a binary search will be used.
|
|
|
|
Note: "gaps" in a value_string array can be filled with "empty" entries eg:
|
|
{value, "Unknown"} so that direct access to the array is possible.
|
|
|
|
Note: the value_string array values are *unsigned*; IOW: -1 is greater than 0.
|
|
So:
|
|
{ -2, -1, 1, 2 }; wrong: linear search will be used (note gap)
|
|
{ 1, 2, -2, -1 }; correct: binary search will be used
|
|
|
|
As a special case:
|
|
{ -2, -1, 0, 1, 2 }; OK: direct(indexed) access will be used (note no gap)
|
|
|
|
The init macro (see below) will perform a check on the value string the first
|
|
time it is used to determine which search algorithm fits and fall back to a
|
|
linear search if the value_string does not meet the criteria above.
|
|
|
|
Use this macro to initialize the extended value_string at compile time:
|
|
|
|
static value_string_ext valstringname_ext = VALUE_STRING_EXT_INIT(valstringname);
|
|
|
|
Extended value strings can be created at run time by calling
|
|
value_string_ext_new(<ptr to value_string array>,
|
|
<total number of entries in the value_string_array>, /* include {0, NULL} entry */
|
|
<value_string_name>);
|
|
|
|
For hf[] array FT_(U)INT* fields that need a 'valstringname_ext' struct, the
|
|
'strings' field would be set to '&valstringname_ext'. Furthermore, the 'display'
|
|
field must be ORed with 'BASE_EXT_STRING' (e.g. BASE_DEC|BASE_EXT_STRING).
|
|
|
|
-- val64_string
|
|
|
|
val64_strings are like value_strings, except that the integer type
|
|
used is a guint64 (instead of guint32). Instead of using the VALS()
|
|
macro for the 'strings' field in the header_field_info struct array,
|
|
'VALS64()' is used.
|
|
|
|
BASE_SPECIAL_VALS can also be used for val64_string.
|
|
|
|
-- val64_string_ext
|
|
|
|
val64_string_ext is like value_string_ext, except that the integer type
|
|
used is a guint64 (instead of guint32).
|
|
|
|
Use this macro to initialize the extended val64_string at compile time:
|
|
|
|
static val64_string_ext val64stringname_ext = VAL64_STRING_EXT_INIT(val64stringname);
|
|
|
|
Extended val64 strings can be created at run time by calling
|
|
val64_string_ext_new(<ptr to val64_string array>,
|
|
<total number of entries in the val64_string_array>, /* include {0, NULL} entry */
|
|
<val64_string_name>);
|
|
|
|
For hf[] array FT_(U)INT* fields that need a 'val64stringname_ext' struct, the
|
|
'strings' field would be set to '&val64stringname_ext'. Furthermore, the 'display'
|
|
field must be ORed with both 'BASE_EXT_STRING' and 'BASE_VAL64_STRING'
|
|
(e.g. BASE_DEC|BASE_EXT_STRING|BASE_VAL64_STRING).
|
|
|
|
-- Unit string
|
|
Some integer fields, of type FT_UINT* and float fields, of type FT_FLOAT
|
|
or FT_DOUBLE, need units of measurement to help convey the field value.
|
|
|
|
A 'unit_name_string' structure is a way to add a unit suffix to a field.
|
|
|
|
typedef struct unit_name_string {
|
|
char *singular; /* name to use for 1 unit */
|
|
char *plural; /* name to use for < 1 or > 1 units */
|
|
} unit_name_string;
|
|
|
|
For fields with that unit name, you would declare a "unit_name_string":
|
|
|
|
static const unit_name_string unitname[] =
|
|
{ "single item name" , "multiple item name" };
|
|
|
|
(the second entry can be NULL if there is no plural form of the unit name.
|
|
This is typically the case when abbreviations are used instead of full words.)
|
|
|
|
There are several "common" unit name structures already defined in
|
|
epan/unit_strings.h. Dissector authors may choose to add the unit name
|
|
structure there rather than locally in a dissector.
|
|
|
|
For hf[] array FT_(U)INT*, FT_FlOAT and FT_DOUBLE fields that need a
|
|
'unit_name_string' struct, the 'strings' field would be set to
|
|
'&units_second_seconds'. Furthermore, the 'display' field must be ORed
|
|
with 'BASE_UNIT_STRING' (e.g. BASE_DEC|BASE_UNIT_STRING).
|
|
|
|
-- Ranges
|
|
If the field has a numeric type that might logically fit in ranges of values
|
|
one can use a range_string struct.
|
|
|
|
Thus a 'range_string' structure is a way to map ranges to strings.
|
|
|
|
typedef struct _range_string {
|
|
guint32 value_min;
|
|
guint32 value_max;
|
|
const gchar *strptr;
|
|
} range_string;
|
|
|
|
For fields of that type, you would declare an array of "range_string"s:
|
|
|
|
static const range_string rvalstringname[] = {
|
|
{ INTVAL_MIN1, INTVALMAX1, "Descriptive String 1" },
|
|
{ INTVAL_MIN2, INTVALMAX2, "Descriptive String 2" },
|
|
{ 0, 0, NULL }
|
|
};
|
|
|
|
If INTVAL_MIN equals INTVAL_MAX for a given entry the range_string
|
|
behavior collapses to the one of value_string. Note that each range_string
|
|
within the array is tested in order, so any 'catch-all' entries need to come
|
|
after specific individual entries.
|
|
|
|
For FT_(U)INT* fields that need a 'range_string' struct, the 'strings' field
|
|
would be set to 'RVALS(rvalstringname)'. Furthermore, 'display' field must be
|
|
ORed with 'BASE_RANGE_STRING' (e.g. BASE_DEC|BASE_RANGE_STRING).
|
|
|
|
-- Booleans
|
|
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.
|
|
|
|
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.
|
|
|
|
Wireshark predefines a whole range of ready made "true_false_string"s
|
|
in tfs.h, included via packet.h.
|
|
|
|
-- Custom
|
|
Custom fields (BASE_CUSTOM) should use CF_FUNC(&custom_format_func) for the
|
|
'strings' field.
|
|
|
|
-- Frame numbers
|
|
FT_FRAMENUMs can use the 'strings' field to indicate their purpose by
|
|
setting the field to 'FRAMENUM_TYPE(x)', where x is one of the values of
|
|
the ft_framenum_type enum:
|
|
|
|
FT_FRAMENUM_NONE
|
|
FT_FRAMENUM_REQUEST
|
|
FT_FRAMENUM_RESPONSE
|
|
FT_FRAMENUM_ACK
|
|
FT_FRAMENUM_DUP_ACK
|
|
FT_FRAMENUM_RETRANS_PREV
|
|
FT_FRAMENUM_RETRANS_NEXT
|
|
|
|
The packet list uses the value to determine the related packet symbol to draw.
|
|
Note that 'strings' field NULL is equal to FRAMENUM_TYPE(FT_FRAMENUM_NONE).
|
|
|
|
-- Note to plugin authors
|
|
Data cannot get exported from DLLs. For this reason plugin authors cannot use
|
|
existing fieldconvert strings (e.g. from existing dissectors or those from
|
|
epan/unit_strings.h). Plugins must define value_strings, unit_name_strings,
|
|
range_strings and true_false_strings locally.
|
|
|
|
bitmask (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.
|
|
This shift is applied before applying string mapping functions or
|
|
filtering.
|
|
|
|
If the field is not a bitfield, then bitmask should be set to 0.
|
|
|
|
blurb (FIELDDESCR)
|
|
------------------
|
|
This is a string giving a proper description of the field. It should be
|
|
at least one grammatically complete sentence, or NULL in which case the
|
|
name field is used. (Please do not use "").
|
|
|
|
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.5.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", HFILL }},
|
|
|
|
{ &hf_field_b,
|
|
{ "Field B", "proto.field_b", FT_UINT16, BASE_DEC, VALS(vs),
|
|
0x0, "Field B represents Bananas", HFILL }}
|
|
};
|
|
|
|
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 Wireshark support them.
|
|
Just omit the "hf" array, and the "proto_register_field_array()" call,
|
|
entirely.
|
|
|
|
It is OK to have header fields with a different format be registered with
|
|
the same abbreviation. For instance, the following is valid:
|
|
|
|
static hf_register_info hf[] = {
|
|
|
|
{ &hf_field_8bit, /* 8-bit version of proto.field */
|
|
{ "Field (8 bit)", "proto.field", FT_UINT8, BASE_DEC, NULL,
|
|
0x00, "Field represents FOO", HFILL }},
|
|
|
|
{ &hf_field_32bit, /* 32-bit version of proto.field */
|
|
{ "Field (32 bit)", "proto.field", FT_UINT32, BASE_DEC, NULL,
|
|
0x00, "Field represents FOO", HFILL }}
|
|
};
|
|
|
|
This way a filter expression can match a header field, irrespective of the
|
|
representation of it in the specific protocol context. This is interesting
|
|
for protocols with variable-width header fields.
|
|
|
|
Note that the formats used must all belong to the same group as defined below:
|
|
- FT_INT8, FT_INT16, FT_INT24 and FT_INT32
|
|
- FT_CHAR, FT_UINT8, FT_UINT16, FT_UINT24, FT_UINT32, FT_IPXNET and FT_FRAMENUM
|
|
- FT_INT40, FT_INT48, FT_INT56 and FT_INT64
|
|
- FT_UINT40, FT_UINT48, FT_UINT56, FT_UINT64 and FT_EUI64
|
|
- FT_ABSOLUTE_TIME and FT_RELATIVE_TIME
|
|
- FT_STRING, FT_STRINGZ, FT_UINT_STRING, FT_STRINGZPAD, and FT_STRINGZTRUNC
|
|
- FT_FLOAT and FT_DOUBLE
|
|
- FT_BYTES, FT_UINT_BYTES, FT_ETHER, FT_AX25, FT_VINES and FT_FCWWN
|
|
- FT_OID, FT_REL_OID and FT_SYSTEM_ID
|
|
|
|
Any field not in a grouping above should *NOT* be used in duplicate field
|
|
abbreviations. The current code does not prevent it, but someday in the future
|
|
it might.
|
|
|
|
The HFILL macro at the end of the struct will set reasonable default values
|
|
for internally used fields.
|
|
|
|
1.5.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_XXX_DO_YYY() 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.
|
|
|
|
The ett_ variables identify particular type of subtree so that if you expand
|
|
one of them, Wireshark keeps track of that and, when you click on
|
|
another packet, it automatically opens all subtrees of that type.
|
|
If you close one of them, all subtrees of that type will be closed when
|
|
you move to another packet.
|
|
|
|
There are many functions that the programmer can use to add either
|
|
protocol or field labels to the proto_tree, for example:
|
|
|
|
proto_item*
|
|
proto_tree_add_item(tree, id, tvb, start, length, encoding);
|
|
|
|
proto_item*
|
|
proto_tree_add_item_ret_int(tree, id, tvb, start, length, encoding,
|
|
*retval);
|
|
|
|
proto_item*
|
|
proto_tree_add_subtree(tree, tvb, start, length, idx, tree_item,
|
|
text);
|
|
|
|
proto_item *
|
|
proto_tree_add_int_format_value(tree, id, tvb, start, length,
|
|
value, format, ...);
|
|
|
|
proto_item *
|
|
proto_tree_add_checksum(proto_tree *tree, tvbuff_t *tvb, const guint offset,
|
|
const int hf_checksum, const int hf_checksum_status,
|
|
struct expert_field* bad_checksum_expert, packet_info *pinfo,
|
|
guint32 computed_checksum, const guint encoding, const guint flags);
|
|
|
|
proto_item *
|
|
proto_tree_add_bitmask(tree, tvb, start, header, ett, fields,
|
|
encoding);
|
|
|
|
proto_item *
|
|
proto_tree_add_bits_item(tree, id, tvb, bit_offset, no_of_bits,
|
|
encoding);
|
|
|
|
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, bit_offset is the offset in bits and no_of_bits
|
|
is the length in bits.
|
|
|
|
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.
|
|
|
|
All available protocol tree functions are declared in epan/proto.h, with
|
|
their documentation. The details of these functions and their parameters
|
|
are described below.
|
|
|
|
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 the encoding of the value as specified by the "encoding" argument.
|
|
|
|
For FT_NONE, FT_BYTES, FT_ETHER, FT_IPv6, FT_IPXNET, FT_OID, FT_REL_OID,
|
|
FT_AX25, FT_VINES, FT_SYSTEM_ID, FT_FCWWN fields, and 'protocol' fields
|
|
the encoding is not relevant; the 'encoding' argument should be
|
|
ENC_NA (Not Applicable).
|
|
|
|
For FT_UINT_BYTES fields, the byte order of the count must be specified
|
|
as well as the 'encoding' for bytes which should be ENC_NA,
|
|
i.e. ENC_LITTLE_ENDIAN|ENC_NA
|
|
|
|
For integral, floating-point, Boolean, FT_GUID, and FT_EUI64 fields,
|
|
the encoding specifies the byte order of the value; the 'encoding'
|
|
argument should be ENC_LITTLE_ENDIAN if the value is little-endian
|
|
and ENC_BIG_ENDIAN if it is big-endian.
|
|
|
|
For FT_IPv4 fields, the encoding also specifies the byte order of the
|
|
value. In almost all cases, the encoding is in network byte order,
|
|
hence big-endian, but in at least one protocol dissected by Wireshark,
|
|
at least one IPv4 address is byte-swapped, so it's in little-endian
|
|
order.
|
|
|
|
For string fields, the encoding specifies the character set used for the
|
|
string and the way individual code points in that character set are
|
|
encoded. For FT_UINT_STRING fields, the byte order of the count must be
|
|
specified; for UCS-2 and UTF-16, the byte order of the encoding must be
|
|
specified (for counted UCS-2 and UTF-16 strings, the byte order of the
|
|
count and the 16-bit values in the string must be the same). In other
|
|
cases the string encoding has no endianness or the endianness is implicitly
|
|
specified and nothing should be used. The character encodings that are
|
|
currently supported are:
|
|
|
|
ENC_ASCII - ASCII (currently treated as UTF-8; in the future,
|
|
all bytes with the 8th bit set will be treated as
|
|
errors)
|
|
ENC_UTF_8 - UTF-8-encoded Unicode
|
|
ENC_UTF_16 - UTF-16-encoded Unicode, with surrogate pairs
|
|
ENC_UCS_2 - UCS-2-encoded subset of Unicode, with no surrogate pairs
|
|
and thus no code points above 0xFFFF
|
|
ENC_UCS_4 - UCS-4-encoded Unicode
|
|
ENC_WINDOWS_1250 - Windows-1250 code page
|
|
ENC_WINDOWS_1251 - Windows-1251 code page
|
|
ENC_WINDOWS_1252 - Windows-1252 code page
|
|
ENC_ISO_646_BASIC - ISO 646 "basic code table"
|
|
ENC_ISO_8859_1 - ISO 8859-1
|
|
ENC_ISO_8859_2 - ISO 8859-2
|
|
ENC_ISO_8859_3 - ISO 8859-3
|
|
ENC_ISO_8859_4 - ISO 8859-4
|
|
ENC_ISO_8859_5 - ISO 8859-5
|
|
ENC_ISO_8859_6 - ISO 8859-6
|
|
ENC_ISO_8859_7 - ISO 8859-7
|
|
ENC_ISO_8859_8 - ISO 8859-8
|
|
ENC_ISO_8859_9 - ISO 8859-9
|
|
ENC_ISO_8859_10 - ISO 8859-10
|
|
ENC_ISO_8859_11 - ISO 8859-11
|
|
ENC_ISO_8859_13 - ISO 8859-13
|
|
ENC_ISO_8859_14 - ISO 8859-14
|
|
ENC_ISO_8859_15 - ISO 8859-15
|
|
ENC_ISO_8859_16 - ISO 8859-16
|
|
ENC_3GPP_TS_23_038_7BITS - GSM 7 bits alphabet as described
|
|
in 3GPP TS 23.038
|
|
ENC_3GPP_TS_23_038_7BITS_UNPACKED - GSM 7 bits alphabet where each
|
|
7 bit character occupies a distinct octet
|
|
ENC_ETSI_TS_102_221_ANNEX_A - Coding scheme for SIM cards with GSM 7 bit
|
|
alphabet, UCS-2 characters, or a mixture of the two as described
|
|
in ETSI TS 102 221 Annex A
|
|
ENC_EBCDIC - EBCDIC
|
|
ENC_EBCDIC_CP037 - EBCDIC code page 037
|
|
ENC_MAC_ROMAN - MAC ROMAN
|
|
ENC_CP437 - DOS code page 437
|
|
ENC_CP855 - DOS code page 855
|
|
ENC_CP866 - DOS code page 866
|
|
ENC_ASCII_7BITS - 7 bits ASCII
|
|
ENC_T61 - ITU T.61
|
|
ENC_BCD_DIGITS_0_9 - packed BCD (one digit per nibble), digits 0-9
|
|
ENC_KEYPAD_ABC_TBCD - keypad-with-a/b/c "telephony packed BCD" = 0-9, *, #, a, b, c
|
|
ENC_KEYPAD_BC_TBCD - keypad-with-B/C "telephony packed BCD" = 0-9, B, C, *, #
|
|
ENC_GB18030 - GB 18030
|
|
ENC_EUC_KR - EUC-KR
|
|
ENC_DECT_STANDARD_8BITS - DECT standard 8 bit character set as defined in
|
|
ETSI EN 300 175-5
|
|
ENC_DECT_STANDARD_4BITS_TBCD - DECT standard 4 bit character set "telephony
|
|
packet BCD" = 0-9, 0xb = SPACE
|
|
|
|
Other encodings will be added in the future.
|
|
|
|
For FT_ABSOLUTE_TIME fields, the encoding specifies the form in which
|
|
the time stamp is specified, as well as its byte order. The time stamp
|
|
encodings that are currently supported are:
|
|
|
|
ENC_TIME_SECS_NSECS - 8, 12, or 16 bytes. For 8 bytes, the first 4
|
|
bytes are seconds and the next 4 bytes are nanoseconds; for 12
|
|
bytes, the first 8 bytes are seconds and the next 4 bytes are
|
|
nanoseconds; for 16 bytes, the first 8 bytes are seconds and
|
|
the next 8 bytes are nanoseconds. The seconds are seconds
|
|
since the UN*X epoch (1970-01-01 00:00:00 UTC). (I.e., a UN*X
|
|
struct timespec with a 4-byte or 8-byte time_t or a structure
|
|
with an 8-byte time_t and an 8-byte nanoseconds field.)
|
|
|
|
ENC_TIME_NTP - 8 bytes; the first 4 bytes are seconds since the NTP
|
|
epoch (1900-01-01 00:00:00 GMT) and the next 4 bytes are 1/2^32's of
|
|
a second since that second. (I.e., a 64-bit count of 1/2^32's of a
|
|
second since the NTP epoch, with the upper 32 bits first and the
|
|
lower 32 bits second, even when little-endian.)
|
|
|
|
ENC_TIME_TOD - 8 bytes, as a count of microseconds since the System/3x0
|
|
and z/Architecture epoch (1900-01-01 00:00:00 GMT).
|
|
|
|
ENC_TIME_RTPS - 8 bytes; the first 4 bytes are seconds since the UN*X
|
|
epoch and the next 4 bytes are 1/2^32's of a second since that
|
|
second. (I.e., it's the offspring of a mating between UN*X time and
|
|
NTP time). It's used by the Object Management Group's Real-Time
|
|
Publish-Subscribe Wire Protocol for the Data Distribution Service.
|
|
|
|
ENC_TIME_SECS_USECS - 8 bytes; the first 4 bytes are seconds since the
|
|
UN*X epoch and the next 4 bytes are microseconds since that
|
|
second. (I.e., a UN*X struct timeval with a 4-byte time_t.)
|
|
|
|
ENC_TIME_SECS - 4 to 8 bytes, representing a value in seconds since
|
|
the UN*X epoch.
|
|
|
|
ENC_TIME_MSECS - 6 to 8 bytes, representing a value in milliseconds
|
|
since the UN*X epoch.
|
|
|
|
ENC_TIME_USECS - 8 bytes, representing a value in microseconds since
|
|
the UN*X epoch.
|
|
|
|
ENC_TIME_NSECS - 8 bytes, representing a value in nanoseconds since
|
|
the UN*X epoch.
|
|
|
|
ENC_TIME_SECS_NTP - 4 bytes, representing a count of seconds since
|
|
the NTP epoch.
|
|
|
|
ENC_TIME_RFC_3971 - 8 bytes, representing a count of 1/64ths of a
|
|
second since the UN*X epoch; see section 5.3.1 "Timestamp Option"
|
|
in RFC 3971.
|
|
|
|
ENC_TIME_MSEC_NTP - 4-8 bytes, representing a count of milliseconds since
|
|
the NTP epoch.
|
|
|
|
ENC_TIME_MIP6 - 8 bytes; the first 48 bits are seconds since the UN*X epoch
|
|
and the remaining 16 bits indicate the number of 1/65536's of a second
|
|
since that second.
|
|
|
|
ENC_TIME_CLASSIC_MAC_OS_SECS - 4-8 bytes, representing a count of seconds
|
|
since January 1, 1904, 00:00:00 UTC.
|
|
|
|
For FT_RELATIVE_TIME fields, the encoding specifies the form in which
|
|
the time stamp is specified, as well as its byte order. The time stamp
|
|
encodings that are currently supported are:
|
|
|
|
ENC_TIME_SECS_NSECS - 8, 12, or 16 bytes. For 8 bytes, the first 4
|
|
bytes are seconds and the next 4 bytes are nanoseconds; for 12
|
|
bytes, the first 8 bytes are seconds and the next 4 bytes are
|
|
nanoseconds; for 16 bytes, the first 8 bytes are seconds and
|
|
the next 8 bytes are nanoseconds.
|
|
|
|
ENC_TIME_SECS_USECS - 8 bytes; the first 4 bytes are seconds and the
|
|
next 4 bytes are microseconds.
|
|
|
|
ENC_TIME_SECS - 4 to 8 bytes, representing a value in seconds.
|
|
|
|
ENC_TIME_MSECS - 6 to 8 bytes, representing a value in milliseconds.
|
|
|
|
ENC_TIME_USECS - 8 bytes, representing a value in microseconds.
|
|
|
|
ENC_TIME_NSECS - 8 bytes, representing a value in nanoseconds.
|
|
|
|
For other types, there is no support for proto_tree_add_item().
|
|
|
|
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
|
|
Identifier (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 Identifier"
|
|
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,
|
|
ENC_BIG_ENDIAN);
|
|
|
|
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, "Adjacent 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
|
|
in the past.
|
|
|
|
proto_tree_add_item_ret_XXX()
|
|
------------------------------
|
|
proto_tree_add_item_ret_XXX is used when you want the displayed value returned
|
|
for further processing only integer and unsigned integer types up to 32 bits are
|
|
supported usage of proper FT_ is checked.
|
|
|
|
proto_tree_add_XXX_item()
|
|
---------------------
|
|
proto_tree_add_XXX_item is used when you wish to do no special formatting,
|
|
but also either wish for the retrieved value from the tvbuff to be handed
|
|
back (to avoid doing tvb_get_...), and/or wish to have the value be decoded
|
|
from the tvbuff in a string-encoded format.
|
|
|
|
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, based on the type of the XXX name and the encoding of
|
|
the value as specified by the "encoding" argument.
|
|
|
|
This function retrieves the value even if the passed-in tree param is NULL,
|
|
so that it can be used by dissectors at all times to both get the value
|
|
and set the tree item to it.
|
|
|
|
Like other proto_tree_add functions, if there is a tree and the value cannot
|
|
be decoded from the tvbuff, then an expert info error is reported. For string
|
|
encoding, this means that a failure to decode the hex value from the string
|
|
results in an expert info error being added to the tree.
|
|
|
|
For string-decoding, the passed-in encoding argument needs to specify the
|
|
string encoding (e.g., ENC_ASCII, ENC_UTF_8) as well as the format. For
|
|
some XXX types, the format is constrained - for example for the encoding format
|
|
for proto_tree_add_time_item() can only be one of the ENC_ISO_8601_* ones
|
|
or ENC_RFC_822 or ENC_RFC_1123. For proto_tree_add_bytes_item() it can only
|
|
be ENC_STR_HEX bit-or'ed with one or more of the ENC_SEP_* separator types.
|
|
|
|
proto_tree_add_protocol_format()
|
|
--------------------------------
|
|
proto_tree_add_protocol_format is used to add the top-level item for the
|
|
protocol when the dissector routine 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_uint64()
|
|
proto_tree_add_int()
|
|
proto_tree_add_int64()
|
|
proto_tree_add_guid()
|
|
proto_tree_add_oid()
|
|
proto_tree_add_eui64()
|
|
------------------------
|
|
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.
|
|
|
|
|
|
proto_tree_add_bytes_with_length() is similar to proto_tree_add_bytes,
|
|
except that the length is not derived from the tvb length. Instead,
|
|
the displayed data size is controlled by 'ptr_length'.
|
|
|
|
For proto_tree_add_bytes_format() and proto_tree_add_bytes_format_value(), the
|
|
'value_ptr' argument is a pointer to a sequence of bytes or NULL if the bytes
|
|
should be taken from the given TVB using the given offset and length.
|
|
|
|
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; this string must be NULL terminated even if the string in the
|
|
TVB is not (as may be the case with FT_STRINGs).
|
|
|
|
For proto_tree_add_boolean(), the 'value' argument is a 32-bit integer.
|
|
It is masked and shifted as defined by the field info after which 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.)
|
|
|
|
For proto_tree_add_uint64(), the 'value' argument is a 64-bit unsigned
|
|
integer value, in host byte order.
|
|
|
|
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.)
|
|
|
|
For proto_tree_add_int64(), the 'value' argument is a 64-bit signed
|
|
integer value, in host byte order.
|
|
|
|
For proto_tree_add_guid(), the 'value_ptr' argument is a pointer to an
|
|
e_guid_t structure.
|
|
|
|
For proto_tree_add_oid(), the 'value_ptr' argument is a pointer to an
|
|
ASN.1 Object Identifier.
|
|
|
|
For proto_tree_add_eui64(), the 'value' argument is a 64-bit integer
|
|
value
|
|
|
|
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_uint64_format()
|
|
proto_tree_add_int_format()
|
|
proto_tree_add_int64_format()
|
|
proto_tree_add_guid_format()
|
|
proto_tree_add_oid_format()
|
|
proto_tree_add_eui64_format()
|
|
----------------------------
|
|
These routines are used to add items to the protocol tree when the
|
|
dissector routine 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_bytes_format_value()
|
|
proto_tree_add_time_format_value()
|
|
proto_tree_add_ipxnet_format_value()
|
|
proto_tree_add_ipv4_format_value()
|
|
proto_tree_add_ipv6_format_value()
|
|
proto_tree_add_ether_format_value()
|
|
proto_tree_add_string_format_value()
|
|
proto_tree_add_boolean_format_value()
|
|
proto_tree_add_float_format_value()
|
|
proto_tree_add_double_format_value()
|
|
proto_tree_add_uint_format_value()
|
|
proto_tree_add_uint64_format_value()
|
|
proto_tree_add_int_format_value()
|
|
proto_tree_add_int64_format_value()
|
|
proto_tree_add_guid_format_value()
|
|
proto_tree_add_oid_format_value()
|
|
proto_tree_add_eui64_format_value()
|
|
------------------------------------
|
|
|
|
These routines are used to add items to the protocol tree when the
|
|
dissector routine wants complete control over how the 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.
|
|
With these routines, unlike the proto_tree_add_XXX_format() routines,
|
|
the name of the field is added automatically as in the
|
|
proto_tree_add_XXX() functions; only the value is added with the format.
|
|
One use case for this would be to add a unit of measurement string to
|
|
the value of the field, however using BASE_UNIT_STRING in the hf_
|
|
definition is now preferred.
|
|
|
|
proto_tree_add_checksum()
|
|
----------------------------
|
|
proto_tree_add_checksum is used to add a checksum field. The hf field
|
|
provided must be the correct size of the checksum (FT_UINT, FT_UINT16,
|
|
FT_UINT32, etc). Additional parameters are there to provide "status"
|
|
and expert info depending on whether the checksum matches the provided
|
|
value. The "status" and expert info can be used in cases except
|
|
where PROTO_CHECKSUM_NO_FLAGS is used.
|
|
|
|
proto_tree_add_subtree()
|
|
---------------------
|
|
proto_tree_add_subtree() is used to add a label to the GUI tree and create
|
|
a subtree for other fields. It will contain no value, so it is not searchable
|
|
in the display filter process.
|
|
|
|
This should only 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 proto_item value returned by
|
|
one of the parameters in 'proto_tree_add_subtree()', 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_subtree()' 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:
|
|
|
|
subtree = proto_tree_add_subtree(tree, tvb, offset, length, ett, &ti, <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 be 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 are extracted.
|
|
|
|
proto_tree_add_subtree_format()
|
|
----------------------------
|
|
This is like proto_tree_add_subtree(), but uses printf-style arguments to
|
|
create the label; 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.
|
|
|
|
proto_tree_add_bits_item()
|
|
--------------------------
|
|
Adds a number of bits to the protocol tree which does not have to be byte
|
|
aligned. The offset and length is in bits.
|
|
Output format:
|
|
|
|
..10 1010 10.. .... "value" (formatted as FT_ indicates).
|
|
|
|
proto_tree_add_bits_ret_val()
|
|
-----------------------------
|
|
Works in the same way but also returns the value of the read bits.
|
|
|
|
proto_tree_add_split_bits_item_ret_val()
|
|
-----------------------------------
|
|
Similar, but is used for items that are made of 2 or more smaller sets of bits (crumbs)
|
|
which are not contiguous, but are concatenated to form the actual value. The size of
|
|
the crumbs and the order of assembly are specified in an array of crumb_spec structures.
|
|
|
|
proto_tree_add_split_bits_crumb()
|
|
---------------------------------
|
|
Helper function for the above, to add text for each crumb as it is encountered.
|
|
|
|
proto_tree_add_ts_23_038_7bits_item()
|
|
-------------------------------------
|
|
Adds a string of a given number of characters and encoded according to 3GPP TS 23.038 7 bits
|
|
alphabet.
|
|
|
|
proto_tree_add_bitmask() et al.
|
|
-------------------------------
|
|
These functions provide easy to use and convenient dissection of many types of common
|
|
bitmasks into individual fields.
|
|
|
|
header is an integer type and must be of type FT_[U]INT{8|16|24|32||40|48|56|64} and
|
|
represents the entire dissectable width of the bitmask.
|
|
|
|
'header' and 'ett' are the hf fields and ett field respectively to create an
|
|
expansion that covers the bytes of the bitmask.
|
|
|
|
'fields' is a NULL terminated array of pointers to hf fields representing
|
|
the individual subfields of the bitmask. These fields must either be integers
|
|
(usually of the same byte width as 'header') or of the type FT_BOOLEAN.
|
|
Each of the entries in 'fields' will be dissected as an item under the
|
|
'header' expansion and also IF the field is a boolean and IF it is set to 1,
|
|
then the name of that boolean field will be printed on the 'header' expansion
|
|
line. For integer type subfields that have a value_string defined, the
|
|
matched string from that value_string will be printed on the expansion line
|
|
as well.
|
|
|
|
Example: (from the SCSI dissector)
|
|
static int hf_scsi_inq_peripheral = -1;
|
|
static int hf_scsi_inq_qualifier = -1;
|
|
static int hf_scsi_inq_devtype = -1;
|
|
...
|
|
static gint ett_scsi_inq_peripheral = -1;
|
|
...
|
|
static int * const peripheral_fields[] = {
|
|
&hf_scsi_inq_qualifier,
|
|
&hf_scsi_inq_devtype,
|
|
NULL
|
|
};
|
|
...
|
|
/* Qualifier and DeviceType */
|
|
proto_tree_add_bitmask(tree, tvb, offset, hf_scsi_inq_peripheral,
|
|
ett_scsi_inq_peripheral, peripheral_fields, ENC_BIG_ENDIAN);
|
|
offset+=1;
|
|
...
|
|
{ &hf_scsi_inq_peripheral,
|
|
{"Peripheral", "scsi.inquiry.peripheral", FT_UINT8, BASE_HEX,
|
|
NULL, 0, NULL, HFILL}},
|
|
{ &hf_scsi_inq_qualifier,
|
|
{"Qualifier", "scsi.inquiry.qualifier", FT_UINT8, BASE_HEX,
|
|
VALS (scsi_qualifier_val), 0xE0, NULL, HFILL}},
|
|
{ &hf_scsi_inq_devtype,
|
|
{"Device Type", "scsi.inquiry.devtype", FT_UINT8, BASE_HEX,
|
|
VALS (scsi_devtype_val), SCSI_DEV_BITS, NULL, HFILL}},
|
|
...
|
|
|
|
Which provides very pretty dissection of this one byte bitmask.
|
|
|
|
Peripheral: 0x05, Qualifier: Device type is connected to logical unit, Device Type: CD-ROM
|
|
000. .... = Qualifier: Device type is connected to logical unit (0x00)
|
|
...0 0101 = Device Type: CD-ROM (0x05)
|
|
|
|
The proto_tree_add_bitmask_text() function is an extended version of
|
|
the proto_tree_add_bitmask() function. In addition, it allows to:
|
|
- Provide a leading text (e.g. "Flags: ") that will appear before
|
|
the comma-separated list of field values
|
|
- Provide a fallback text (e.g. "None") that will be appended if
|
|
no fields warranted a change to the top-level title.
|
|
- Using flags, specify which fields will affect the top-level title.
|
|
|
|
There are the following flags defined:
|
|
|
|
BMT_NO_APPEND - the title is taken "as-is" from the 'name' argument.
|
|
BMT_NO_INT - only boolean flags are added to the title.
|
|
BMT_NO_FALSE - boolean flags are only added to the title if they are set.
|
|
BMT_NO_TFS - only add flag name to the title, do not use true_false_string
|
|
|
|
The proto_tree_add_bitmask_with_flags() function is an extended version
|
|
of the proto_tree_add_bitmask() function. It allows using flags to specify
|
|
which fields will affect the top-level title. The flags are the
|
|
same BMT_NO_* flags as used in the proto_tree_add_bitmask_text() function.
|
|
|
|
The proto_tree_add_bitmask() behavior can be obtained by providing
|
|
both 'name' and 'fallback' arguments as NULL, and a flags of
|
|
(BMT_NO_FALSE|BMT_NO_TFS).
|
|
|
|
The proto_tree_add_bitmask_len() function is intended for protocols where
|
|
bitmask length is permitted to vary, so a length is specified explicitly
|
|
along with the bitmask value. USB Video "bmControl" and "bControlSize"
|
|
fields follow this pattern. The primary intent of this is "forward
|
|
compatibility," enabling an interpreter coded for version M of a structure
|
|
to comprehend fields in version N of the structure, where N > M and
|
|
bControlSize increases from version M to version N.
|
|
|
|
proto_tree_add_bitmask_len() is an extended version of proto_tree_add_bitmask()
|
|
that uses an explicitly specified (rather than inferred) length to control
|
|
dissection. Because of this, it may encounter two cases that
|
|
proto_tree_add_bitmask() and proto_tree_add_bitmask_text() may not:
|
|
- A length that exceeds that of the 'header' and bitmask subfields.
|
|
In this case the least-significant bytes of the bitmask are dissected.
|
|
An expert warning is generated in this case, because the dissection code
|
|
likely needs to be updated for a new revision of the protocol.
|
|
- A length that is shorter than that of the 'header' and bitmask subfields.
|
|
In this case, subfields whose data is fully present are dissected,
|
|
and other subfields are not. No warning is generated in this case,
|
|
because the dissection code is likely for a later revision of the protocol
|
|
than the packet it was called to interpret.
|
|
|
|
|
|
proto_item_set_generated()
|
|
--------------------------
|
|
proto_item_set_generated is used to mark fields as not being read from the
|
|
captured data directly, but inferred from one or more values.
|
|
|
|
One of the primary uses of this is the presentation of verification of
|
|
checksums. Every IP packet has a checksum line, which can present the result
|
|
of the checksum verification, if enabled in the preferences. The result is
|
|
presented as a subtree, where the result is enclosed in square brackets
|
|
indicating a generated field.
|
|
|
|
Header checksum: 0x3d42 [correct]
|
|
[Checksum Status: Good (1)]
|
|
|
|
proto_item_set_hidden()
|
|
-----------------------
|
|
proto_item_set_hidden is used to hide fields, which have already been added
|
|
to the tree, from being visible in the displayed tree.
|
|
|
|
NOTE that creating hidden fields is actually quite a bad idea from a UI design
|
|
perspective because the user (someone who did not write nor has ever seen the
|
|
code) has no way of knowing that hidden fields are there to be filtered on
|
|
thus defeating the whole purpose of putting them there. A Better Way might
|
|
be to add the fields (that might otherwise be hidden) to a subtree where they
|
|
won't be seen unless the user opens the subtree--but they can be found if the
|
|
user wants.
|
|
|
|
One use for hidden fields (which would be better implemented using visible
|
|
fields in a subtree) follows: 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() and hide them with proto_item_set_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_item *pi;
|
|
|
|
pi = proto_tree_add_item(tree, hf_tr_rif_ring, ...,
|
|
ENC_BIG_ENDIAN);
|
|
proto_item_set_hidden(pi);
|
|
}
|
|
for(i = 0; i < num_rings - 1; i++) {
|
|
proto_item *pi;
|
|
|
|
pi = proto_tree_add_item(tree, hf_tr_rif_bridge, ...,
|
|
ENC_BIG_ENDIAN);
|
|
proto_item_set_hidden(pi);
|
|
}
|
|
|
|
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_item_set_url
|
|
------------------
|
|
proto_item_set_url is used to mark fields as containing a URL. This can only
|
|
be done with fields of type FT_STRING(Z). If these fields are presented they
|
|
are underlined, as could be done in a browser. These fields are sensitive to
|
|
clicks as well, launching the configured browser with this URL as parameter.
|
|
|
|
1.6 Utility routines.
|
|
|
|
1.6.1 val_to_str, val_to_str_const, try_val_to_str and try_val_to_str_idx
|
|
|
|
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.
|
|
|
|
val_to_str() handles the most common case:
|
|
|
|
const 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.
|
|
You can use it in a call to generate a COL_INFO line for a frame such as
|
|
|
|
col_add_fstr(COL_INFO, ", %s", val_to_str(val, table, "Unknown %d"));
|
|
|
|
If you don't need to display 'val' in your fmt string, you can use
|
|
val_to_str_const() which just takes a string constant instead and returns it
|
|
unmodified when 'val' isn't found.
|
|
|
|
If you need to handle the failure case in some custom way, try_val_to_str()
|
|
will return NULL if val isn't found:
|
|
|
|
const gchar*
|
|
try_val_to_str(guint32 val, const value_string *vs)
|
|
|
|
Note that, you must check whether 'try_val_to_str()' returns NULL, and arrange
|
|
that its return value not be dereferenced if it's NULL. 'try_val_to_str_idx()'
|
|
behaves similarly, except it also returns an index into the value_string array,
|
|
or -1 if 'val' was not found.
|
|
|
|
The *_ext functions are "extended" versions of those already described. They
|
|
should be used for large value-string arrays which contain many entries. They
|
|
implement value to string conversions which will do either a direct access or
|
|
a binary search of the value string array if possible. See
|
|
"Extended Value Strings" under section 1.6 "Constructing the protocol tree" for
|
|
more information.
|
|
|
|
See epan/value_string.h for detailed information on the various value_string
|
|
functions.
|
|
|
|
To handle 64-bit values, there are an equivalent set of functions. These are:
|
|
|
|
const gchar *
|
|
val64_to_str(const guint64 val, const val64_string *vs, const char *fmt)
|
|
|
|
const gchar *
|
|
val64_to_str_const(const guint64 val, const val64_string *vs, const char *unknown_str);
|
|
|
|
const gchar *
|
|
try_val64_to_str(const guint64 val, const val64_string *vs);
|
|
|
|
const gchar *
|
|
try_val64_to_str_idx(const guint64 val, const val64_string *vs, gint *idx);
|
|
|
|
|
|
1.6.2 rval_to_str, try_rval_to_str and try_rval_to_str_idx
|
|
|
|
A dissector may need to convert a range of values to a string, using a
|
|
'range_string' structure.
|
|
|
|
Most of the same functions exist as with regular value_strings (see section
|
|
1.6.1) except with the names 'rval' instead of 'val'.
|
|
|
|
|
|
1.7 Calling Other Dissectors.
|
|
|
|
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).
|
|
|
|
To create a new TVBUFF_SUBSET that begins at a specified offset in a
|
|
parent tvbuff, and runs to the end of the parent tvbuff, the routine
|
|
tvbuff_new_subset_remaining() is used:
|
|
|
|
next_tvb = tvb_new_subset_remaining(tvb, offset);
|
|
|
|
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 byte at offset 0.
|
|
|
|
To create a new TVBUFF_SUBSET that begins at a specified offset in a
|
|
parent tvbuff, with a specified number of bytes in the payload, the
|
|
routine tvbuff_new_subset_length() is used:
|
|
|
|
next_tvb = tvb_new_subset_length(tvb, offset, 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 byte at offset 0.
|
|
|
|
reported_length is the number of bytes that the current protocol
|
|
says should be in the payload.
|
|
|
|
In the few cases where the number of bytes available in the new subset
|
|
must be explicitly specified, rather than being calculated based on the
|
|
number of bytes in the payload, the routine tvb_new_subset_length_caplen()
|
|
is used:
|
|
|
|
next_tvb = tvb_new_subset_length_caplen(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 byte at offset 0.
|
|
|
|
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.
|
|
|
|
To call a dissector you need to get the handle of the dissector using
|
|
find_dissector(), passing it the string name of the dissector. The setting
|
|
of the handle is usually done once at startup during the proto_reg_handoff
|
|
function within the calling dissector.
|
|
|
|
1.7.1 Dissector Tables
|
|
|
|
Another way to call a subdissector is to setup a dissector table. A dissector
|
|
table is a list of subdissectors grouped by a common identifier (integer or
|
|
string) in a dissector. Subdissectors will register themselves with the dissector
|
|
table using their unique identifier using one of the following APIs:
|
|
|
|
void dissector_add_uint(const char *abbrev, const guint32 pattern,
|
|
dissector_handle_t handle);
|
|
|
|
void dissector_add_uint_range(const char *abbrev, struct epan_range *range,
|
|
dissector_handle_t handle);
|
|
|
|
void dissector_add_string(const char *name, const gchar *pattern,
|
|
dissector_handle_t handle);
|
|
|
|
void dissector_add_for_decode_as(const char *name,
|
|
dissector_handle_t handle);
|
|
|
|
dissector_add_for_decode_as doesn't add a unique identifier in the dissector
|
|
table, but it lets the user add it from the command line or, in Wireshark,
|
|
through the "Decode As" UI.
|
|
|
|
Then when the dissector hits the common identifier field, it will use one of the
|
|
following APIs to invoke the subdissector:
|
|
|
|
int dissector_try_uint(dissector_table_t sub_dissectors,
|
|
const guint32 uint_val, tvbuff_t *tvb, packet_info *pinfo,
|
|
proto_tree *tree);
|
|
|
|
int dissector_try_uint_new(dissector_table_t sub_dissectors,
|
|
const guint32 uint_val, tvbuff_t *tvb, packet_info *pinfo,
|
|
proto_tree *tree, const gboolean add_proto_name, void *data);
|
|
|
|
int dissector_try_string(dissector_table_t sub_dissectors, const gchar *string,
|
|
tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
|
|
|
|
These pass a subset of the remaining packet (typically the rest of the
|
|
packet) for the dissector table to determine which subdissector is called.
|
|
This allows dissection of a packet to be expanded outside of dissector without
|
|
having to modify the dissector directly.
|
|
|
|
|
|
1.8 Editing CMakeLists.txt to add your dissector.
|
|
|
|
To arrange that your dissector will be built as part of Wireshark, you
|
|
must add the name of the source file for your dissector to the DISSECTOR_SRC
|
|
section of epan/dissectors/CMakeLists.txt
|
|
|
|
|
|
1.9 Using the git source code tree.
|
|
|
|
See <https://www.wireshark.org/develop.html>
|
|
|
|
|
|
1.10 Submitting code for your new dissector.
|
|
|
|
See <https://www.wireshark.org/docs/wsdg_html_chunked/ChSrcContribute.html>
|
|
and <https://gitlab.com/wireshark/wireshark/-/wikis/Development/SubmittingPatches>.
|
|
|
|
- VERIFY that your dissector code does not use prohibited or deprecated APIs
|
|
as follows:
|
|
perl <wireshark_root>/tools/checkAPIs.pl <source-filename(s)>
|
|
|
|
- VERIFY that your dissector code does not contain any header field related
|
|
problems:
|
|
perl <wireshark_root>/tools/checkhf.pl <source-filename(s)>
|
|
|
|
- VERIFY that your dissector code does not contain any display filter related
|
|
problems:
|
|
perl <wireshark_root>/tools/checkfiltername.pl <source-filename(s)>
|
|
|
|
- CHECK your dissector with CppCheck (http://cppcheck.sourceforge.net/) using
|
|
Wireshark's customized configuration. This is particularly important on
|
|
Windows, since Microsoft's compiler warnings are quite thin:
|
|
./tools/cppcheck/cppcheck.sh <source-filename(s)>
|
|
|
|
- TEST YOUR DISSECTOR BEFORE SUBMITTING IT.
|
|
Use fuzz-test.sh and/or randpkt against your dissector. These are
|
|
described at <https://gitlab.com/wireshark/wireshark/-/wikis/FuzzTesting>.
|
|
|
|
- Subscribe to <mailto:wireshark-dev[AT]wireshark.org> by sending an email to
|
|
<mailto:wireshark-dev-request[AT]wireshark.org?body="help"> or visiting
|
|
<https://www.wireshark.org/lists/>.
|
|
|
|
- 'git diff' to verify all your changes look good.
|
|
|
|
- 'git add' all the files you changed.
|
|
|
|
- 'git commit' to commit (locally) your changes. First line of commit message
|
|
should be a summary of the changes followed by an empty line and a more
|
|
verbose description.
|
|
|
|
- 'git push downstream HEAD' to push the changes to GitLab. (This assumes
|
|
that you have a remote named "downstream" that points to a fork of
|
|
https://gitlab.com/wireshark/wireshark.)
|
|
|
|
- Create a Wiki page on the protocol at <https://gitlab.com/wireshark/editor-wiki>.
|
|
(You'll need to request access to https://gitlab.com/wireshark/wiki-editors.)
|
|
A template is provided so it is easy to setup in a consistent style.
|
|
See: <https://gitlab.com/wireshark/wireshark/-/wikis/HowToEdit>
|
|
and <https://gitlab.com/wireshark/wireshark/-/wikis/ProtocolReference>
|
|
|
|
- If possible, add sample capture files to the sample captures page at
|
|
<https://gitlab.com/wireshark/wireshark/-/wikis/SampleCaptures>. These
|
|
files are used by the automated build system for fuzz testing.
|
|
|
|
- If you don't think the wiki is the right place for your sample capture,
|
|
submit a bug report to the Wireshark issue database, found at
|
|
<https://gitlab.com/wireshark/wireshark/-/issues>, qualified as an
|
|
enhancement and attach your sample capture there. Normally a new
|
|
dissector won't be accepted without a sample capture! If you open a
|
|
bug be sure to cross-link your GitLab merge request.
|
|
|
|
2. Advanced dissector topics.
|
|
|
|
2.1 Introduction.
|
|
|
|
Some of the advanced features are being worked on constantly. When using them
|
|
it is wise to check the relevant header and source files for additional details.
|
|
|
|
2.2 Following "conversations".
|
|
|
|
In Wireshark a conversation is defined as a series of data packets 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.
|
|
|
|
2.2.1 Conversation Routines
|
|
|
|
There are nine routines that you will use to work with a conversation:
|
|
conversation_new, conversation_new_full, find_conversation,
|
|
find_conversation_full, find_or_create_conversation,
|
|
conversation_add_proto_data, conversation_get_proto_data,
|
|
conversation_delete_proto_data, and conversation_set_dissector.
|
|
|
|
2.2.1.1 The conversation_init function.
|
|
|
|
This is an internal routine for the conversation code. As such 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_add_proto_data 'data' variable. You are
|
|
responsible for this clean up if you pass a malloc'ed pointer
|
|
in this variable.
|
|
|
|
See item 2.2.1.5 for more information about use of the 'data' pointer.
|
|
|
|
|
|
2.2.1.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 known 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(guint32 setup_frame, address *addr1,
|
|
address *addr2, port_type ptype, guint32 port1, guint32 port2,
|
|
guint options);
|
|
|
|
Where:
|
|
guint32 setup_frame = The lowest numbered frame for this conversation
|
|
address* addr1 = first data packet address
|
|
address* addr2 = second data packet address
|
|
endpoint_type etype = endpoint type, defined in conversation.h
|
|
guint32 port1 = first data packet port
|
|
guint32 port2 = second data packet port
|
|
guint options = conversation options, NO_ADDR2 and/or NO_PORT2
|
|
|
|
setup_frame indicates the first frame for this conversation, and is used to
|
|
distinguish multiple conversations with the same addr1/port1 and addr2/port2
|
|
pair that occur within the same capture session.
|
|
|
|
"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.1.3 The conversation_new_full function.
|
|
|
|
This routine will create a new conversation based upon an arbitrary
|
|
lists of elements. Elements can be addresses, strings, unsigned
|
|
integers, or unsigned 64-bit integers. Unlike conversation_new, element
|
|
lists are matched strictly; wildcards aren't (yet) supported.
|
|
|
|
The conversation_new_full prototype:
|
|
conversation_t *conversation_new(const guint32 setup_frame,
|
|
conversation_element_t *elements);
|
|
|
|
Where:
|
|
guint32 setup_frame = The lowest numbered frame for
|
|
this conversation
|
|
conversation_element_t *elements = An array of data types and
|
|
values which identify this conversation. The array MUST be
|
|
terminated with a CE_ENDPOINT element.
|
|
|
|
2.2.1.4 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(guint32 frame_num, address *addr_a,
|
|
address *addr_b, port_type ptype, guint32 port_a, guint32 port_b,
|
|
guint options);
|
|
|
|
Where:
|
|
guint32 frame_num = a frame number to match
|
|
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
|
|
|
|
frame_num is a frame number to match. The conversation returned is where
|
|
(frame_num >= conversation->setup_frame
|
|
&& frame_num < conversation->next->setup_frame)
|
|
Suppose there are a total of 3 conversations (A, B, and C) that match
|
|
addr_a/port_a and addr_b/port_b, where the setup_frame used in
|
|
conversation_new() for A, B and C are 10, 50, and 100 respectively. The
|
|
frame_num passed in find_conversation is compared to the setup_frame of each
|
|
conversation. So if (frame_num >= 10 && frame_num < 50), conversation A is
|
|
returned. If (frame_num >= 50 && frame_num < 100), conversation B is returned.
|
|
If (frame_num >= 100) conversation C is returned.
|
|
|
|
"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.1.5 The find_conversation_full function.
|
|
|
|
Call this routine to look up a conversation based on an element list. If
|
|
no conversation is found, the routine will return a NULL value.
|
|
|
|
The find_conversation_full prototype:
|
|
|
|
conversation_t *find_conversation_full(guint32 frame_num,
|
|
conversation_element_t *elements);
|
|
|
|
Where:
|
|
guint32 setup_frame = The lowest numbered frame for
|
|
this conversation
|
|
conversation_element_t *elements = An array of data types and
|
|
values which identify this conversation. The array MUST be
|
|
terminated with a CE_ENDPOINT element.
|
|
|
|
2.2.1.6 The find_conversation_pinfo function.
|
|
|
|
This convenience function will find an existing conversation (by calling
|
|
find_conversation())
|
|
|
|
The find_conversation_pinfo prototype:
|
|
|
|
extern conversation_t *find_conversation_pinfo(packet_info *pinfo, const guint options);
|
|
|
|
Where:
|
|
packet_info *pinfo = the packet_info structure
|
|
const guint options = conversation options, NO_ADDR_B and/or NO_PORT_B
|
|
|
|
The frame number and the addresses necessary for find_conversation() are
|
|
taken from the addresses and ports in the pinfo structure,
|
|
pinfo->conv_endpoint if pinfo->use_endpoint is set, or
|
|
pinfo->conv_elements if it is set.
|
|
|
|
2.2.1.7 The find_or_create_conversation function.
|
|
|
|
This convenience function will find an existing conversation (by calling
|
|
find_conversation()) and, if a conversation does not already exist, create a
|
|
new conversation by calling conversation_new().
|
|
|
|
The find_or_create_conversation prototype:
|
|
|
|
extern conversation_t *find_or_create_conversation(packet_info *pinfo);
|
|
|
|
Where:
|
|
packet_info *pinfo = the packet_info structure
|
|
|
|
The frame number and the addresses necessary for find_conversation() and
|
|
conversation_new() are taken from the pinfo structure (as is commonly done)
|
|
and no 'options' are used.
|
|
|
|
2.2.1.8 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. "data" usually points to "wmem_alloc'd" memory; the
|
|
memory will be automatically freed each time a new dissection begins
|
|
and thus need not be managed (freed) by the dissector.
|
|
Using the protocol number allows several dissectors to
|
|
associate data with a given conversation.
|
|
|
|
|
|
2.2.1.9 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 created 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.1.10 The conversation_delete_proto_data function.
|
|
|
|
After you are finished with a conversation, you can remove your association
|
|
with this function. Please note that ONLY the conversation entry is
|
|
removed. If you have allocated any memory for your data (other than with wmem_alloc),
|
|
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 created with proto_register_protocol,
|
|
typically in the proto_register_XXXX portion of a dissector.
|
|
|
|
2.2.1.11 The conversation_set_dissector function
|
|
|
|
This function sets the protocol dissector to be invoked whenever
|
|
conversation parameters (addresses, port_types, ports, etc) are matched
|
|
during the dissection of a packet.
|
|
|
|
The conversation_set_dissector prototype:
|
|
|
|
void conversation_set_dissector(conversation_t *conversation, const dissector_handle_t handle);
|
|
|
|
Where:
|
|
conversation_t *conv = the conversation in question
|
|
const dissector_handle_t handle = the dissector handle.
|
|
|
|
|
|
2.2.2 Using timestamps relative to the conversation
|
|
|
|
There is a framework to calculate timestamps relative to the start of the
|
|
conversation. First of all the timestamp of the first packet that has been
|
|
seen in the conversation must be kept in the protocol data to be able
|
|
to calculate the timestamp of the current packet relative to the start
|
|
of the conversation. The timestamp of the last packet that was seen in the
|
|
conversation should also be kept in the protocol data. This way the
|
|
delta time between the current packet and the previous packet in the
|
|
conversation can be calculated.
|
|
|
|
So add the following items to the struct that is used for the protocol data:
|
|
|
|
nstime_t ts_first;
|
|
nstime_t ts_prev;
|
|
|
|
The ts_prev value should only be set during the first run through the
|
|
packets (ie PINFO_FD_VISITED(pinfo) is false).
|
|
|
|
Next step is to use the per-packet information (described in section 2.5)
|
|
to keep the calculated delta timestamp, as it can only be calculated
|
|
on the first run through the packets. This is because a packet can be
|
|
selected in random order once the whole file has been read.
|
|
|
|
After calculating the conversation timestamps, it is time to put them in
|
|
the appropriate columns with the function 'col_set_time' (described in
|
|
section 1.5.9). The column used for relative timestamps is:
|
|
|
|
COL_REL_TIME, /* Delta time to last frame in conversation */
|
|
|
|
Last but not least, there MUST be a preference in each dissector that
|
|
uses conversation timestamps that makes it possible to enable and
|
|
disable the calculation of conversation timestamps. The main argument
|
|
for this is that a higher level conversation is able to overwrite
|
|
the values of lower level conversations in these two columns. Being
|
|
able to actively select which protocols may overwrite the conversation
|
|
timestamp columns gives the user the power to control these columns.
|
|
(A second reason is that conversation timestamps use the per-packet
|
|
data structure which uses additional memory, which should be avoided
|
|
if these timestamps are not needed)
|
|
|
|
Have a look at the differences to packet-tcp.[ch] in SVN 22966 and
|
|
SVN 23058 to see the implementation of conversation timestamps for
|
|
the tcp-dissector.
|
|
|
|
|
|
2.2.3 The example conversation code using wmem_file_scope memory.
|
|
|
|
For a conversation between two IP addresses and ports you can use this as an
|
|
example. This example uses wmem_alloc() with wmem_file_scope() to allocate
|
|
memory and stores the data pointer in the conversation 'data' variable.
|
|
|
|
/************************ Global values ************************/
|
|
|
|
/* define your structure here */
|
|
typedef struct {
|
|
|
|
} my_entry_t;
|
|
|
|
/* 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->num, &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 = wmem_alloc(wmem_file_scope(), sizeof(my_entry_t));
|
|
|
|
/*** add your code here to setup the new data structure ***/
|
|
|
|
/* create the conversation with your data pointer */
|
|
|
|
conversation = conversation_new(pinfo->num, &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 protocol register routine *****************/
|
|
|
|
my_proto = proto_register_protocol("My Protocol", "My Protocol", "my_proto");
|
|
|
|
|
|
2.2.4 An example conversation code that starts at a specific frame number.
|
|
|
|
Sometimes a dissector has determined that a new conversation is needed that
|
|
starts at a specific frame number, when a capture session encompasses multiple
|
|
conversation that reuse the same src/dest ip/port pairs. You can use the
|
|
conversation->setup_frame returned by find_conversation with
|
|
pinfo->num to determine whether or not there already exists a conversation
|
|
that starts at the specific frame number.
|
|
|
|
/* in the dissector routine */
|
|
|
|
conversation = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst,
|
|
pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
|
|
if (conversation == NULL || (conversation->setup_frame != pinfo->num)) {
|
|
/* It's not part of any conversation or the returned
|
|
* conversation->setup_frame doesn't match the current frame
|
|
* create a new one.
|
|
*/
|
|
conversation = conversation_new(pinfo->num, &pinfo->src,
|
|
&pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport,
|
|
NULL, 0);
|
|
}
|
|
|
|
|
|
2.2.5 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_or_create_conversation(pinfo);
|
|
|
|
request_key.conversation = conversation->index;
|
|
request_key.service = pntoh16(&rxh->serviceId);
|
|
request_key.callnumber = pntoh32(&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 = wmem_alloc(wmem_file_scope(), sizeof(struct afs_request_key));
|
|
*new_request_key = request_key;
|
|
|
|
request_val = wmem_alloc(wmem_file_scope(), sizeof(struct afs_request_val));
|
|
request_val -> opcode = pntoh32(&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 known, 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.
|
|
Before we create these conversations or assign a dissector to them we should
|
|
first check that the conversation does not already exist and if it exists
|
|
whether it is registered to our protocol or not.
|
|
We should do this because it is uncommon but it does happen that multiple
|
|
different protocols can use the same socketpair during different stages of
|
|
an application cycle. By keeping track of the frame number a conversation
|
|
was started in Wireshark can still tell these different protocols apart.
|
|
|
|
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 */
|
|
|
|
/* First check if a conversation already exists for this
|
|
socketpair
|
|
*/
|
|
conversation = find_conversation(pinfo->num,
|
|
&pinfo->src, &pinfo->dst, protocol,
|
|
src_port, dst_port, 0);
|
|
|
|
/* If there is no such conversation, or if there is one but for
|
|
someone else's protocol then we just create a new conversation
|
|
and assign our protocol to it.
|
|
*/
|
|
if ( (conversation == NULL) ||
|
|
(conversation->dissector_handle != sub_dissector_handle) ) {
|
|
new_conv_info = wmem_alloc(wmem_file_scope(), sizeof(struct _new_conv_info));
|
|
new_conv_info->data1 = value1;
|
|
|
|
/* create the conversation for the dynamic port */
|
|
conversation = conversation_new(pinfo->num,
|
|
&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.
|
|
|
|
Some server applications can use the same port for different protocols during
|
|
different stages of a transaction. For example it might initially use SNMP
|
|
to perform some discovery and later switch to use TFTP using the same port.
|
|
In order to handle this properly we must first check whether such a
|
|
conversation already exists or not and if it exists we also check whether the
|
|
registered dissector_handle for that conversation is "our" dissector or not.
|
|
If not we create a new conversation on top of the previous one and set this new
|
|
conversation to use our protocol.
|
|
Since Wireshark keeps track of the frame number where a conversation started
|
|
wireshark will still be able to keep the packets apart even though they do use
|
|
the same socketpair.
|
|
(See packet-tftp.c and packet-snmp.c for examples of this)
|
|
|
|
There are two support routines that will allow the second port and/or
|
|
address to be set later.
|
|
|
|
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 to 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 = wmem_alloc(wmem_file_scope(), sizeof(struct _new_conv_info));
|
|
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. */
|
|
|
|
/* First check if a conversation already exists for this
|
|
IP/protocol/port
|
|
*/
|
|
conversation = find_conversation(pinfo->num,
|
|
&server_src_addr, 0, protocol,
|
|
server_src_port, 0, NO_ADDR2 | NO_PORT_B);
|
|
/* If there is no such conversation, or if there is one but for
|
|
someone else's protocol then we just create a new conversation
|
|
and assign our protocol to it.
|
|
*/
|
|
if ( (conversation == NULL) ||
|
|
(conversation->dissector_handle != sub_dissector_handle) ) {
|
|
conversation = conversation_new(pinfo->num,
|
|
&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 processed by the
|
|
dissector. The information is added with the p_add_proto_data function and
|
|
retrieved 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, however the
|
|
data pointer memory scope must match that of the scope parameter.
|
|
The two most common use cases for p_add_proto_data/p_get_proto_data are for
|
|
persistent data about the packet for the lifetime of the capture (file scope)
|
|
and to exchange data between dissectors across a single packet (packet scope).
|
|
It is also used to provide packet data for Decode As dialog (packet scope).
|
|
|
|
These functions are declared in <epan/proto_data.h>.
|
|
|
|
void
|
|
p_add_proto_data(wmem_allocator_t *scope, packet_info *pinfo, int proto, guint32 key, void *proto_data)
|
|
void *
|
|
p_get_proto_data(wmem_allocator_t *scope, packet_info *pinfo, int proto, guint32 key)
|
|
|
|
Where:
|
|
scope - Lifetime of the data to be stored, typically wmem_file_scope()
|
|
or pinfo->pool (packet scope). Must match scope of data
|
|
allocated.
|
|
pinfo - The packet info pointer.
|
|
proto - Protocol id returned by the proto_register_protocol call
|
|
during initialization
|
|
key - key associated with 'proto_data'
|
|
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))
|
|
or
|
|
module_t *prefs_register_protocol_subtree(const char *subtree, int 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 called when preferences are
|
|
applied. It may be NULL, which inhibits the callback.
|
|
subtree - grouping preferences tree node name (several protocols can
|
|
be grouped under one preferences subtree)
|
|
|
|
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_t *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)
|
|
|
|
/* Register a preference with a password (a character-string) value. */
|
|
/* The value is hold during runtime, only in memory. It is never written to disk */
|
|
void prefs_register_password_preference(module_t *module, const char *name,
|
|
const char *title, const char *description, char **var)
|
|
|
|
/* Register a preference with a file name (string) value.
|
|
* File name preferences are basically like string preferences
|
|
* except that the GUI gives the user the ability to browse for the
|
|
* file. Set for_writing TRUE to show a Save dialog instead of normal Open.
|
|
*/
|
|
void prefs_register_filename_preference(module_t *module, const char *name,
|
|
const char *title, const char *description, char **var,
|
|
gboolean for_writing)
|
|
|
|
/* Register a preference with a range of unsigned integers (e.g.,
|
|
* "1-20,30-40").
|
|
*/
|
|
void prefs_register_range_preference(module_t *module, const char *name,
|
|
const char *title, const char *description, range_t *var,
|
|
guint32 max_value)
|
|
|
|
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. Make sure that
|
|
only lower-case ASCII letters, numbers, underscores and
|
|
dots appear in the preference name.
|
|
title - Field title in the preferences dialog
|
|
description - Comments added to the preference file above the
|
|
preference value and shown as tooltip in the GUI, or NULL
|
|
var - pointer to the storage location that is updated when the
|
|
field is changed in the preference dialog box. Note that
|
|
with string preferences the given pointer is overwritten
|
|
with a pointer to a new copy of the string during the
|
|
preference registration. The passed-in string may be
|
|
freed, but you must keep another pointer to the string
|
|
in order to free it.
|
|
base - Base that the unsigned integer is expected to be in,
|
|
see strtoul(3).
|
|
enumvals - an array of enum_val_t structures. This must be
|
|
NULL-terminated; the members of that structure are:
|
|
|
|
a short name, to be used with the "-o" flag - it
|
|
should not contain spaces or upper-case letters,
|
|
so that it's easier to put in a command line;
|
|
|
|
a description, which is used in the GUI (and
|
|
which, for compatibility reasons, is currently
|
|
what's written to the preferences file) - it can
|
|
contain spaces, capital letters, punctuation,
|
|
etc.;
|
|
|
|
the numerical value corresponding to that name
|
|
and description
|
|
radio_buttons - TRUE if the field is to be displayed in the
|
|
preferences dialog as a set of radio buttons,
|
|
FALSE if it is to be displayed as an option
|
|
menu
|
|
max_value - The maximum allowed value for a range (0 is the minimum).
|
|
|
|
These functions are declared in <epan/prefs.h>.
|
|
|
|
An example from packet-rtpproxy.c -
|
|
|
|
proto_rtpproxy = proto_register_protocol ( "Sippy RTPproxy Protocol", "RTPproxy", "rtpproxy");
|
|
|
|
...
|
|
|
|
rtpproxy_module = prefs_register_protocol(proto_rtpproxy, proto_reg_handoff_rtpproxy);
|
|
|
|
prefs_register_bool_preference(rtpproxy_module, "establish_conversation",
|
|
"Establish Media Conversation",
|
|
"Specifies that RTP/RTCP/T.38/MSRP/etc streams are decoded based "
|
|
"upon port numbers found in RTPproxy answers",
|
|
&rtpproxy_establish_conversation);
|
|
|
|
prefs_register_uint_preference(rtpproxy_module, "reply.timeout",
|
|
"RTPproxy reply timeout", /* Title */
|
|
"Maximum timeout value in waiting for reply from RTPProxy (in milliseconds).", /* Descr */
|
|
10,
|
|
&rtpproxy_timeout);
|
|
|
|
This will create preferences "rtpproxy.establish_conversation" and
|
|
"rtpproxy.reply.timeout", the first of which is an Boolean and the
|
|
second of which is a unsigned integer.
|
|
|
|
Note that a warning will pop up if you've saved such preference to the
|
|
preference file and you subsequently take the code out. The way to make
|
|
a preference obsolete is to register it as such:
|
|
|
|
/* Register a preference that used to be supported but no longer is. */
|
|
void prefs_register_obsolete_preference(module_t *module,
|
|
const char *name);
|
|
|
|
2.7 Reassembly/desegmentation for protocols running atop TCP.
|
|
|
|
There are two main ways of reassembling a Protocol Data Unit (PDU) which
|
|
spans across multiple TCP segments. The first approach 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 method is more
|
|
generic but requires more code and is less efficient.
|
|
|
|
2.7.1 Using tcp_dissect_pdus().
|
|
|
|
For the first method, you register two different dissection methods, one
|
|
for the TCP case, and one for the other cases. It is a good idea to
|
|
also 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-hartip.c:
|
|
|
|
#include "packet-tcp.h"
|
|
|
|
dissector_handle_t hartip_tcp_handle;
|
|
dissector_handle_t hartip_udp_handle;
|
|
|
|
hartip_tcp_handle = create_dissector_handle(dissect_hartip_tcp, proto_hartip);
|
|
hartip_udp_handle = create_dissector_handle(dissect_hartip_udp, proto_hartip);
|
|
|
|
dissector_add_uint("udp.port", HARTIP_PORT, hartip_udp_handle);
|
|
dissector_add_uint_with_preference("tcp.port", HARTIP_PORT, hartip_tcp_handle);
|
|
|
|
The dissect_hartip_udp function does very little work and calls
|
|
dissect_hartip_common, while dissect_hartip_tcp calls tcp_dissect_pdus with a
|
|
reference to a callback which will be called with reassembled data:
|
|
|
|
static int
|
|
dissect_hartip_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
void *data)
|
|
{
|
|
if (!tvb_bytes_exist(tvb, 0, HARTIP_HEADER_LENGTH))
|
|
return 0;
|
|
|
|
tcp_dissect_pdus(tvb, pinfo, tree, hartip_desegment, HARTIP_HEADER_LENGTH,
|
|
get_dissect_hartip_len, dissect_hartip_pdu, data);
|
|
return tvb_reported_length(tvb);
|
|
}
|
|
|
|
(The dissect_hartip_pdu function acts similarly to dissect_hartip_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 packet_info pointer, a tvbuff
|
|
pointer and an offset value representing the offset into the tvbuff
|
|
at which a PDU begins, and a void pointer for user data, and should
|
|
return the total length of the PDU in bytes (or 0 if more bytes are
|
|
needed to determine the message length).
|
|
The routine must not throw exceptions (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);
|
|
|
|
a new_dissector_t routine to dissect the pdu that's passed a tvbuff
|
|
pointer, packet_info pointer, proto_tree pointer and a void pointer for
|
|
user data, with the tvbuff containing a possibly-reassembled PDU. (The
|
|
"reported_length" of the tvbuff will be the length of the PDU);
|
|
|
|
a void pointer to user data that is passed to the length-determining
|
|
routine, and the dissector routine referenced in the previous parameter.
|
|
|
|
2.7.2 Modifying the pinfo struct.
|
|
|
|
The second reassembly mode is preferred when the dissector cannot determine
|
|
how many bytes it will need to read in order to determine the size of a PDU.
|
|
It may also be useful if your dissector needs to support reassembly from
|
|
protocols other than TCP.
|
|
|
|
Your dissect_PROTO will initially be passed a tvbuff containing the payload of
|
|
the first packet. It should dissect as much data as it can, noting that it may
|
|
contain more than one complete PDU. If the end of the provided tvbuff coincides
|
|
with the end of a PDU then all is well and your dissector can just return as
|
|
normal. (If it is a new-style dissector, it should return the number of bytes
|
|
successfully processed.)
|
|
|
|
If the dissector discovers that the end of the tvbuff does /not/ coincide with
|
|
the end of a PDU, (ie, there is half of a PDU at the end of the tvbuff), it can
|
|
indicate this to the parent dissector, by updating the pinfo struct. The
|
|
desegment_offset field is the offset in the tvbuff at which the dissector will
|
|
continue processing when next called. The desegment_len field should contain
|
|
the estimated number of additional bytes required for completing the PDU. Next
|
|
time your dissect_PROTO is called, it will be passed a tvbuff composed of the
|
|
end of the data from the previous tvbuff together with desegment_len more bytes.
|
|
|
|
If the dissector cannot tell how many more bytes it will need, it should set
|
|
desegment_len=DESEGMENT_ONE_MORE_SEGMENT; it will then be called again as soon
|
|
as any more data becomes available. Dissectors should set the desegment_len to a
|
|
reasonable value when possible rather than always setting
|
|
DESEGMENT_ONE_MORE_SEGMENT as it will generally be more efficient. Also, you
|
|
*must not* set desegment_len=1 in this case, in the hope that you can change
|
|
your mind later: once you return a positive value from desegment_len, your PDU
|
|
boundary is set in stone.
|
|
|
|
static hf_register_info hf[] = {
|
|
{&hf_cstring,
|
|
{"C String", "c.string", FT_STRING, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Dissect a buffer containing ASCII C strings.
|
|
*
|
|
* @param tvb The buffer to dissect.
|
|
* @param pinfo Packet Info.
|
|
* @param tree The protocol tree.
|
|
* @param data Optional data parameter given by parent dissector.
|
|
**/
|
|
static int dissect_cstr(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, void *data _U_)
|
|
{
|
|
guint offset = 0;
|
|
while(offset < tvb_reported_length(tvb)) {
|
|
gint available = tvb_reported_length_remaining(tvb, offset);
|
|
gint len = tvb_strnlen(tvb, offset, available);
|
|
|
|
if( -1 == len ) {
|
|
/* we ran out of data: ask for more */
|
|
pinfo->desegment_offset = offset;
|
|
pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
|
|
return (offset + available);
|
|
}
|
|
|
|
col_set_str(pinfo->cinfo, COL_INFO, "C String");
|
|
|
|
len += 1; /* Add one for the '\0' */
|
|
|
|
if (tree) {
|
|
proto_tree_add_item(tree, hf_cstring, tvb, offset, len, ENC_ASCII);
|
|
}
|
|
offset += (guint)len;
|
|
}
|
|
|
|
/* if we get here, then the end of the tvb coincided with the end of a
|
|
string. Happy days. */
|
|
return tvb_captured_length(tvb);
|
|
}
|
|
|
|
This simple dissector will repeatedly return DESEGMENT_ONE_MORE_SEGMENT
|
|
requesting more data until the tvbuff contains a complete C string. The C string
|
|
will then be added to the protocol tree. Note that there may be more
|
|
than one complete C string in the tvbuff, so the dissection is done in a
|
|
loop.
|
|
|
|
2.8 Using udp_dissect_pdus().
|
|
|
|
As noted in section 2.7.1, TCP has an API to dissect its PDU that can handle
|
|
a PDU spread across multiple packets or multiple PDUs spread across a single
|
|
packet. This section describes a similar mechanism for UDP, but is only
|
|
applicable for one or more PDUs in a single packet. If a protocol runs on top
|
|
of TCP as well as UDP, a common PDU dissection function can be created for both.
|
|
|
|
To register the distinct dissector functions, consider the following
|
|
example using UDP and TCP dissection, stolen from packet-dnp.c:
|
|
|
|
#include "packet-tcp.h"
|
|
#include "packet-udp.h"
|
|
|
|
dissector_handle_t dnp3_tcp_handle;
|
|
dissector_handle_t dnp3_udp_handle;
|
|
|
|
dnp3_tcp_handle = create_dissector_handle(dissect_dnp3_tcp, proto_dnp3);
|
|
dnp3_udp_handle = create_dissector_handle(dissect_dnp3_udp, proto_dnp3);
|
|
|
|
dissector_add_uint("tcp.port", TCP_PORT_DNP, dnp3_tcp_handle);
|
|
dissector_add_uint("udp.port", UDP_PORT_DNP, dnp3_udp_handle);
|
|
|
|
Both dissect_dnp3_tcp and dissect_dnp3_udp call tcp_dissect_pdus and
|
|
udp_dissect_pdus respectively, with a reference to the same callbacks which
|
|
are called to handle PDU data.
|
|
|
|
static int
|
|
dissect_dnp3_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
|
|
{
|
|
return udp_dissect_pdus(tvb, pinfo, tree, DNP_HDR_LEN, dnp3_udp_check_header,
|
|
get_dnp3_message_len, dissect_dnp3_message, data);
|
|
}
|
|
|
|
static int
|
|
dissect_dnp3_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
|
|
{
|
|
if (!check_dnp3_header(tvb, FALSE)) {
|
|
return 0;
|
|
}
|
|
|
|
tcp_dissect_pdus(tvb, pinfo, tree, TRUE, DNP_HDR_LEN,
|
|
get_dnp3_message_len, dissect_dnp3_message, data);
|
|
|
|
return tvb_captured_length(tvb);
|
|
}
|
|
|
|
(udp_dissect_pdus has an option of a heuristic check function within it while
|
|
tcp_dissect_pdus does not, so it's done outside)
|
|
|
|
The arguments to udp_dissect_pdus are:
|
|
|
|
the tvbuff pointer, packet_info pointer, and proto_tree pointer
|
|
passed to the dissector;
|
|
|
|
the number of bytes of PDU data required to determine the length
|
|
of the PDU;
|
|
|
|
an optional routine (passing NULL is okay) that takes as arguments a
|
|
packet_info pointer, a tvbuff pointer and an offset value representing the
|
|
offset into the tvbuff at which a PDU begins, and a void pointer for user
|
|
data, and should return TRUE if the packet belongs to the dissector.
|
|
The routine must not throw exceptions (it is guaranteed that the
|
|
number of bytes specified by the previous argument to
|
|
udp_dissect_pdus is available, but more data might not be available,
|
|
so don't refer to any data past that);
|
|
|
|
a routine that takes as arguments a packet_info pointer, a tvbuff
|
|
pointer and an offset value representing the offset into the tvbuff
|
|
at which a PDU begins, and a void pointer for user data, and should
|
|
return the total length of the PDU in bytes. If return value is 0,
|
|
it's treated the same as a failed heuristic.
|
|
The routine must not throw exceptions (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);
|
|
|
|
a new_dissector_t routine to dissect the pdu that's passed a tvbuff
|
|
pointer, packet_info pointer, proto_tree pointer and a void pointer for
|
|
user data, with the tvbuff containing a possibly-reassembled PDU. (The
|
|
"reported_length" of the tvbuff will be the length of the PDU);
|
|
|
|
a void pointer to user data that is passed to the length-determining
|
|
routine, and the dissector routine referenced in the previous parameter.
|
|
|
|
2.9 PINOs (Protocols in name only)
|
|
|
|
For the typical dissector there is a 1-1 relationship between it and it's
|
|
protocol. However, there are times when a protocol needs multiple "names"
|
|
because it has multiple dissection functions going into the same dissector
|
|
table. The multiple names removes confusion when picking dissection through
|
|
Decode As functionality.
|
|
|
|
Once the "main" protocol name has been created through proto_register_protocol,
|
|
additional "pinos" can be created with proto_register_protocol_in_name_only.
|
|
These pinos have all of the naming conventions of a protocol, but are stored
|
|
separately as to remove confusion from real protocols. "pinos" the main
|
|
protocol's properties for things like enable/disable. i.e. If the "main"
|
|
protocol has been disabled, all of its pinos will be disabled as well.
|
|
Pinos should not have any fields registered with them or heuristic tables
|
|
associated with them.
|
|
|
|
Another use case for pinos is when a protocol contains a TLV design and it
|
|
wants to create a dissector table to handle dissection of the "V". Dissector
|
|
tables require a "protocol", but the dissection functions for that table
|
|
typically aren't a protocol. In this case proto_register_protocol_in_name_only
|
|
creates the necessary placeholder for the dissector table. In addition, because
|
|
a dissector table exists, "V"s of the TLVs can be dissected outside of the
|
|
original dissector file.
|
|
|
|
2.10 Creating Decode As functionality.
|
|
|
|
While the Decode As functionality is available through the GUI, the underlying
|
|
functionality is controlled by dissectors themselves. To create Decode As
|
|
functionality for a dissector, two things are required:
|
|
1. A dissector table
|
|
2. A series of structures to assist the GUI in how to present the dissector
|
|
table data.
|
|
|
|
Consider the following example using IP dissection, stolen from packet-ip.c:
|
|
|
|
static build_valid_func ip_da_build_value[1] = {ip_value};
|
|
static decode_as_value_t ip_da_values = {ip_prompt, 1, ip_da_build_value};
|
|
static decode_as_t ip_da = {"ip", "ip.proto", 1, 0, &ip_da_values, NULL, NULL,
|
|
decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL};
|
|
...
|
|
ip_dissector_table = register_dissector_table("ip.proto", "IP protocol", ip_proto, FT_UINT8, BASE_DEC);
|
|
...
|
|
register_decode_as(&ip_da);
|
|
|
|
ip_da_build_value contains all of the function pointers (typically just 1) that
|
|
can be used to retrieve the value(s) that go into the dissector table. This is
|
|
usually data saved by the dissector during packet dissector with an API like
|
|
p_add_proto_data and retrieved in the "value" function with p_get_proto_data.
|
|
|
|
ip_da_values contains all of the function pointers (typically just 1) that
|
|
provide the text explaining the name and use of the value field that will
|
|
be passed to the dissector table to change the dissection output.
|
|
|
|
ip_da pulls everything together including the dissector (protocol) name, the
|
|
"layer type" of the dissector, the dissector table name, the function pointer
|
|
values as well as handlers for populating, applying and resetting the changes
|
|
to the dissector table through Decode As GUI functionality. For dissector
|
|
tables that are an integer or string type, the provided "default" handling
|
|
functions shown in the example should suffice.
|
|
|
|
All entries into a dissector table that use Decode As must have a unique
|
|
protocol ID. If a protocol wants multiple entries into a dissector table,
|
|
a pino should be used (see section 2.9)
|
|
|
|
2.11 ptvcursors.
|
|
|
|
The ptvcursor API allows a simpler approach to writing dissectors for
|
|
simple protocols. The ptvcursor API works best for protocols whose fields
|
|
are static and whose format does not depend on the value of other fields.
|
|
However, even if only a portion of your protocol is statically defined,
|
|
then that portion could make use of ptvcursors.
|
|
|
|
The ptvcursor API lets you extract data from a tvbuff, and add it to a
|
|
protocol tree in one step. It also keeps track of the position in the
|
|
tvbuff so that you can extract data again without having to compute any
|
|
offsets --- hence the "cursor" name of the API.
|
|
|
|
The three steps for a simple protocol are:
|
|
1. Create a new ptvcursor with ptvcursor_new()
|
|
2. Add fields with multiple calls of ptvcursor_add()
|
|
3. Delete the ptvcursor with ptvcursor_free()
|
|
|
|
ptvcursor offers the possibility to add subtrees in the tree as well. It can be
|
|
done in very simple steps :
|
|
1. Create a new subtree with ptvcursor_push_subtree(). The old subtree is
|
|
pushed in a stack and the new subtree will be used by ptvcursor.
|
|
2. Add fields with multiple calls of ptvcursor_add(). The fields will be
|
|
added in the new subtree created at the previous step.
|
|
3. Pop the previous subtree with ptvcursor_pop_subtree(). The previous
|
|
subtree is again used by ptvcursor.
|
|
Note that at the end of the parsing of a packet you must have popped each
|
|
subtree you pushed. If it's not the case, the dissector will generate an error.
|
|
|
|
To use the ptvcursor API, include the "ptvcursor.h" file. The PGM dissector
|
|
is an example of how to use it. You don't need to look at it as a guide;
|
|
instead, the API description here should be good enough.
|
|
|
|
2.11.1 ptvcursor API.
|
|
|
|
ptvcursor_t*
|
|
ptvcursor_new(proto_tree* tree, tvbuff_t* tvb, gint offset)
|
|
This creates a new ptvcursor_t object for iterating over a tvbuff.
|
|
You must call this and use this ptvcursor_t object so you can use the
|
|
ptvcursor API.
|
|
|
|
proto_item*
|
|
ptvcursor_add(ptvcursor_t* ptvc, int hf, gint length, const guint encoding)
|
|
This will extract 'length' bytes from the tvbuff and place it in
|
|
the proto_tree as field 'hf', which is a registered header_field. The
|
|
pointer to the proto_item that is created is passed back to you. Internally,
|
|
the ptvcursor advances its cursor so the next call to ptvcursor_add
|
|
starts where this call finished. The 'encoding' parameter is relevant for
|
|
certain type of fields (See above under proto_tree_add_item()).
|
|
|
|
proto_item*
|
|
ptvcursor_add_ret_uint(ptvcursor_t* ptvc, int hf, gint length, const guint encoding, guint32 *retval);
|
|
Like ptvcursor_add, but returns uint value retrieved
|
|
|
|
proto_item*
|
|
ptvcursor_add_ret_int(ptvcursor_t* ptvc, int hf, gint length, const guint encoding, gint32 *retval);
|
|
Like ptvcursor_add, but returns int value retrieved
|
|
|
|
proto_item*
|
|
ptvcursor_add_ret_string(ptvcursor_t* ptvc, int hf, gint length, const guint encoding, wmem_allocator_t *scope, const guint8 **retval);
|
|
Like ptvcursor_add, but returns string retrieved
|
|
|
|
proto_item*
|
|
ptvcursor_add_ret_boolean(ptvcursor_t* ptvc, int hf, gint length, const guint encoding, gboolean *retval);
|
|
Like ptvcursor_add, but returns boolean value retrieved
|
|
|
|
proto_item*
|
|
ptvcursor_add_no_advance(ptvcursor_t* ptvc, int hf, gint length, const guint encoding)
|
|
Like ptvcursor_add, but does not advance the internal cursor.
|
|
|
|
void
|
|
ptvcursor_advance(ptvcursor_t* ptvc, gint length)
|
|
Advances the internal cursor without adding anything to the proto_tree.
|
|
|
|
void
|
|
ptvcursor_free(ptvcursor_t* ptvc)
|
|
Frees the memory associated with the ptvcursor. You must call this
|
|
after your dissection with the ptvcursor API is completed.
|
|
|
|
|
|
proto_tree*
|
|
ptvcursor_push_subtree(ptvcursor_t* ptvc, proto_item* it, gint ett_subtree)
|
|
Pushes the current subtree in the tree stack of the cursor, creates a new
|
|
one and sets this one as the working tree.
|
|
|
|
void
|
|
ptvcursor_pop_subtree(ptvcursor_t* ptvc);
|
|
Pops a subtree in the tree stack of the cursor
|
|
|
|
proto_tree*
|
|
ptvcursor_add_with_subtree(ptvcursor_t* ptvc, int hfindex, gint length,
|
|
const guint encoding, gint ett_subtree);
|
|
Adds an item to the tree and creates a subtree.
|
|
If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH.
|
|
In this case, at the next pop, the item length will be equal to the advancement
|
|
of the cursor since the creation of the subtree.
|
|
|
|
proto_tree*
|
|
ptvcursor_add_text_with_subtree(ptvcursor_t* ptvc, gint length,
|
|
gint ett_subtree, const char* format, ...);
|
|
Add a text node to the tree and create a subtree.
|
|
If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH.
|
|
In this case, at the next pop, the item length will be equal to the advancement
|
|
of the cursor since the creation of the subtree.
|
|
|
|
2.11.2 Miscellaneous functions.
|
|
|
|
tvbuff_t*
|
|
ptvcursor_tvbuff(ptvcursor_t* ptvc)
|
|
Returns the tvbuff associated with the ptvcursor.
|
|
|
|
gint
|
|
ptvcursor_current_offset(ptvcursor_t* ptvc)
|
|
Returns the current offset.
|
|
|
|
proto_tree*
|
|
ptvcursor_tree(ptvcursor_t* ptvc)
|
|
Returns the proto_tree associated with the ptvcursor.
|
|
|
|
void
|
|
ptvcursor_set_tree(ptvcursor_t* ptvc, proto_tree *tree)
|
|
Sets a new proto_tree for the ptvcursor.
|
|
|
|
proto_tree*
|
|
ptvcursor_set_subtree(ptvcursor_t* ptvc, proto_item* it, gint ett_subtree);
|
|
Creates a subtree and adds it to the cursor as the working tree but does
|
|
not save the old working tree.
|
|
|
|
2.12 Optimizations
|
|
|
|
A protocol dissector may be called in 2 different ways - with, or
|
|
without a non-null "tree" argument.
|
|
|
|
If the proto_tree argument is null, Wireshark does not need to use
|
|
the protocol tree information from your dissector, and therefore is
|
|
passing the dissector a null "tree" argument so that it doesn't
|
|
need to do work necessary to build the protocol tree.
|
|
|
|
In the interest of speed, if "tree" is NULL, avoid building a
|
|
protocol tree and adding stuff to it, or even looking at any packet
|
|
data needed only if you're building the protocol tree, if possible.
|
|
|
|
Note, however, that you must fill in column information, create
|
|
conversations, reassemble packets, do calls to "expert" functions,
|
|
build any other persistent state needed for dissection, and call
|
|
subdissectors regardless of whether "tree" is NULL or not.
|
|
|
|
This might be inconvenient to do without doing most of the
|
|
dissection work; the routines for adding items to the protocol tree
|
|
can be passed a null protocol tree pointer, in which case they'll
|
|
return a null item pointer, and "proto_item_add_subtree()" returns
|
|
a null tree pointer if passed a null item pointer, so, if you're
|
|
careful not to dereference any null tree or item pointers, you can
|
|
accomplish this by doing all the dissection work. This might not
|
|
be as efficient as skipping that work if you're not building a
|
|
protocol tree, but if the code would have a lot of tests whether
|
|
"tree" is null if you skipped that work, you might still be better
|
|
off just doing all that work regardless of whether "tree" is null
|
|
or not.
|
|
|
|
Note also that there is no guarantee, the first time the dissector is
|
|
called, whether "tree" will be null or not; your dissector must work
|
|
correctly, building or updating whatever state information is
|
|
necessary, in either case.
|
|
|
|
/*
|
|
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
|
*
|
|
* Local variables:
|
|
* c-basic-offset: 4
|
|
* tab-width: 8
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
*/
|