summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-vpp.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-vpp.c')
-rw-r--r--epan/dissectors/packet-vpp.c491
1 files changed, 491 insertions, 0 deletions
diff --git a/epan/dissectors/packet-vpp.c b/epan/dissectors/packet-vpp.c
new file mode 100644
index 00000000..8e7525fd
--- /dev/null
+++ b/epan/dissectors/packet-vpp.c
@@ -0,0 +1,491 @@
+/* packet-vpp.c
+ *
+ * Routines for the disassembly of fd.io vpp project
+ * dispatch captures
+ *
+ * Copyright 2019, Dave Barach <wireshark@barachs.net>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <epan/packet.h>
+#include <epan/expert.h>
+#include <epan/to_str.h>
+#include <epan/nlpid.h>
+#include <epan/etypes.h>
+#include <epan/ws_printf.h>
+#include <wiretap/wtap.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 int hf_vpp_major_version = -1;
+static int hf_vpp_minor_version = -1;
+static int hf_vpp_protocol_hint = -1;
+
+static expert_module_t* expert_vpp;
+
+static expert_field ei_vpp_major_version_error = EI_INIT;
+static expert_field ei_vpp_minor_version_error = EI_INIT;
+static expert_field ei_vpp_protocol_hint_error = EI_INIT;
+
+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;
+
+#define VPP_MAJOR_VERSION 1
+#define VPP_MINOR_VERSION 0
+#define IP4_TYPICAL_VERSION_LENGTH 0x45
+#define IP6_TYPICAL_VERSION_AND_TRAFFIC_CLASS 0x60
+
+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_withoutfcs) \
+_(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, packet_info *pinfo,
+ 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(pinfo->pool, 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, pinfo, 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, pinfo, 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, pinfo, 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, pinfo, 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);
+ /* If the major version doesn't match, quit on the spot */
+ if(major_version != VPP_MAJOR_VERSION) {
+ proto_item *major_version_item;
+ major_version_item =
+ proto_tree_add_item(tree, hf_vpp_major_version,
+ tvb, offset, 1, ENC_NA);
+ expert_add_info_format(pinfo, major_version_item,
+ &ei_vpp_major_version_error,
+ "Major Version Mismatch read %d not %d",
+ (int)major_version, VPP_MAJOR_VERSION);
+ return tvb_captured_length(tvb);
+ }
+ offset++;
+
+ minor_version = tvb_get_guint8(tvb, offset);
+ /* If the minor version doesn't match, make a note and try to continue */
+ if(minor_version != VPP_MINOR_VERSION) {
+ proto_item *minor_version_item;
+ minor_version_item =
+ proto_tree_add_item(tree, hf_vpp_minor_version,
+ tvb, offset, 1, ENC_NA);
+ expert_add_info_format(pinfo, minor_version_item,
+ &ei_vpp_minor_version_error,
+ "Minor Version Mismatch read %d not %d",
+ (int)minor_version, VPP_MINOR_VERSION);
+ }
+ offset++;
+
+ /* 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);
+
+ if(protocol_hint >= array_length(next_dissectors)) {
+ proto_item *protocol_hint_item;
+ protocol_hint_item =
+ proto_tree_add_item(tree, hf_vpp_protocol_hint,
+ tvb, offset, 1, ENC_NA);
+ expert_add_info_format(pinfo, protocol_hint_item,
+ &ei_vpp_protocol_hint_error,
+ "Protocol hint %d out of range, max %d",
+ (int)protocol_hint,
+ (int)array_length(next_dissectors));
+ protocol_hint = 0;
+ }
+
+ 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(pinfo->pool, 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
+ */
+ /* 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 IP4_TYPICAL_VERSION_LENGTH:
+ use_this_dissector = next_dissectors[VLIB_NODE_PROTO_HINT_IP4];
+ break;
+ case IP6_TYPICAL_VERSION_AND_TRAFFIC_CLASS:
+ 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 },
+ },
+ { &hf_vpp_major_version,
+ { "MajorVersion", "vpp.MajorVersion", FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL },
+ },
+ { &hf_vpp_minor_version,
+ { "MinorVersion", "vpp.MinorVersion", FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL },
+ },
+ { &hf_vpp_protocol_hint,
+ { "ProtocolHint", "vpp.ProtocolHint", FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL },
+ },
+ };
+
+ static ei_register_info vpp_ei[] = {
+ { &ei_vpp_major_version_error,
+ { "vpp.bad_major_version", PI_MALFORMED, PI_ERROR,
+ "Bad Major Version", EXPFILL }},
+ { &ei_vpp_minor_version_error,
+ { "vpp.bad_minor_version", PI_UNDECODED, PI_WARN,
+ "Bad Minor Version", EXPFILL }},
+ { &ei_vpp_protocol_hint_error,
+ { "vpp.bad_protocol_hint", PI_PROTOCOL, PI_WARN,
+ "Bad Protocol Hint", EXPFILL }},
+ };
+
+ 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);
+
+ expert_vpp = expert_register_protocol(proto_vpp);
+ expert_register_field_array(expert_vpp, vpp_ei, array_length(vpp_ei));
+
+ 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 - 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:
+ */