7b01e9e897
Change-Id: Ia34a4278eedc8cf450688b1fa0291e1f976868d3 Signed-off-by: Dave Barach <dave@barachs.net>
433 lines
14 KiB
C
433 lines
14 KiB
C
/* packet-vpp.c
|
|
*
|
|
* Routines for the disassembly of fd.io vpp project
|
|
* dispatch captures
|
|
*
|
|
* Copyright (c) 2018 Cisco and/or its affiliates.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* This version is not to be upstreamed as-is, since it hooks up the
|
|
* vpp dissector to WTAP_ENCAP_USER13, a test encap type.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <epan/packet.h>
|
|
#include <epan/expert.h>
|
|
#include <epan/to_str.h>
|
|
#include <epan/in_cksum.h>
|
|
#include <epan/nlpid.h>
|
|
#include <epan/etypes.h>
|
|
#include <stdio.h>
|
|
#include <wsutil/ws_printf.h>
|
|
|
|
void proto_register_vpp(void);
|
|
void proto_reg_handoff_vpp(void);
|
|
|
|
static int proto_vpp = -1;
|
|
static int proto_vpp_metadata = -1;
|
|
static int proto_vpp_opaque = -1;
|
|
static int proto_vpp_opaque2 = -1;
|
|
static int proto_vpp_trace = -1;
|
|
static int hf_vpp_nodename = -1;
|
|
static int hf_vpp_metadata = -1;
|
|
static int hf_vpp_buffer_index = -1;
|
|
static int hf_vpp_buffer_opaque = -1;
|
|
static int hf_vpp_buffer_opaque2 = -1;
|
|
static int hf_vpp_buffer_trace = -1;
|
|
|
|
static gint ett_vpp = -1;
|
|
static gint ett_vpp_opaque = -1;
|
|
static gint ett_vpp_opaque2 = -1;
|
|
static gint ett_vpp_metadata = -1;
|
|
static gint ett_vpp_trace = -1;
|
|
|
|
static dissector_handle_t vpp_dissector_handle;
|
|
static dissector_handle_t vpp_opaque_dissector_handle;
|
|
static dissector_handle_t vpp_opaque2_dissector_handle;
|
|
static dissector_handle_t vpp_metadata_dissector_handle;
|
|
static dissector_handle_t vpp_trace_dissector_handle;
|
|
|
|
typedef enum
|
|
{
|
|
VLIB_NODE_PROTO_HINT_NONE = 0,
|
|
VLIB_NODE_PROTO_HINT_ETHERNET,
|
|
VLIB_NODE_PROTO_HINT_IP4,
|
|
VLIB_NODE_PROTO_HINT_IP6,
|
|
VLIB_NODE_PROTO_HINT_TCP,
|
|
VLIB_NODE_PROTO_HINT_UDP,
|
|
VLIB_NODE_N_PROTO_HINTS,
|
|
} vlib_node_proto_hint_t;
|
|
|
|
static dissector_handle_t next_dissectors[VLIB_NODE_N_PROTO_HINTS];
|
|
|
|
/* List of next dissectors hints that we know about */
|
|
#define foreach_next_dissector \
|
|
_(VLIB_NODE_PROTO_HINT_ETHERNET, eth_maybefcs) \
|
|
_(VLIB_NODE_PROTO_HINT_IP4, ip) \
|
|
_(VLIB_NODE_PROTO_HINT_IP6, ipv6) \
|
|
_(VLIB_NODE_PROTO_HINT_TCP, tcp) \
|
|
_(VLIB_NODE_PROTO_HINT_UDP, udp)
|
|
|
|
static void
|
|
add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
|
|
gint len, int hf)
|
|
{
|
|
gint next;
|
|
int line_len;
|
|
int data_len;
|
|
|
|
while (len > 0) {
|
|
line_len = tvb_find_line_end(tvb, start, len, &next, FALSE);
|
|
data_len = next - start;
|
|
proto_tree_add_string(tree, hf, tvb, start, data_len,
|
|
tvb_format_stringzpad(tvb, start, line_len));
|
|
start += data_len;
|
|
len -= data_len;
|
|
}
|
|
}
|
|
|
|
static int
|
|
dissect_vpp_metadata (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
void* data _U_)
|
|
{
|
|
int offset = 0;
|
|
proto_item *ti;
|
|
proto_tree *metadata_tree;
|
|
gint metadata_string_length;
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "VPP-Metadata");
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
ti = proto_tree_add_item(tree, proto_vpp_metadata, tvb, offset, -1, ENC_NA);
|
|
metadata_tree = proto_item_add_subtree(ti, ett_vpp_metadata);
|
|
|
|
/* How long is the metadata string? */
|
|
metadata_string_length = tvb_strsize (tvb, offset);
|
|
|
|
add_multi_line_string_to_tree (metadata_tree, tvb, 0,
|
|
metadata_string_length,
|
|
hf_vpp_metadata);
|
|
return tvb_captured_length(tvb);
|
|
}
|
|
|
|
static int
|
|
dissect_vpp_trace (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
void* data _U_)
|
|
{
|
|
int offset = 0;
|
|
proto_item *ti;
|
|
proto_tree *trace_tree;
|
|
gint trace_string_length;
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "VPP-Trace");
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
ti = proto_tree_add_item(tree, proto_vpp_trace, tvb, offset, -1, ENC_NA);
|
|
trace_tree = proto_item_add_subtree(ti, ett_vpp_trace);
|
|
|
|
/* How long is the trace string? */
|
|
trace_string_length = tvb_strsize (tvb, offset);
|
|
|
|
add_multi_line_string_to_tree (trace_tree, tvb, 0,
|
|
trace_string_length,
|
|
hf_vpp_buffer_trace);
|
|
return tvb_captured_length(tvb);
|
|
}
|
|
|
|
|
|
static int
|
|
dissect_vpp_opaque (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
void* data _U_)
|
|
{
|
|
int offset = 0;
|
|
proto_item *ti;
|
|
proto_tree *opaque_tree;
|
|
gint opaque_string_length;
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "VPP-Opaque");
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
ti = proto_tree_add_item(tree, proto_vpp_opaque, tvb, offset, -1, ENC_NA);
|
|
opaque_tree = proto_item_add_subtree(ti, ett_vpp_opaque);
|
|
|
|
opaque_string_length = tvb_strsize (tvb, offset);
|
|
add_multi_line_string_to_tree (opaque_tree, tvb, 0, opaque_string_length,
|
|
hf_vpp_buffer_opaque);
|
|
|
|
return tvb_captured_length(tvb);
|
|
}
|
|
|
|
static int
|
|
dissect_vpp_opaque2 (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|
void* data _U_)
|
|
{
|
|
int offset = 0;
|
|
proto_item *ti;
|
|
proto_tree *opaque2_tree;
|
|
gint opaque2_string_length;
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "VPP-Opaque2");
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
ti = proto_tree_add_item(tree, proto_vpp_opaque2, tvb, offset, -1, ENC_NA);
|
|
opaque2_tree = proto_item_add_subtree(ti, ett_vpp_opaque2);
|
|
|
|
opaque2_string_length = tvb_strsize (tvb, offset);
|
|
add_multi_line_string_to_tree (opaque2_tree, tvb, 0, opaque2_string_length,
|
|
hf_vpp_buffer_opaque2);
|
|
|
|
return tvb_captured_length(tvb);
|
|
}
|
|
|
|
|
|
static int
|
|
dissect_vpp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
|
|
{
|
|
proto_item *ti;
|
|
proto_tree *vpp_tree;
|
|
tvbuff_t *metadata_tvb, *opaque_tvb, *opaque2_tvb, *eth_tvb, *trace_tvb;
|
|
int offset = 0;
|
|
guint8 major_version, minor_version, string_count, protocol_hint;
|
|
guint8 *name;
|
|
guint len;
|
|
guint8 maybe_protocol_id;
|
|
dissector_handle_t use_this_dissector;
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "VPP");
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
|
|
|
ti = proto_tree_add_item(tree, proto_vpp, tvb, offset, -1, ENC_NA);
|
|
vpp_tree = proto_item_add_subtree(ti, ett_vpp);
|
|
|
|
major_version = tvb_get_guint8 (tvb, offset);
|
|
offset++;
|
|
|
|
minor_version = tvb_get_guint8 (tvb, offset);
|
|
offset++;
|
|
|
|
if (major_version != 1 || minor_version != 0)
|
|
ws_debug_printf ("WARNING: version mismatch (%d, %d)",
|
|
major_version, minor_version);
|
|
|
|
/* Number of counted strings in this trace record */
|
|
string_count = tvb_get_guint8 (tvb, offset);
|
|
offset++;
|
|
|
|
/*
|
|
* Hint: protocol which should be at b->data[b->current_data]
|
|
* It will be a while before vpp sends useful hints for every
|
|
* possible node, see heuristic below
|
|
*/
|
|
protocol_hint = tvb_get_guint8 (tvb, offset);
|
|
offset++;
|
|
|
|
/* Buffer Index */
|
|
proto_tree_add_item(vpp_tree, hf_vpp_buffer_index, tvb,
|
|
offset, 4, ENC_BIG_ENDIAN);
|
|
offset += 4;
|
|
|
|
/* Nodename */
|
|
len = tvb_strsize (tvb, offset);
|
|
name = tvb_get_string_enc (wmem_packet_scope(), tvb, offset, len,
|
|
ENC_ASCII);
|
|
proto_tree_add_string (tree, hf_vpp_nodename, tvb, offset, len, name);
|
|
offset += len;
|
|
|
|
/* Metadata */
|
|
len = tvb_strsize (tvb, offset);
|
|
metadata_tvb = tvb_new_subset_remaining (tvb, offset);
|
|
call_dissector (vpp_metadata_dissector_handle, metadata_tvb, pinfo, tree);
|
|
offset += len;
|
|
|
|
/* Opaque */
|
|
len = tvb_strsize (tvb, offset);
|
|
opaque_tvb = tvb_new_subset_remaining (tvb, offset);
|
|
call_dissector (vpp_opaque_dissector_handle, opaque_tvb, pinfo, tree);
|
|
offset += len;
|
|
|
|
/* Opaque2 */
|
|
len = tvb_strsize (tvb, offset);
|
|
opaque2_tvb = tvb_new_subset_remaining (tvb, offset);
|
|
call_dissector (vpp_opaque2_dissector_handle, opaque2_tvb, pinfo, tree);
|
|
offset += len;
|
|
|
|
/* Trace, if present */
|
|
if (string_count > 4)
|
|
{
|
|
len = tvb_strsize (tvb, offset);
|
|
trace_tvb = tvb_new_subset_remaining (tvb, offset);
|
|
call_dissector (vpp_trace_dissector_handle, trace_tvb, pinfo, tree);
|
|
offset += len;
|
|
}
|
|
|
|
eth_tvb = tvb_new_subset_remaining (tvb, offset);
|
|
|
|
/*
|
|
* Delegate the rest of the packet dissection to the per-node
|
|
* next dissector in the foreach_node_to_dissector_pair list
|
|
*
|
|
* Failing that, pretend its an ethernet packet
|
|
*/
|
|
if (protocol_hint >= array_length(next_dissectors)) {
|
|
ws_debug_printf ("protocol_hint %d out of range (max %d)",
|
|
(int) protocol_hint,
|
|
(int) array_length(next_dissectors));
|
|
protocol_hint = 0;
|
|
}
|
|
/* See setup for hint == 0 below */
|
|
use_this_dissector = next_dissectors [protocol_hint];
|
|
if (protocol_hint == 0) {
|
|
maybe_protocol_id = tvb_get_guint8 (tvb, offset);
|
|
|
|
switch (maybe_protocol_id) {
|
|
case 0x45:
|
|
use_this_dissector = next_dissectors[VLIB_NODE_PROTO_HINT_IP4];
|
|
break;
|
|
case 0x60:
|
|
use_this_dissector = next_dissectors[VLIB_NODE_PROTO_HINT_IP6];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
call_dissector (use_this_dissector, eth_tvb, pinfo, tree);
|
|
return tvb_captured_length(tvb);
|
|
}
|
|
|
|
void
|
|
proto_register_vpp(void)
|
|
{
|
|
static hf_register_info vpp_hf[] = {
|
|
{ &hf_vpp_buffer_index,
|
|
{ "BufferIndex", "vpp.BufferIndex", FT_UINT32, BASE_HEX, NULL, 0x0,
|
|
NULL, HFILL },
|
|
},
|
|
{ &hf_vpp_nodename,
|
|
{ "NodeName", "vpp.NodeName", FT_STRINGZ, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL },
|
|
},
|
|
};
|
|
|
|
static hf_register_info metadata_hf[] = {
|
|
{ &hf_vpp_metadata,
|
|
{ "Metadata", "vpp.metadata",
|
|
FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL },
|
|
},
|
|
};
|
|
|
|
static hf_register_info opaque_hf[] = {
|
|
{ &hf_vpp_buffer_opaque,
|
|
{ "Opaque", "vpp.opaque",
|
|
FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL },
|
|
},
|
|
};
|
|
|
|
static hf_register_info opaque2_hf[] = {
|
|
{ &hf_vpp_buffer_opaque2,
|
|
{ "Opaque2", "vpp.opaque2",
|
|
FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL },
|
|
},
|
|
};
|
|
|
|
static hf_register_info trace_hf[] = {
|
|
{ &hf_vpp_buffer_trace,
|
|
{ "Trace", "vpp.trace", FT_STRINGZ, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL },
|
|
},
|
|
};
|
|
|
|
static gint *vpp_ett[] = {
|
|
&ett_vpp,
|
|
};
|
|
static gint *ett_metadata[] = {
|
|
&ett_vpp_metadata,
|
|
};
|
|
static gint *ett_opaque[] = {
|
|
&ett_vpp_opaque,
|
|
};
|
|
static gint *ett_opaque2[] = {
|
|
&ett_vpp_opaque2,
|
|
};
|
|
static gint *ett_trace[] = {
|
|
&ett_vpp_trace,
|
|
};
|
|
|
|
proto_vpp = proto_register_protocol("VPP Dispatch Trace", "VPP", "vpp");
|
|
proto_register_field_array(proto_vpp, vpp_hf, array_length(vpp_hf));
|
|
proto_register_subtree_array (vpp_ett, array_length(vpp_ett));
|
|
register_dissector("vpp", dissect_vpp, proto_vpp);
|
|
|
|
proto_vpp_metadata = proto_register_protocol("VPP Buffer Metadata",
|
|
"VPP-Metadata",
|
|
"vpp-metadata");
|
|
proto_register_field_array(proto_vpp_metadata, metadata_hf,
|
|
array_length(metadata_hf));
|
|
proto_register_subtree_array (ett_metadata, array_length(ett_metadata));
|
|
register_dissector("vppMetadata", dissect_vpp_metadata, proto_vpp_metadata);
|
|
|
|
proto_vpp_opaque = proto_register_protocol("VPP Buffer Opaque", "VPP-Opaque",
|
|
"vpp-opaque");
|
|
proto_register_field_array(proto_vpp_opaque, opaque_hf,
|
|
array_length(opaque_hf));
|
|
proto_register_subtree_array (ett_opaque, array_length(ett_opaque));
|
|
register_dissector("vppOpaque", dissect_vpp_opaque, proto_vpp_opaque);
|
|
|
|
proto_vpp_opaque2 = proto_register_protocol("VPP Buffer Opaque2", "VPP-Opaque2",
|
|
"vpp-opaque2");
|
|
proto_register_field_array(proto_vpp_opaque2, opaque2_hf,
|
|
array_length(opaque2_hf));
|
|
proto_register_subtree_array (ett_opaque2, array_length(ett_opaque2));
|
|
register_dissector("vppOpaque2", dissect_vpp_opaque2, proto_vpp_opaque2);
|
|
|
|
|
|
proto_vpp_trace = proto_register_protocol("VPP Buffer Trace", "VPP-Trace",
|
|
"vpp-trace");
|
|
proto_register_field_array(proto_vpp_trace, trace_hf,
|
|
array_length(trace_hf));
|
|
proto_register_subtree_array (ett_trace, array_length(ett_trace));
|
|
register_dissector("vppTrace", dissect_vpp_trace, proto_vpp_trace);
|
|
|
|
#define _(idx,dname) next_dissectors[idx] = find_dissector (#dname);
|
|
foreach_next_dissector;
|
|
#undef _
|
|
|
|
/* if all else fails, dissect data as if ethernet MAC */
|
|
next_dissectors[VLIB_NODE_PROTO_HINT_NONE] =
|
|
next_dissectors [VLIB_NODE_PROTO_HINT_ETHERNET];
|
|
}
|
|
|
|
void
|
|
proto_reg_handoff_vpp(void)
|
|
{
|
|
vpp_dissector_handle = find_dissector("vpp");
|
|
vpp_metadata_dissector_handle = find_dissector("vppMetadata");
|
|
vpp_opaque_dissector_handle = find_dissector("vppOpaque");
|
|
vpp_opaque2_dissector_handle = find_dissector("vppOpaque2");
|
|
vpp_trace_dissector_handle = find_dissector("vppTrace");
|
|
dissector_add_uint("wtap_encap", WTAP_ENCAP_VPP, vpp_dissector_handle);
|
|
}
|
|
|
|
/*
|
|
* Editor modelines - http://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:
|
|
*/
|