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