Metadata / opaque formatting belongs in vpp
[vpp.git] / extras / wireshark / packet-vpp.c
1 /* packet-vpp.c
2  * 
3  * Routines for the disassembly of fd.io vpp project 
4  * dispatch captures
5  *
6  * Copyright (c) 2018 Cisco and/or its affiliates.
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at:
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * 
19  * This version is not to be upstreamed as-is, since it hooks up the
20  * vpp dissector to WTAP_ENCAP_USER13, a test encap type.
21  */
22
23 #include "config.h"
24
25 #include <epan/packet.h>
26 #include <epan/expert.h>
27 #include <epan/to_str.h>
28 #include <epan/in_cksum.h>
29 #include <epan/nlpid.h>
30 #include <epan/etypes.h>
31 #include <stdio.h>
32 #include <wsutil/ws_printf.h>
33
34 void proto_register_vpp(void);
35 void proto_reg_handoff_vpp(void);
36
37 static int proto_vpp = -1;
38 static int proto_vpp_metadata = -1;
39 static int proto_vpp_opaque = -1;
40 static int proto_vpp_opaque2 = -1;
41 static int proto_vpp_trace = -1;
42 static int hf_vpp_nodename = -1;
43 static int hf_vpp_metadata = -1;
44 static int hf_vpp_buffer_index = -1;
45 static int hf_vpp_buffer_opaque = -1;
46 static int hf_vpp_buffer_opaque2 = -1;
47 static int hf_vpp_buffer_trace = -1;
48
49 static gint ett_vpp = -1;
50 static gint ett_vpp_opaque = -1;
51 static gint ett_vpp_opaque2 = -1;
52 static gint ett_vpp_metadata = -1;
53 static gint ett_vpp_trace = -1;
54
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;
60
61 typedef enum
62   {
63     VLIB_NODE_PROTO_HINT_NONE = 0,
64     VLIB_NODE_PROTO_HINT_ETHERNET,
65     VLIB_NODE_PROTO_HINT_IP4,
66     VLIB_NODE_PROTO_HINT_IP6,
67     VLIB_NODE_PROTO_HINT_TCP,
68     VLIB_NODE_PROTO_HINT_UDP,
69     VLIB_NODE_N_PROTO_HINTS,
70   } vlib_node_proto_hint_t;
71
72 static dissector_handle_t next_dissectors[VLIB_NODE_N_PROTO_HINTS];
73
74 /* List of next dissectors hints that we know about */
75 #define foreach_next_dissector                  \
76 _(VLIB_NODE_PROTO_HINT_ETHERNET, eth_maybefcs)  \
77 _(VLIB_NODE_PROTO_HINT_IP4, ip)                 \
78 _(VLIB_NODE_PROTO_HINT_IP6, ipv6)               \
79 _(VLIB_NODE_PROTO_HINT_TCP, tcp)                \
80 _(VLIB_NODE_PROTO_HINT_UDP, udp)
81
82 static void
83 add_multi_line_string_to_tree(proto_tree *tree, tvbuff_t *tvb, gint start,
84   gint len, int hf)
85 {
86     gint next;
87     int  line_len;
88     int  data_len;
89
90     while (len > 0) {
91         line_len = tvb_find_line_end(tvb, start, len, &next, FALSE);
92         data_len = next - start;
93         proto_tree_add_string(tree, hf, tvb, start, data_len, 
94                               tvb_format_stringzpad(tvb, start, line_len));
95         start += data_len;
96         len   -= data_len;
97     }
98 }
99
100 static int
101 dissect_vpp_metadata (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, 
102                     void* data _U_)
103 {
104     int         offset   = 0;
105     proto_item *ti;
106     proto_tree *metadata_tree;
107     gint metadata_string_length;
108     
109     col_set_str(pinfo->cinfo, COL_PROTOCOL, "VPP-Metadata");
110     col_clear(pinfo->cinfo, COL_INFO);
111
112     ti = proto_tree_add_item(tree, proto_vpp_metadata, tvb, offset, -1, ENC_NA);
113     metadata_tree = proto_item_add_subtree(ti, ett_vpp_metadata);
114
115     /* How long is the metadata string? */
116     metadata_string_length = tvb_strsize (tvb, offset);
117     
118     add_multi_line_string_to_tree (metadata_tree, tvb, 0,
119                                    metadata_string_length, 
120                                    hf_vpp_metadata);
121     return tvb_captured_length(tvb);
122 }
123
124 static int
125 dissect_vpp_trace (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, 
126                     void* data _U_)
127 {
128     int         offset   = 0;
129     proto_item *ti;
130     proto_tree *trace_tree;
131     gint trace_string_length;
132     
133     col_set_str(pinfo->cinfo, COL_PROTOCOL, "VPP-Trace");
134     col_clear(pinfo->cinfo, COL_INFO);
135
136     ti = proto_tree_add_item(tree, proto_vpp_trace, tvb, offset, -1, ENC_NA);
137     trace_tree = proto_item_add_subtree(ti, ett_vpp_trace);
138
139     /* How long is the trace string? */
140     trace_string_length = tvb_strsize (tvb, offset);
141     
142     add_multi_line_string_to_tree (trace_tree, tvb, 0,
143                                    trace_string_length, 
144                                    hf_vpp_buffer_trace);
145     return tvb_captured_length(tvb);
146 }
147
148
149 static int
150 dissect_vpp_opaque (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, 
151                     void* data _U_)
152 {
153     int         offset   = 0;
154     proto_item *ti;
155     proto_tree *opaque_tree;
156     gint opaque_string_length;
157
158     col_set_str(pinfo->cinfo, COL_PROTOCOL, "VPP-Opaque");
159     col_clear(pinfo->cinfo, COL_INFO);
160
161     ti = proto_tree_add_item(tree, proto_vpp_opaque, tvb, offset, -1, ENC_NA);
162     opaque_tree = proto_item_add_subtree(ti, ett_vpp_opaque);
163
164     opaque_string_length = tvb_strsize (tvb, offset); 
165     add_multi_line_string_to_tree (opaque_tree, tvb, 0, opaque_string_length, 
166                                    hf_vpp_buffer_opaque);
167
168     return tvb_captured_length(tvb);
169 }
170
171 static int
172 dissect_vpp_opaque2 (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, 
173                     void* data _U_)
174 {
175     int         offset   = 0;
176     proto_item *ti;
177     proto_tree *opaque2_tree;
178     gint opaque2_string_length;
179
180     col_set_str(pinfo->cinfo, COL_PROTOCOL, "VPP-Opaque2");
181     col_clear(pinfo->cinfo, COL_INFO);
182
183     ti = proto_tree_add_item(tree, proto_vpp_opaque2, tvb, offset, -1, ENC_NA);
184     opaque2_tree = proto_item_add_subtree(ti, ett_vpp_opaque2);
185
186     opaque2_string_length = tvb_strsize (tvb, offset); 
187     add_multi_line_string_to_tree (opaque2_tree, tvb, 0, opaque2_string_length, 
188                                    hf_vpp_buffer_opaque2);
189
190     return tvb_captured_length(tvb);
191 }
192
193
194 static int
195 dissect_vpp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
196 {
197     proto_item *ti;
198     proto_tree *vpp_tree;
199     tvbuff_t *metadata_tvb, *opaque_tvb, *opaque2_tvb, *eth_tvb, *trace_tvb;
200     int         offset   = 0;
201     guint8 major_version, minor_version, string_count, protocol_hint;
202     guint8 *name;
203     guint len;
204     guint8 maybe_protocol_id; 
205     dissector_handle_t use_this_dissector;
206
207     col_set_str(pinfo->cinfo, COL_PROTOCOL, "VPP");
208     col_clear(pinfo->cinfo, COL_INFO);
209
210     ti = proto_tree_add_item(tree, proto_vpp, tvb, offset, -1, ENC_NA);
211     vpp_tree = proto_item_add_subtree(ti, ett_vpp);
212
213     major_version = tvb_get_guint8 (tvb, offset);
214     offset++;
215
216     minor_version = tvb_get_guint8 (tvb, offset);
217     offset++;
218
219     if (major_version != 1 || minor_version != 0)
220         ws_debug_printf ("WARNING: version mismatch (%d, %d)",
221                          major_version, minor_version);
222
223     /* Number of counted strings in this trace record */
224     string_count = tvb_get_guint8 (tvb, offset);
225     offset++;
226
227     /* 
228      * Hint: protocol which should be at b->data[b->current_data]
229      * It will be a while before vpp sends useful hints for every
230      * possible node, see heuristic below
231      */
232     protocol_hint = tvb_get_guint8 (tvb, offset);
233     offset++;
234
235     /* Buffer Index */
236     proto_tree_add_item(vpp_tree, hf_vpp_buffer_index, tvb,
237                         offset, 4, ENC_BIG_ENDIAN);
238     offset += 4;
239
240     /* Nodename */
241     len = tvb_strsize (tvb, offset); 
242     name = tvb_get_string_enc (wmem_packet_scope(), tvb, offset, len,
243                                ENC_ASCII);
244     proto_tree_add_string (tree, hf_vpp_nodename, tvb, offset, len, name);
245     offset += len;
246
247     /* Metadata */
248     len = tvb_strsize (tvb, offset);
249     metadata_tvb = tvb_new_subset_remaining (tvb, offset);
250     call_dissector (vpp_metadata_dissector_handle, metadata_tvb, pinfo, tree);
251     offset += len;
252
253     /* Opaque */
254     len = tvb_strsize (tvb, offset);
255     opaque_tvb = tvb_new_subset_remaining (tvb, offset);
256     call_dissector (vpp_opaque_dissector_handle, opaque_tvb, pinfo, tree);
257     offset += len;
258
259     /* Opaque2 */
260     len = tvb_strsize (tvb, offset);
261     opaque2_tvb = tvb_new_subset_remaining (tvb, offset);
262     call_dissector (vpp_opaque2_dissector_handle, opaque2_tvb, pinfo, tree);
263     offset += len;
264
265     /* Trace, if present */
266     if (string_count > 4)
267     {
268         len = tvb_strsize (tvb, offset);
269         trace_tvb = tvb_new_subset_remaining (tvb, offset);
270         call_dissector (vpp_trace_dissector_handle, trace_tvb, pinfo, tree);
271         offset += len;
272     }
273
274     eth_tvb = tvb_new_subset_remaining (tvb, offset);
275     
276     /* 
277      * Delegate the rest of the packet dissection to the per-node
278      * next dissector in the foreach_node_to_dissector_pair list
279      *
280      * Failing that, pretend its an ethernet packet
281      */ 
282     if (protocol_hint >= array_length(next_dissectors)) {
283         ws_debug_printf ("protocol_hint %d out of range (max %d)",
284                          (int) protocol_hint, 
285                          (int) array_length(next_dissectors));
286         protocol_hint = 0;
287     }
288     /* See setup for hint == 0 below */
289     use_this_dissector = next_dissectors [protocol_hint];
290     if (protocol_hint == 0) {
291         maybe_protocol_id = tvb_get_guint8 (tvb, offset);
292             
293         switch (maybe_protocol_id) {
294         case 0x45:
295             use_this_dissector = next_dissectors[VLIB_NODE_PROTO_HINT_IP4];
296             break;
297         case 0x60:
298             use_this_dissector = next_dissectors[VLIB_NODE_PROTO_HINT_IP6];
299             break;
300         default:
301             break;
302         }
303     }
304     call_dissector (use_this_dissector, eth_tvb, pinfo, tree);
305     return tvb_captured_length(tvb);
306 }
307
308 void
309 proto_register_vpp(void)
310 {
311   static hf_register_info vpp_hf[] = {
312       { &hf_vpp_buffer_index,
313         { "BufferIndex", "vpp.BufferIndex",  FT_UINT32, BASE_HEX, NULL, 0x0,
314           NULL, HFILL },
315       },
316       { &hf_vpp_nodename,
317         { "NodeName", "vpp.NodeName",  FT_STRINGZ, BASE_NONE, NULL, 0x0,
318           NULL, HFILL },
319       },
320   };
321
322   static hf_register_info metadata_hf[] = {
323       { &hf_vpp_metadata,
324         { "Metadata", "vpp.metadata",  
325           FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL },
326       },
327   };
328
329   static hf_register_info opaque_hf[] = {
330       { &hf_vpp_buffer_opaque,
331         { "Opaque", "vpp.opaque",  
332           FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL },
333       },
334   };
335
336   static hf_register_info opaque2_hf[] = {
337       { &hf_vpp_buffer_opaque2,
338         { "Opaque2", "vpp.opaque2",  
339           FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL },
340       },
341   };
342
343   static hf_register_info trace_hf[] = {
344       { &hf_vpp_buffer_trace,
345         { "Trace", "vpp.trace",  FT_STRINGZ, BASE_NONE, NULL, 0x0,
346           NULL, HFILL },
347       },
348   };
349
350   static gint *vpp_ett[] = {
351     &ett_vpp,
352   };
353   static gint *ett_metadata[] = {
354     &ett_vpp_metadata,
355   };
356   static gint *ett_opaque[] = {
357     &ett_vpp_opaque,
358   };
359   static gint *ett_opaque2[] = {
360     &ett_vpp_opaque2,
361   };
362   static gint *ett_trace[] = {
363     &ett_vpp_trace,
364   };
365
366   proto_vpp = proto_register_protocol("VPP Dispatch Trace", "VPP", "vpp");
367   proto_register_field_array(proto_vpp, vpp_hf, array_length(vpp_hf));
368   proto_register_subtree_array (vpp_ett, array_length(vpp_ett));
369   register_dissector("vpp", dissect_vpp, proto_vpp);
370
371   proto_vpp_metadata = proto_register_protocol("VPP Buffer Metadata", 
372                                                "VPP-Metadata", 
373                                                "vpp-metadata");
374   proto_register_field_array(proto_vpp_metadata, metadata_hf, 
375                              array_length(metadata_hf));
376   proto_register_subtree_array (ett_metadata, array_length(ett_metadata));
377   register_dissector("vppMetadata", dissect_vpp_metadata, proto_vpp_metadata);
378
379   proto_vpp_opaque = proto_register_protocol("VPP Buffer Opaque", "VPP-Opaque", 
380                                              "vpp-opaque");
381   proto_register_field_array(proto_vpp_opaque, opaque_hf, 
382                              array_length(opaque_hf));
383   proto_register_subtree_array (ett_opaque, array_length(ett_opaque));
384   register_dissector("vppOpaque", dissect_vpp_opaque, proto_vpp_opaque);
385
386   proto_vpp_opaque2 = proto_register_protocol("VPP Buffer Opaque2", "VPP-Opaque2", 
387                                              "vpp-opaque2");
388   proto_register_field_array(proto_vpp_opaque2, opaque2_hf, 
389                              array_length(opaque2_hf));
390   proto_register_subtree_array (ett_opaque2, array_length(ett_opaque2));
391   register_dissector("vppOpaque2", dissect_vpp_opaque2, proto_vpp_opaque2);
392
393
394   proto_vpp_trace = proto_register_protocol("VPP Buffer Trace", "VPP-Trace", 
395                                              "vpp-trace");
396   proto_register_field_array(proto_vpp_trace, trace_hf, 
397                              array_length(trace_hf));
398   proto_register_subtree_array (ett_trace, array_length(ett_trace));
399   register_dissector("vppTrace", dissect_vpp_trace, proto_vpp_trace);
400   
401 #define _(idx,dname) next_dissectors[idx] = find_dissector (#dname);
402   foreach_next_dissector;
403 #undef _
404
405   /* if all else fails, dissect data as if ethernet MAC */
406   next_dissectors[VLIB_NODE_PROTO_HINT_NONE] = 
407       next_dissectors [VLIB_NODE_PROTO_HINT_ETHERNET];
408 }
409
410 void
411 proto_reg_handoff_vpp(void)
412 {
413     vpp_dissector_handle = find_dissector("vpp");
414     vpp_metadata_dissector_handle = find_dissector("vppMetadata");
415     vpp_opaque_dissector_handle = find_dissector("vppOpaque");
416     vpp_opaque2_dissector_handle = find_dissector("vppOpaque2");
417     vpp_trace_dissector_handle = find_dissector("vppTrace");
418     dissector_add_uint("wtap_encap", WTAP_ENCAP_USER13, vpp_dissector_handle);
419 }
420
421 /*
422  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
423  *
424  * Local variables:
425  * c-basic-offset: 4
426  * tab-width: 8
427  * indent-tabs-mode: nil
428  * End:
429  *
430  * vi: set shiftwidth=4 tabstop=8 expandtab:
431  * :indentSize=4:tabSize=8:noTabs=true:
432  */