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
,
247 proto_tree_add_item(opt_tree
, hf_geneve_opt_unknown_data
, tvb
, offset
,
254 dissect_geneve_options(tvbuff_t
*tvb
, packet_info
*pinfo
,
255 proto_tree
*geneve_tree
, int offset
, int len
)
257 proto_item
*opts_item
;
258 proto_tree
*opts_tree
;
263 opts_item
= proto_tree_add_item(geneve_tree
, hf_geneve_options
, tvb
,
264 offset
, len
, ENC_NA
);
265 proto_item_set_text(opts_item
, "Options: (%u bytes)", len
);
266 opts_tree
= proto_item_add_subtree(opts_item
, ett_geneve_options
);
269 opt_class
= tvb_get_ntohs(tvb
, offset
);
270 opt_type
= tvb_get_uint8(tvb
, offset
+ 2);
271 opt_len
= 4 + ((tvb_get_uint8(tvb
, offset
+ 3) & OPT_LEN_MASK
) * 4);
274 proto_tree_add_expert_format(opts_tree
, pinfo
,
275 &ei_geneve_opt_len_invalid
, tvb
,
277 "%s (length of %u is past end of options)",
278 format_option_name(pinfo
->pool
, opt_class
, opt_type
),
283 dissect_option(pinfo
->pool
, tvb
, opts_tree
, offset
, opt_class
, opt_type
, opt_len
);
291 dissect_geneve(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
293 proto_item
*ti
, *rsvd_item
;
294 proto_tree
*geneve_tree
;
302 static int * const flag_fields
[] = {
304 &hf_geneve_flag_critical
,
305 &hf_geneve_flag_reserved
,
309 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Geneve");
310 col_clear(pinfo
->cinfo
, COL_INFO
);
312 ti
= proto_tree_add_item(tree
, proto_geneve
, tvb
, offset
, -1, ENC_NA
);
313 geneve_tree
= proto_item_add_subtree(ti
, ett_geneve
);
316 ver_opt
= tvb_get_uint8(tvb
, offset
);
317 ver
= ver_opt
>> VER_SHIFT
;
318 proto_tree_add_uint(geneve_tree
, hf_geneve_version
, tvb
,
321 if (ver
!= GENEVE_VER
) {
322 proto_tree_add_expert_format(geneve_tree
, pinfo
,
323 &ei_geneve_ver_unknown
, tvb
, offset
, 1,
324 "Unknown version %u", ver
);
325 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Unknown Geneve version %u", ver
);
329 opts_len
= (ver_opt
& HDR_OPTS_LEN_MASK
) * 4;
330 proto_tree_add_uint(geneve_tree
, hf_geneve_option_length
, tvb
,
331 offset
, 1, opts_len
);
335 flags
= tvb_get_uint8(tvb
, offset
);
336 proto_tree_add_bitmask(geneve_tree
, tvb
, offset
, hf_geneve_flags
, ett_geneve_flags
, flag_fields
, ENC_BIG_ENDIAN
);
340 proto_tree_add_item(geneve_tree
, hf_geneve_proto_type
, tvb
,
341 offset
, 2, ENC_BIG_ENDIAN
);
343 proto_type
= tvb_get_ntohs(tvb
, offset
);
344 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Encapsulated %s",
345 val_to_str(proto_type
, etype_vals
, "0x%04x (unknown)"));
350 proto_tree_add_item(geneve_tree
, hf_geneve_vni
, tvb
, offset
, 3,
352 proto_item_append_text(ti
, ", VNI: 0x%06x%s", tvb_get_ntoh24(tvb
, offset
),
353 flags
& FLAG_OAM
? ", OAM" : "");
357 rsvd_item
= proto_tree_add_item(geneve_tree
, hf_geneve_reserved
, tvb
,
358 offset
, 1, ENC_BIG_ENDIAN
);
359 if (!tvb_get_uint8(tvb
, offset
)) {
360 proto_item_set_hidden(rsvd_item
);
365 if (tree
&& opts_len
) {
366 dissect_geneve_options(tvb
, pinfo
, geneve_tree
, offset
, opts_len
);
370 proto_item_set_len(ti
, offset
);
372 next_tvb
= tvb_new_subset_remaining(tvb
, offset
);
373 if (!dissector_try_uint(ethertype_dissector_table
, proto_type
, next_tvb
, pinfo
, tree
))
374 call_data_dissector(next_tvb
, pinfo
, tree
);
376 return tvb_captured_length(tvb
);
379 /* Register Geneve with Wireshark */
381 proto_register_geneve(void)
383 static hf_register_info hf
[] = {
384 { &hf_geneve_version
,
385 { "Version", "geneve.version",
386 FT_UINT8
, BASE_DEC
, NULL
, 0x00,
390 { "Flags", "geneve.flags",
391 FT_UINT8
, BASE_HEX
, NULL
, 0x00,
394 { &hf_geneve_flag_oam
,
395 { "Operations, Administration and Management Frame", "geneve.flags.oam",
396 FT_BOOLEAN
, 8, NULL
, 0x80,
399 { &hf_geneve_flag_critical
,
400 { "Critical Options Present", "geneve.flags.critical",
401 FT_BOOLEAN
, 8, NULL
, 0x40,
404 { &hf_geneve_flag_reserved
,
405 { "Reserved", "geneve.flags.reserved",
406 FT_BOOLEAN
, 8, NULL
, 0x3F,
409 { &hf_geneve_proto_type
,
410 { "Protocol Type", "geneve.proto_type",
411 FT_UINT16
, BASE_HEX
, VALS(etype_vals
), 0x0,
415 { "Virtual Network Identifier (VNI)", "geneve.vni",
416 FT_UINT24
, BASE_HEX_DEC
, NULL
, 0x0,
419 { &hf_geneve_reserved
,
420 { "Reserved", "geneve.reserved",
421 FT_UINT8
, BASE_HEX
, NULL
, 0x00,
424 { &hf_geneve_options
,
425 { "Geneve Options", "geneve.options",
426 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
429 { &hf_geneve_option_class
,
430 { "Class", "geneve.option.class",
431 FT_UINT16
, BASE_HEX
| BASE_RANGE_STRING
, RVALS(class_id_names
), 0x00,
434 { &hf_geneve_option_type
,
435 { "Type", "geneve.option.type",
436 FT_UINT8
, BASE_HEX
, NULL
, 0x00,
439 { &hf_geneve_option_type_critical
,
440 { "Critical Option", "geneve.option.type.critical",
441 FT_BOOLEAN
, 8, NULL
, 0x80,
444 { &hf_geneve_option_flags
,
445 { "Flags", "geneve.option.flags",
446 FT_UINT8
, BASE_HEX
, NULL
, 0x00,
449 { &hf_geneve_option_flags_reserved
,
450 { "Reserved", "geneve.option.flags.reserved",
451 FT_BOOLEAN
, 8, NULL
, 0xE0,
454 { &hf_geneve_option_length
,
455 { "Length", "geneve.option.length",
456 FT_UINT8
, BASE_DEC
|BASE_UNIT_STRING
, UNS(&units_byte_bytes
), 0x00,
460 { "Option", "geneve.option",
461 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
464 { &hf_geneve_opt_gcp_vnid
,
465 { "GCP Virtual Network ID", "geneve.option.gcp.vnid",
466 FT_UINT32
, BASE_DEC
, NULL
, 0x00,
469 { &hf_geneve_opt_gcp_reserved
,
470 { "GCP Reserved bits", "geneve.option.gcp.reserved",
471 FT_BOOLEAN
, 32, NULL
, 0x0000000E,
474 { &hf_geneve_opt_gcp_direction
,
475 { "GCP Traffic Direction", "geneve.option.gcp.direction",
476 FT_BOOLEAN
, 32, TFS(&tfs_geneve_gcp_direction
), 0x00000001,
479 { &hf_geneve_opt_gcp_endpoint
,
480 { "GCP Endpoint ID", "geneve.option.gcp.endpoint",
481 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
484 { &hf_geneve_opt_gcp_profile
,
485 { "GCP Profile ID", "geneve.option.gcp.profile",
486 FT_UINT64
, BASE_DEC
, NULL
, 0x00,
489 { &hf_geneve_opt_cpkt_seqnum
,
490 { "cPacket Packet ID", "geneve.option.cPacket.packetid",
491 FT_UINT32
, BASE_DEC
, NULL
, 0x00,
494 { &hf_geneve_opt_cpkt_origlen
,
495 { "cPacket Original length", "geneve.option.cPacket.orig_len",
496 FT_UINT16
, BASE_DEC
, NULL
, 0x00,
499 { &hf_geneve_opt_cpkt_reserved
,
500 { "cPacket Reserved", "geneve.option.cPacket.reserved",
501 FT_UINT8
, BASE_HEX
, NULL
, 0x00,
504 { &hf_geneve_opt_cpkt_version
,
505 { "cPacket Metadata version", "geneve.option.cPacket.version",
506 FT_UINT8
, BASE_DEC
, NULL
, 0x00,
509 { &hf_geneve_opt_cpkt_timestamp
,
510 { "cPacket Timestamp", "geneve.option.cPacket.timestamp",
511 FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_UTC
, NULL
, 0x00,
514 { &hf_geneve_opt_cpkt_ts_sec
,
515 { "cPacket Timestamp (s)", "geneve.option.cPacket.ts_sec",
516 FT_UINT48
, BASE_DEC
, NULL
, 0x00,
519 { &hf_geneve_opt_cpkt_ts_nsec
,
520 { "cPacket Timestamp (ns)", "geneve.option.cPacket.ts_nsec",
521 FT_UINT32
, BASE_DEC
, NULL
, 0x00,
524 { &hf_geneve_opt_cpkt_ts_fracns
,
525 { "cPacket Timestamp (frac. ns)", "geneve.option.cPacket.ts_fracns",
526 FT_UINT16
, BASE_DEC
, NULL
, 0x00,
529 { &hf_geneve_opt_cpkt_devid
,
530 { "cPacket Device ID", "geneve.option.cPacket.device_id",
531 FT_UINT16
, BASE_DEC
, NULL
, 0x00,
534 { &hf_geneve_opt_cpkt_portid
,
535 { "cPacket Port ID", "geneve.option.cPacket.port_id",
536 FT_UINT16
, BASE_DEC
, NULL
, 0x00,
539 { &hf_geneve_opt_unknown_data
,
540 { "Unknown Option Data", "geneve.option.unknown.data",
541 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
546 static int *ett
[] = {
550 &ett_geneve_opt_flags
,
551 &ett_geneve_opt_data
,
554 static ei_register_info ei
[] = {
555 { &ei_geneve_ver_unknown
, { "geneve.version.unknown",
556 PI_PROTOCOL
, PI_WARN
, "Unknown version", EXPFILL
}},
557 { &ei_geneve_opt_len_invalid
, { "geneve.option.length.invalid",
558 PI_PROTOCOL
, PI_WARN
, "Invalid length for option", EXPFILL
}},
561 expert_module_t
*expert_geneve
;
563 /* Register the protocol name and description */
564 proto_geneve
= proto_register_protocol("Generic Network Virtualization Encapsulation",
567 proto_register_field_array(proto_geneve
, hf
, array_length(hf
));
568 proto_register_subtree_array(ett
, array_length(ett
));
570 expert_geneve
= expert_register_protocol(proto_geneve
);
571 expert_register_field_array(expert_geneve
, ei
, array_length(ei
));
573 geneve_handle
= register_dissector("geneve", dissect_geneve
, proto_geneve
);
577 proto_reg_handoff_geneve(void)
579 dissector_add_uint_with_preference("udp.port", UDP_PORT_GENEVE
, geneve_handle
);
581 ethertype_dissector_table
= find_dissector_table("ethertype");
585 * Editor modelines - https://www.wireshark.org/tools/modelines.html
590 * indent-tabs-mode: nil
593 * vi: set shiftwidth=4 tabstop=8 expandtab:
594 * :indentSize=4:tabSize=8:noTabs=true: