3 * Routines for the disassembly of fd.io vpp project
6 * Copyright 2019, Dave Barach <wireshark@barachs.net>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include <epan/packet.h>
18 #include <epan/expert.h>
19 #include <epan/to_str.h>
20 #include <epan/nlpid.h>
21 #include <epan/etypes.h>
22 #include <epan/ws_printf.h>
23 #include <wiretap/wtap.h>
25 void proto_register_vpp(void);
26 void proto_reg_handoff_vpp(void);
29 static int proto_vpp_metadata
;
30 static int proto_vpp_opaque
;
31 static int proto_vpp_opaque2
;
32 static int proto_vpp_trace
;
33 static int hf_vpp_nodename
;
34 static int hf_vpp_metadata
;
35 static int hf_vpp_buffer_index
;
36 static int hf_vpp_buffer_opaque
;
37 static int hf_vpp_buffer_opaque2
;
38 static int hf_vpp_buffer_trace
;
39 static int hf_vpp_major_version
;
40 static int hf_vpp_minor_version
;
41 static int hf_vpp_protocol_hint
;
43 static expert_module_t
* expert_vpp
;
45 static expert_field ei_vpp_major_version_error
;
46 static expert_field ei_vpp_minor_version_error
;
47 static expert_field ei_vpp_protocol_hint_error
;
50 static int ett_vpp_opaque
;
51 static int ett_vpp_opaque2
;
52 static int ett_vpp_metadata
;
53 static int ett_vpp_trace
;
55 static dissector_handle_t vpp_dissector_handle
;
56 static dissector_handle_t vpp_opaque_dissector_handle
;
57 static dissector_handle_t vpp_opaque2_dissector_handle
;
58 static dissector_handle_t vpp_metadata_dissector_handle
;
59 static dissector_handle_t vpp_trace_dissector_handle
;
61 #define VPP_MAJOR_VERSION 1
62 #define VPP_MINOR_VERSION 0
63 #define IP4_TYPICAL_VERSION_LENGTH 0x45
64 #define IP6_TYPICAL_VERSION_AND_TRAFFIC_CLASS 0x60
68 VLIB_NODE_PROTO_HINT_NONE
= 0,
69 VLIB_NODE_PROTO_HINT_ETHERNET
,
70 VLIB_NODE_PROTO_HINT_IP4
,
71 VLIB_NODE_PROTO_HINT_IP6
,
72 VLIB_NODE_PROTO_HINT_TCP
,
73 VLIB_NODE_PROTO_HINT_UDP
,
74 VLIB_NODE_N_PROTO_HINTS
,
75 } vlib_node_proto_hint_t
;
77 static dissector_handle_t next_dissectors
[VLIB_NODE_N_PROTO_HINTS
];
79 /* List of next dissectors hints that we know about */
80 #define foreach_next_dissector \
81 _(VLIB_NODE_PROTO_HINT_ETHERNET, eth_withoutfcs) \
82 _(VLIB_NODE_PROTO_HINT_IP4, ip) \
83 _(VLIB_NODE_PROTO_HINT_IP6, ipv6) \
84 _(VLIB_NODE_PROTO_HINT_TCP, tcp) \
85 _(VLIB_NODE_PROTO_HINT_UDP, udp)
88 add_multi_line_string_to_tree(proto_tree
*tree
, tvbuff_t
*tvb
, packet_info
*pinfo
,
97 line_len
= tvb_find_line_end(tvb
, start
, len
, &next
, false);
98 data_len
= next
- start
;
99 proto_tree_add_string(tree
, hf
, tvb
, start
, data_len
,
100 tvb_format_stringzpad(pinfo
->pool
, tvb
, start
, line_len
));
107 dissect_vpp_metadata(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
112 proto_tree
*metadata_tree
;
113 int metadata_string_length
;
115 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "VPP-Metadata");
116 col_clear(pinfo
->cinfo
, COL_INFO
);
118 ti
= proto_tree_add_item(tree
, proto_vpp_metadata
, tvb
, offset
, -1, ENC_NA
);
119 metadata_tree
= proto_item_add_subtree(ti
, ett_vpp_metadata
);
121 /* How long is the metadata string? */
122 metadata_string_length
= tvb_strsize(tvb
, offset
);
124 add_multi_line_string_to_tree(metadata_tree
, tvb
, pinfo
, 0,
125 metadata_string_length
,
127 return tvb_captured_length(tvb
);
131 dissect_vpp_trace(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
136 proto_tree
*trace_tree
;
137 int trace_string_length
;
139 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "VPP-Trace");
140 col_clear(pinfo
->cinfo
, COL_INFO
);
142 ti
= proto_tree_add_item(tree
, proto_vpp_trace
, tvb
, offset
, -1, ENC_NA
);
143 trace_tree
= proto_item_add_subtree(ti
, ett_vpp_trace
);
145 /* How long is the trace string? */
146 trace_string_length
= tvb_strsize(tvb
, offset
);
148 add_multi_line_string_to_tree(trace_tree
, tvb
, pinfo
, 0,
150 hf_vpp_buffer_trace
);
151 return tvb_captured_length(tvb
);
156 dissect_vpp_opaque(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
161 proto_tree
*opaque_tree
;
162 int opaque_string_length
;
164 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "VPP-Opaque");
165 col_clear(pinfo
->cinfo
, COL_INFO
);
167 ti
= proto_tree_add_item(tree
, proto_vpp_opaque
, tvb
, offset
, -1, ENC_NA
);
168 opaque_tree
= proto_item_add_subtree(ti
, ett_vpp_opaque
);
170 opaque_string_length
= tvb_strsize(tvb
, offset
);
171 add_multi_line_string_to_tree(opaque_tree
, tvb
, pinfo
, 0, opaque_string_length
,
172 hf_vpp_buffer_opaque
);
174 return tvb_captured_length(tvb
);
178 dissect_vpp_opaque2(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
183 proto_tree
*opaque2_tree
;
184 int opaque2_string_length
;
186 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "VPP-Opaque2");
187 col_clear(pinfo
->cinfo
, COL_INFO
);
189 ti
= proto_tree_add_item(tree
, proto_vpp_opaque2
, tvb
, offset
, -1, ENC_NA
);
190 opaque2_tree
= proto_item_add_subtree(ti
, ett_vpp_opaque2
);
192 opaque2_string_length
= tvb_strsize(tvb
, offset
);
193 add_multi_line_string_to_tree(opaque2_tree
, tvb
, pinfo
, 0, opaque2_string_length
,
194 hf_vpp_buffer_opaque2
);
196 return tvb_captured_length(tvb
);
201 dissect_vpp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
204 proto_tree
*vpp_tree
;
205 tvbuff_t
*metadata_tvb
, *opaque_tvb
, *opaque2_tvb
, *eth_tvb
, *trace_tvb
;
207 uint8_t major_version
, minor_version
, string_count
, protocol_hint
;
210 uint8_t maybe_protocol_id
;
211 dissector_handle_t use_this_dissector
;
213 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "VPP");
214 col_clear(pinfo
->cinfo
, COL_INFO
);
216 ti
= proto_tree_add_item(tree
, proto_vpp
, tvb
, offset
, -1, ENC_NA
);
217 vpp_tree
= proto_item_add_subtree(ti
, ett_vpp
);
219 major_version
= tvb_get_uint8(tvb
, offset
);
220 /* If the major version doesn't match, quit on the spot */
221 if(major_version
!= VPP_MAJOR_VERSION
) {
222 proto_item
*major_version_item
;
224 proto_tree_add_item(tree
, hf_vpp_major_version
,
225 tvb
, offset
, 1, ENC_NA
);
226 expert_add_info_format(pinfo
, major_version_item
,
227 &ei_vpp_major_version_error
,
228 "Major Version Mismatch read %d not %d",
229 (int)major_version
, VPP_MAJOR_VERSION
);
230 return tvb_captured_length(tvb
);
234 minor_version
= tvb_get_uint8(tvb
, offset
);
235 /* If the minor version doesn't match, make a note and try to continue */
236 if(minor_version
!= VPP_MINOR_VERSION
) {
237 proto_item
*minor_version_item
;
239 proto_tree_add_item(tree
, hf_vpp_minor_version
,
240 tvb
, offset
, 1, ENC_NA
);
241 expert_add_info_format(pinfo
, minor_version_item
,
242 &ei_vpp_minor_version_error
,
243 "Minor Version Mismatch read %d not %d",
244 (int)minor_version
, VPP_MINOR_VERSION
);
248 /* Number of counted strings in this trace record */
249 string_count
= tvb_get_uint8(tvb
, offset
);
253 * Hint: protocol which should be at b->data[b->current_data]
254 * It will be a while before vpp sends useful hints for every
255 * possible node, see heuristic below.
257 protocol_hint
= tvb_get_uint8(tvb
, offset
);
259 if(protocol_hint
>= array_length(next_dissectors
)) {
260 proto_item
*protocol_hint_item
;
262 proto_tree_add_item(tree
, hf_vpp_protocol_hint
,
263 tvb
, offset
, 1, ENC_NA
);
264 expert_add_info_format(pinfo
, protocol_hint_item
,
265 &ei_vpp_protocol_hint_error
,
266 "Protocol hint %d out of range, max %d",
268 (int)array_length(next_dissectors
));
275 proto_tree_add_item(vpp_tree
, hf_vpp_buffer_index
, tvb
,
276 offset
, 4, ENC_BIG_ENDIAN
);
280 len
= tvb_strsize(tvb
, offset
);
281 name
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, len
,
283 proto_tree_add_string(tree
, hf_vpp_nodename
, tvb
, offset
, len
, name
);
287 len
= tvb_strsize(tvb
, offset
);
288 metadata_tvb
= tvb_new_subset_remaining(tvb
, offset
);
289 call_dissector(vpp_metadata_dissector_handle
, metadata_tvb
, pinfo
, tree
);
293 len
= tvb_strsize(tvb
, offset
);
294 opaque_tvb
= tvb_new_subset_remaining(tvb
, offset
);
295 call_dissector(vpp_opaque_dissector_handle
, opaque_tvb
, pinfo
, tree
);
299 len
= tvb_strsize(tvb
, offset
);
300 opaque2_tvb
= tvb_new_subset_remaining(tvb
, offset
);
301 call_dissector(vpp_opaque2_dissector_handle
, opaque2_tvb
, pinfo
, tree
);
304 /* Trace, if present */
305 if(string_count
> 4) {
306 len
= tvb_strsize(tvb
, offset
);
307 trace_tvb
= tvb_new_subset_remaining(tvb
, offset
);
308 call_dissector(vpp_trace_dissector_handle
, trace_tvb
, pinfo
, tree
);
312 eth_tvb
= tvb_new_subset_remaining(tvb
, offset
);
315 * Delegate the rest of the packet dissection to the per-node
316 * next dissector in the foreach_node_to_dissector_pair list
318 * Failing that, pretend its an ethernet packet
320 /* See setup for hint == 0 below */
321 use_this_dissector
= next_dissectors
[protocol_hint
];
322 if(protocol_hint
== 0) {
323 maybe_protocol_id
= tvb_get_uint8(tvb
, offset
);
325 switch(maybe_protocol_id
) {
326 case IP4_TYPICAL_VERSION_LENGTH
:
327 use_this_dissector
= next_dissectors
[VLIB_NODE_PROTO_HINT_IP4
];
329 case IP6_TYPICAL_VERSION_AND_TRAFFIC_CLASS
:
330 use_this_dissector
= next_dissectors
[VLIB_NODE_PROTO_HINT_IP6
];
336 call_dissector(use_this_dissector
, eth_tvb
, pinfo
, tree
);
337 return tvb_captured_length(tvb
);
341 proto_register_vpp(void)
343 static hf_register_info vpp_hf
[] = {
344 { &hf_vpp_buffer_index
,
345 { "BufferIndex", "vpp.BufferIndex", FT_UINT32
, BASE_HEX
, NULL
, 0x0,
349 { "NodeName", "vpp.NodeName", FT_STRINGZ
, BASE_NONE
, NULL
, 0x0,
352 { &hf_vpp_major_version
,
353 { "MajorVersion", "vpp.MajorVersion", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
356 { &hf_vpp_minor_version
,
357 { "MinorVersion", "vpp.MinorVersion", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
360 { &hf_vpp_protocol_hint
,
361 { "ProtocolHint", "vpp.ProtocolHint", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
366 static ei_register_info vpp_ei
[] = {
367 { &ei_vpp_major_version_error
,
368 { "vpp.bad_major_version", PI_MALFORMED
, PI_ERROR
,
369 "Bad Major Version", EXPFILL
}},
370 { &ei_vpp_minor_version_error
,
371 { "vpp.bad_minor_version", PI_UNDECODED
, PI_WARN
,
372 "Bad Minor Version", EXPFILL
}},
373 { &ei_vpp_protocol_hint_error
,
374 { "vpp.bad_protocol_hint", PI_PROTOCOL
, PI_WARN
,
375 "Bad Protocol Hint", EXPFILL
}},
378 static hf_register_info metadata_hf
[] = {
380 { "Metadata", "vpp.metadata",
381 FT_STRINGZ
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
},
385 static hf_register_info opaque_hf
[] = {
386 { &hf_vpp_buffer_opaque
,
387 { "Opaque", "vpp.opaque",
388 FT_STRINGZ
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
},
392 static hf_register_info opaque2_hf
[] = {
393 { &hf_vpp_buffer_opaque2
,
394 { "Opaque2", "vpp.opaque2",
395 FT_STRINGZ
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
},
399 static hf_register_info trace_hf
[] = {
400 { &hf_vpp_buffer_trace
,
401 { "Trace", "vpp.trace", FT_STRINGZ
, BASE_NONE
, NULL
, 0x0,
406 static int *vpp_ett
[] = {
409 static int *ett_metadata
[] = {
412 static int *ett_opaque
[] = {
415 static int *ett_opaque2
[] = {
418 static int *ett_trace
[] = {
422 proto_vpp
= proto_register_protocol("VPP Dispatch Trace", "VPP", "vpp");
423 proto_register_field_array(proto_vpp
, vpp_hf
, array_length(vpp_hf
));
424 proto_register_subtree_array(vpp_ett
, array_length(vpp_ett
));
425 register_dissector("vpp", dissect_vpp
, proto_vpp
);
427 expert_vpp
= expert_register_protocol(proto_vpp
);
428 expert_register_field_array(expert_vpp
, vpp_ei
, array_length(vpp_ei
));
430 proto_vpp_metadata
= proto_register_protocol("VPP Buffer Metadata",
433 proto_register_field_array(proto_vpp_metadata
, metadata_hf
,
434 array_length(metadata_hf
));
435 proto_register_subtree_array(ett_metadata
, array_length(ett_metadata
));
436 register_dissector("vppMetadata", dissect_vpp_metadata
, proto_vpp_metadata
);
438 proto_vpp_opaque
= proto_register_protocol("VPP Buffer Opaque", "VPP-Opaque",
440 proto_register_field_array(proto_vpp_opaque
, opaque_hf
,
441 array_length(opaque_hf
));
442 proto_register_subtree_array(ett_opaque
, array_length(ett_opaque
));
443 register_dissector("vppOpaque", dissect_vpp_opaque
, proto_vpp_opaque
);
445 proto_vpp_opaque2
= proto_register_protocol("VPP Buffer Opaque2",
446 "VPP-Opaque2", "vpp-opaque2");
447 proto_register_field_array(proto_vpp_opaque2
, opaque2_hf
,
448 array_length(opaque2_hf
));
449 proto_register_subtree_array(ett_opaque2
, array_length(ett_opaque2
));
450 register_dissector("vppOpaque2", dissect_vpp_opaque2
, proto_vpp_opaque2
);
453 proto_vpp_trace
= proto_register_protocol("VPP Buffer Trace", "VPP-Trace",
455 proto_register_field_array(proto_vpp_trace
, trace_hf
,
456 array_length(trace_hf
));
457 proto_register_subtree_array(ett_trace
, array_length(ett_trace
));
458 register_dissector("vppTrace", dissect_vpp_trace
, proto_vpp_trace
);
460 #define _(idx,dname) next_dissectors[idx] = find_dissector(#dname);
461 foreach_next_dissector
;
464 /* if all else fails, dissect data as if ethernet MAC */
465 next_dissectors
[VLIB_NODE_PROTO_HINT_NONE
] =
466 next_dissectors
[VLIB_NODE_PROTO_HINT_ETHERNET
];
470 proto_reg_handoff_vpp(void)
472 vpp_dissector_handle
= find_dissector("vpp");
473 vpp_metadata_dissector_handle
= find_dissector("vppMetadata");
474 vpp_opaque_dissector_handle
= find_dissector("vppOpaque");
475 vpp_opaque2_dissector_handle
= find_dissector("vppOpaque2");
476 vpp_trace_dissector_handle
= find_dissector("vppTrace");
477 dissector_add_uint("wtap_encap", WTAP_ENCAP_VPP
, vpp_dissector_handle
);
481 * Editor modelines - https://www.wireshark.org/tools/modelines.html
486 * indent-tabs-mode: nil
489 * vi: set shiftwidth=4 tabstop=8 expandtab:
490 * :indentSize=4:tabSize=8:noTabs=true: