2 * Routines for the disassembly of Cisco's ERSPAN protocol
4 * Copyright 2005 Joerg Mayer (see AUTHORS file)
5 * Updates for newer versions by Jason Masker <jason at masker.net>
6 * Updates to support ERSPAN3 by Peter Membrey <peter@membrey.hk>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: GPL-2.0-or-later
15 * https://tools.ietf.org/html/draft-foschiano-erspan-03
17 * For ERSPAN packets, the "protocol type" field value in the GRE header
18 * is 0x88BE (types I and II) or 0x22EB (type III).
20 * For 0x88BE, if the GRE header doesn't have the "sequence number present"
21 * flag set, it's type I, with no ERSPAN header, otherwise it has an
22 * ERSPAN header (it's supposed to be type II, but we look at the version
23 * in the ERSPAN header; should we report an error if it's not version 1?).
25 * For 0x22EB, it always has an ERSPAN header (it's supposed to be type III,
26 * but we look at the version in the ERSPAN header; should we report an
27 * error if it's not version 2?).
32 #include <epan/packet.h>
33 #include <epan/expert.h>
35 #include "packet-gre.h"
37 void proto_register_erspan(void);
38 void proto_reg_handoff_erspan(void);
40 static int proto_erspan
;
42 static int ett_erspan
;
44 static int hf_erspan_version
;
45 static int hf_erspan_vlan
;
46 static int hf_erspan_cos
;
47 static int hf_erspan_encap
;
48 static int hf_erspan_truncated
;
49 static int hf_erspan_spanid
;
50 static int hf_erspan_reserved
;
51 static int hf_erspan_index
;
52 static int hf_erspan_timestamp
;
53 static int hf_erspan_direction
;
55 static int hf_erspan_bso
;
56 static int hf_erspan_sgt
;
57 static int hf_erspan_p
;
58 static int hf_erspan_ft
;
59 static int hf_erspan_hw
;
60 static int hf_erspan_gra
;
61 static int hf_erspan_o
;
63 /* Optional Sub-header */
64 static int hf_erspan_platid
;
66 static int hf_erspan_pid1_rsvd1
;
67 static int hf_erspan_pid1_domain_id
;
68 static int hf_erspan_pid1_port_index
;
70 static int hf_erspan_pid3_rsvd1
;
71 static int hf_erspan_pid3_port_index
;
72 static int hf_erspan_pid3_timestamp
;
74 static int hf_erspan_pid4_rsvd1
;
75 static int hf_erspan_pid4_rsvd2
;
76 static int hf_erspan_pid4_rsvd3
;
77 /* Platform ID = 5 or 6 */
78 static int hf_erspan_pid5_switchid
;
79 static int hf_erspan_pid5_port_index
;
80 static int hf_erspan_pid5_timestamp
;
81 /* Platform ID = 7 (or 0) */
82 static int hf_erspan_pid7_rsvd1
;
83 static int hf_erspan_pid7_source_index
;
84 static int hf_erspan_pid7_timestamp
;
85 /* ID: 0x0, 0x2, 0x8-0x63 are reserved. */
86 static int hf_erspan_pid_rsvd
;
88 static expert_field ei_erspan_version_unknown
;
90 #define PROTO_SHORT_NAME "ERSPAN"
91 #define PROTO_LONG_NAME "Encapsulated Remote Switch Packet ANalysis"
93 static const true_false_string tfs_direction
= { "Egress", "Ingress" };
95 #define ERSPAN_ENCAP_00 0
96 #define ERSPAN_ENCAP_01 1
97 #define ERSPAN_ENCAP_10 2
98 #define ERSPAN_ENCAP_11 3
99 static const value_string erspan_encap_vals
[] = {
100 {ERSPAN_ENCAP_00
, "Originally without VLAN tag"},
101 {ERSPAN_ENCAP_01
, "Originally ISL encapsulated"},
102 {ERSPAN_ENCAP_10
, "Originally 802.1Q encapsulated"},
103 {ERSPAN_ENCAP_11
, "VLAN tag preserved in frame"},
108 static const value_string erspan_bso_vals
[] = {
109 {0, "Good or unknown integrity"},
111 {2, "Oversized frame"},
112 {3, "CRC or alignment error"},
117 static const value_string erspan_truncated_vals
[] = {
118 {0, "Not truncated"},
124 #define ERSPAN_FT_ETHERNET 0
125 #define ERSPAN_FT_IP 2
127 static const value_string erspan_ft_vals
[] = {
128 {ERSPAN_FT_ETHERNET
, "Ethernet"},
129 {ERSPAN_FT_IP
, "IP"},
134 static const value_string erspan_version_vals
[] = {
141 static const value_string erspan_granularity_vals
[] = {
142 {0, "100 microseconds"},
143 {1, "100 nanoseconds"},
145 {3, "Custom granularity"},
150 static dissector_handle_t ethnofcs_handle
;
153 dissect_erspan(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
156 proto_tree
*erspan_tree
= NULL
;
160 uint32_t frame_type
= ERSPAN_FT_ETHERNET
;
162 ti
= proto_tree_add_item(tree
, proto_erspan
, tvb
, offset
, -1,
164 erspan_tree
= proto_item_add_subtree(ti
, ett_erspan
);
166 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, PROTO_SHORT_NAME
);
167 col_set_str(pinfo
->cinfo
, COL_INFO
, PROTO_SHORT_NAME
":");
170 * Dissect the version field, which is present in all versions
173 proto_tree_add_item_ret_uint(erspan_tree
, hf_erspan_version
, tvb
,
174 offset
, 2, ENC_BIG_ENDIAN
, &version
);
176 /* Put the version in the header. */
177 proto_item_append_text(ti
, " %s", val_to_str_const(version
, erspan_version_vals
, "Unknown"));
180 * Now dissect the rest of the header, based on the version.
184 uint32_t vlan
, vlan_encap
;
186 proto_tree_add_item_ret_uint(erspan_tree
, hf_erspan_vlan
, tvb
, offset
, 2,
187 ENC_BIG_ENDIAN
, &vlan
);
190 proto_tree_add_item(erspan_tree
, hf_erspan_cos
, tvb
, offset
, 2,
192 proto_tree_add_item_ret_uint(erspan_tree
, hf_erspan_encap
, tvb
,
193 offset
, 2, ENC_BIG_ENDIAN
, &vlan_encap
);
194 if (pinfo
->vlan_id
== 0 && vlan_encap
!= ERSPAN_ENCAP_11
) {
195 pinfo
->vlan_id
= vlan
;
197 proto_tree_add_item(erspan_tree
, hf_erspan_truncated
, tvb
, offset
, 2,
199 proto_tree_add_item(erspan_tree
, hf_erspan_spanid
, tvb
, offset
, 2,
203 proto_tree_add_item(erspan_tree
, hf_erspan_reserved
, tvb
,
204 offset
, 4, ENC_BIG_ENDIAN
);
205 proto_tree_add_item(erspan_tree
, hf_erspan_index
, tvb
,
206 offset
, 4, ENC_BIG_ENDIAN
);
211 uint32_t subheader
= 0;
214 proto_tree_add_item_ret_uint(erspan_tree
, hf_erspan_vlan
, tvb
, offset
, 2,
215 ENC_BIG_ENDIAN
, &vlan
);
216 pinfo
->vlan_id
= vlan
;
219 proto_tree_add_item(erspan_tree
, hf_erspan_cos
, tvb
, offset
, 2,
221 proto_tree_add_item(erspan_tree
, hf_erspan_bso
, tvb
, offset
, 2,
223 proto_tree_add_item(erspan_tree
, hf_erspan_truncated
, tvb
, offset
, 2,
225 proto_tree_add_item(erspan_tree
, hf_erspan_spanid
, tvb
, offset
, 2,
229 proto_tree_add_item(erspan_tree
, hf_erspan_timestamp
, tvb
,
230 offset
, 4, ENC_BIG_ENDIAN
);
233 proto_tree_add_item(erspan_tree
, hf_erspan_sgt
, tvb
,
234 offset
, 2, ENC_BIG_ENDIAN
);
237 proto_tree_add_item(erspan_tree
, hf_erspan_p
, tvb
,
238 offset
, 2, ENC_BIG_ENDIAN
);
240 proto_tree_add_item_ret_uint(erspan_tree
, hf_erspan_ft
, tvb
,
241 offset
, 2, ENC_BIG_ENDIAN
, &frame_type
);
243 proto_tree_add_item(erspan_tree
, hf_erspan_hw
, tvb
,
244 offset
, 2, ENC_BIG_ENDIAN
);
246 proto_tree_add_item(erspan_tree
, hf_erspan_direction
, tvb
,
247 offset
, 2, ENC_BIG_ENDIAN
);
249 proto_tree_add_item(erspan_tree
, hf_erspan_gra
, tvb
,
250 offset
, 2, ENC_BIG_ENDIAN
);
252 proto_tree_add_item_ret_uint(erspan_tree
, hf_erspan_o
, tvb
,
253 offset
, 2, ENC_BIG_ENDIAN
, &subheader
);
256 /* Platform Specific SubHeader, 8 octets, optional */
258 int32_t platform_id
= tvb_get_ntohl(tvb
, offset
) >> 26;
260 proto_tree_add_item(erspan_tree
, hf_erspan_platid
, tvb
,
261 offset
, 4, ENC_BIG_ENDIAN
);
263 switch (platform_id
) {
265 proto_tree_add_item(erspan_tree
, hf_erspan_pid1_rsvd1
,
266 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
267 proto_tree_add_item(erspan_tree
, hf_erspan_pid1_domain_id
,
268 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
271 proto_tree_add_item(erspan_tree
, hf_erspan_pid1_port_index
,
272 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
277 proto_tree_add_item(erspan_tree
, hf_erspan_pid3_rsvd1
,
278 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
279 proto_tree_add_item(erspan_tree
, hf_erspan_pid3_port_index
,
280 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
283 proto_tree_add_item(erspan_tree
, hf_erspan_pid3_timestamp
,
284 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
289 proto_tree_add_item(erspan_tree
, hf_erspan_pid4_rsvd1
, tvb
,
290 offset
, 4, ENC_BIG_ENDIAN
);
291 proto_tree_add_item(erspan_tree
, hf_erspan_pid4_rsvd2
, tvb
,
292 offset
, 4, ENC_BIG_ENDIAN
);
295 proto_tree_add_item(erspan_tree
, hf_erspan_pid4_rsvd3
, tvb
,
296 offset
, 4, ENC_BIG_ENDIAN
);
302 proto_tree_add_item(erspan_tree
, hf_erspan_pid5_switchid
,
303 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
304 proto_tree_add_item(erspan_tree
, hf_erspan_pid5_port_index
,
305 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
308 proto_tree_add_item(erspan_tree
, hf_erspan_pid5_timestamp
,
309 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
314 case 0: /* In some implementations it is used as an alias to 0x07. */
315 proto_tree_add_item(erspan_tree
, hf_erspan_pid7_rsvd1
,
316 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
317 proto_tree_add_item(erspan_tree
, hf_erspan_pid7_source_index
,
318 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
321 proto_tree_add_item(erspan_tree
, hf_erspan_pid7_timestamp
,
322 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
326 /* ID: 0x0, 0x2, 0x8-0x63 are reserved. */
327 proto_tree_add_item(erspan_tree
, hf_erspan_pid_rsvd
,
328 tvb
, offset
, 8, ENC_BIG_ENDIAN
);
339 ti_ver
= proto_tree_add_item(erspan_tree
, hf_erspan_version
, tvb
, offset
, 2,
341 expert_add_info(pinfo
, ti_ver
, &ei_erspan_version_unknown
);
346 frame_tvb
= tvb_new_subset_remaining(tvb
, offset
);
347 switch (frame_type
) {
349 case ERSPAN_FT_ETHERNET
:
350 call_dissector(ethnofcs_handle
, frame_tvb
, pinfo
, tree
);
354 call_data_dissector(frame_tvb
, pinfo
, tree
);
357 return tvb_captured_length(tvb
);
361 dissect_erspan_88BE(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
363 bool has_erspan_header
;
366 * Frames with a GRE type of 0x88BE have an ERSPAN header iff
367 * the "sequence number present" flag is set in the GRE header.
371 * We weren't handed the GRE flags or version.
373 * This can happen if a Linux cooked capture is done and
374 * we get a packet from an "ipgre" interface.
376 * For now, we just assume this is Type I, with no
379 has_erspan_header
= false;
381 gre_hdr_info_t
*gre_hdr_info
= (gre_hdr_info_t
*)data
;
383 if (gre_hdr_info
->flags_and_ver
& GRE_SEQUENCE
) {
385 * "sequence number present" set, so it has a
388 has_erspan_header
= true;
391 * Not present, so no header.
393 has_erspan_header
= false;
397 if (has_erspan_header
) {
399 * We have a header, so dissect it, and then handle
402 return dissect_erspan(tvb
, pinfo
, tree
, data
);
405 * No header, so just hand the payload off to the
406 * Ethernet dissector. Put in a placeholder for
411 ti
= proto_tree_add_item(tree
, proto_erspan
, tvb
, 0, 0,
413 proto_item_append_text(ti
, " Type I");
415 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, PROTO_SHORT_NAME
);
416 col_set_str(pinfo
->cinfo
, COL_INFO
, PROTO_SHORT_NAME
":");
418 call_dissector(ethnofcs_handle
, tvb
, pinfo
, tree
);
419 return tvb_captured_length(tvb
);
424 dissect_erspan_22EB(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
427 * Frames with a GRE type of 0x22EB always have an ERSPAN
430 return dissect_erspan(tvb
, pinfo
, tree
, data
);
434 proto_register_erspan(void)
436 expert_module_t
* expert_erspan
;
438 static hf_register_info hf
[] = {
440 { &hf_erspan_version
,
441 { "Version", "erspan.version", FT_UINT16
, BASE_DEC
, VALS(erspan_version_vals
),
442 0xf000, NULL
, HFILL
}},
445 { "Vlan", "erspan.vlan", FT_UINT16
, BASE_DEC
, NULL
,
446 0x0fff, NULL
, HFILL
}},
449 { "COS", "erspan.cos", FT_UINT16
, BASE_DEC
, NULL
,
450 0xe000, NULL
, HFILL
}},
453 { "Encap", "erspan.encap", FT_UINT16
, BASE_DEC
, VALS(erspan_encap_vals
),
454 0x1800, NULL
, HFILL
}},
457 { "Bad/Short/Oversized", "erspan.bso", FT_UINT16
, BASE_DEC
, VALS(erspan_bso_vals
),
458 0x1800, NULL
, HFILL
}},
461 { &hf_erspan_truncated
,
462 { "Truncated", "erspan.truncated", FT_UINT16
, BASE_DEC
, VALS(erspan_truncated_vals
),
463 0x0400, "ERSPAN packet exceeded the MTU size", HFILL
}},
466 { "SpanID", "erspan.spanid", FT_UINT16
, BASE_DEC
, NULL
,
467 0x03ff, NULL
, HFILL
}},
469 { &hf_erspan_reserved
,
470 { "Reserved", "erspan.reserved", FT_UINT32
, BASE_DEC
, NULL
,
471 0xfff00000, NULL
, HFILL
}},
474 { "Index", "erspan.index", FT_UINT32
, BASE_DEC
, NULL
,
475 0x000fffff, NULL
, HFILL
}},
477 { &hf_erspan_timestamp
,
478 { "Timestamp", "erspan.timestamp", FT_UINT32
, BASE_DEC
, NULL
,
483 { "Security Group Tag", "erspan.sgt", FT_UINT16
, BASE_DEC
, NULL
,
487 { "Has Ethernet PDU", "erspan.p", FT_UINT16
, BASE_DEC
, NULL
,
488 0x8000, NULL
, HFILL
}},
492 { "Frame Type", "erspan.ft", FT_UINT16
, BASE_DEC
, VALS(erspan_ft_vals
),
493 0x7C00, NULL
, HFILL
}},
496 { "Hardware ID", "erspan.hw", FT_UINT16
, BASE_DEC
, NULL
,
497 0x03f0, NULL
, HFILL
}},
500 { "Timestamp granularity", "erspan.gra", FT_UINT16
, BASE_DEC
, VALS(erspan_granularity_vals
),
501 0x0006, NULL
, HFILL
}},
503 { &hf_erspan_direction
,
504 { "Direction", "erspan.direction", FT_BOOLEAN
, 16, TFS(&tfs_direction
),
505 0x0008, NULL
, HFILL
}},
508 { "Optional Sub headers", "erspan.o", FT_UINT16
, BASE_DEC
, NULL
,
509 0x0001, NULL
, HFILL
}},
511 /* Sub-header Fields, optional */
513 { "Platform ID", "erspan.platid", FT_UINT32
, BASE_DEC
, NULL
,
514 0xfc000000, NULL
, HFILL
}},
517 { &hf_erspan_pid1_rsvd1
,
518 { "Reserved", "erspan.pid1.rsvd1", FT_UINT32
, BASE_DEC
, NULL
,
519 0x03fff000, NULL
, HFILL
}},
521 { &hf_erspan_pid1_domain_id
,
522 { "VSM Domain ID", "erspan.pid1.vsmid", FT_UINT32
, BASE_DEC
, NULL
,
523 0x00000fff, NULL
, HFILL
}},
525 { &hf_erspan_pid1_port_index
,
526 { "Port ID/Index", "erspan.pid1.port_index", FT_UINT32
, BASE_DEC
, NULL
,
530 { &hf_erspan_pid3_rsvd1
,
531 { "Reserved", "erspan.pid3.rsvd1", FT_UINT32
, BASE_DEC
, NULL
,
532 0x03ffc000, NULL
, HFILL
}},
534 { &hf_erspan_pid3_port_index
,
535 { "Port ID/Index", "erspan.pid3.port_index", FT_UINT32
, BASE_DEC
, NULL
,
536 0x00003fff, NULL
, HFILL
}},
538 { &hf_erspan_pid3_timestamp
,
539 { "Upper 32-bit Timestamp", "erspan.pid3.timestamp", FT_UINT32
, BASE_DEC
, NULL
,
543 { &hf_erspan_pid4_rsvd1
,
544 { "Reserved", "erspan.pid4.rsvd1", FT_UINT32
, BASE_DEC
, NULL
,
545 0x03ffc000, NULL
, HFILL
}},
547 { &hf_erspan_pid4_rsvd2
,
548 { "Reserved", "erspan.pid4.rsvd2", FT_UINT32
, BASE_DEC
, NULL
,
549 0x00003fff, NULL
, HFILL
}},
551 { &hf_erspan_pid4_rsvd3
,
552 { "Reserved", "erspan.pid4.rsvd3", FT_UINT32
, BASE_DEC
, NULL
,
553 0xffffffff, NULL
, HFILL
}},
556 { &hf_erspan_pid5_switchid
,
557 { "Switch ID", "erspan.pid5.switchid", FT_UINT32
, BASE_DEC
, NULL
,
558 0x03ff0000, NULL
, HFILL
}},
560 { &hf_erspan_pid5_port_index
,
561 { "Port ID/Index", "erspan.pid5.port_index", FT_UINT32
, BASE_DEC
, NULL
,
562 0x0000ffff, NULL
, HFILL
}},
564 { &hf_erspan_pid5_timestamp
,
565 { "Timestamp (seconds)", "erspan.pid5.timestamp", FT_UINT32
, BASE_DEC
, NULL
,
569 { &hf_erspan_pid7_rsvd1
,
570 { "Reserved", "erspan.pid7.rsvd1", FT_UINT32
, BASE_DEC
, NULL
,
571 0x03f00000, NULL
, HFILL
}},
573 { &hf_erspan_pid7_source_index
,
574 { "Source Index", "erspan.pid7.source_index", FT_UINT32
, BASE_DEC
, NULL
,
575 0x000fffff, NULL
, HFILL
}},
577 { &hf_erspan_pid7_timestamp
,
578 { "Upper 32-bit Timestamp", "erspan.pid7.timestamp", FT_UINT32
, BASE_DEC
, NULL
,
582 { &hf_erspan_pid_rsvd
,
583 { "Reserved", "erspan.pid.rsvd", FT_UINT64
, BASE_DEC
, NULL
,
584 0x03ffffff, NULL
, HFILL
}},
588 static int *ett
[] = {
592 static ei_register_info ei
[] = {
593 { &ei_erspan_version_unknown
, { "erspan.version.unknown", PI_UNDECODED
, PI_WARN
, "Unknown version, please report or test to use fake ERSPAN preference", EXPFILL
}},
596 proto_erspan
= proto_register_protocol(PROTO_LONG_NAME
, PROTO_SHORT_NAME
, "erspan");
597 proto_register_field_array(proto_erspan
, hf
, array_length(hf
));
598 proto_register_subtree_array(ett
, array_length(ett
));
599 expert_erspan
= expert_register_protocol(proto_erspan
);
600 expert_register_field_array(expert_erspan
, ei
, array_length(ei
));
602 register_dissector("erspan", dissect_erspan
, proto_erspan
);
606 proto_reg_handoff_erspan(void)
608 dissector_handle_t erspan_88BE_handle
;
609 dissector_handle_t erspan_22EB_handle
;
611 ethnofcs_handle
= find_dissector_add_dependency("eth_withoutfcs", proto_erspan
);
613 erspan_88BE_handle
= create_dissector_handle(dissect_erspan_88BE
, proto_erspan
);
614 dissector_add_uint("gre.proto", GRE_ERSPAN_88BE
, erspan_88BE_handle
);
615 erspan_22EB_handle
= create_dissector_handle(dissect_erspan_22EB
, proto_erspan
);
616 dissector_add_uint("gre.proto", GRE_ERSPAN_22EB
, erspan_22EB_handle
);
621 * Editor modelines - https://www.wireshark.org/tools/modelines.html
626 * indent-tabs-mode: t
629 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
630 * :indentSize=8:tabSize=8:noTabs=false: