1 /* packet-geneve.c
2  * Routines for Geneve - Generic Network Virtualization Encapsulation
3  * https://tools.ietf.org/html/draft-ietf-nvo3-geneve
4  *
5  * Copyright (c) 2014 VMware, Inc. All Rights Reserved.
6  * Author: Jesse Gross <jesse@nicira.com>
7  *
8  * Copyright 2021, Atul Sharma <asharm37@ncsu.edu>
9  *
10  * Wireshark - Network traffic analyzer
11  * By Gerald Combs <gerald@wireshark.org>
12  * Copyright 1998 Gerald Combs
13  *
14  * SPDX-License-Identifier: GPL-2.0-or-later
15  */
16 
17 
18 #include "config.h"
19 
20 #include <epan/packet.h>
21 #include <epan/etypes.h>
22 #include <epan/expert.h>
23 
24 #define UDP_PORT_GENEVE  6081
25 #define GENEVE_VER 0
26 
27 #define VER_SHIFT 6
28 #define HDR_OPTS_LEN_MASK 0x3F
29 
30 #define FLAG_OAM (1 << 7)
31 
32 #define OPT_TYPE_CRITICAL (1 << 7)
33 #define OPT_FLAGS_SHIFT 5
34 #define OPT_LEN_MASK 0x1F
35 
36 static const range_string class_id_names[] = {
37     { 0, 0xFF, "Standard" },
38     { 0x0100, 0x0100, "Linux" },
39     { 0x0101, 0x0101, "Open vSwitch" },
40     { 0x0102, 0x0102, "Open Virtual Networking (OVN)" },
41     { 0x0103, 0x0103, "In-band Network Telemetry (INT)" },
42     { 0x0104, 0x0104, "VMware" },
43     { 0x0105, 0x0105, "Amazon.com, Inc."},
44     { 0x0106, 0x0106, "Cisco Systems, Inc." },
45     { 0x0107, 0x0107, "Oracle Corporation" },
46     { 0x0108, 0x0110, "Amazon.com, Inc." },
47     { 0x0111, 0x0118, "IBM" },
48     { 0x0119, 0x0128, "Ericsson" },
49     { 0x0129, 0xFEFF, "Unassigned" },
50     { 0xFFF0, 0xFFFF, "Experimental" },
51     { 0, 0, NULL }
52 };
53 
54 void proto_register_geneve(void);
55 void proto_reg_handoff_geneve(void);
56 
57 static int proto_geneve = -1;
58 
59 static int hf_geneve_version = -1;
60 static int hf_geneve_flags = -1;
61 static int hf_geneve_flag_oam = -1;
62 static int hf_geneve_flag_critical = -1;
63 static int hf_geneve_flag_reserved = -1;
64 static int hf_geneve_proto_type = -1;
65 static int hf_geneve_vni = -1;
66 static int hf_geneve_reserved = -1;
67 static int hf_geneve_options = -1;
68 static int hf_geneve_option_class = -1;
69 static int hf_geneve_option_type = -1;
70 static int hf_geneve_option_type_critical = -1;
71 static int hf_geneve_option_flags = -1;
72 static int hf_geneve_option_flags_reserved = -1;
73 static int hf_geneve_option_length = -1;
74 static int hf_geneve_opt_unknown = -1;
75 static int hf_geneve_opt_unknown_data = -1;
76 
77 static int ett_geneve = -1;
78 static int ett_geneve_flags = -1;
79 static int ett_geneve_opt_flags = -1;
80 static int ett_geneve_options = -1;
81 static int ett_geneve_unknown_opt = -1;
82 
83 static expert_field ei_geneve_ver_unknown = EI_INIT;
84 static expert_field ei_geneve_opt_len_invalid = EI_INIT;
85 
86 static dissector_table_t ethertype_dissector_table;
87 
88 static const char *
89 format_unknown_option_name(guint16 opt_class, guint8 opt_type)
90 {
91     const char *name;
92 
93     name = wmem_strdup_printf(wmem_packet_scope(),
94                               "Unknown, Class: %s (0x%04x) Type: 0x%02x",
95                               rval_to_str_const(opt_class, class_id_names, "Unknown"),
96                               opt_class, opt_type);
97 
98     return name;
99 }
100 
101 static void
102 dissect_unknown_option(tvbuff_t *tvb, proto_tree *opts_tree, int offset,
103                        guint16 opt_class, guint8 opt_type, int len)
104 {
105     proto_item *opt_item, *type_item, *hidden_item, *flag_item;
106     proto_tree *opt_tree, *flag_tree;
107     const char *critical;
108     guint8 flags;
109 
110     critical = opt_type & OPT_TYPE_CRITICAL ? "Critical" : "Non-critical";
111 
112     opt_item = proto_tree_add_item(opts_tree, hf_geneve_opt_unknown,
113                                    tvb, offset, len, ENC_NA);
114     proto_item_set_text(opt_item, "%s (%s)",
115                         format_unknown_option_name(opt_class, opt_type),
116                         critical);
117 
118     opt_tree = proto_item_add_subtree(opt_item, ett_geneve_unknown_opt);
119 
120     proto_tree_add_item(opt_tree, hf_geneve_option_class, tvb,
121                         offset, 2, ENC_BIG_ENDIAN);
122     offset += 2;
123 
124     type_item = proto_tree_add_item(opt_tree, hf_geneve_option_type, tvb,
125                                     offset, 1, ENC_BIG_ENDIAN);
126     proto_item_append_text(type_item, " (%s)", critical);
127     hidden_item = proto_tree_add_item(opt_tree, hf_geneve_option_type_critical,
128                                       tvb, offset, 1, ENC_BIG_ENDIAN);
129     proto_item_set_hidden(hidden_item);
130     offset += 1;
131 
132     flags = tvb_get_guint8(tvb, offset) >> OPT_FLAGS_SHIFT;
133     flag_item = proto_tree_add_uint(opt_tree, hf_geneve_option_flags, tvb,
134                                     offset, 1, flags);
135     flag_tree = proto_item_add_subtree(flag_item, ett_geneve_opt_flags);
136     proto_tree_add_item(flag_tree, hf_geneve_option_flags_reserved, tvb,
137                         offset, 1, ENC_BIG_ENDIAN);
138     if (flags) {
139         proto_item_append_text(flag_item, " (RSVD)");
140     } else {
141         proto_item_set_hidden(flag_item);
142     }
143 
144     proto_tree_add_uint(opt_tree, hf_geneve_option_length, tvb, offset, 1, len);
145     offset += 1;
146 
147     proto_tree_add_item(opt_tree, hf_geneve_opt_unknown_data, tvb, offset,
148                         len - 4, ENC_NA);
149 }
150 
151 static void
152 dissect_geneve_options(tvbuff_t *tvb, packet_info *pinfo,
153                        proto_tree *geneve_tree, int offset, int len)
154 {
155     proto_item *opts_item;
156     proto_tree *opts_tree;
157     guint16 opt_class;
158     guint8 opt_type;
159     guint8 opt_len;
160 
161     opts_item = proto_tree_add_item(geneve_tree, hf_geneve_options, tvb,
162                                     offset, len, ENC_NA);
163     proto_item_set_text(opts_item, "Options: (%u bytes)", len);
164     opts_tree = proto_item_add_subtree(opts_item, ett_geneve_options);
165 
166     while (len > 0) {
167         opt_class = tvb_get_ntohs(tvb, offset);
168         opt_type = tvb_get_guint8(tvb, offset + 2);
169         opt_len = 4 + ((tvb_get_guint8(tvb, offset + 3) & OPT_LEN_MASK) * 4);
170 
171         if (opt_len > len) {
172             proto_tree_add_expert_format(opts_tree, pinfo,
173                                          &ei_geneve_opt_len_invalid, tvb,
174                                          offset + 3, 1,
175                                          "%s (length of %u is past end of options)",
176                                          format_unknown_option_name(opt_class,
177                                                                     opt_type),
178                                          opt_len);
179             return;
180         }
181 
182         dissect_unknown_option(tvb, opts_tree, offset,
183                                opt_class, opt_type, opt_len);
184 
185         offset += opt_len;
186         len -= opt_len;
187     };
188 }
189 
190 static int
191 dissect_geneve(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
192 {
193     proto_item *ti, *rsvd_item;
194     proto_tree *geneve_tree;
195     tvbuff_t *next_tvb;
196     int offset = 0;
197     guint8 ver_opt;
198     guint8 ver;
199     guint8 flags;
200     guint16 proto_type;
201     int opts_len;
202     static int * const flag_fields[] = {
203         &hf_geneve_flag_oam,
204         &hf_geneve_flag_critical,
205         &hf_geneve_flag_reserved,
206         NULL
207     };
208 
209     col_set_str(pinfo->cinfo, COL_PROTOCOL, "Geneve");
210     col_clear(pinfo->cinfo, COL_INFO);
211 
212     ti = proto_tree_add_item(tree, proto_geneve, tvb, offset, -1, ENC_NA);
213     geneve_tree = proto_item_add_subtree(ti, ett_geneve);
214 
215     /* Version. */
216     ver_opt = tvb_get_guint8(tvb, offset);
217     ver = ver_opt >> VER_SHIFT;
218     proto_tree_add_uint(geneve_tree, hf_geneve_version, tvb,
219                         offset, 1, ver);
220 
221     if (ver != GENEVE_VER) {
222         proto_tree_add_expert_format(geneve_tree, pinfo,
223                                      &ei_geneve_ver_unknown, tvb, offset, 1,
224                                      "Unknown version %u", ver);
225         col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown Geneve version %u", ver);
226     }
227 
228     /* Option length. */
229     opts_len = (ver_opt & HDR_OPTS_LEN_MASK) * 4;
230     proto_tree_add_uint(geneve_tree, hf_geneve_option_length, tvb,
231                                      offset, 1, opts_len);
232     offset += 1;
233 
234     /* Flags. */
235     flags = tvb_get_guint8(tvb, offset);
236     proto_tree_add_bitmask(geneve_tree, tvb, offset, hf_geneve_flags, ett_geneve_flags, flag_fields, ENC_BIG_ENDIAN);
237     offset += 1;
238 
239     /* Protocol Type. */
240     proto_tree_add_item(geneve_tree, hf_geneve_proto_type, tvb,
241                         offset, 2, ENC_BIG_ENDIAN);
242 
243     proto_type = tvb_get_ntohs(tvb, offset);
244     col_add_fstr(pinfo->cinfo, COL_INFO, "Encapsulated %s",
245                  val_to_str(proto_type, etype_vals, "0x%04x (unknown)"));
246 
247     offset += 2;
248 
249     /* VNI. */
250     proto_tree_add_item(geneve_tree, hf_geneve_vni, tvb, offset, 3,
251                         ENC_BIG_ENDIAN);
252     proto_item_append_text(ti, ", VNI: 0x%06x%s", tvb_get_ntoh24(tvb, offset),
253                            flags & FLAG_OAM ? ", OAM" : "");
254     offset += 3;
255 
256     /* Reserved. */
257     rsvd_item = proto_tree_add_item(geneve_tree, hf_geneve_reserved, tvb,
258                                     offset, 1, ENC_BIG_ENDIAN);
259     if (!tvb_get_guint8(tvb, offset)) {
260         proto_item_set_hidden(rsvd_item);
261     }
262     offset += 1;
263 
264     /* Options. */
265     if (tree && opts_len) {
266         dissect_geneve_options(tvb, pinfo, geneve_tree, offset, opts_len);
267     }
268     offset += opts_len;
269 
270     proto_item_set_len(ti, offset);
271 
272     next_tvb = tvb_new_subset_remaining(tvb, offset);
273     if (!dissector_try_uint(ethertype_dissector_table, proto_type, next_tvb, pinfo, tree))
274         call_data_dissector(next_tvb, pinfo, tree);
275 
276     return tvb_captured_length(tvb);
277 }
278 
279 /* Register Geneve with Wireshark */
280 void
281 proto_register_geneve(void)
282 {
283     static hf_register_info hf[] = {
284         { &hf_geneve_version,
285           { "Version", "geneve.version",
286             FT_UINT8, BASE_DEC, NULL, 0x00,
287             NULL, HFILL }
288         },
289         { &hf_geneve_flags,
290           { "Flags", "geneve.flags",
291             FT_UINT8, BASE_HEX, NULL, 0x00,
292             NULL, HFILL }
293         },
294         { &hf_geneve_flag_oam,
295           { "Operations, Administration and Management Frame", "geneve.flags.oam",
296             FT_BOOLEAN, 8, NULL, 0x80,
297             NULL, HFILL }
298         },
299         { &hf_geneve_flag_critical,
300           { "Critical Options Present", "geneve.flags.critical",
301             FT_BOOLEAN, 8, NULL, 0x40,
302             NULL, HFILL }
303         },
304         { &hf_geneve_flag_reserved,
305           { "Reserved", "geneve.flags.reserved",
306             FT_BOOLEAN, 8, NULL, 0x3F,
307             NULL, HFILL }
308         },
309         { &hf_geneve_proto_type,
310           { "Protocol Type", "geneve.proto_type",
311             FT_UINT16, BASE_HEX, VALS(etype_vals), 0x0,
312             NULL, HFILL }
313         },
314         { &hf_geneve_vni,
315           { "Virtual Network Identifier (VNI)", "geneve.vni",
316             FT_UINT24, BASE_HEX, NULL, 0x0,
317             NULL, HFILL }
318         },
319         { &hf_geneve_reserved,
320           { "Reserved", "geneve.reserved",
321             FT_UINT8, BASE_HEX, NULL, 0x00,
322             NULL, HFILL }
323         },
324         { &hf_geneve_options,
325           { "Geneve Options", "geneve.options",
326             FT_BYTES, BASE_NONE, NULL, 0x00,
327             NULL, HFILL }
328         },
329         { &hf_geneve_option_class,
330           { "Class", "geneve.option.class",
331             FT_UINT16, BASE_HEX | BASE_RANGE_STRING, RVALS(class_id_names), 0x00,
332             NULL, HFILL }
333         },
334         { &hf_geneve_option_type,
335           { "Type", "geneve.option.type",
336             FT_UINT8, BASE_HEX, NULL, 0x00,
337             NULL, HFILL }
338         },
339         { &hf_geneve_option_type_critical,
340           { "Critical Option", "geneve.option.type.critical",
341             FT_BOOLEAN, 8, NULL, 0x80,
342             NULL, HFILL }
343         },
344         { &hf_geneve_option_flags,
345           { "Flags", "geneve.option.flags",
346             FT_UINT8, BASE_HEX, NULL, 0x00,
347             NULL, HFILL }
348         },
349         { &hf_geneve_option_flags_reserved,
350           { "Reserved", "geneve.option.flags.reserved",
351             FT_BOOLEAN, 8, NULL, 0xE0,
352             NULL, HFILL }
353         },
354         { &hf_geneve_option_length,
355           { "Length", "geneve.option.length",
356             FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x00,
357             NULL, HFILL }
358         },
359         { &hf_geneve_opt_unknown,
360           { "Unknown Option", "geneve.option.unknown",
361             FT_BYTES, BASE_NONE, NULL, 0x00,
362             NULL, HFILL }
363         },
364         { &hf_geneve_opt_unknown_data,
365           { "Option Data", "geneve.option.unknown.data",
366             FT_BYTES, BASE_NONE, NULL, 0x00,
367             NULL, HFILL }
368         },
369     };
370 
371     static gint *ett[] = {
372         &ett_geneve,
373         &ett_geneve_flags,
374         &ett_geneve_options,
375         &ett_geneve_opt_flags,
376         &ett_geneve_unknown_opt,
377     };
378 
379     static ei_register_info ei[] = {
380        { &ei_geneve_ver_unknown, { "geneve.version.unknown",
381          PI_PROTOCOL, PI_WARN, "Unknown version", EXPFILL }},
382        { &ei_geneve_opt_len_invalid, { "geneve.option.length.invalid",
383          PI_PROTOCOL, PI_WARN, "Invalid length for option", EXPFILL }},
384     };
385 
386     expert_module_t *expert_geneve;
387 
388     /* Register the protocol name and description */
389     proto_geneve = proto_register_protocol("Generic Network Virtualization Encapsulation",
390                                           "Geneve", "geneve");
391 
392     proto_register_field_array(proto_geneve, hf, array_length(hf));
393     proto_register_subtree_array(ett, array_length(ett));
394 
395     expert_geneve = expert_register_protocol(proto_geneve);
396     expert_register_field_array(expert_geneve, ei, array_length(ei));
397 }
398 
399 void
400 proto_reg_handoff_geneve(void)
401 {
402     dissector_handle_t geneve_handle;
403 
404     geneve_handle = create_dissector_handle(dissect_geneve, proto_geneve);
405     dissector_add_uint_with_preference("udp.port", UDP_PORT_GENEVE, geneve_handle);
406 
407     ethertype_dissector_table = find_dissector_table("ethertype");
408 }
409 
410 /*
411  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
412  *
413  * Local variables:
414  * c-basic-offset: 4
415  * tab-width: 8
416  * indent-tabs-mode: nil
417  * End:
418  *
419  * vi: set shiftwidth=4 tabstop=8 expandtab:
420  * :indentSize=4:tabSize=8:noTabs=true:
421  */
422