diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
commit | e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch) | |
tree | 68cb5ef9081156392f1dd62a00c6ccc1451b93df /doc/README.dissector | |
parent | Initial commit. (diff) | |
download | wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip |
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'doc/README.dissector')
-rw-r--r-- | doc/README.dissector | 3723 |
1 files changed, 3723 insertions, 0 deletions
diff --git a/doc/README.dissector b/doc/README.dissector new file mode 100644 index 00000000..464bba49 --- /dev/null +++ b/doc/README.dissector @@ -0,0 +1,3723 @@ +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 discuss on +the developer mailing list or upload merge requests to gitlab. +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: + +- doc/README.heuristic - what are heuristic dissectors and how to write them +- doc/README.plugins - how to "pluginize" a dissector +- doc/README.request_response_tracking - how to track req./resp. times and such +- doc/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_IEEE_11073_SFLOAT, FT_IEEE_11073_FLOAT, + FT_FLOAT, FT_DOUBLE, FT_ABSOLUTE_TIME, FT_RELATIVE_TIME, + FT_STRING, FT_STRINGZ, FT_STRINGZPAD, FT_STRINGZTRUNC, + FT_UINT_STRING, FT_ETHER, FT_BYTES, FT_UINT_BYTES, FT_IPv4, + FT_IPv6, FT_IPXNET, FT_FRAMENUM, FT_PROTOCOL, FT_EUI64, FT_GUID, + FT_OID, FT_REL_OID, FT_AX25, FT_VINES, FT_SYSTEM_ID, 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: + +uint8_t tvb_get_bits8(tvbuff_t *tvb, int bit_offset, const int no_of_bits); +uint16_t tvb_get_bits16(tvbuff_t *tvb, unsigned bit_offset, const int no_of_bits, const unsigned encoding); +uint32_t tvb_get_bits32(tvbuff_t *tvb, unsigned bit_offset, const int no_of_bits, const unsigned encoding); +uint64_t tvb_get_bits64(tvbuff_t *tvb, unsigned bit_offset, const int no_of_bits, const unsigned encoding); + +Single-byte accessors for 8-bit unsigned integers (uint8_t) and 8-bit +signed integers (int8_t): + +uint8_t tvb_get_guint8(tvbuff_t *tvb, const int offset); +int8_t tvb_get_gint8(tvbuff_t *tvb, const int offset); + +Network-to-host-order accessors: + +16-bit unsigned (uint16_t) and signed (int16_t) integers: + +uint16_t tvb_get_ntohs(tvbuff_t *tvb, const int offset); +int16_t tvb_get_ntohis(tvbuff_t *tvb, const int offset); + +24-bit unsigned and signed integers: + +uint32_t tvb_get_ntoh24(tvbuff_t *tvb, const int offset); +int32_t tvb_get_ntohi24(tvbuff_t *tvb, const int offset); + +32-bit unsigned (uint32_t) and signed (int32_t) integers: + +uint32_t tvb_get_ntohl(tvbuff_t *tvb, const int offset); +int32_t tvb_get_ntohil(tvbuff_t *tvb, const int offset); + +40-bit unsigned and signed integers: + +uint64_t tvb_get_ntoh40(tvbuff_t *tvb, const int offset); +int64_t tvb_get_ntohi40(tvbuff_t *tvb, const int offset); + +48-bit unsigned and signed integers: + +uint64_t tvb_get_ntoh48(tvbuff_t *tvb, const int offset); +int64_t tvb_get_ntohi48(tvbuff_t *tvb, const int offset); + +56-bit unsigned and signed integers: + +uint64_t tvb_get_ntoh56(tvbuff_t *tvb, const int offset); +int64_t tvb_get_ntohi56(tvbuff_t *tvb, const int offset); + +64-bit unsigned (uint64_t) and signed (int64_t) integers: + +uint64_t tvb_get_ntoh64(tvbuff_t *tvb, const int offset); +int64_t tvb_get_ntohi64(tvbuff_t *tvb, const int offset); + +Single-precision and double-precision IEEE floating-point numbers: + +float tvb_get_ntohieee_float(tvbuff_t *tvb, const int offset); +double tvb_get_ntohieee_double(tvbuff_t *tvb, const int offset); + +Little-Endian-to-host-order accessors: + +16-bit unsigned (uint16_t) and signed (int16_t) integers: + +uint16_t tvb_get_letohs(tvbuff_t *tvb, const int offset); +int16_t tvb_get_letohis(tvbuff_t *tvb, const int offset); + +24-bit unsigned and signed integers: + +uint32_t tvb_get_letoh24(tvbuff_t *tvb, const int offset); +int32_t tvb_get_letohi24(tvbuff_t *tvb, const int offset); + +32-bit unsigned (uint32_t) and signed (int32_t) integers: + +uint32_t tvb_get_letohl(tvbuff_t *tvb, const int offset); +int32_t tvb_get_letohil(tvbuff_t *tvb, const int offset); + +40-bit unsigned and signed integers: + +uint64_t tvb_get_letoh40(tvbuff_t *tvb, const int offset); +int64_t tvb_get_letohi40(tvbuff_t *tvb, const int offset); + +48-bit unsigned and signed integers: + +uint64_t tvb_get_letoh48(tvbuff_t *tvb, const int offset); +int64_t tvb_get_letohi48(tvbuff_t *tvb, const int offset); + +56-bit unsigned and signed integers: + +uint64_t tvb_get_letoh56(tvbuff_t *tvb, const int offset); +int64_t tvb_get_letohi56(tvbuff_t *tvb, const int offset); + +64-bit unsigned (uint64_t) and signed (int64_t) integers: + +uint64_t tvb_get_letoh64(tvbuff_t *tvb, const int offset); +int64_t tvb_get_letohi64(tvbuff_t *tvb, const int 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 unsigned for 32-bit +and smaller types, and int64_t or uint64_t 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: + +float tvb_get_letohieee_float(tvbuff_t *tvb, const int offset); +double tvb_get_letohieee_double(tvbuff_t *tvb, const int offset); + +Encoding-to_host-order accessors: + +16-bit unsigned (uint16_t) and signed (int16_t) integers: + +uint16_t tvb_get_guint16(tvbuff_t *tvb, const int offset, const unsigned encoding); +int16_t tvb_get_gint16(tvbuff_t *tvb, const int offset, const unsigned encoding); + +24-bit unsigned and signed integers: + +uint32_t tvb_get_guint24(tvbuff_t *tvb, const int offset, const unsigned encoding); +int32_t tvb_get_gint24(tvbuff_t *tvb, const int offset, const unsigned encoding); + +32-bit unsigned (uint32_t) and signed (int32_t) integers: + +uint32_t tvb_get_guint32(tvbuff_t *tvb, const int offset, const unsigned encoding); +int32_t tvb_get_gint32(tvbuff_t *tvb, const int offset, const unsigned encoding); + +40-bit unsigned and signed integers: + +uint64_t tvb_get_guint40(tvbuff_t *tvb, const int offset, const unsigned encoding); +int64_t tvb_get_gint40(tvbuff_t *tvb, const int offset, const unsigned encoding); + +48-bit unsigned and signed integers: + +uint64_t tvb_get_guint48(tvbuff_t *tvb, const int offset, const unsigned encoding); +int64_t tvb_get_gint48(tvbuff_t *tvb, const int offset, const unsigned encoding); + +56-bit unsigned and signed integers: + +uint64_t tvb_get_guint56(tvbuff_t *tvb, const int offset, const unsigned encoding); +int64_t tvb_get_gint56(tvbuff_t *tvb, const int offset, const unsigned encoding); + +64-bit unsigned (uint64_t) and signed (int64_t) integers: + +uint64_t tvb_get_guint64(tvbuff_t *tvb, const int offset, const unsigned encoding); +int64_t tvb_get_gint64(tvbuff_t *tvb, const int offset, const unsigned encoding); + +Single-precision and double-precision IEEE floating-point numbers: + +float tvb_get_ieee_float(tvbuff_t *tvb, const int offset, const unsigned encoding); +double tvb_get_ieee_double(tvbuff_t *tvb, const int offset, const unsigned 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: + +uint32_t tvb_get_ipv4(tvbuff_t *tvb, const int offset); +void tvb_get_ipv6(tvbuff_t *tvb, const int 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. + +char *tvb_ip_to_str(wmem_allocator_t *scope, tvbuff_t *tvb, const int offset) +char *tvb_ip6_to_str(wmem_allocator_t *scope, tvbuff_t *tvb, const int 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 int offset, e_guid_t *guid); +void tvb_get_letohguid(tvbuff_t *tvb, const int offset, e_guid_t *guid); +void tvb_get_guid(tvbuff_t *tvb, const int offset, e_guid_t *guid, const unsigned encoding); + +String accessors: + +uint8_t *tvb_get_string_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const int offset, const int length, const unsigned 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). + +uint8_t *tvb_get_stringz_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const int offset, int *lengthp, const unsigned 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). + +int tvb_get_nstringz(tvbuff_t *tvb, const int offset, const unsigned bufsize, uint8_t* buffer); +int tvb_get_nstringz0(tvbuff_t *tvb, const int offset, const unsigned bufsize, uint8_t* 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. + +char *tvb_get_ts_23_038_7bits_string(wmem_allocator_t *scope, tvbuff_t *tvb, + const int bit_offset, int 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: + +char *tvb_bytes_to_str(wmem_allocator_t *scope, tvbuff_t *tvb, const int offset, const int 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. + +char *tvb_bytes_to_str_punct(wmem_allocator_t *scope, tvbuff_t *tvb, + const int offset, const int len, const char 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 int offset, const int length, + const unsigned encoding, GByteArray* bytes, int *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. + +char *tvb_bcd_dig_to_wmem_packet_str(tvbuff_t *tvb, const int offset, const int len, dgt_set_t *dgt, bool 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 int 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 int 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 uint8_t* tvb_get_ptr(tvbuff_t *tvb, const int offset, const int 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: + + unsigned tvb_captured_length(const tvbuff_t *tvb); + +Get reported length of buffer: + + unsigned 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; + uint64_t 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_IEEE_11073_SFLOAT A 16-bit floating point number, consisting + of an 4-bit exponent and 12-bit mantissa. + FT_IEEE_11073_FLOAT A 32-bit floating point number, consisting + of an 8-bit exponent and 24-bit mantissa. + 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(char *, uint32_t); + +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(char *, uint64_t); + +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 { + uint32_t value; + char *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 uint64_t (instead of uint32_t). 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 uint64_t (instead of uint32_t). + +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.) + +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 +'&unitname'. Furthermore, the 'display' field must be ORed +with 'BASE_UNIT_STRING' (e.g. BASE_DEC|BASE_UNIT_STRING). + +There are several "common" unit name structures already defined in +epan/unit_strings.h, e.g. 'units_second_seconds'. Dissector authors may choose +to add the unit name structure there rather than locally in a dissector. + +-- 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 { + uint32_t value_min; + uint32_t value_max; + const char *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, FT_DOUBLE, FT_IEEE_11073_SFLOAT and FT_IEEE_11073_FLOAT +- 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 int ett_eg = -1; + static int ett_field_a = -1; + + static int *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 unsigned offset, + const int hf_checksum, const int hf_checksum_status, + struct expert_field* bad_checksum_expert, packet_info *pinfo, + uint32_t computed_checksum, const unsigned encoding, const unsigned 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 UTF-16, UCS-2, and UCS-4, the byte order of the encoding +must be specified, and optionally ENC_BOM can also be indicated to detect +an initial BYTE ORDER MARK (the specified value is used if the field does +not begin with a BOM.) For counted UTF-16, UCS-2, and UCS-4 strings, the +byte order of the count and the characters in the string must be the same, +unless a BOM overrides the value for the characters. 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 (aka UTF-32) + 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_EBCDIC_CP500 - EBCDIC code page 500 + 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_IMF_DATE_TIME. 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 int 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 char* + val_to_str(uint32_t 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 char* + try_val_to_str(uint32_t 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 char * + val64_to_str(const uint64_t val, const val64_string *vs, const char *fmt) + + const char * + val64_to_str_const(const uint64_t val, const val64_string *vs, const char *unknown_str); + + const char * + try_val64_to_str(const uint64_t val, const val64_string *vs); + + const char * + try_val64_to_str_idx(const uint64_t val, const val64_string *vs, int *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 uint32_t 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 char *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 uint32_t uint_val, tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree); + + int dissector_try_uint_new(dissector_table_t sub_dissectors, + const uint32_t uint_val, tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree, const bool add_proto_name, void *data); + + int dissector_try_string(dissector_table_t sub_dissectors, const char *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 ctype 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(uint32_t setup_frame, address *addr1, + address *addr2, conversation_type ctype, uint32_t port1, uint32_t port2, + unsigned options); + +Where: + uint32_t setup_frame = The lowest numbered frame for this conversation + address* addr1 = first data packet address + address* addr2 = second data packet address + conversation_type ctype = conversation type, defined in conversation.h + uint32_t port1 = first data packet port + uint32_t port2 = second data packet port + unsigned 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_full(const uint32_t setup_frame, + conversation_element_t *elements); + +Where: + uint32_t 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(uint32_t frame_num, address *addr_a, + address *addr_b, conversation_type ctype, uint32_t port_a, uint32_t port_b, + unsigned options); + +Where: + uint32_t frame_num = a frame number to match + address* addr_a = first address + address* addr_b = second address + conversation_type ctype = conversation type + uint32_t port_a = first data packet port + uint32_t port_b = second data packet port + unsigned 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(uint32_t frame_num, + conversation_element_t *elements); + +Where: + uint32_t 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 unsigned options); + +Where: + packet_info *pinfo = the packet_info structure + const unsigned 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_addr_port_endpoints if pinfo->use_conv_addr_port_endpoints 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, + conversation_pt_to_conversation_type(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, + conversation_pt_to_conversation_type(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, + conversation_pt_to_conversation_type(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, + conversation_pt_to_conversation_type(pinfo->ptype), + pinfo->srcport, pinfo->destport, 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, +register_dissector, or register_dissector_with_description. + +register_dissector_with_description takes as arguments a string giving a name +for the dissector, a string with a human-readable summary of the dissector, 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 +as returned by proto_register_protocol. + +create_dissector_handle takes as arguments a pointer to the dissector +function and a protocol ID as returned by proto_register_protocol. +It is recommended to use one of the above two functions instead of this one, +since they allow the dissector to be referenced by name from the command line, +by other dissectors via calls to find_dissector, etc. + +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, conversation_type, + 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 = register_dissector("PROTOABBREV", 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, uint32_t 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, conversation_type, + 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, uint32_t key, void *proto_data) +void * +p_get_proto_data(wmem_allocator_t *scope, packet_info *pinfo, int proto, uint32_t 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, unsigned base, unsigned *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, bool *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, int *var, + const enum_val_t *enumvals, bool 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, + bool 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, + uint32_t 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_udp_handle = register_dissector_with_description("hart_ip", "HART-IP over UDP", dissect_hartip_udp, proto_hartip); + hartip_tcp_handle = register_dissector_with_description("hart_ip.tcp", "HART-IP over TCP", dissect_hartip_tcp, proto_hartip); + + dissector_add_uint_with_preference("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 bool 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_) +{ + unsigned offset = 0; + while(offset < tvb_reported_length(tvb)) { + int available = tvb_reported_length_remaining(tvb, offset); + int 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 += (unsigned)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 = register_dissector("dnp3.tcp", dissect_dnp3_tcp, proto_dnp3); + dnp3_udp_handle = register_dissector("dnp3.udp", dissect_dnp3_udp, proto_dnp3); + + dissector_add_uint_with_preference("tcp.port", TCP_PORT_DNP, dnp3_tcp_handle); + dissector_add_uint_with_preference("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 its +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, int 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, int length, const unsigned 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, int length, const unsigned encoding, uint32_t *retval); + Like ptvcursor_add, but returns uint value retrieved + +proto_item* +ptvcursor_add_ret_int(ptvcursor_t* ptvc, int hf, int length, const unsigned encoding, int32_t *retval); + Like ptvcursor_add, but returns int value retrieved + +proto_item* +ptvcursor_add_ret_string(ptvcursor_t* ptvc, int hf, int length, const unsigned encoding, wmem_allocator_t *scope, const uint8_t **retval); + Like ptvcursor_add, but returns string retrieved + +proto_item* +ptvcursor_add_ret_boolean(ptvcursor_t* ptvc, int hf, int length, const unsigned encoding, bool *retval); + Like ptvcursor_add, but returns boolean value retrieved + +proto_item* +ptvcursor_add_no_advance(ptvcursor_t* ptvc, int hf, int length, const unsigned encoding) + Like ptvcursor_add, but does not advance the internal cursor. + +void +ptvcursor_advance(ptvcursor_t* ptvc, int 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, int 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, int length, + const unsigned encoding, int 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, int length, + int 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. + +int +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, int 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: + */ |