1 /* packet-isl.c
2  * Routines for Cisco ISL Ethernet header disassembly
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 #include "config.h"
12 
13 #include <epan/packet.h>
14 #include <epan/exceptions.h>
15 #include <epan/show_exception.h>
16 #include <epan/capture_dissectors.h>
17 
18 #include "packet-isl.h"
19 #include "packet-eth.h"
20 #include "packet-tr.h"
21 
22 void proto_register_isl(void);
23 void proto_reg_handoff_isl(void);
24 
25 /*
26  * See
27  *
28  *  http://www.cisco.com/c/en/us/support/docs/lan-switching/8021q/17056-741-4.html
29  *
30  * and
31  *
32  *  http://docstore.mik.ua/univercd/cc/td/doc/product/lan/trsrb/frames.htm
33  *
34  * for information on ISL.
35  */
36 static int proto_isl = -1;
37 static int hf_isl_dst = -1;
38 static int hf_isl_type = -1;
39 static int hf_isl_user_eth = -1;
40 static int hf_isl_user = -1;
41 static int hf_isl_src = -1;
42 static int hf_isl_addr = -1;
43 static int hf_isl_len = -1;
44 static int hf_isl_hsa = -1;
45 static int hf_isl_dsap = -1;
46 static int hf_isl_ssap = -1;
47 static int hf_isl_control = -1;
48 static int hf_isl_vlan_id = -1;
49 static int hf_isl_bpdu = -1;
50 static int hf_isl_index = -1;
51 static int hf_isl_reserved = -1;
52 /* static int hf_isl_crc = -1; */
53 static int hf_isl_src_vlan_id = -1;
54 static int hf_isl_explorer = -1;
55 static int hf_isl_dst_route_descriptor = -1;
56 static int hf_isl_src_route_descriptor = -1;
57 static int hf_isl_fcs_not_incl = -1;
58 static int hf_isl_esize = -1;
59 static int hf_isl_trailer = -1;
60 
61 static gint ett_isl = -1;
62 static gint ett_isl_dst = -1;
63 
64 #define ISL_HEADER_SIZE 26
65 
66 #define TYPE_ETHER      0x0
67 #define TYPE_TR         0x1
68 #define TYPE_FDDI       0x2
69 #define TYPE_ATM        0x3
70 
71 #define USER_PRIORITY_NORMAL    0x0
72 #define USER_PRIORITY_1         0x1
73 #define USER_PRIORITY_2         0x2
74 #define USER_PRIORITY_HIGHEST   0x3
75 
76 static dissector_handle_t eth_withfcs_handle;
77 static dissector_handle_t tr_handle;
78 
79 static capture_dissector_handle_t eth_cap_handle;
80 static capture_dissector_handle_t tr_cap_handle;
81 
82 static gboolean
capture_isl(const guchar * pd,int offset,int len,capture_packet_info_t * cpinfo,const union wtap_pseudo_header * pseudo_header _U_)83 capture_isl(const guchar *pd, int offset, int len, capture_packet_info_t *cpinfo, const union wtap_pseudo_header *pseudo_header _U_)
84 {
85   guint8 type;
86 
87   if (!BYTES_ARE_IN_FRAME(offset, len, ISL_HEADER_SIZE))
88     return FALSE;
89 
90   type = (pd[offset+5] >> 4)&0x0F;
91 
92   switch (type) {
93 
94   case TYPE_ETHER:
95     offset += 14+12;    /* skip the header */
96     return call_capture_dissector(eth_cap_handle, pd, offset, len, cpinfo, pseudo_header);
97 
98   case TYPE_TR:
99     offset += 14+17;    /* skip the header */
100     return call_capture_dissector(tr_cap_handle, pd, offset, len, cpinfo, pseudo_header);
101     break;
102   }
103 
104   return FALSE;
105 }
106 
107 static const value_string type_vals[] = {
108   {TYPE_ETHER, "Ethernet"},
109   {TYPE_TR,    "Token-Ring"},
110   {TYPE_FDDI,  "FDDI"},
111   {TYPE_ATM,   "ATM"},
112   {0,          NULL}
113 };
114 
115 static const value_string user_vals[] = {
116   {USER_PRIORITY_NORMAL,  "Normal Priority"},
117   {USER_PRIORITY_1,       "Priority 1"},
118   {USER_PRIORITY_2,       "Priority 2"},
119   {USER_PRIORITY_HIGHEST, "Highest Priority"},
120   {0,                     NULL}
121 };
122 
123 static const true_false_string explorer_tfs = {
124   "Explorer frame",
125   "Data frame"
126 };
127 
128 void
dissect_isl(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,int fcs_len)129 dissect_isl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int fcs_len)
130 {
131   proto_tree *volatile fh_tree = NULL;
132   proto_tree *dst_tree;
133   proto_item *ti, *hidden_item;
134   volatile guint8 type;
135   volatile guint16 length;
136   gint captured_length;
137   tvbuff_t *volatile payload_tvb = NULL;
138   tvbuff_t *volatile next_tvb;
139   tvbuff_t *volatile trailer_tvb = NULL;
140   const char *saved_proto;
141 
142   col_set_str(pinfo->cinfo, COL_PROTOCOL, "ISL");
143   col_clear(pinfo->cinfo, COL_INFO);
144 
145   type = (tvb_get_guint8(tvb, 5) >> 4)&0x0F;
146 
147   if (tree) {
148     ti = proto_tree_add_protocol_format(tree, proto_isl, tvb, 0, ISL_HEADER_SIZE,
149                                         "ISL");
150     fh_tree = proto_item_add_subtree(ti, ett_isl);
151 
152     ti = proto_tree_add_item(fh_tree, hf_isl_dst, tvb, 0, 6, ENC_NA);
153     hidden_item = proto_tree_add_item(fh_tree, hf_isl_addr, tvb, 0, 6, ENC_NA);
154     proto_item_set_hidden(hidden_item);
155     dst_tree = proto_item_add_subtree(ti, ett_isl_dst);
156     proto_tree_add_item(dst_tree, hf_isl_type, tvb, 5, 1, ENC_BIG_ENDIAN);
157 
158     switch (type) {
159 
160     case TYPE_ETHER:
161       proto_tree_add_item(dst_tree, hf_isl_user_eth, tvb, 5, 1, ENC_BIG_ENDIAN);
162       break;
163 
164     default:
165       /* XXX - the spec appears to indicate that the "User" field is
166          used for TYPE_TR to distinguish between types of packets. */
167       proto_tree_add_item(dst_tree, hf_isl_user, tvb, 5, 1, ENC_BIG_ENDIAN);
168       break;
169     }
170     proto_tree_add_item(fh_tree, hf_isl_src, tvb, 6, 6, ENC_NA);
171     hidden_item = proto_tree_add_item(fh_tree, hf_isl_addr, tvb, 6, 6, ENC_NA);
172     proto_item_set_hidden(hidden_item);
173   }
174   length = tvb_get_ntohs(tvb, 12);
175   if (tree)
176     proto_tree_add_uint(fh_tree, hf_isl_len, tvb, 12, 2, length);
177 
178   if (length != 0) {
179     /* The length field was set; it's like an 802.3 length field, so
180        treat it similarly, by constructing a tvbuff containing only
181        the data specified by the length field. */
182 
183     TRY {
184       payload_tvb = tvb_new_subset_length(tvb, 14, length);
185       trailer_tvb = tvb_new_subset_remaining(tvb, 14 + length);
186     }
187     CATCH_BOUNDS_ERRORS {
188       /* Either:
189 
190           the packet doesn't have "length" bytes worth of
191           captured data left in it - or it may not even have
192           "length" bytes worth of data in it, period -
193           so the "tvb_new_subset_length_caplen()" creating "payload_tvb"
194           threw an exception
195 
196          or
197 
198           the packet has exactly "length" bytes worth of
199           captured data left in it, so the "tvb_new_subset_remaining()"
200           creating "trailer_tvb" threw an exception.
201 
202          In either case, this means that all the data in the frame
203          is within the length value, so we give all the data to the
204          next protocol and have no trailer. */
205       payload_tvb = tvb_new_subset_length_caplen(tvb, 14, -1, length);
206       trailer_tvb = NULL;
207     }
208     ENDTRY;
209   } else {
210     /* The length field is 0; make it the length remaining in the packet
211        after the first 14 bytes. */
212     length = tvb_reported_length_remaining(tvb, 14);
213     payload_tvb = tvb_new_subset_remaining(tvb, 14);
214     trailer_tvb = NULL;
215   }
216 
217   if (tree) {
218     /* This part looks sort of like a SNAP-encapsulated LLC header... */
219     proto_tree_add_item(fh_tree, hf_isl_dsap, payload_tvb, 0, 1, ENC_BIG_ENDIAN);
220     proto_tree_add_item(fh_tree, hf_isl_ssap, payload_tvb, 1, 1, ENC_BIG_ENDIAN);
221     proto_tree_add_item(fh_tree, hf_isl_control, payload_tvb, 2, 1, ENC_BIG_ENDIAN);
222 
223     /* ...but this is the manufacturer's ID portion of the source address
224        field (which is, admittedly, an OUI). */
225     proto_tree_add_item(fh_tree, hf_isl_hsa, payload_tvb, 3, 3, ENC_BIG_ENDIAN);
226   }
227   col_add_fstr(pinfo->cinfo, COL_INFO, "VLAN ID: %u",
228                tvb_get_ntohs(tvb, 20) >> 1);
229   if (tree) {
230     proto_tree_add_item(fh_tree, hf_isl_vlan_id, payload_tvb, 6, 2, ENC_BIG_ENDIAN);
231     proto_tree_add_item(fh_tree, hf_isl_bpdu, payload_tvb, 6, 2, ENC_BIG_ENDIAN);
232     proto_tree_add_item(fh_tree, hf_isl_index, payload_tvb, 8, 2, ENC_BIG_ENDIAN);
233     proto_tree_add_item(fh_tree, hf_isl_reserved, payload_tvb, 10, 2, ENC_BIG_ENDIAN);
234   }
235 
236   switch (type) {
237 
238   case TYPE_ETHER:
239     /* The length of the encapsulated frame is the length from the
240        header, minus 12 bytes for the part of the ISL header that
241        follows the length. */
242     if (length >= 12) {
243       /* Well, we at least had that much data in the frame.  Try
244          dissecting what's left as an Ethernet frame. */
245       length -= 12;
246 
247       /* Trim the captured length. */
248       captured_length = tvb_captured_length_remaining(payload_tvb, 12);
249 
250       /* Make sure it's not bigger than the actual length. */
251       if (captured_length > length)
252         captured_length = length;
253 
254       next_tvb = tvb_new_subset_length_caplen(payload_tvb, 12, captured_length, length);
255 
256       /* Dissect the payload as an Ethernet frame.
257 
258         Catch BoundsError and ReportedBoundsError, so that if the
259         reported length of "next_tvb" was reduced by some dissector
260         before an exception was thrown, we can still put in an item
261         for the trailer. */
262       saved_proto = pinfo->current_proto;
263       TRY {
264         /* Frames encapsulated in ISL include an FCS. */
265         call_dissector(eth_withfcs_handle, next_tvb, pinfo, tree);
266       }
267       CATCH_NONFATAL_ERRORS {
268         /* Somebody threw an exception that indicates a problem with
269            the payload, but doesn't indicate anything that would
270            keep us from dissecting the trailer.
271 
272            Show the exception, and then drive on to show the trailer,
273            restoring the protocol value that was in effect before we
274            called the subdissector.
275         */
276 
277         show_exception(next_tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
278         pinfo->current_proto = saved_proto;
279       }
280       ENDTRY;
281 
282       /* Now add the Ethernet trailer and FCS.
283          XXX - do this only if we're encapsulated in Ethernet? */
284       add_ethernet_trailer(pinfo, tree, fh_tree, hf_isl_trailer, tvb, trailer_tvb, fcs_len);
285     }
286     break;
287 
288   case TYPE_TR:
289     if (tree) {
290       proto_tree_add_item(fh_tree, hf_isl_src_vlan_id, payload_tvb, 10, 2, ENC_BIG_ENDIAN);
291       proto_tree_add_item(fh_tree, hf_isl_explorer, payload_tvb, 10, 2, ENC_BIG_ENDIAN);
292       proto_tree_add_item(fh_tree, hf_isl_dst_route_descriptor, payload_tvb, 12, 2, ENC_BIG_ENDIAN);
293       proto_tree_add_item(fh_tree, hf_isl_src_route_descriptor, payload_tvb, 14, 2, ENC_BIG_ENDIAN);
294       /* This doesn't appear to be present in at least one capture I've seen. */
295       proto_tree_add_item(fh_tree, hf_isl_fcs_not_incl, payload_tvb, 16, 1, ENC_BIG_ENDIAN);
296       proto_tree_add_item(fh_tree, hf_isl_esize, payload_tvb, 16, 1, ENC_BIG_ENDIAN);
297     }
298     next_tvb = tvb_new_subset_remaining(payload_tvb, 17);
299     call_dissector(tr_handle, next_tvb, pinfo, tree);
300     break;
301 
302   default:
303     next_tvb = tvb_new_subset_remaining(payload_tvb, 12);
304     call_data_dissector(next_tvb, pinfo, tree);
305     break;
306   }
307 }
308 
309 void
proto_register_isl(void)310 proto_register_isl(void)
311 {
312   static hf_register_info hf[] = {
313     { &hf_isl_dst,
314       { "Destination",        "isl.dst", FT_ETHER, BASE_NONE, NULL, 0x0,
315         "Destination Address", HFILL }},
316     { &hf_isl_type,
317       { "Type",               "isl.type", FT_UINT8, BASE_DEC,
318         VALS(type_vals), 0xF0, NULL, HFILL }},
319     { &hf_isl_user_eth,
320       { "User",               "isl.user_eth", FT_UINT8, BASE_DEC,
321         VALS(user_vals), 0x03, "Priority while passing through switch", HFILL }},
322     { &hf_isl_user,
323       { "User",               "isl.user", FT_UINT8, BASE_HEX, NULL, 0x0F,
324         "User-defined bits", HFILL }},
325     { &hf_isl_src,
326       { "Source",             "isl.src", FT_ETHER, BASE_NONE, NULL, 0x0,
327         "Source Hardware Address", HFILL }},
328     { &hf_isl_addr,
329       { "Source or Destination Address", "isl.addr", FT_ETHER, BASE_NONE, NULL, 0x0,
330         "Source or Destination Hardware Address", HFILL }},
331     { &hf_isl_len,
332       { "Length",             "isl.len", FT_UINT16, BASE_DEC, NULL, 0x0,
333         NULL, HFILL }},
334     { &hf_isl_hsa,
335       { "HSA",                "isl.hsa", FT_UINT24, BASE_HEX, NULL, 0x0,
336         "High bits of source address", HFILL }},
337     { &hf_isl_dsap,
338       { "DSAP",               "isl.dsap", FT_UINT8, BASE_HEX, NULL, 0x0,
339         NULL, HFILL }},
340     { &hf_isl_ssap,
341       { "SSAP",               "isl.ssap", FT_UINT8, BASE_HEX, NULL, 0x0,
342         NULL, HFILL }},
343     { &hf_isl_control,
344       { "Control",            "isl.control", FT_UINT8, BASE_HEX, NULL, 0x0,
345         NULL, HFILL }},
346     { &hf_isl_vlan_id,
347       { "VLAN ID",            "isl.vlan_id", FT_UINT16, BASE_DEC, NULL,
348         0xFFFE, "Virtual LAN ID (Color)", HFILL }},
349     { &hf_isl_bpdu,
350       { "BPDU/CDP/VTP",               "isl.bpdu", FT_BOOLEAN, 16,
351         TFS(&tfs_yes_no), 0x0001, "BPDU/CDP/VTP indicator", HFILL }},
352     { &hf_isl_index,
353       { "Index",              "isl.index", FT_UINT16, BASE_DEC, NULL, 0x0,
354         "Port index of packet source", HFILL }},
355     { &hf_isl_reserved,
356       { "Reserved",           "isl.reserved", FT_UINT16, BASE_HEX, NULL, 0x0,
357         "ISL Reserved", HFILL }},
358 #if 0
359     { &hf_isl_crc,
360       { "CRC",                "isl.crc", FT_UINT32, BASE_HEX, NULL, 0x0,
361         "CRC field of encapsulated frame", HFILL }},
362 #endif
363     { &hf_isl_src_vlan_id,
364       { "Source VLAN ID",     "isl.src_vlan_id", FT_UINT16, BASE_DEC, NULL,
365         0xFFFE, "Source Virtual LAN ID (Color)", HFILL }},
366     { &hf_isl_explorer,
367       { "Explorer",           "isl.explorer", FT_BOOLEAN, 16,
368         TFS(&explorer_tfs), 0x0001, NULL, HFILL }},
369     { &hf_isl_dst_route_descriptor,
370       { "Destination route descriptor",       "isl.dst_route_desc",
371         FT_UINT16, BASE_HEX, NULL, 0x0,
372         "Route descriptor to be used for forwarding", HFILL }},
373     { &hf_isl_src_route_descriptor,
374       { "Source-route descriptor",    "isl.src_route_desc",
375         FT_UINT16, BASE_HEX, NULL, 0x0,
376         "Route descriptor to be used for source learning", HFILL }},
377     { &hf_isl_fcs_not_incl,
378       { "FCS Not Included",   "isl.fcs_not_incl", FT_BOOLEAN, 9,
379         NULL, 0x40, NULL, HFILL }},
380     { &hf_isl_esize,
381       { "Esize",      "isl.esize", FT_UINT8, BASE_DEC, NULL,
382         0x3F, "Frame size for frames less than 64 bytes", HFILL }},
383     { &hf_isl_trailer,
384       { "Trailer",    "isl.trailer", FT_BYTES, BASE_NONE, NULL, 0x0,
385         "Ethernet Trailer or Checksum", HFILL }},
386   };
387   static gint *ett[] = {
388     &ett_isl,
389     &ett_isl_dst,
390   };
391 
392   proto_isl = proto_register_protocol("Cisco ISL", "ISL", "isl");
393   proto_register_field_array(proto_isl, hf, array_length(hf));
394   proto_register_subtree_array(ett, array_length(ett));
395 
396   register_capture_dissector("isl", capture_isl, proto_isl);
397 }
398 
399 void
proto_reg_handoff_isl(void)400 proto_reg_handoff_isl(void)
401 {
402   /*
403    * Get handles for the Ethernet and Token Ring dissectors.
404    */
405   eth_withfcs_handle = find_dissector_add_dependency("eth_withfcs", proto_isl);
406   tr_handle = find_dissector_add_dependency("tr", proto_isl);
407 
408   eth_cap_handle = find_capture_dissector("eth");
409   tr_cap_handle = find_capture_dissector("tr");
410 }
411 
412 /*
413  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
414  *
415  * Local Variables:
416  * c-basic-offset: 2
417  * tab-width: 8
418  * indent-tabs-mode: nil
419  * End:
420  *
421  * ex: set shiftwidth=2 tabstop=8 expandtab:
422  * :indentSize=2:tabSize=8:noTabs=true:
423  */
424