2 * Routines for 802.1ah ethernet header disassembly
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include <epan/packet.h>
14 #include <epan/capture_dissectors.h>
15 #include <wsutil/pint.h>
16 #include <epan/addr_resolv.h>
18 #include "packet-ipx.h"
19 #include "packet-llc.h"
20 #include <epan/etypes.h>
21 #include <epan/prefs.h>
23 void proto_register_ieee8021ah(void);
24 void proto_reg_handoff_ieee8021ah(void);
26 static dissector_handle_t ieee8021ah_handle
;
27 static dissector_handle_t ieee8021ad_handle
;
28 static dissector_handle_t ethertype_handle
;
30 static capture_dissector_handle_t ipx_cap_handle
;
31 static capture_dissector_handle_t llc_cap_handle
;
33 static void dissect_ieee8021ah_common(tvbuff_t
*tvb
, packet_info
*pinfo
,
34 proto_tree
*tree
, proto_tree
*parent
, int tree_index
);
36 /* GLOBALS ************************************************************/
38 /* ethertype for 802.1ah tag - encapsulating an Ethernet packet */
39 static unsigned int ieee8021ah_ethertype
= ETHERTYPE_IEEE_802_1AH
;
41 static int proto_ieee8021ah
;
42 static int proto_ieee8021ad
;
44 /* dot1ad B-tag fields */
45 static int hf_ieee8021ad_priority
;
46 static int hf_ieee8021ad_cfi
;
47 static int hf_ieee8021ad_id
;
48 static int hf_ieee8021ad_svid
;
49 static int hf_ieee8021ad_cvid
;
51 /* dot1ah C-tag fields */
52 static int hf_ieee8021ah_priority
;
53 static int hf_ieee8021ah_drop
; /* drop eligibility */
54 static int hf_ieee8021ah_nca
; /* no customer addresses (c_daddr & c_saddr are 0) */
55 static int hf_ieee8021ah_res1
; /* 2 bits reserved; ignored on receive */
56 static int hf_ieee8021ah_res2
; /* 2 bits reserved; delete frame if non-zero */
57 static int hf_ieee8021ah_isid
; /* I-SID */
58 static int hf_ieee8021ah_c_daddr
; /* encapsulated customer dest addr */
59 static int hf_ieee8021ah_c_saddr
; /* encapsulated customer src addr */
61 static int hf_ieee8021ah_etype
;
62 /* static int hf_ieee8021ah_len; */
63 static int hf_ieee8021ah_trailer
;
65 static int ett_ieee8021ah
;
66 static int ett_ieee8021ad
;
68 #define IEEE8021AD_LEN 4
69 #define IEEE8021AH_LEN 18
70 #define IEEE8021AH_ISIDMASK 0x00FFFFFF
72 /* FUNCTIONS ************************************************************/
76 capture_ieee8021ah(const unsigned char *pd
, int offset
, int len
, capture_packet_info_t
*cpinfo
, const union wtap_pseudo_header
*pseudo_header _U_
)
80 if (!BYTES_ARE_IN_FRAME(offset
, len
, IEEE8021AH_LEN
+ 1))
83 encap_proto
= pntoh16( &pd
[offset
+ IEEE8021AH_LEN
- 2] );
84 if (encap_proto
<= IEEE_802_3_MAX_LEN
) {
85 if ( pd
[offset
+ IEEE8021AH_LEN
] == 0xff
86 && pd
[offset
+ IEEE8021AH_LEN
+ 1] == 0xff ) {
87 return call_capture_dissector(ipx_cap_handle
, pd
, offset
+ IEEE8021AH_LEN
, len
, cpinfo
, pseudo_header
);
90 return call_capture_dissector(llc_cap_handle
, pd
, offset
+ IEEE8021AH_LEN
, len
, cpinfo
, pseudo_header
);
94 return try_capture_dissector("ethertype", encap_proto
, pd
, offset
+ IEEE8021AH_LEN
, len
, cpinfo
, pseudo_header
);
97 /* Dissector *************************************************************/
99 int dissect_ieee8021ad(tvbuff_t
*tvb
, packet_info
*pinfo
,
100 proto_tree
*tree
, void* data _U_
)
102 proto_tree
*ptree
= NULL
;
103 proto_tree
*tagtree
= NULL
;
105 uint16_t encap_proto
;
106 int proto_tree_index
;
107 ethertype_data_t ethertype_data
;
109 tvbuff_t
*next_tvb
= NULL
;
110 proto_tree
*ieee8021ad_tree
;
111 proto_tree
*ieee8021ad_tag_tree
;
114 proto_tree_index
= proto_ieee8021ad
;
116 /* add info to column display */
117 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "802.1ad");
118 col_clear(pinfo
->cinfo
, COL_INFO
);
120 tci
= tvb_get_ntohs( tvb
, 0 );
122 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
123 "PRI: %d DROP: %d ID: %d",
124 (tci
>> 13), ((tci
>> 12) & 1), (tci
& 0xFFF));
126 /* create the protocol tree */
127 ptree
= proto_tree_add_item(tree
, proto_tree_index
, tvb
, 0, IEEE8021AD_LEN
, ENC_NA
);
128 ieee8021ad_tree
= proto_item_add_subtree(ptree
, ett_ieee8021ad
);
130 encap_proto
= tvb_get_ntohs(tvb
, IEEE8021AD_LEN
- 2);
131 ethertype_data
.fh_tree
= ieee8021ad_tree
;
132 ethertype_data
.trailer_id
= hf_ieee8021ah_trailer
;
133 ethertype_data
.fcs_len
= 0;
135 /* If it's a 1ah frame, create subtree for B-Tag, rename overall
136 tree to 802.1ah, pass to 1ah dissector */
137 if (encap_proto
== ETHERTYPE_IEEE_802_1AH
) {
139 tagtree
= proto_tree_add_item(ptree
, proto_tree_index
, tvb
, 0, 2, ENC_NA
);
140 ieee8021ad_tag_tree
= proto_item_add_subtree(tagtree
, ett_ieee8021ad
);
143 proto_tree_add_uint(ieee8021ad_tag_tree
, hf_ieee8021ad_priority
, tvb
,
145 proto_tree_add_uint(ieee8021ad_tag_tree
, hf_ieee8021ad_cfi
, tvb
, 0, 1, tci
);
146 proto_tree_add_uint(ieee8021ad_tag_tree
, hf_ieee8021ad_id
, tvb
, 0, 2, tci
);
148 /* set label of B-tag subtree */
149 proto_item_set_text(ieee8021ad_tag_tree
, "B-Tag, B-VID: %d", tci
& 0x0FFF);
152 next_tvb
= tvb_new_subset_remaining(tvb
, IEEE8021AD_LEN
);
155 /* add bvid to label */
156 proto_item_set_text(ptree
, "IEEE 802.1ah, B-VID: %d", tci
& 0x0FFF);
158 dissect_ieee8021ah_common(next_tvb
, pinfo
, ptree
, tree
, proto_tree_index
);
161 dissect_ieee8021ah_common(next_tvb
, pinfo
, tree
, NULL
, proto_tree_index
);
164 } else if (encap_proto
== ETHERTYPE_IEEE_802_1AD
) {
165 /* two VLAN tags (i.e. Q-in-Q) */
166 ctci
= tvb_get_ntohs(tvb
, IEEE8021AD_LEN
);
170 proto_tree_add_uint(ieee8021ad_tree
, hf_ieee8021ad_priority
, tvb
,
172 proto_tree_add_uint(ieee8021ad_tree
, hf_ieee8021ad_cfi
, tvb
, 0, 1, tci
);
173 proto_tree_add_uint(ieee8021ad_tree
, hf_ieee8021ad_svid
, tvb
, 0, 2, tci
);
174 proto_tree_add_uint(ieee8021ad_tree
, hf_ieee8021ad_priority
, tvb
,
175 IEEE8021AD_LEN
, 1, ctci
);
176 proto_tree_add_uint(ieee8021ad_tree
, hf_ieee8021ad_cfi
, tvb
,
177 IEEE8021AD_LEN
, 1, ctci
);
178 proto_tree_add_uint(ieee8021ad_tree
, hf_ieee8021ad_cvid
, tvb
, IEEE8021AD_LEN
,
182 proto_item_set_text(ptree
, "IEEE 802.1ad, S-VID: %d, C-VID: %d", tci
& 0x0FFF,
185 ethertype_data
.etype
= tvb_get_ntohs(tvb
, IEEE8021AD_LEN
* 2 - 2);
186 proto_tree_add_uint(ieee8021ad_tree
, hf_ieee8021ah_etype
, tvb
,
187 IEEE8021AD_LEN
* 2 - 2, 2, ethertype_data
.etype
);
189 ethertype_data
.payload_offset
= IEEE8021AD_LEN
* 2;
191 /* 802.1ad tags are always followed by an ethertype; call next
192 dissector based on ethertype */
193 call_dissector_with_data(ethertype_handle
, tvb
, pinfo
, tree
, ðertype_data
);
195 /* Something else (shouldn't really happen, but we'll support it anyways) */
198 proto_tree_add_uint(ieee8021ad_tree
, hf_ieee8021ad_priority
, tvb
,
200 proto_tree_add_uint(ieee8021ad_tree
, hf_ieee8021ad_cfi
, tvb
, 0, 1, tci
);
201 proto_tree_add_uint(ieee8021ad_tree
, hf_ieee8021ad_id
, tvb
, 0, 2, tci
);
204 /* label should be 802.1ad not .1ah */
205 proto_item_set_text(ptree
, "IEEE 802.1ad, ID: %d", tci
& 0x0FFF);
207 /* Add the Ethernet type to the protocol tree */
208 proto_tree_add_uint(ieee8021ad_tree
, hf_ieee8021ah_etype
, tvb
,
209 IEEE8021AD_LEN
- 2, 2, encap_proto
);
211 ethertype_data
.etype
= encap_proto
;
212 ethertype_data
.payload_offset
= IEEE8021AD_LEN
;
214 /* 802.1ad tags are always followed by an ethertype; call next
215 dissector based on ethertype */
216 call_dissector_with_data(ethertype_handle
, tvb
, pinfo
, tree
, ðertype_data
);
218 return tvb_captured_length(tvb
);
222 dissect_ieee8021ah_common(tvbuff_t
*tvb
, packet_info
*pinfo
,
223 proto_tree
*tree
, proto_tree
*parent
, int tree_index
) {
225 uint16_t encap_proto
;
227 ethertype_data_t ethertype_data
;
229 proto_tree
*ieee8021ah_tag_tree
;
231 tci
= tvb_get_ntohl( tvb
, 0 );
233 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
234 "PRI: %d Drop: %d NCA: %d Res1: %d Res2: %d I-SID: %d",
235 (tci
>> 29), ((tci
>> 28) & 1), ((tci
>> 27) & 1),
236 ((tci
>> 26) & 1), ((tci
>> 24) & 3), tci
& IEEE8021AH_ISIDMASK
);
238 /* create the protocol tree */
240 ieee8021ah_tag_tree
= NULL
;
244 ptree
= proto_tree_add_item(tree
, tree_index
, tvb
, 0, 4, ENC_NA
);
245 ieee8021ah_tag_tree
= proto_item_add_subtree(ptree
, ett_ieee8021ah
);
248 proto_tree_add_uint(ieee8021ah_tag_tree
, hf_ieee8021ah_priority
, tvb
,
250 proto_tree_add_uint(ieee8021ah_tag_tree
, hf_ieee8021ah_drop
, tvb
, 0, 1, tci
);
251 proto_tree_add_uint(ieee8021ah_tag_tree
, hf_ieee8021ah_nca
, tvb
, 0, 1, tci
);
252 proto_tree_add_uint(ieee8021ah_tag_tree
, hf_ieee8021ah_res1
, tvb
, 0, 1, tci
);
253 proto_tree_add_uint(ieee8021ah_tag_tree
, hf_ieee8021ah_res2
, tvb
, 0, 1, tci
);
254 proto_tree_add_uint(ieee8021ah_tag_tree
, hf_ieee8021ah_isid
, tvb
, 1, 3, tci
);
256 proto_item_set_text(ieee8021ah_tag_tree
, "I-Tag, I-SID: %d",
257 tci
& IEEE8021AH_ISIDMASK
);
259 proto_tree_add_item(tree
, hf_ieee8021ah_c_daddr
, tvb
, 4, 6, ENC_NA
);
260 proto_tree_add_item(tree
, hf_ieee8021ah_c_saddr
, tvb
, 10, 6, ENC_NA
);
262 /* add text to 802.1ad label */
264 proto_item_append_text(tree
, ", I-SID: %d, C-Src: %s, C-Dst: %s",
265 tci
& IEEE8021AH_ISIDMASK
,
266 tvb_address_with_resolution_to_str(pinfo
->pool
, tvb
, AT_ETHER
, 10),
267 tvb_address_with_resolution_to_str(pinfo
->pool
, tvb
, AT_ETHER
, 4));
271 encap_proto
= tvb_get_ntohs(tvb
, IEEE8021AH_LEN
- 2);
272 proto_tree_add_uint(tree
, hf_ieee8021ah_etype
, tvb
,
273 IEEE8021AD_LEN
- 2, 2, encap_proto
);
275 /* 802.1ah I-tags are always followed by an ethertype; call next
276 dissector based on ethertype */
278 /* If this was preceded by a 802.1ad tag, must pass original tree
279 to next dissector, not 802.1ad tree */
280 ethertype_data
.etype
= encap_proto
;
281 ethertype_data
.fh_tree
= tree
;
282 ethertype_data
.payload_offset
= IEEE8021AH_LEN
;
283 ethertype_data
.trailer_id
= hf_ieee8021ah_trailer
;
284 ethertype_data
.fcs_len
= 0;
287 call_dissector_with_data(ethertype_handle
, tvb
, pinfo
, parent
, ðertype_data
);
290 call_dissector_with_data(ethertype_handle
, tvb
, pinfo
, tree
, ðertype_data
);
295 int dissect_ieee8021ah(tvbuff_t
*tvb
, packet_info
*pinfo
,
296 proto_tree
*tree
, void* data _U_
)
300 int proto_tree_index
;
301 proto_tree
*ieee8021ah_tree
;
304 proto_tree_index
= proto_ieee8021ah
;
306 /* add info to column display */
307 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "802.1ah");
308 col_clear(pinfo
->cinfo
, COL_INFO
);
310 tci
= tvb_get_ntohl( tvb
, 0 );
312 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
313 "PRI: %d Drop: %d NCA: %d Res1: %d Res2: %d I-SID: %d",
314 (tci
>> 29), ((tci
>> 28) & 1), ((tci
>> 27) & 1),
315 ((tci
>> 26) & 1), ((tci
>> 24) & 3), (tci
& 0x00FFFFFF));
317 pi
= proto_tree_add_item(tree
, proto_tree_index
, tvb
, 0, IEEE8021AH_LEN
, ENC_NA
);
318 ieee8021ah_tree
= proto_item_add_subtree(pi
, ett_ieee8021ah
);
320 if (ieee8021ah_tree
) {
321 dissect_ieee8021ah_common(tvb
, pinfo
, ieee8021ah_tree
, tree
, proto_tree_index
);
323 dissect_ieee8021ah_common(tvb
, pinfo
, tree
, NULL
, proto_tree_index
);
325 return tvb_captured_length(tvb
);
328 /* Protocol Registration **************************************************/
331 proto_register_ieee8021ah(void)
333 static hf_register_info hf
[] = {
334 { &hf_ieee8021ah_priority
, {
335 "Priority", "ieee8021ah.priority", FT_UINT32
, BASE_DEC
,
336 0, 0xE0000000, NULL
, HFILL
}},
337 { &hf_ieee8021ah_drop
, {
338 "DROP", "ieee8021ah.drop", FT_UINT32
, BASE_DEC
,
339 0, 0x10000000, NULL
, HFILL
}},
340 { &hf_ieee8021ah_nca
, {
341 "NCA", "ieee8021ah.nca", FT_UINT32
, BASE_DEC
,
342 0, 0x08000000, "No Customer Addresses", HFILL
}},
343 { &hf_ieee8021ah_res1
, {
344 "RES1", "ieee8021ah.res1", FT_UINT32
, BASE_DEC
,
345 0, 0x04000000, "Reserved1", HFILL
}},
346 { &hf_ieee8021ah_res2
, {
347 "RES2", "ieee8021ah.res2", FT_UINT32
, BASE_DEC
,
348 0, 0x03000000, "Reserved2", HFILL
}},
349 { &hf_ieee8021ah_isid
, {
350 "I-SID", "ieee8021ah.isid", FT_UINT32
, BASE_DEC
,
351 0, 0x00FFFFFF, NULL
, HFILL
}},
352 { &hf_ieee8021ah_c_daddr
, {
353 "C-Destination", "ieee8021ah.cdst", FT_ETHER
, BASE_NONE
,
354 NULL
, 0x0, "Customer Destination Address", HFILL
}},
355 { &hf_ieee8021ah_c_saddr
, {
356 "C-Source", "ieee8021ah.csrc", FT_ETHER
, BASE_NONE
,
357 NULL
, 0x0, "Customer Source Address", HFILL
}},
358 { &hf_ieee8021ah_etype
, {
359 "Type", "ieee8021ah.etype", FT_UINT16
, BASE_HEX
,
360 VALS(etype_vals
), 0x0, NULL
, HFILL
}},
362 { &hf_ieee8021ah_len
, {
363 "Length", "ieee8021ah.len", FT_UINT16
, BASE_DEC
,
364 NULL
, 0x0, NULL
, HFILL
}},
366 { &hf_ieee8021ah_trailer
, {
367 "Trailer", "ieee8021ah.trailer", FT_BYTES
, BASE_NONE
,
368 NULL
, 0x0, "802.1ah Trailer", HFILL
}}
371 static hf_register_info hf_1ad
[] = {
372 { &hf_ieee8021ad_priority
, {
373 "Priority", "ieee8021ad.priority", FT_UINT16
, BASE_DEC
,
374 0, 0xE000, NULL
, HFILL
}},
375 { &hf_ieee8021ad_cfi
, {
376 "DEI", "ieee8021ad.dei", FT_UINT16
, BASE_DEC
,
377 0, 0x1000, "Drop Eligibility", HFILL
}},
378 { &hf_ieee8021ad_id
, {
379 "ID", "ieee8021ad.id", FT_UINT16
, BASE_DEC
,
380 0, 0x0FFF, "Vlan ID", HFILL
}},
381 { &hf_ieee8021ad_svid
, {
382 "ID", "ieee8021ad.svid", FT_UINT16
, BASE_DEC
,
383 0, 0x0FFF, "S-Vlan ID", HFILL
}},
384 { &hf_ieee8021ad_cvid
, {
385 "ID", "ieee8021ad.cvid", FT_UINT16
, BASE_DEC
,
386 0, 0x0FFF, "C-Vlan ID", HFILL
}},
389 static int *ett
[] = {
395 module_t
*ieee8021ah_module
;
399 proto_ieee8021ah
= proto_register_protocol("IEEE 802.1ah", "IEEE 802.1AH",
401 ieee8021ah_handle
= register_dissector("ieee8021ah", dissect_ieee8021ah
,
403 proto_register_field_array(proto_ieee8021ah
, hf
, array_length(hf
));
405 proto_ieee8021ad
= proto_register_protocol("IEEE 802.1ad", "IEEE 802.1AD",
407 ieee8021ad_handle
= register_dissector("ieee8021ad", dissect_ieee8021ad
,
409 proto_register_field_array(proto_ieee8021ad
, hf_1ad
, array_length(hf_1ad
));
411 /* register subtree array for both */
412 proto_register_subtree_array(ett
, array_length(ett
));
414 /* add a user preference to set the 802.1ah ethertype */
415 ieee8021ah_module
= prefs_register_protocol(proto_ieee8021ah
,
416 proto_reg_handoff_ieee8021ah
);
417 prefs_register_uint_preference(ieee8021ah_module
, "8021ah_ethertype",
418 "802.1ah Ethertype (in hex)",
419 "(Hexadecimal) Ethertype used to indicate IEEE 802.1ah tag.",
420 16, &ieee8021ah_ethertype
);
424 proto_reg_handoff_ieee8021ah(void)
426 static bool prefs_initialized
= false;
427 static unsigned int old_ieee8021ah_ethertype
;
428 static capture_dissector_handle_t ieee8021ah_cap_handle
;
430 if (!prefs_initialized
){
431 dissector_add_uint("ethertype", ETHERTYPE_IEEE_802_1AD
, ieee8021ad_handle
);
432 ethertype_handle
= find_dissector_add_dependency("ethertype", proto_ieee8021ah
);
433 find_dissector_add_dependency("ethertype", proto_ieee8021ad
);
434 ieee8021ah_cap_handle
= create_capture_dissector_handle(capture_ieee8021ah
, proto_ieee8021ah
);
435 capture_dissector_add_uint("ethertype", ETHERTYPE_IEEE_802_1AD
, ieee8021ah_cap_handle
);
436 capture_dissector_add_uint("ethertype", ETHERTYPE_IEEE_802_1AH
, ieee8021ah_cap_handle
);
438 ipx_cap_handle
= find_capture_dissector("ipx");
439 llc_cap_handle
= find_capture_dissector("llc");
441 prefs_initialized
= true;
444 dissector_delete_uint("ethertype", old_ieee8021ah_ethertype
, ieee8021ah_handle
);
447 old_ieee8021ah_ethertype
= ieee8021ah_ethertype
;
448 dissector_add_uint("ethertype", ieee8021ah_ethertype
, ieee8021ah_handle
);
452 * Editor modelines - https://www.wireshark.org/tools/modelines.html
457 * indent-tabs-mode: nil
460 * vi: set shiftwidth=4 tabstop=8 expandtab:
461 * :indentSize=4:tabSize=8:noTabs=true: