RTPS: Added Topic Information feature. This feature shows the Topic

Name and the Type Name of the sample by consulting a hash table
that relates GUIDs and this information.

This information is very useful to any analysis performed to RTPS
data. It can be disabled using a checkbox so it doesn't impact
performance when capturing (default = disabled).

Bug: 11729
Change-Id: Ic9fa3a777dfed3cb46166b8e7c9783a12c161e7d
Reviewed-on: https://code.wireshark.org/review/11602
Petri-Dish: Pascal Quantin <pascal.quantin@gmail.com>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Pascal Quantin <pascal.quantin@gmail.com>
This commit is contained in:
Juanjo Martin 2015-11-06 16:40:11 +01:00 committed by Pascal Quantin
parent 67d9daa65b
commit 1df7c07326
2 changed files with 178 additions and 30 deletions

View File

@ -373,6 +373,7 @@ static gint ett_rtps_app_ack_virtual_writer = -1;
static gint ett_rtps_product_version = -1;
static gint ett_rtps_property_list = -1;
static gint ett_rtps_property = -1;
static gint ett_rtps_topic_info = -1;
static expert_field ei_rtps_sm_octets_to_next_header_error = EI_INIT;
static expert_field ei_rtps_port_invalid = EI_INIT;
@ -390,6 +391,7 @@ static expert_field ei_rtps_unsupported_non_builtin_param_seq = EI_INIT;
/* Preferences */
/***************************************************************************/
static guint rtps_max_batch_samples_dissected = 16;
static gboolean enable_topic_info = FALSE;
/***************************************************************************/
/* Value-to-String Tables */
@ -1150,6 +1152,22 @@ static const int* NACK_FLAGS[] = {
};
#endif
typedef struct _endpoint_guid {
guint32 host_id;
guint32 app_id;
guint32 instance_id;
guint32 entity_id;
} endpoint_guid;
#define MAX_TOPIC_AND_TYPE_LENGTH 256
typedef struct _type_mapping {
endpoint_guid guid;
gchar type_name[MAX_TOPIC_AND_TYPE_LENGTH];
gchar topic_name[MAX_TOPIC_AND_TYPE_LENGTH];
gint fields_visited;
} type_mapping;
static wmem_map_t * registry = NULL;
/***************************************************************************/
/* Inline macros */
@ -2760,6 +2778,82 @@ static int rtps_util_add_fragment_number_set(proto_tree *tree, packet_info *pinf
return offset;
}
static void rtps_util_store_type_mapping(tvbuff_t *tvb, gint offset,
type_mapping * type_mapping_object, const gchar * value,
gint topic_info_add_id) {
if (enable_topic_info && type_mapping_object) {
switch (topic_info_add_id) {
case TOPIC_INFO_ADD_GUID: {
type_mapping_object->guid.host_id = tvb_get_ntohl(tvb, offset);
type_mapping_object->guid.app_id = tvb_get_ntohl(tvb, offset+4);
type_mapping_object->guid.instance_id = tvb_get_ntohl(tvb, offset+8);
type_mapping_object->guid.entity_id = tvb_get_ntohl(tvb, offset+12);
type_mapping_object->fields_visited =
type_mapping_object->fields_visited | TOPIC_INFO_ADD_GUID;
break;
}
case TOPIC_INFO_ADD_TOPIC_NAME: {
g_strlcpy(type_mapping_object->topic_name, value, MAX_TOPIC_AND_TYPE_LENGTH);
type_mapping_object->fields_visited =
type_mapping_object->fields_visited | TOPIC_INFO_ADD_TOPIC_NAME;
break;
}
case TOPIC_INFO_ADD_TYPE_NAME: {
g_strlcpy(type_mapping_object->type_name, value, MAX_TOPIC_AND_TYPE_LENGTH);
type_mapping_object->fields_visited =
type_mapping_object->fields_visited | TOPIC_INFO_ADD_TYPE_NAME;
break;
}
default:
break;
}
if ((type_mapping_object->fields_visited & TOPIC_INFO_ALL_SET) == TOPIC_INFO_ALL_SET &&
!wmem_map_lookup(registry, &(type_mapping_object->guid))) {
if (((type_mapping_object->guid.entity_id & 0x02) == 0x02)){
/* If it is an application defined writer matches 0x02 */
wmem_map_insert(registry, &(type_mapping_object->guid), type_mapping_object);
}
}
}
}
guint hash_by_guid(gconstpointer key) {
endpoint_guid * guid = (endpoint_guid *) key;
return g_int_hash(&(guid->app_id));
}
gboolean compare_by_guid(gconstpointer a, gconstpointer b) {
endpoint_guid * guid_a = (endpoint_guid *) a;
endpoint_guid * guid_b = (endpoint_guid *) b;
return memcmp(guid_a, guid_b, sizeof(endpoint_guid)) == 0;
}
static type_mapping * rtps_util_get_topic_info(endpoint_guid * guid) {
/* At this point, we know the boolean enable_topic_info is true */
type_mapping * result = NULL;
if (guid)
result = (type_mapping *)wmem_map_lookup(registry, guid);
return result;
}
static void rtps_util_show_topic_info(proto_tree *tree, tvbuff_t *tvb,
gint offset, endpoint_guid * guid) {
if (enable_topic_info && tree) {
type_mapping * type_mapping_object = rtps_util_get_topic_info(guid);
if (type_mapping_object != NULL) {
proto_tree * topic_info_tree;
proto_item * ti;
topic_info_tree = proto_tree_add_subtree(tree, tvb, offset, 0,
ett_rtps_topic_info, NULL, "[Topic Information (from Discovery)]");
ti = proto_tree_add_string(topic_info_tree, hf_rtps_param_topic_name, tvb, offset, 0,
type_mapping_object->topic_name);
PROTO_ITEM_SET_GENERATED(ti);
ti = proto_tree_add_string(topic_info_tree, hf_rtps_param_type_name, tvb, offset, 0,
type_mapping_object->type_name);
PROTO_ITEM_SET_GENERATED(ti);
}
}
}
/* *********************************************************************** */
/* * Parameter Sequence dissector * */
/* *********************************************************************** */
@ -2780,12 +2874,10 @@ static int rtps_util_add_fragment_number_set(proto_tree *tree, packet_info *pinf
static gboolean dissect_parameter_sequence_rti(proto_tree *rtps_parameter_tree, packet_info *pinfo, tvbuff_t *tvb,
proto_item *parameter_item, proto_item * param_len_item, gint offset,
gboolean little_endian, int param_length,
guint16 parameter) {
gboolean little_endian, int param_length, guint16 parameter) {
switch(parameter) {
/* 0...2...........7...............15.............23...............31
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | PID_DEFAULT_MULTICAST_LOCATOR | 0x0018 |
@ -3173,7 +3265,7 @@ static gboolean dissect_parameter_sequence_toc(proto_tree *rtps_parameter_tree,
static gboolean dissect_parameter_sequence_v1(proto_tree *rtps_parameter_tree, packet_info *pinfo, tvbuff_t *tvb,
proto_item *parameter_item, proto_item * param_len_item, gint offset,
gboolean little_endian, int size, int param_length,
guint16 parameter, guint16 version) {
guint16 parameter, guint16 version, type_mapping * type_mapping_object) {
proto_tree *subtree;
switch(parameter) {
@ -3220,10 +3312,17 @@ static gboolean dissect_parameter_sequence_v1(proto_tree *rtps_parameter_tree, p
* | ... |
* +---------------+---------------+---------------+---------------+
*/
case PID_TOPIC_NAME:
case PID_TOPIC_NAME: {
const gchar * retVal = NULL;
guint32 str_size = NEXT_guint32(tvb, offset, little_endian);
retVal = (gchar*)tvb_get_string_enc(wmem_packet_scope(), tvb, offset+4, str_size, ENC_ASCII);
rtps_util_store_type_mapping(tvb, offset, type_mapping_object, retVal, TOPIC_INFO_ADD_TOPIC_NAME);
rtps_util_add_string(rtps_parameter_tree, tvb, offset, hf_rtps_param_topic_name, little_endian);
break;
}
/* 0...2...........7...............15.............23...............31
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -3249,9 +3348,17 @@ static gboolean dissect_parameter_sequence_v1(proto_tree *rtps_parameter_tree, p
* | ... |
* +---------------+---------------+---------------+---------------+
*/
case PID_TYPE_NAME:
case PID_TYPE_NAME: {
const gchar * retVal = NULL;
guint32 str_size = NEXT_guint32(tvb, offset, little_endian);
retVal = (gchar*) tvb_get_string_enc(wmem_packet_scope(), tvb, offset+4, str_size, ENC_ASCII);
rtps_util_store_type_mapping(tvb, offset, type_mapping_object, retVal, TOPIC_INFO_ADD_TYPE_NAME);
rtps_util_add_string(rtps_parameter_tree, tvb, offset, hf_rtps_param_type_name, little_endian);
break;
}
/* 0...2...........7...............15.............23...............31
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -4124,7 +4231,8 @@ static gboolean dissect_parameter_sequence_v1(proto_tree *rtps_parameter_tree, p
static gboolean dissect_parameter_sequence_v2(proto_tree *rtps_parameter_tree, packet_info *pinfo, tvbuff_t *tvb,
proto_item *parameter_item _U_, proto_item *param_len_item,
gint offset, gboolean little_endian, int param_length,
guint16 parameter, guint32 *pStatusInfo, guint16 vendor_id _U_) {
guint16 parameter, guint32 *pStatusInfo, guint16 vendor_id _U_,
type_mapping * type_mapping_object) {
proto_item *ti;
switch(parameter) {
@ -4347,6 +4455,7 @@ static gboolean dissect_parameter_sequence_v2(proto_tree *rtps_parameter_tree, p
*/
case PID_ENDPOINT_GUID:
ENSURE_LENGTH(16);
rtps_util_store_type_mapping(tvb, offset, type_mapping_object, NULL, TOPIC_INFO_ADD_GUID);
rtps_util_add_generic_guid_v2(rtps_parameter_tree, tvb, offset,
hf_rtps_endpoint_guid, hf_rtps_param_host_id, hf_rtps_param_app_id,
hf_rtps_param_instance_id, hf_rtps_param_app_kind, hf_rtps_param_counter,
@ -4370,6 +4479,10 @@ static gint dissect_parameter_sequence(proto_tree *tree, packet_info *pinfo, tvb
guint16 parameter, param_length;
gint original_offset = offset;
gboolean dissect_return_value = FALSE;
type_mapping * type_mapping_object = NULL;
if (!pinfo->fd->flags.visited)
type_mapping_object = wmem_new(wmem_file_scope(), type_mapping);
rtps_parameter_sequence_tree = proto_tree_add_subtree_format(tree, tvb, offset, -1, ett_rtps_parameter_sequence, &ti, "%s:", label);
@ -4442,11 +4555,11 @@ static gint dissect_parameter_sequence(proto_tree *tree, packet_info *pinfo, tvb
if (!dissect_return_value) {
if (!dissect_parameter_sequence_v1(rtps_parameter_tree, pinfo, tvb, param_item, param_len_item,
offset, little_endian, size, param_length, parameter, version)) {
offset, little_endian, size, param_length, parameter, version, type_mapping_object)) {
if ((version < 0x0200) ||
!dissect_parameter_sequence_v2(rtps_parameter_tree, pinfo, tvb, param_item, param_len_item,
offset, little_endian, param_length, parameter,
pStatusInfo, vendor_id)) {
pStatusInfo, vendor_id, type_mapping_object)) {
if (param_length > 0) {
proto_tree_add_item(rtps_parameter_tree, hf_rtps_parameter_data, tvb,
offset, param_length, ENC_NA);
@ -4614,7 +4727,8 @@ static void dissect_APP_ACK_CONF(tvbuff_t *tvb,
* type specified the type of endianess of the payload.
*/
static void dissect_serialized_data(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, gint offset,
int size, const char *label, guint16 vendor_id, gboolean is_discovery_data) {
int size, const char *label, guint16 vendor_id, gboolean is_discovery_data,
endpoint_guid * guid) {
proto_item *ti;
proto_tree *rtps_parameter_sequence_tree;
guint16 encapsulation_id;
@ -4623,6 +4737,9 @@ static void dissect_serialized_data(proto_tree *tree, packet_info *pinfo, tvbuff
/* Creates the sub-tree */
rtps_parameter_sequence_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_rtps_serialized_data, &ti, label);
/* Add Topic Information if enabled (checked inside)*/
rtps_util_show_topic_info(rtps_parameter_sequence_tree, tvb, offset, guid);
/* Encapsulation ID */
encapsulation_id = NEXT_guint16(tvb, offset, ENC_BIG_ENDIAN); /* Always big endian */
proto_tree_add_uint(rtps_parameter_sequence_tree, hf_rtps_param_serialize_encap_kind, tvb, offset, 2, encapsulation_id);
@ -4658,7 +4775,7 @@ static void dissect_serialized_data(proto_tree *tree, packet_info *pinfo, tvbuff
break;
default:
proto_tree_add_item(rtps_parameter_sequence_tree, hf_rtps_data_serialize_data, tvb,
proto_tree_add_item(rtps_parameter_sequence_tree, hf_rtps_data_serialize_data, tvb,
offset, size, ENC_NA);
}
}
@ -4842,7 +4959,8 @@ static void dissect_APP_ACK(tvbuff_t *tvb,
interval_payload_length,
"payload",
vendor_id,
FALSE /* from_builtin_writer */);
FALSE /* from_builtin_writer */,
NULL /* guid pointer */);
offset += interval_payload_length;
}
@ -5086,7 +5204,7 @@ static void dissect_DATA_v1(tvbuff_t *tvb, packet_info *pinfo, gint offset, guin
static void dissect_DATA_v2(tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 flags,
gboolean little_endian, int octets_to_next_header, proto_tree *tree,
guint16 vendor_id) {
guint16 vendor_id, endpoint_guid *guid) {
/*
*
* 0...2...........7...............15.............23...............31
@ -5193,9 +5311,10 @@ static void dissect_DATA_v2(tvbuff_t *tvb, packet_info *pinfo, gint offset, guin
if ((flags & FLAG_DATA_D_v2) != 0) {
from_builtin_writer =
(((wid & 0xc2) == 0xc2) || ((wid & 0xc3) == 0xc3)) ? TRUE : FALSE;
guid->entity_id = wid;
dissect_serialized_data(tree, pinfo, tvb, offset,
octets_to_next_header - (offset - old_offset) + 4,
"serializedData", vendor_id, from_builtin_writer);
"serializedData", vendor_id, from_builtin_writer, guid);
}
info_summary_append_ex(pinfo, wid, status_info);
}
@ -5323,7 +5442,7 @@ static void dissect_DATA_FRAG(tvbuff_t *tvb, packet_info *pinfo, gint offset, gu
(((wid & 0xc2) == 0xc2) || ((wid & 0xc3) == 0xc3)) ? TRUE : FALSE;
dissect_serialized_data(tree, pinfo, tvb, offset,
octets_to_next_header - (offset - old_offset) + 4,
"serializedData", vendor_id, from_builtin_writer);
"serializedData", vendor_id, from_builtin_writer, NULL);
}
}
@ -5460,7 +5579,7 @@ static void dissect_NOKEY_DATA(tvbuff_t *tvb, packet_info *pinfo, gint offset, g
(((wid & 0xc2) == 0xc2) || ((wid & 0xc3) == 0xc3)) ? TRUE : FALSE;
dissect_serialized_data(tree, pinfo, tvb, offset,
octets_to_next_header - (offset - old_offset) + 4,
"serializedData", vendor_id, from_builtin_writer);
"serializedData", vendor_id, from_builtin_writer, NULL);
}
}
@ -5563,7 +5682,7 @@ static void dissect_NOKEY_DATA_FRAG(tvbuff_t *tvb, packet_info *pinfo, gint offs
(((wid & 0xc2) == 0xc2) || ((wid & 0xc3) == 0xc3)) ? TRUE : FALSE;
dissect_serialized_data(tree, pinfo, tvb,offset,
octets_to_next_header - (offset - old_offset) + 4,
"serializedData", vendor_id, from_builtin_writer);
"serializedData", vendor_id, from_builtin_writer, NULL);
}
}
@ -6263,7 +6382,7 @@ static void dissect_HEARTBEAT_FRAG(tvbuff_t *tvb, packet_info *pinfo, gint offse
/* *********************************************************************** */
static void dissect_RTPS_DATA(tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 flags,
gboolean little_endian, int octets_to_next_header, proto_tree *tree,
guint16 vendor_id, gboolean is_session) {
guint16 vendor_id, gboolean is_session, endpoint_guid * guid) {
/*
*
* 0...2...........7...............15.............23...............31
@ -6448,11 +6567,11 @@ static void dissect_RTPS_DATA(tvbuff_t *tvb, packet_info *pinfo, gint offset, gu
from_builtin_writer =
(((wid & 0xc2) == 0xc2) || ((wid & 0xc3) == 0xc3)) ? TRUE : FALSE;
guid->entity_id = wid;
/* At the end still dissect the rest of the bytes as raw data */
dissect_serialized_data(tree, pinfo, tvb, offset,
octets_to_next_header - (offset - old_offset) + 4,
label, vendor_id, from_builtin_writer);
label, vendor_id, from_builtin_writer, guid);
}
}
@ -6581,7 +6700,7 @@ static void dissect_RTPS_DATA_FRAG(tvbuff_t *tvb, packet_info *pinfo, gint offse
dissect_serialized_data(tree, pinfo, tvb, offset,
octets_to_next_header - (offset - old_offset) + 4,
label, vendor_id, from_builtin_writer);
label, vendor_id, from_builtin_writer, NULL);
}
info_summary_append_ex(pinfo, wid, status_info);
}
@ -7279,7 +7398,8 @@ static void dissect_INFO_REPLY(tvbuff_t *tvb, packet_info *pinfo, gint offset, g
static gboolean dissect_rtps_submessage_v2(tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 flags,
gboolean little_endian, guint8 submessageId, guint16 vendor_id, gint octets_to_next_header,
proto_tree *rtps_submessage_tree, proto_item *submessage_item)
proto_tree *rtps_submessage_tree, proto_item *submessage_item,
endpoint_guid * guid)
{
switch (submessageId)
{
@ -7324,7 +7444,7 @@ static gboolean dissect_rtps_submessage_v2(tvbuff_t *tvb, packet_info *pinfo, gi
case SUBMESSAGE_RTPS_DATA_SESSION:
case SUBMESSAGE_RTPS_DATA:
dissect_RTPS_DATA(tvb, pinfo, offset, flags, little_endian, octets_to_next_header,
rtps_submessage_tree, vendor_id, (submessageId == SUBMESSAGE_RTPS_DATA_SESSION));
rtps_submessage_tree, vendor_id, (submessageId == SUBMESSAGE_RTPS_DATA_SESSION), guid);
break;
case SUBMESSAGE_RTPS_DATA_FRAG:
@ -7346,7 +7466,8 @@ static gboolean dissect_rtps_submessage_v2(tvbuff_t *tvb, packet_info *pinfo, gi
static gboolean dissect_rtps_submessage_v1(tvbuff_t *tvb, packet_info *pinfo, gint offset, guint8 flags, gboolean little_endian,
guint8 submessageId, guint16 version, guint16 vendor_id, gint octets_to_next_header,
proto_tree *rtps_submessage_tree, proto_item *submessage_item)
proto_tree *rtps_submessage_tree, proto_item *submessage_item,
endpoint_guid * guid)
{
switch (submessageId)
{
@ -7356,9 +7477,11 @@ static gboolean dissect_rtps_submessage_v1(tvbuff_t *tvb, packet_info *pinfo, gi
case SUBMESSAGE_DATA:
if (version < 0x0200) {
dissect_DATA_v1(tvb, pinfo, offset, flags, little_endian, octets_to_next_header, rtps_submessage_tree);
dissect_DATA_v1(tvb, pinfo, offset, flags, little_endian,
octets_to_next_header, rtps_submessage_tree);
} else {
dissect_DATA_v2(tvb, pinfo, offset, flags, little_endian, octets_to_next_header, rtps_submessage_tree, vendor_id);
dissect_DATA_v2(tvb, pinfo, offset, flags, little_endian,
octets_to_next_header, rtps_submessage_tree, vendor_id, guid);
}
break;
@ -7419,6 +7542,7 @@ static gboolean dissect_rtps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree
gint next_submsg, octets_to_next_header;
int sub_hf;
const value_string *sub_vals;
endpoint_guid guid;
/* Check 'RTPS' signature:
* A header is invalid if it has less than 16 octets
@ -7459,6 +7583,10 @@ static gboolean dissect_rtps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree
rtps_util_add_guid_prefix_v2(rtps_tree, tvb, offset+8, hf_rtps_guid_prefix,
hf_rtps_host_id, hf_rtps_app_id, hf_rtps_sm_counter, NULL);
guid.host_id = tvb_get_ntohl(tvb, offset+8);
guid.app_id = tvb_get_ntohl(tvb, offset+12);
guid.instance_id = tvb_get_ntohl(tvb, offset+16);
#ifdef RTI_BUILD
pinfo->guid_prefix_host = tvb_get_ntohl(tvb, offset + 8);
pinfo->guid_prefix_app = tvb_get_ntohl(tvb, offset + 12);
@ -7598,10 +7726,10 @@ static gboolean dissect_rtps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree
*/
if (!dissect_rtps_submessage_v1(tvb, pinfo, offset, flags, little_endian,
submessageId, version, vendor_id,
octets_to_next_header, rtps_submessage_tree, ti)) {
octets_to_next_header, rtps_submessage_tree, ti, &guid)) {
if ((version < 0x0200) ||
!dissect_rtps_submessage_v2(tvb, pinfo, offset, flags, little_endian, submessageId,
vendor_id, octets_to_next_header, rtps_submessage_tree, ti)) {
vendor_id, octets_to_next_header, rtps_submessage_tree, ti, &guid)) {
proto_tree_add_uint(rtps_submessage_tree, hf_rtps_sm_flags,
tvb, offset + 1, 1, flags);
proto_tree_add_uint(rtps_submessage_tree,
@ -7638,6 +7766,13 @@ static gboolean dissect_rtps_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *
return dissect_rtps(tvb, pinfo, tree, offset);
}
static void
rtps_init(void)
{
if (enable_topic_info)
registry = wmem_map_new(wmem_file_scope(), hash_by_guid, compare_by_guid);
}
void proto_register_rtps(void) {
static hf_register_info hf[] = {
@ -9282,6 +9417,7 @@ void proto_register_rtps(void) {
&ett_rtps_transport_info,
&ett_rtps_property_list,
&ett_rtps_property,
&ett_rtps_topic_info,
};
static ei_register_info ei[] = {
@ -9318,6 +9454,13 @@ void proto_register_rtps(void) {
"a DATA_BATCH submessage. Increasing this value may affect "
"performances if the trace has a lot of big batched samples.",
10, &rtps_max_batch_samples_dissected);
prefs_register_bool_preference(rtps_module, "enable_topic_info",
"Enable Topic Information feature",
"Shows the Topic Name and Type Name of the samples.",
&enable_topic_info);
register_init_routine(rtps_init);
}

View File

@ -458,7 +458,12 @@ typedef enum {
#define NDDS_TRANSPORT_CLASSID_PCIE (12)
#define NDDS_TRANSPORT_CLASSID_ITP (13)
#define TOPIC_INFO_ADD_GUID (1)
#define TOPIC_INFO_ADD_TYPE_NAME (2)
#define TOPIC_INFO_ADD_TOPIC_NAME (4)
#define TOPIC_INFO_ALL_SET (TOPIC_INFO_ADD_GUID | \
TOPIC_INFO_ADD_TYPE_NAME | \
TOPIC_INFO_ADD_TOPIC_NAME)
/* Utilities to add elements to the protocol tree for packet-rtps.h and packet-rtps2.h */
extern guint16 rtps_util_add_protocol_version(proto_tree *tree, tvbuff_t* tvb, gint offset);
extern guint16 rtps_util_add_vendor_id(proto_tree *tree, tvbuff_t * tvb, gint offset);