2 * Routines for Geneve - Generic Network Virtualization Encapsulation
3 * https://tools.ietf.org/html/draft-ietf-nvo3-geneve
5 * Copyright (c) 2024 cPacket Networks, Inc. All Rights Reserved.
6 * Author: Martin Greenberg <mgreenberg@cpacket.com>
8 * Copyright (c) 2014 VMware, Inc. All Rights Reserved.
9 * Author: Jesse Gross <jesse@nicira.com>
11 * Copyright 2021, Atul Sharma <asharm37@ncsu.edu>
13 * Wireshark - Network traffic analyzer
14 * By Gerald Combs <gerald@wireshark.org>
15 * Copyright 1998 Gerald Combs
17 * SPDX-License-Identifier: GPL-2.0-or-later
23 #include <epan/packet.h>
24 #include <epan/etypes.h>
25 #include <epan/expert.h>
26 #include <epan/value_string.h>
28 #include <epan/unit_strings.h>
30 #define UDP_PORT_GENEVE 6081
34 #define HDR_OPTS_LEN_MASK 0x3F
36 #define FLAG_OAM (1 << 7)
38 #define OPT_TYPE_CRITICAL (1 << 7)
39 #define OPT_FLAGS_SHIFT 5
40 #define OPT_LEN_MASK 0x1F
42 static const range_string class_id_names
[] = {
43 { 0, 0xFF, "Standard" },
44 { 0x0100, 0x0100, "Linux" },
45 { 0x0101, 0x0101, "Open vSwitch" },
46 { 0x0102, 0x0102, "Open Virtual Networking (OVN)" },
47 { 0x0103, 0x0103, "In-band Network Telemetry (INT)" },
48 { 0x0104, 0x0104, "VMware" },
49 { 0x0105, 0x0105, "Amazon.com, Inc."},
50 { 0x0106, 0x0106, "Cisco Systems, Inc." },
51 { 0x0107, 0x0107, "Oracle Corporation" },
52 { 0x0108, 0x0110, "Amazon.com, Inc." },
53 { 0x0111, 0x0118, "IBM" },
54 { 0x0119, 0x0128, "Ericsson" },
55 { 0x0129, 0x0129, "Oxide Computer Company" },
56 { 0x0130, 0x0131, "Cisco Systems, Inc." },
57 { 0x0132, 0x0135, "Google LLC" },
58 { 0x0136, 0x0136, "InfoQuick Global Connection Tech Ltd." },
59 { 0x0137, 0x0163, "Unssigned" },
60 { 0x0164, 0x0164, "cPacket Networks, Inc." },
61 { 0x0165, 0xFEFF, "Unassigned" },
62 { 0xFFF0, 0xFFFF, "Experimental" },
66 #define GENEVE_GCP_VNID 0x013201
67 #define GENEVE_GCP_ENDPOINT 0x013202
68 #define GENEVE_GCP_PROFILE 0x013203
69 #define GENEVE_CPACKET_METADATA 0x016400
71 static const val64_string option_names
[] = {
72 { GENEVE_GCP_VNID
, "GCP Virtual Network ID" },
73 { GENEVE_GCP_ENDPOINT
, "GCP Endpoint ID" },
74 { GENEVE_GCP_PROFILE
, "GCP Profile ID" },
75 { GENEVE_CPACKET_METADATA
, "cPacket Meta-data" },
79 void proto_register_geneve(void);
80 void proto_reg_handoff_geneve(void);
82 static dissector_handle_t geneve_handle
;
84 static int proto_geneve
;
86 static int hf_geneve_version
;
87 static int hf_geneve_flags
;
88 static int hf_geneve_flag_oam
;
89 static int hf_geneve_flag_critical
;
90 static int hf_geneve_flag_reserved
;
91 static int hf_geneve_proto_type
;
92 static int hf_geneve_vni
;
93 static int hf_geneve_reserved
;
94 static int hf_geneve_options
;
95 static int hf_geneve_option_class
;
96 static int hf_geneve_option_type
;
97 static int hf_geneve_option_type_critical
;
98 static int hf_geneve_option_flags
;
99 static int hf_geneve_option_flags_reserved
;
100 static int hf_geneve_option_length
;
101 static int hf_geneve_option
;
102 static int hf_geneve_opt_gcp_vnid
;
103 static int hf_geneve_opt_gcp_reserved
;
104 static int hf_geneve_opt_gcp_direction
;
105 static int hf_geneve_opt_gcp_endpoint
;
106 static int hf_geneve_opt_gcp_profile
;
107 static int hf_geneve_opt_cpkt_seqnum
;
108 static int hf_geneve_opt_cpkt_origlen
;
109 static int hf_geneve_opt_cpkt_reserved
;
110 static int hf_geneve_opt_cpkt_timestamp
;
111 static int hf_geneve_opt_cpkt_ts_sec
;
112 static int hf_geneve_opt_cpkt_ts_nsec
;
113 static int hf_geneve_opt_cpkt_ts_fracns
;
114 static int hf_geneve_opt_cpkt_version
;
115 static int hf_geneve_opt_cpkt_devid
;
116 static int hf_geneve_opt_cpkt_portid
;
118 static int hf_geneve_opt_unknown_data
;
120 static int ett_geneve
;
121 static int ett_geneve_flags
;
122 static int ett_geneve_opt_flags
;
123 static int ett_geneve_options
;
124 static int ett_geneve_opt_data
;
126 static expert_field ei_geneve_ver_unknown
;
127 static expert_field ei_geneve_opt_len_invalid
;
129 static dissector_table_t ethertype_dissector_table
;
131 static const struct true_false_string tfs_geneve_gcp_direction
= {
137 format_option_name(wmem_allocator_t
*scope
, uint16_t opt_class
, uint8_t opt_type
)
141 name
= wmem_strdup_printf(scope
,
142 "%s, Class: %s (0x%04x) Type: 0x%02x",
143 val64_to_str_const(((uint64_t)opt_class
<< 8) | opt_type
,
144 option_names
, "Unknown"),
145 rval_to_str_const(opt_class
, class_id_names
, "Unknown"),
146 opt_class
, opt_type
);
152 dissect_option(wmem_allocator_t
*scope
, tvbuff_t
*tvb
, proto_tree
*opts_tree
, int offset
,
153 uint16_t opt_class
, uint8_t opt_type
, int len
)
155 proto_item
*opt_item
, *type_item
, *hidden_item
, *flag_item
;
156 proto_tree
*opt_tree
, *flag_tree
;
157 const char *critical
;
160 critical
= opt_type
& OPT_TYPE_CRITICAL
? "Critical" : "Non-critical";
162 opt_item
= proto_tree_add_item(opts_tree
, hf_geneve_option
,
163 tvb
, offset
, len
, ENC_NA
);
164 proto_item_set_text(opt_item
, "%s (%s)",
165 format_option_name(scope
, opt_class
, opt_type
),
168 opt_tree
= proto_item_add_subtree(opt_item
, ett_geneve_opt_data
);
170 proto_tree_add_item(opt_tree
, hf_geneve_option_class
, tvb
,
171 offset
, 2, ENC_BIG_ENDIAN
);
174 type_item
= proto_tree_add_item(opt_tree
, hf_geneve_option_type
, tvb
,
175 offset
, 1, ENC_BIG_ENDIAN
);
176 proto_item_append_text(type_item
, " (%s)", critical
);
177 hidden_item
= proto_tree_add_item(opt_tree
, hf_geneve_option_type_critical
,
178 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
179 proto_item_set_hidden(hidden_item
);
182 flags
= tvb_get_uint8(tvb
, offset
) >> OPT_FLAGS_SHIFT
;
183 flag_item
= proto_tree_add_uint(opt_tree
, hf_geneve_option_flags
, tvb
,
185 flag_tree
= proto_item_add_subtree(flag_item
, ett_geneve_opt_flags
);
186 proto_tree_add_item(flag_tree
, hf_geneve_option_flags_reserved
, tvb
,
187 offset
, 1, ENC_BIG_ENDIAN
);
189 proto_item_append_text(flag_item
, " (RSVD)");
191 proto_item_set_hidden(flag_item
);
194 proto_tree_add_uint(opt_tree
, hf_geneve_option_length
, tvb
, offset
, 1, len
);
197 switch (((uint64_t)opt_class
<< 8) | opt_type
) {
198 case GENEVE_GCP_VNID
:
199 proto_tree_add_bits_item(opt_tree
, hf_geneve_opt_gcp_vnid
, tvb
, offset
* 8,
201 proto_tree_add_item(opt_tree
, hf_geneve_opt_gcp_direction
, tvb
, offset
,
203 proto_tree_add_item(opt_tree
, hf_geneve_opt_gcp_reserved
, tvb
, offset
,
206 case GENEVE_GCP_ENDPOINT
:
207 proto_tree_add_item(opt_tree
, hf_geneve_opt_gcp_endpoint
, tvb
, offset
,
210 case GENEVE_GCP_PROFILE
:
211 proto_tree_add_item(opt_tree
, hf_geneve_opt_gcp_profile
, tvb
, offset
,
212 len
- 4, ENC_BIG_ENDIAN
);
214 case GENEVE_CPACKET_METADATA
:
215 proto_tree_add_item(opt_tree
, hf_geneve_opt_cpkt_seqnum
, tvb
, offset
,
218 proto_tree_add_item(opt_tree
, hf_geneve_opt_cpkt_origlen
, tvb
, offset
,
221 proto_tree_add_item(opt_tree
, hf_geneve_opt_cpkt_reserved
, tvb
, offset
,
224 proto_tree_add_item(opt_tree
, hf_geneve_opt_cpkt_version
, tvb
, offset
,
227 // PTPv2 timestamp has more resolution than NStime supports/displays,
228 // but parse appropriate subsection of into NStime for user convenience
229 proto_tree_add_time_item(opt_tree
, hf_geneve_opt_cpkt_timestamp
, tvb
, offset
+2, 8,
230 ENC_TIME_SECS_NSECS
, NULL
, NULL
, NULL
);
231 proto_tree_add_item(opt_tree
, hf_geneve_opt_cpkt_ts_sec
, tvb
, offset
,
234 proto_tree_add_item(opt_tree
, hf_geneve_opt_cpkt_ts_nsec
, tvb
, offset
,
237 proto_tree_add_item(opt_tree
, hf_geneve_opt_cpkt_ts_fracns
, tvb
, offset
,
240 proto_tree_add_item(opt_tree
, hf_geneve_opt_cpkt_devid
, tvb
, offset
,
243 proto_tree_add_item(opt_tree
, hf_geneve_opt_cpkt_portid
, tvb
, offset
,
248 proto_tree_add_item(opt_tree
, hf_geneve_opt_unknown_data
, tvb
, offset
,
255 dissect_geneve_options(tvbuff_t
*tvb
, packet_info
*pinfo
,
256 proto_tree
*geneve_tree
, int offset
, int len
)
258 proto_item
*opts_item
;
259 proto_tree
*opts_tree
;
264 opts_item
= proto_tree_add_item(geneve_tree
, hf_geneve_options
, tvb
,
265 offset
, len
, ENC_NA
);
266 proto_item_set_text(opts_item
, "Options: (%u bytes)", len
);
267 opts_tree
= proto_item_add_subtree(opts_item
, ett_geneve_options
);
270 opt_class
= tvb_get_ntohs(tvb
, offset
);
271 opt_type
= tvb_get_uint8(tvb
, offset
+ 2);
272 opt_len
= 4 + ((tvb_get_uint8(tvb
, offset
+ 3) & OPT_LEN_MASK
) * 4);
275 proto_tree_add_expert_format(opts_tree
, pinfo
,
276 &ei_geneve_opt_len_invalid
, tvb
,
278 "%s (length of %u is past end of options)",
279 format_option_name(pinfo
->pool
, opt_class
, opt_type
),
284 dissect_option(pinfo
->pool
, tvb
, opts_tree
, offset
, opt_class
, opt_type
, opt_len
);
292 dissect_geneve(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
294 proto_item
*ti
, *rsvd_item
;
295 proto_tree
*geneve_tree
;
303 static int * const flag_fields
[] = {
305 &hf_geneve_flag_critical
,
306 &hf_geneve_flag_reserved
,
310 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Geneve");
311 col_clear(pinfo
->cinfo
, COL_INFO
);
313 ti
= proto_tree_add_item(tree
, proto_geneve
, tvb
, offset
, -1, ENC_NA
);
314 geneve_tree
= proto_item_add_subtree(ti
, ett_geneve
);
317 ver_opt
= tvb_get_uint8(tvb
, offset
);
318 ver
= ver_opt
>> VER_SHIFT
;
319 proto_tree_add_uint(geneve_tree
, hf_geneve_version
, tvb
,
322 if (ver
!= GENEVE_VER
) {
323 proto_tree_add_expert_format(geneve_tree
, pinfo
,
324 &ei_geneve_ver_unknown
, tvb
, offset
, 1,
325 "Unknown version %u", ver
);
326 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Unknown Geneve version %u", ver
);
330 opts_len
= (ver_opt
& HDR_OPTS_LEN_MASK
) * 4;
331 proto_tree_add_uint(geneve_tree
, hf_geneve_option_length
, tvb
,
332 offset
, 1, opts_len
);
336 flags
= tvb_get_uint8(tvb
, offset
);
337 proto_tree_add_bitmask(geneve_tree
, tvb
, offset
, hf_geneve_flags
, ett_geneve_flags
, flag_fields
, ENC_BIG_ENDIAN
);
341 proto_tree_add_item(geneve_tree
, hf_geneve_proto_type
, tvb
,
342 offset
, 2, ENC_BIG_ENDIAN
);
344 proto_type
= tvb_get_ntohs(tvb
, offset
);
345 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Encapsulated %s",
346 val_to_str(proto_type
, etype_vals
, "0x%04x (unknown)"));
351 proto_tree_add_item(geneve_tree
, hf_geneve_vni
, tvb
, offset
, 3,
353 proto_item_append_text(ti
, ", VNI: 0x%06x%s", tvb_get_ntoh24(tvb
, offset
),
354 flags
& FLAG_OAM
? ", OAM" : "");
358 rsvd_item
= proto_tree_add_item(geneve_tree
, hf_geneve_reserved
, tvb
,
359 offset
, 1, ENC_BIG_ENDIAN
);
360 if (!tvb_get_uint8(tvb
, offset
)) {
361 proto_item_set_hidden(rsvd_item
);
366 if (tree
&& opts_len
) {
367 dissect_geneve_options(tvb
, pinfo
, geneve_tree
, offset
, opts_len
);
371 proto_item_set_len(ti
, offset
);
373 next_tvb
= tvb_new_subset_remaining(tvb
, offset
);
374 if (!dissector_try_uint(ethertype_dissector_table
, proto_type
, next_tvb
, pinfo
, tree
))
375 call_data_dissector(next_tvb
, pinfo
, tree
);
377 return tvb_captured_length(tvb
);
380 /* Register Geneve with Wireshark */
382 proto_register_geneve(void)
384 static hf_register_info hf
[] = {
385 { &hf_geneve_version
,
386 { "Version", "geneve.version",
387 FT_UINT8
, BASE_DEC
, NULL
, 0x00,
391 { "Flags", "geneve.flags",
392 FT_UINT8
, BASE_HEX
, NULL
, 0x00,
395 { &hf_geneve_flag_oam
,
396 { "Operations, Administration and Management Frame", "geneve.flags.oam",
397 FT_BOOLEAN
, 8, NULL
, 0x80,
400 { &hf_geneve_flag_critical
,
401 { "Critical Options Present", "geneve.flags.critical",
402 FT_BOOLEAN
, 8, NULL
, 0x40,
405 { &hf_geneve_flag_reserved
,
406 { "Reserved", "geneve.flags.reserved",
407 FT_BOOLEAN
, 8, NULL
, 0x3F,
410 { &hf_geneve_proto_type
,
411 { "Protocol Type", "geneve.proto_type",
412 FT_UINT16
, BASE_HEX
, VALS(etype_vals
), 0x0,
416 { "Virtual Network Identifier (VNI)", "geneve.vni",
417 FT_UINT24
, BASE_HEX_DEC
, NULL
, 0x0,
420 { &hf_geneve_reserved
,
421 { "Reserved", "geneve.reserved",
422 FT_UINT8
, BASE_HEX
, NULL
, 0x00,
425 { &hf_geneve_options
,
426 { "Geneve Options", "geneve.options",
427 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
430 { &hf_geneve_option_class
,
431 { "Class", "geneve.option.class",
432 FT_UINT16
, BASE_HEX
| BASE_RANGE_STRING
, RVALS(class_id_names
), 0x00,
435 { &hf_geneve_option_type
,
436 { "Type", "geneve.option.type",
437 FT_UINT8
, BASE_HEX
, NULL
, 0x00,
440 { &hf_geneve_option_type_critical
,
441 { "Critical Option", "geneve.option.type.critical",
442 FT_BOOLEAN
, 8, NULL
, 0x80,
445 { &hf_geneve_option_flags
,
446 { "Flags", "geneve.option.flags",
447 FT_UINT8
, BASE_HEX
, NULL
, 0x00,
450 { &hf_geneve_option_flags_reserved
,
451 { "Reserved", "geneve.option.flags.reserved",
452 FT_BOOLEAN
, 8, NULL
, 0xE0,
455 { &hf_geneve_option_length
,
456 { "Length", "geneve.option.length",
457 FT_UINT8
, BASE_DEC
|BASE_UNIT_STRING
, UNS(&units_byte_bytes
), 0x00,
461 { "Option", "geneve.option",
462 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
465 { &hf_geneve_opt_gcp_vnid
,
466 { "GCP Virtual Network ID", "geneve.option.gcp.vnid",
467 FT_UINT32
, BASE_DEC
, NULL
, 0x00,
470 { &hf_geneve_opt_gcp_reserved
,
471 { "GCP Reserved bits", "geneve.option.gcp.reserved",
472 FT_BOOLEAN
, 32, NULL
, 0x0000000E,
475 { &hf_geneve_opt_gcp_direction
,
476 { "GCP Traffic Direction", "geneve.option.gcp.direction",
477 FT_BOOLEAN
, 32, TFS(&tfs_geneve_gcp_direction
), 0x00000001,
480 { &hf_geneve_opt_gcp_endpoint
,
481 { "GCP Endpoint ID", "geneve.option.gcp.endpoint",
482 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
485 { &hf_geneve_opt_gcp_profile
,
486 { "GCP Profile ID", "geneve.option.gcp.profile",
487 FT_UINT64
, BASE_DEC
, NULL
, 0x00,
490 { &hf_geneve_opt_cpkt_seqnum
,
491 { "cPacket Packet ID", "geneve.option.cPacket.packetid",
492 FT_UINT32
, BASE_DEC
, NULL
, 0x00,
495 { &hf_geneve_opt_cpkt_origlen
,
496 { "cPacket Original length", "geneve.option.cPacket.orig_len",
497 FT_UINT16
, BASE_DEC
, NULL
, 0x00,
500 { &hf_geneve_opt_cpkt_reserved
,
501 { "cPacket Reserved", "geneve.option.cPacket.reserved",
502 FT_UINT8
, BASE_HEX
, NULL
, 0x00,
505 { &hf_geneve_opt_cpkt_version
,
506 { "cPacket Metadata version", "geneve.option.cPacket.version",
507 FT_UINT8
, BASE_DEC
, NULL
, 0x00,
510 { &hf_geneve_opt_cpkt_timestamp
,
511 { "cPacket Timestamp", "geneve.option.cPacket.timestamp",
512 FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_UTC
, NULL
, 0x00,
515 { &hf_geneve_opt_cpkt_ts_sec
,
516 { "cPacket Timestamp (s)", "geneve.option.cPacket.ts_sec",
517 FT_UINT48
, BASE_DEC
, NULL
, 0x00,
520 { &hf_geneve_opt_cpkt_ts_nsec
,
521 { "cPacket Timestamp (ns)", "geneve.option.cPacket.ts_nsec",
522 FT_UINT32
, BASE_DEC
, NULL
, 0x00,
525 { &hf_geneve_opt_cpkt_ts_fracns
,
526 { "cPacket Timestamp (frac. ns)", "geneve.option.cPacket.ts_fracns",
527 FT_UINT16
, BASE_DEC
, NULL
, 0x00,
530 { &hf_geneve_opt_cpkt_devid
,
531 { "cPacket Device ID", "geneve.option.cPacket.device_id",
532 FT_UINT16
, BASE_DEC
, NULL
, 0x00,
535 { &hf_geneve_opt_cpkt_portid
,
536 { "cPacket Port ID", "geneve.option.cPacket.port_id",
537 FT_UINT16
, BASE_DEC
, NULL
, 0x00,
540 { &hf_geneve_opt_unknown_data
,
541 { "Unknown Option Data", "geneve.option.unknown.data",
542 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
547 static int *ett
[] = {
551 &ett_geneve_opt_flags
,
552 &ett_geneve_opt_data
,
555 static ei_register_info ei
[] = {
556 { &ei_geneve_ver_unknown
, { "geneve.version.unknown",
557 PI_PROTOCOL
, PI_WARN
, "Unknown version", EXPFILL
}},
558 { &ei_geneve_opt_len_invalid
, { "geneve.option.length.invalid",
559 PI_PROTOCOL
, PI_WARN
, "Invalid length for option", EXPFILL
}},
562 expert_module_t
*expert_geneve
;
564 /* Register the protocol name and description */
565 proto_geneve
= proto_register_protocol("Generic Network Virtualization Encapsulation",
568 proto_register_field_array(proto_geneve
, hf
, array_length(hf
));
569 proto_register_subtree_array(ett
, array_length(ett
));
571 expert_geneve
= expert_register_protocol(proto_geneve
);
572 expert_register_field_array(expert_geneve
, ei
, array_length(ei
));
574 geneve_handle
= register_dissector("geneve", dissect_geneve
, proto_geneve
);
578 proto_reg_handoff_geneve(void)
580 dissector_add_uint_with_preference("udp.port", UDP_PORT_GENEVE
, geneve_handle
);
582 ethertype_dissector_table
= find_dissector_table("ethertype");
586 * Editor modelines - https://www.wireshark.org/tools/modelines.html
591 * indent-tabs-mode: nil
594 * vi: set shiftwidth=4 tabstop=8 expandtab:
595 * :indentSize=4:tabSize=8:noTabs=true: