forked from osmocom/wireshark
![Michael Mann](/assets/img/avatar_default.png)
This patch introduces new APIs to allow dissectors to have a preference for a (TCP) port, but the underlying data is actually part of Decode As functionality. For now the APIs are intentionally separate from the regular APIs that register a dissector within a dissector table. It may be possible to eventually combine the two so that all dissectors that register with a dissector table have an opportunity to "automatically" have a preference to adjust the "table value" through the preferences dialog. The tcp.port dissector table was used as the guinea pig. This will eventually be expanded to other dissector tables as well (most notably UDP ports). Some dissectors that "shared" a TCP/UDP port preference were also converted. It also removed the need for some preference callback functions (mostly when the callback function was the proto_reg_handoff function) so there is cleanup around that. Dissectors that has a port preference whose default was 0 were switched to using the dissector_add_for_decode_as_with_preference API rather than dissector_add_uint_with_preference Also added comments for TCP ports used that aren't IANA registered. Change-Id: I99604f95d426ad345f4b494598d94178b886eb67 Reviewed-on: https://code.wireshark.org/review/17724 Reviewed-by: Michael Mann <mmann78@netscape.net>
654 lines
22 KiB
C
654 lines
22 KiB
C
/* packet-tcpros.c
|
|
* Routines for Robot Operating System TCP protocol (TCPROS)
|
|
* Copyright 2015, Guillaume Autran (see AUTHORS file)
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <epan/packet.h>
|
|
#include <epan/etypes.h>
|
|
#include <epan/prefs.h>
|
|
|
|
#include "packet-tcp.h"
|
|
|
|
|
|
#define SIZE_OF_LENGTH_FIELD 4
|
|
#define SIZE_OF_LENGTH_STAMP (4 + 4)
|
|
|
|
void proto_register_tcpros(void);
|
|
void proto_reg_handoff_tcpros(void);
|
|
|
|
|
|
static int proto_tcpros = -1;
|
|
static dissector_handle_t tcpros_handle;
|
|
|
|
/** desegmentation of TCPROS over TCP */
|
|
static gboolean tcpros_desegment = TRUE;
|
|
|
|
|
|
static int hf_tcpros_connection_header = -1;
|
|
static int hf_tcpros_connection_header_length = -1;
|
|
static int hf_tcpros_connection_header_content = -1;
|
|
static int hf_tcpros_connection_header_field = -1;
|
|
static int hf_tcpros_connection_header_field_length = -1;
|
|
static int hf_tcpros_connection_header_field_data = -1;
|
|
static int hf_tcpros_connection_header_field_name = -1;
|
|
static int hf_tcpros_connection_header_field_value = -1;
|
|
static int hf_tcpros_clock = -1;
|
|
static int hf_tcpros_clock_length = -1;
|
|
static int hf_tcpros_message = -1;
|
|
static int hf_tcpros_message_length = -1;
|
|
static int hf_tcpros_message_body = -1;
|
|
static int hf_tcpros_message_header = -1;
|
|
static int hf_tcpros_message_header_seq = -1;
|
|
static int hf_tcpros_message_header_stamp = -1;
|
|
static int hf_tcpros_message_header_stamp_sec = -1;
|
|
static int hf_tcpros_message_header_stamp_nsec = -1;
|
|
static int hf_tcpros_message_header_frame = -1;
|
|
static int hf_tcpros_message_header_frame_length = -1;
|
|
static int hf_tcpros_message_header_frame_value = -1;
|
|
static int hf_tcpros_message_payload = -1;
|
|
|
|
static gint ett_tcpros = -1;
|
|
|
|
/**
|
|
* This is the ROS connection header dissector. The general packet format is described
|
|
* here: http://wiki.ros.org/ROS/TCPROS
|
|
* In short, a connection header looks like such: '4-byte length + [4-byte field length + "field=value" ]*'
|
|
*/
|
|
static gint
|
|
dissect_ros_connection_header_field(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, gint offset)
|
|
{
|
|
proto_item *ti;
|
|
proto_tree *field_tree;
|
|
|
|
guint32 fLen = 0;
|
|
gint sep, ret = 0;
|
|
|
|
/** Do we have enough for a length field? (ie: 4 bytes) */
|
|
if( tvb_reported_length_remaining(tvb, offset) > SIZE_OF_LENGTH_FIELD ) {
|
|
/** Get the length of the next field */
|
|
fLen = tvb_get_letohl(tvb, offset);
|
|
|
|
/** Display the field as a utf-8 string */
|
|
ti = proto_tree_add_item(tree, hf_tcpros_connection_header_field, tvb, offset, SIZE_OF_LENGTH_FIELD, ENC_UTF_8|ENC_LITTLE_ENDIAN);
|
|
field_tree = proto_item_add_subtree(ti, ett_tcpros);
|
|
|
|
|
|
proto_tree_add_item(field_tree, hf_tcpros_connection_header_field_length, tvb, offset, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
|
|
offset += SIZE_OF_LENGTH_FIELD;
|
|
ti = proto_tree_add_item(field_tree, hf_tcpros_connection_header_field_data, tvb, offset, fLen, ENC_UTF_8|ENC_NA);
|
|
|
|
/** Look for the '=' separator */
|
|
sep = (tvb_find_guint8(tvb, offset, fLen, '=') - offset);
|
|
|
|
/** If we find a separator, then split field name and value */
|
|
if( sep > 0 ) {
|
|
const guint8* field;
|
|
field_tree = proto_item_add_subtree(ti, ett_tcpros);
|
|
proto_tree_add_item_ret_string(field_tree, hf_tcpros_connection_header_field_name, tvb, offset, sep, ENC_UTF_8|ENC_NA, wmem_packet_scope(), &field);
|
|
proto_tree_add_item(field_tree, hf_tcpros_connection_header_field_value, tvb, offset+sep+1, fLen - sep - 1, ENC_UTF_8|ENC_NA);
|
|
|
|
col_append_str(pinfo->cinfo, COL_INFO, field);
|
|
}
|
|
ret = fLen + SIZE_OF_LENGTH_FIELD;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gint
|
|
dissect_ros_connection_header(tvbuff_t *tvb, proto_tree *root_tree, packet_info *pinfo, gint offset)
|
|
{
|
|
proto_item *ti;
|
|
proto_tree *sub_tree;
|
|
|
|
gint consumed_len = 0;
|
|
guint32 header_len = tvb_get_letohl(tvb, offset);
|
|
|
|
col_append_str(pinfo->cinfo, COL_INFO, "[ROS Conn] Metadata: [");
|
|
|
|
/** We got a connection header */
|
|
ti = proto_tree_add_item(root_tree, hf_tcpros_connection_header, tvb, offset, SIZE_OF_LENGTH_FIELD, ENC_NA|ENC_LITTLE_ENDIAN);
|
|
sub_tree = proto_item_add_subtree(ti, ett_tcpros);
|
|
|
|
proto_tree_add_item(sub_tree, hf_tcpros_connection_header_length, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
|
|
consumed_len += SIZE_OF_LENGTH_FIELD;
|
|
|
|
ti = proto_tree_add_item(sub_tree, hf_tcpros_connection_header_content, tvb, offset + consumed_len, header_len, ENC_NA);
|
|
sub_tree = proto_item_add_subtree(ti, ett_tcpros);
|
|
|
|
header_len += SIZE_OF_LENGTH_FIELD;
|
|
|
|
while( consumed_len < (gint)header_len ) {
|
|
gint len = dissect_ros_connection_header_field(tvb, sub_tree, pinfo, offset + consumed_len);
|
|
consumed_len += len;
|
|
if( len == 0 ) {
|
|
break;
|
|
}
|
|
if( consumed_len < (gint)header_len ) {
|
|
col_append_str(pinfo->cinfo, COL_INFO, ",");
|
|
}
|
|
}
|
|
col_append_str(pinfo->cinfo, COL_INFO, "]");
|
|
|
|
return consumed_len;
|
|
}
|
|
|
|
|
|
static gint
|
|
dissect_ros_message_header_stamp(tvbuff_t *tvb, proto_tree *root_tree, packet_info *pinfo, gint offset)
|
|
{
|
|
proto_item *ti;
|
|
proto_tree *sub_tree;
|
|
|
|
gint consumed_len = 0;
|
|
guint32 sec, nsec;
|
|
|
|
ti = proto_tree_add_item(root_tree, hf_tcpros_message_header_stamp, tvb, offset + consumed_len, SIZE_OF_LENGTH_STAMP, ENC_LITTLE_ENDIAN);
|
|
sub_tree = proto_item_add_subtree(ti, ett_tcpros);
|
|
|
|
/** Seconds */
|
|
proto_tree_add_item(sub_tree, hf_tcpros_message_header_stamp_sec, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
|
|
sec = tvb_get_letohl(tvb, offset + consumed_len);
|
|
consumed_len += SIZE_OF_LENGTH_FIELD;
|
|
|
|
/** Nano seconds */
|
|
proto_tree_add_item(sub_tree, hf_tcpros_message_header_stamp_nsec, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
|
|
nsec = tvb_get_letohl(tvb, offset + consumed_len);
|
|
consumed_len += SIZE_OF_LENGTH_FIELD;
|
|
|
|
/** Info */
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, "Timestamp: %d.%d ", sec, nsec);
|
|
|
|
return consumed_len;
|
|
}
|
|
|
|
static gint
|
|
dissect_ros_clock(tvbuff_t *tvb, proto_tree *root_tree, packet_info *pinfo, gint offset)
|
|
{
|
|
proto_item *ti;
|
|
proto_tree *sub_tree;
|
|
|
|
gint consumed_len = 0;
|
|
|
|
col_append_str(pinfo->cinfo, COL_INFO, "[ROS Clock] ");
|
|
|
|
/** We got a ROS Clock msg */
|
|
ti = proto_tree_add_item(root_tree, hf_tcpros_clock, tvb, offset, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
|
|
sub_tree = proto_item_add_subtree(ti, ett_tcpros);
|
|
|
|
proto_tree_add_item(sub_tree, hf_tcpros_clock_length, tvb, offset, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
|
|
consumed_len += SIZE_OF_LENGTH_FIELD;
|
|
|
|
consumed_len += dissect_ros_message_header_stamp(tvb, sub_tree, pinfo, offset + consumed_len);
|
|
|
|
return consumed_len;
|
|
}
|
|
|
|
static gint
|
|
dissect_ros_message_header(tvbuff_t *tvb, proto_tree *root_tree, packet_info *pinfo, gint offset)
|
|
{
|
|
proto_item *ti;
|
|
proto_tree *sub_tree;
|
|
|
|
gint consumed_len = 0;
|
|
guint32 frame_id_len;
|
|
guint32 seq;
|
|
guint header_len;
|
|
const guint8* frame_str;
|
|
|
|
|
|
frame_id_len = tvb_get_letohl(tvb, offset + consumed_len + SIZE_OF_LENGTH_FIELD + SIZE_OF_LENGTH_STAMP);
|
|
header_len = SIZE_OF_LENGTH_FIELD + SIZE_OF_LENGTH_STAMP + SIZE_OF_LENGTH_FIELD + frame_id_len;
|
|
|
|
/** Header */
|
|
ti = proto_tree_add_item(root_tree, hf_tcpros_message_header, tvb, offset + consumed_len, header_len, ENC_NA);
|
|
sub_tree = proto_item_add_subtree(ti, ett_tcpros);
|
|
|
|
/** Sequence number */
|
|
proto_tree_add_item(sub_tree, hf_tcpros_message_header_seq, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
|
|
seq = tvb_get_letohl(tvb, offset + consumed_len);
|
|
consumed_len += SIZE_OF_LENGTH_FIELD;
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, "Seq: %d ", seq);
|
|
|
|
/** Timestamp */
|
|
consumed_len += dissect_ros_message_header_stamp(tvb, sub_tree, pinfo, offset + consumed_len);
|
|
|
|
/** Frame ID */
|
|
ti = proto_tree_add_item(sub_tree, hf_tcpros_message_header_frame, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_UTF_8|ENC_LITTLE_ENDIAN);
|
|
sub_tree = proto_item_add_subtree(ti, ett_tcpros);
|
|
|
|
proto_tree_add_item(sub_tree, hf_tcpros_message_header_frame_length, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
|
|
consumed_len += SIZE_OF_LENGTH_FIELD;
|
|
|
|
proto_tree_add_item_ret_string(sub_tree, hf_tcpros_message_header_frame_value, tvb, offset + consumed_len, frame_id_len, ENC_UTF_8|ENC_NA, wmem_packet_scope(), &frame_str);
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, "Frame ID: '%s' ", frame_str);
|
|
consumed_len += frame_id_len;
|
|
|
|
return consumed_len;
|
|
}
|
|
|
|
|
|
/**
|
|
* This is the ROS message dissector. A ROS message contains two parts: a msg header; a msg payload.
|
|
* Because the packet is all in binary format, we don't really know the payload format (we don't know the payload type either).
|
|
* However, every packet has the same header as defined here: http://docs.ros.org/api/std_msgs/html/msg/Header.html
|
|
* So, we can break this one down and display it.
|
|
*/
|
|
static gint
|
|
dissect_ros_message(tvbuff_t *tvb, proto_tree *root_tree, packet_info *pinfo, gint offset)
|
|
{
|
|
proto_item *ti;
|
|
proto_tree *sub_tree;
|
|
|
|
gint consumed_len = 0;
|
|
guint32 total_len = tvb_get_letohl(tvb, offset);
|
|
guint payload_len;
|
|
|
|
col_append_str(pinfo->cinfo, COL_INFO, "[ROS Msg] ");
|
|
|
|
/** We got a ROS msg */
|
|
ti = proto_tree_add_item(root_tree, hf_tcpros_message, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_NA | ENC_LITTLE_ENDIAN);
|
|
sub_tree = proto_item_add_subtree(ti, ett_tcpros);
|
|
|
|
proto_tree_add_item(sub_tree, hf_tcpros_message_length, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
|
|
consumed_len += SIZE_OF_LENGTH_FIELD;
|
|
|
|
/** Body */
|
|
ti = proto_tree_add_item(sub_tree, hf_tcpros_message_body, tvb, offset + consumed_len, total_len, ENC_NA);
|
|
sub_tree = proto_item_add_subtree(ti, ett_tcpros);
|
|
|
|
/** Body.Header */
|
|
consumed_len += dissect_ros_message_header(tvb, sub_tree, pinfo, offset + consumed_len);
|
|
|
|
/** Body.Payload */
|
|
payload_len = (total_len + SIZE_OF_LENGTH_FIELD) - consumed_len;
|
|
proto_tree_add_item(sub_tree, hf_tcpros_message_payload, tvb, offset + consumed_len, payload_len, ENC_NA);
|
|
consumed_len += payload_len;
|
|
|
|
|
|
return consumed_len;
|
|
}
|
|
|
|
|
|
/**
|
|
* This is the poor man's way to differentiate between a connection header packet and a message packet.
|
|
*/
|
|
static gboolean
|
|
is_connection_header(tvbuff_t *tvb, packet_info *pinfo _U_ , guint offset)
|
|
{
|
|
gboolean is_con_header = FALSE;
|
|
guint32 len1 = tvb_get_letohl(tvb, offset);
|
|
guint32 len2 = tvb_get_letohl(tvb, offset + SIZE_OF_LENGTH_FIELD);
|
|
|
|
|
|
if( len1 > len2 ) {
|
|
is_con_header = TRUE;
|
|
}
|
|
|
|
|
|
return is_con_header;
|
|
}
|
|
|
|
static gboolean
|
|
is_rosheaderfield(tvbuff_t *tvb, packet_info *pinfo _U_ , guint offset)
|
|
{
|
|
/** ROS Header Field:
|
|
4-byte len + string */
|
|
gint available = tvb_reported_length_remaining(tvb, offset);
|
|
gint string_len = 0;
|
|
gint i;
|
|
|
|
if( available < 4 )
|
|
return FALSE;
|
|
|
|
string_len = tvb_get_letohl(tvb, offset);
|
|
if( string_len < 0 )
|
|
return FALSE;
|
|
|
|
/** If we don't have enough data for the whole string, assume its not */
|
|
if( available < (string_len + 4) )
|
|
return FALSE;
|
|
/** Check for a valid ascii character and not nil */
|
|
for( i = 0; i < string_len; i++ ) {
|
|
gint8 ch = tvb_get_guint8(tvb, offset + 4 + i);
|
|
if( !g_ascii_isalnum(ch) || 0x00 == ch )
|
|
return FALSE;
|
|
}
|
|
|
|
/** Assume it is */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
is_rosconnection_header(tvbuff_t *tvb, packet_info *pinfo _U_ , guint offset)
|
|
{
|
|
/** ROS Connection Headers: http://wiki.ros.org/ROS/Connection%20Header
|
|
4-byte length + [4-byte length + string] */
|
|
gint available = tvb_reported_length_remaining(tvb, offset);
|
|
gint msg_len = 0;
|
|
|
|
if( available < 8+1 )
|
|
return FALSE;
|
|
|
|
msg_len = tvb_get_letohl(tvb, offset);
|
|
if( msg_len < 4+1 )
|
|
return FALSE;
|
|
|
|
/** Check first header field */
|
|
if( !is_rosheaderfield(tvb, pinfo, offset + 4) )
|
|
return FALSE;
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
is_rosclock(tvbuff_t *tvb, packet_info *pinfo _U_ , guint offset)
|
|
{
|
|
/** ROS Clock message: http://docs.ros.org/api/rosgraph_msgs/html/msg/Clock.html
|
|
4-byte length + 8-byte timestamp == 12 bytes exactly */
|
|
gint available = tvb_reported_length_remaining(tvb, offset);
|
|
if( available < 12 )
|
|
return FALSE;
|
|
|
|
if( tvb_get_letohl(tvb, offset) != 8 )
|
|
return FALSE;
|
|
|
|
/** This is highly likely a clock message. */
|
|
if( available != 12 )
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
is_rosmsg(tvbuff_t *tvb, packet_info *pinfo _U_ , guint offset)
|
|
{
|
|
/** Most ROS messages start with a header: http://docs.ros.org/jade/api/std_msgs/html/msg/Header.html
|
|
4-byte size + 4-byte sequence id + 8-byte timestamp + 4-byte frame id length + frame id */
|
|
gint available = tvb_reported_length_remaining(tvb, offset);
|
|
gint string_len = 0;
|
|
gint msg_len = 0;
|
|
|
|
if( available < 20 )
|
|
return FALSE;
|
|
|
|
msg_len = tvb_get_letohl(tvb, offset);
|
|
if( msg_len < 16 )
|
|
return FALSE;
|
|
|
|
/** Check to see if the frame id length is reasonable */
|
|
string_len = tvb_get_letohl(tvb, offset + 4 + 4 + 8);
|
|
if( string_len > (msg_len - (4 + 8 + 4)) )
|
|
return FALSE;
|
|
|
|
/** This is highly likely a ROS message. */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
dissect_ros_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean is_tcp _U_ )
|
|
{
|
|
proto_item *ti;
|
|
proto_tree *root_tree;
|
|
|
|
guint offset;
|
|
|
|
|
|
/** Clear out stuff in the info column */
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
ti = proto_tree_add_item(tree, proto_tcpros, tvb, 0, -1, ENC_NA);
|
|
root_tree = proto_item_add_subtree(ti, ett_tcpros);
|
|
|
|
offset = 0;
|
|
|
|
while(offset < tvb_reported_length(tvb)) {
|
|
gint available = tvb_reported_length_remaining(tvb, offset);
|
|
|
|
if( (available < SIZE_OF_LENGTH_FIELD) || ((guint)available < tvb_get_letohl(tvb, offset)) ) {
|
|
/** we ran out of data: ask for more */
|
|
pinfo->desegment_offset = offset;
|
|
pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
|
|
return;
|
|
}
|
|
/** There are two types of packet: Connection Headers and ROS Message. Which one is it? */
|
|
if( is_rosclock(tvb, pinfo, offset) ) {
|
|
/** This is a ROS Clock message. */
|
|
offset += dissect_ros_clock(tvb, root_tree, pinfo, offset);
|
|
} else if( is_rosmsg(tvb, pinfo, offset) ) {
|
|
/** We have a ROS message. */
|
|
offset += dissect_ros_message(tvb, root_tree, pinfo, offset);
|
|
} else if( is_rosconnection_header(tvb, pinfo, offset) ) {
|
|
/** Check for a connection header */
|
|
offset += dissect_ros_connection_header(tvb, root_tree, pinfo, offset);
|
|
} else if( is_connection_header(tvb, pinfo, offset) ) {
|
|
/** We have a ROS connection header. */
|
|
offset += dissect_ros_connection_header(tvb, root_tree, pinfo, offset);
|
|
} else {
|
|
/** We have a ROS message. */
|
|
offset += dissect_ros_message(tvb, root_tree, pinfo, offset);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static guint
|
|
get_tcpros_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
|
|
{
|
|
guint32 plen;
|
|
|
|
/*
|
|
* Get the length of the TCPROS packet.
|
|
*/
|
|
plen = tvb_get_letohl(tvb, offset);
|
|
|
|
/*
|
|
* That length doesn't include the length field itself; add that in.
|
|
*/
|
|
return plen + SIZE_OF_LENGTH_FIELD;
|
|
}
|
|
|
|
|
|
static int
|
|
dissect_tcpros_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
|
|
{
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCPROS");
|
|
|
|
dissect_ros_common(tvb, pinfo, tree, TRUE);
|
|
return tvb_reported_length(tvb);
|
|
}
|
|
|
|
static int
|
|
dissect_tcpros(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
|
|
{
|
|
tcp_dissect_pdus(tvb, pinfo, tree, tcpros_desegment, SIZE_OF_LENGTH_FIELD, get_tcpros_pdu_len,
|
|
dissect_tcpros_pdu, data);
|
|
return tvb_reported_length(tvb);
|
|
}
|
|
|
|
|
|
void
|
|
proto_register_tcpros(void)
|
|
{
|
|
static hf_register_info hf[] = {
|
|
{ &hf_tcpros_connection_header, { "ROS Connection", "tcpros.header",
|
|
FT_UINT_BYTES, BASE_NONE, NULL, 0,
|
|
"Message Header", HFILL } },
|
|
{ &hf_tcpros_connection_header_length, { "Header Length", "tcpros.header_length",
|
|
FT_UINT32, BASE_DEC, NULL, 0,
|
|
"Message Header Length", HFILL } },
|
|
{ &hf_tcpros_connection_header_content, { "Header Content", "tcpros.header_content",
|
|
FT_BYTES, BASE_NONE, NULL, 0,
|
|
"Message Header Content", HFILL } },
|
|
|
|
{ &hf_tcpros_connection_header_field, { "Field", "tcpros.header_field",
|
|
FT_UINT_STRING, BASE_NONE, NULL, 0,
|
|
"Message Header Field", HFILL } },
|
|
{ &hf_tcpros_connection_header_field_length, { "Field Length", "tcpros.header_field_length",
|
|
FT_UINT32, BASE_DEC, NULL, 0,
|
|
"Message Header Field Length", HFILL } },
|
|
{ &hf_tcpros_connection_header_field_data, { "Field Content", "tcpros.header_field_data",
|
|
FT_STRING, BASE_NONE, NULL, 0,
|
|
"Message Header Field Content", HFILL } },
|
|
{ &hf_tcpros_connection_header_field_name, { "Name", "tcpros.header_field_name",
|
|
FT_STRING, BASE_NONE, NULL, 0,
|
|
"Message Header Field Name", HFILL } },
|
|
{ &hf_tcpros_connection_header_field_value, { "Value", "tcpros.header_field_value",
|
|
FT_STRING, BASE_NONE, NULL, 0,
|
|
"Message Header Field Value", HFILL } },
|
|
|
|
{ &hf_tcpros_clock, { "ROS Clock", "tcpros.clock",
|
|
FT_UINT_BYTES, BASE_NONE, NULL, 0,
|
|
"ROS Clock Packet", HFILL } },
|
|
{ &hf_tcpros_clock_length, { "Clock Length", "tcpros.clock.length",
|
|
FT_UINT32, BASE_DEC, NULL, 0,
|
|
"ROS Clock Packet length", HFILL } },
|
|
|
|
{ &hf_tcpros_message, { "ROS Message", "tcpros.message",
|
|
FT_UINT_BYTES, BASE_NONE, NULL, 0,
|
|
"ROS Message Packet", HFILL } },
|
|
{ &hf_tcpros_message_length, { "Message Length", "tcpros.message.length",
|
|
FT_UINT32, BASE_DEC, NULL, 0,
|
|
"ROS Message Packet length", HFILL } },
|
|
{ &hf_tcpros_message_body, { "Message Content", "tcpros.message.body",
|
|
FT_BYTES, BASE_NONE, NULL, 0,
|
|
"ROS Message Packet Body", HFILL } },
|
|
|
|
{ &hf_tcpros_message_header, { "Header", "tcpros.message.header",
|
|
FT_BYTES, BASE_NONE, NULL, 0,
|
|
"ROS Message Header", HFILL } },
|
|
{ &hf_tcpros_message_header_seq, { "Sequence ID", "tcpros.message.header.seq",
|
|
FT_UINT32, BASE_DEC, NULL, 0,
|
|
"ROS Message Header Sequence", HFILL } },
|
|
{ &hf_tcpros_message_header_stamp, { "Timestamp", "tcpros.message.header.stamp",
|
|
FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0,
|
|
"ROS Message Header Stamp", HFILL } },
|
|
{ &hf_tcpros_message_header_stamp_sec, { "Seconds", "tcpros.message.header.stamp.sec",
|
|
FT_UINT32, BASE_DEC, NULL, 0,
|
|
"ROS Message Header Stamp Sec", HFILL } },
|
|
{ &hf_tcpros_message_header_stamp_nsec, { "Nanoseconds", "tcpros.message.header.stamp.nsec",
|
|
FT_UINT32, BASE_DEC, NULL, 0,
|
|
"ROS Message Header Stamp NSec", HFILL } },
|
|
|
|
{ &hf_tcpros_message_header_frame, { "Frame ID", "tcpros.message.header.frame",
|
|
FT_UINT_STRING, BASE_NONE, NULL, 0,
|
|
"ROS Message Header Frame ID", HFILL } },
|
|
{ &hf_tcpros_message_header_frame_length, { "Length", "tcpros.message.header.frame.len",
|
|
FT_UINT32, BASE_DEC, NULL, 0,
|
|
"ROS Message Header Frame ID Length", HFILL } },
|
|
{ &hf_tcpros_message_header_frame_value, { "Value", "tcpros.message.header.frame.value",
|
|
FT_STRING, BASE_NONE, NULL, 0,
|
|
"ROS Message Header Frame ID Value", HFILL } },
|
|
|
|
{ &hf_tcpros_message_payload, { "Payload", "tcpros.message.payload",
|
|
FT_BYTES, BASE_NONE, NULL, 0,
|
|
"ROS Message Packet Payload", HFILL } },
|
|
|
|
};
|
|
|
|
static gint *ett[] = {
|
|
&ett_tcpros,
|
|
};
|
|
|
|
module_t *tcpros_module;
|
|
|
|
proto_tcpros = proto_register_protocol("TCP based Robot Operating System protocol (TCPROS)", "TCPROS", "tcpros");
|
|
|
|
proto_register_field_array(proto_tcpros, hf, array_length(hf));
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
|
|
|
|
tcpros_module = prefs_register_protocol(proto_tcpros, NULL);
|
|
|
|
prefs_register_bool_preference(tcpros_module, "desegment_tcpros_messages",
|
|
"Reassemble TCPROS messages spanning multiple TCP segments",
|
|
"Whether the TCPROS dissector should reassemble messages spanning multiple TCP segments."
|
|
" To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
|
|
&tcpros_desegment);
|
|
|
|
|
|
}
|
|
|
|
/* Heuristics test */
|
|
static gboolean
|
|
test_tcpros(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data _U_)
|
|
{
|
|
if (tvb_captured_length(tvb) < 8)
|
|
return FALSE;
|
|
|
|
if( is_rosclock(tvb, pinfo, offset) )
|
|
return TRUE;
|
|
if( is_rosmsg(tvb, pinfo, offset) )
|
|
return TRUE;
|
|
if( is_rosconnection_header(tvb, pinfo, offset) )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
dissect_tcpros_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
|
|
{
|
|
conversation_t *conversation;
|
|
|
|
if (!test_tcpros(pinfo, tvb, 0, data))
|
|
return FALSE;
|
|
|
|
conversation = find_or_create_conversation(pinfo);
|
|
conversation_set_dissector(conversation, tcpros_handle);
|
|
|
|
dissect_tcpros(tvb, pinfo, tree, data);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
proto_reg_handoff_tcpros(void)
|
|
{
|
|
tcpros_handle = create_dissector_handle(dissect_tcpros, proto_tcpros);
|
|
dissector_add_for_decode_as_with_preference("tcp.port", tcpros_handle); /* for "decode-as" */
|
|
|
|
/* register as heuristic dissector */
|
|
heur_dissector_add("tcp", dissect_tcpros_heur_tcp, "TCPROS over TCP",
|
|
"TCPROS_tcp", proto_tcpros, HEURISTIC_DISABLE);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Editor modelines - http://www.wireshark.org/tools/modelines.html
|
|
*
|
|
* Local variables:
|
|
* c-basic-offset: 8
|
|
* tab-width: 8
|
|
* indent-tabs-mode: t
|
|
* End:
|
|
*
|
|
* vi: set shiftwidth=8 tabstop=8 noexpandtab:
|
|
* :indentSize=8:tabSize=8:noTabs=false:
|
|
*/
|