udp_dissect_pdus follow-up

Add heuristic support
Better documentation

Change-Id: I236c1f4d3613aa58d608aee0e5edc40c3b158d25
Reviewed-on: https://code.wireshark.org/review/10120
Petri-Dish: Michael Mann <mmann78@netscape.net>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Michael Mann <mmann78@netscape.net>
This commit is contained in:
Michael Mann 2015-08-18 23:14:09 -04:00
parent 69e61db3ae
commit ad1b785fe8
5 changed files with 153 additions and 51 deletions

View File

@ -3147,7 +3147,92 @@ 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 ptvcursors.
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 = new_create_dissector_handle(dissect_dnp3_tcp, proto_dnp3);
dnp3_udp_handle = new_create_dissector_handle(dissect_dnp3_udp, proto_dnp3);
dissector_add_uint("tcp.port", TCP_PORT_DNP, dnp3_tcp_handle);
dissector_add_uint("udp.port", UDP_PORT_DNP, dnp3_udp_handle);
Both dissect_dnp3_tcp and dissect_dnp3_udp call tcp_dissect_pdus and
udp_dissect_pdus respectively, with a reference to the same callbacks which
are called to handle PDU data.
static int
dissect_dnp3_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
return udp_dissect_pdus(tvb, pinfo, tree, DNP_HDR_LEN, dnp3_udp_check_header,
get_dnp3_message_len, dissect_dnp3_message, data);
}
static int
dissect_dnp3_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
if (!check_dnp3_header(tvb, FALSE)) {
return 0;
}
tcp_dissect_pdus(tvb, pinfo, tree, TRUE, DNP_HDR_LEN,
get_dnp3_message_len, dissect_dnp3_message, data);
return tvb_captured_length(tvb);
}
(udp_dissect_pdus has an option of a heuristic check function within it while
tcp_dissect_pdus does not, so it's done outside)
The arguments to udp_dissect_pdus are:
the tvbuff pointer, packet_info pointer, and proto_tree pointer
passed to the dissector;
the number of bytes of PDU data required to determine the length
of the PDU;
an optional routine (passing NULL is okay) that takes as arguments a
packet_info pointer, a tvbuff pointer and an offset value representing the
offset into the tvbuff at which a PDU begins, and a void pointer for user
data, and should return TRUE if the packet belongs to the dissector.
The routine must not throw exceptions (it is guaranteed that the
number of bytes specified by the previous argument to
udp_dissect_pdus is available, but more data might not be available,
so don't refer to any data past that);
a routine that takes as arguments a packet_info pointer, a tvbuff
pointer and an offset value representing the offset into the tvbuff
at which a PDU begins, and a void pointer for user data, and should
return the total length of the PDU in bytes. If return value is 0,
it's treated the same as a failed heuristic.
The routine must not throw exceptions (it is guaranteed that the
number of bytes specified by the previous argument to
tcp_dissect_pdus is available, but more data might not be available,
so don't refer to any data past that);
a new_dissector_t routine to dissect the pdu that's passed a tvbuff
pointer, packet_info pointer, proto_tree pointer and a void pointer for
user data, with the tvbuff containing a possibly-reassembled PDU. (The
"reported_length" of the tvbuff will be the length of the PDU);
a void pointer to user data that is passed to the length-determining
routine, and the dissector routine referenced in the previous parameter.
2.9 ptvcursors.
The ptvcursor API allows a simpler approach to writing dissectors for
simple protocols. The ptvcursor API works best for protocols whose fields
@ -3180,7 +3265,7 @@ 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.8.1 ptvcursor API.
2.9.1 ptvcursor API.
ptvcursor_t*
ptvcursor_new(proto_tree* tree, tvbuff_t* tvb, gint offset)
@ -3236,7 +3321,7 @@ 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.8.2 Miscellaneous functions.
2.9.2 Miscellaneous functions.
tvbuff_t*
ptvcursor_tvbuff(ptvcursor_t* ptvc)
@ -3259,7 +3344,7 @@ ptvcursor_set_subtree(ptvcursor_t* ptvc, proto_item* it, gint ett_subtree);
Creates a subtree and adds it to the cursor as the working tree but does
not save the old working tree.
2.9 Optimizations
2.10 Optimizations
A protocol dissector may be called in 2 different ways - with, or
without a non-null "tree" argument.

View File

@ -103,7 +103,7 @@ Heuristic Code Example
----------------------
You can find a lot of code examples in the Wireshark sources, e.g.:
grep -l heur_dissector_add epan/dissectors/*.c
returns 163 files (November 2014).
returns 177 files (October 2015).
For the above example criteria, the following code example might do the work
(combine this with the dissector skeleton in README.developer):
@ -118,7 +118,7 @@ static dissector_handle_t PROTOABBREV_pdu_handle;
/* Heuristics test */
static gboolean
test_PROTOABBREV(tvbuff_t *tvb)
test_PROTOABBREV(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_)
{
/* 0) Verify needed bytes available in tvb so tvb_get...() doesn't cause exception.
if (tvb_captured_length(tvb) < 5)
@ -172,7 +172,7 @@ dissect_PROTOABBREV_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, voi
static gboolean
dissect_PROTOABBREV_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
if (!test_PROTOABBREV(tvb))
if (!test_PROTOABBREV(pinfo, tvb, 0, data))
return FALSE;
/* specify that dissect_PROTOABBREV is to be called directly from now on for
@ -190,26 +190,21 @@ dissect_PROTOABBREV_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree
return (TRUE);
}
static int
dissect_PROTOABBREV_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
udp_dissect_pdus(tvb, pinfo, tree, TRUE, 5, NULL,
get_PROTOABBREV_len, dissect_PROTOABBREV_pdu, data);
return tvb_reported_length(tvb);
}
static gboolean
dissect_PROTOABBREV_heur_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
...
If (!test_PROTOABBREV(tvb))
return FALSE;
/* specify that dissect_PROTOABBREV is to be called directly from now on for
* packets for this "connection" ... but only do this if your heuristic sits directly
* on top of (was called by) a dissector which established a conversation for the
* protocol "port type". In other words: only directly over TCP, UDP, DCCP, ...
* otherwise you'll be overriding the dissector that called your heuristic dissector.
*/
conversation = find_or_create_conversation(pinfo);
conversation_set_dissector(conversation, PROTOABBREV_pdu_handle);
/* and do the dissection */
dissect_PROTOABBREV_pdu(tvb, pinfo, tree, data);
return (TRUE);
return (udp_dissect_pdus(tvb, pinfo, tree, TRUE, 5, test_PROTOABBREV,
get_PROTOABBREV_len, dissect_PROTOABBREV_pdu, data) != 0);
}
void
@ -221,9 +216,9 @@ proto_reg_handoff_PROTOABBREV(void)
proto_PROTOABBREV);
/* register as heuristic dissector for both TCP and UDP */
heur_dissector_add("tcp", dissect_PROTOABBREV_tcp_heur, "PROTOABBREV over TCP",
heur_dissector_add("tcp", dissect_PROTOABBREV_heur_tcp, "PROTOABBREV over TCP",
"PROTOABBREV_tcp", proto_PROTOABBREV, HEURISTIC_ENABLE);
heur_dissector_add("udp", dissect_PROTOABBREV_udp_heur, "PROTOABBREV over UDP",
heur_dissector_add("udp", dissect_PROTOABBREV_heur_udp, "PROTOABBREV over UDP",
"PROTOABBREV_udp", proto_PROTOABBREV, HEURISTIC_ENABLE);
#ifdef OPTIONAL

View File

@ -3521,30 +3521,31 @@ dissect_dnp3_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
return TRUE;
}
static gboolean
dnp3_udp_check_header(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_)
{
return check_dnp3_header(tvb, FALSE);
}
static gboolean
dnp3_udp_check_header_heur(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_)
{
return check_dnp3_header(tvb, TRUE);
}
static int
dissect_dnp3_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
if (!check_dnp3_header(tvb, FALSE)) {
return 0;
}
udp_dissect_pdus(tvb, pinfo, tree, DNP_HDR_LEN,
return udp_dissect_pdus(tvb, pinfo, tree, DNP_HDR_LEN, dnp3_udp_check_header,
get_dnp3_message_len, dissect_dnp3_message, data);
return tvb_captured_length(tvb);
}
static gboolean
dissect_dnp3_udp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
if (!check_dnp3_header(tvb, FALSE)) {
return FALSE;
}
return (udp_dissect_pdus(tvb, pinfo, tree, DNP_HDR_LEN, dnp3_udp_check_header_heur,
get_dnp3_message_len, dissect_dnp3_message, data) != 0);
udp_dissect_pdus(tvb, pinfo, tree, DNP_HDR_LEN,
get_dnp3_message_len, dissect_dnp3_message, data);
return TRUE;
}
static void

View File

@ -549,9 +549,10 @@ decode_udp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
call_dissector(data_handle,next_tvb, pinfo, tree);
}
void
int
udp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
guint fixed_len, guint (*get_pdu_len)(packet_info *, tvbuff_t *, int, void*),
guint fixed_len, gboolean (*heuristic_check)(packet_info *, tvbuff_t *, int, void*),
guint (*get_pdu_len)(packet_info *, tvbuff_t *, int, void*),
new_dissector_t dissect_pdu, void* dissector_data)
{
volatile int offset = 0;
@ -579,10 +580,26 @@ udp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
*/
captured_length_remaining = tvb_ensure_captured_length_remaining(tvb, offset);
/*
* If there is a heuristic function, check it
*/
if ((heuristic_check != NULL) &&
((*heuristic_check)(pinfo, tvb, offset, dissector_data) == FALSE)) {
return offset;
}
/*
* Get the length of the PDU.
*/
plen = (*get_pdu_len)(pinfo, tvb, offset, dissector_data);
if (plen == 0) {
/*
* Either protocol has variable length (which isn't supposed by UDP)
* or packet doesn't belong to protocol
*/
return offset;
}
if (plen < fixed_len) {
/*
* Either:
@ -600,7 +617,7 @@ udp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
* Report this as a bounds error.
*/
show_reported_bounds_error(tvb, pinfo, tree);
return;
return offset;
}
curr_layer_num = pinfo->curr_layer_num-1;
@ -668,6 +685,8 @@ udp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
if (offset <= offset_before)
break;
}
return offset;
}
static void

View File

@ -115,20 +115,22 @@ WS_DLL_PUBLIC struct udp_analysis *get_udp_conversation_data(conversation_t *,
* fixed-length chunk of data that contains enough information
* to determine the length of the PDU, followed by rest of the PDU.
*
* The first three arguments are the arguments passed to the dissector
* that calls this routine.
*
* "fixed_len" is the length of the fixed-length part of the PDU.
*
* "get_pdu_len()" is a routine called to get the length of the PDU from
* @param tvb the tvbuff with the (remaining) packet data passed to dissector
* @param pinfo the packet info of this packet (additional info) passed to dissector
* @param tree the protocol tree to be build or NULL passed to dissector
* @param fixed_len is the length of the fixed-length part of the PDU.
* @param heuristic_check is the optional routine called to see if dissection
* should be done; it's passed "pinfo", "tvb", "offset" and "dissector_data".
* @param get_pdu_len is a routine called to get the length of the PDU from
* the fixed-length part of the PDU; it's passed "pinfo", "tvb", "offset" and
* "dissector_data".
*
* "dissect_pdu()" is the routine to dissect a PDU.
* @param dissect_pdu the sub-dissector to be called
* @param dissector_data parameter to pass to subdissector
*/
WS_DLL_PUBLIC void
WS_DLL_PUBLIC int
udp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
guint fixed_len, guint (*get_pdu_len)(packet_info *, tvbuff_t *, int, void*),
guint fixed_len, gboolean (*heuristic_check)(packet_info *, tvbuff_t *, int, void*),
guint (*get_pdu_len)(packet_info *, tvbuff_t *, int, void*),
new_dissector_t dissect_pdu, void* dissector_data);
#ifdef __cplusplus