From Kovarththanan Rajaratnam via bug 3548:
[obnox/wireshark/wip.git] / epan / dissectors / packet-isl.c
1 /* packet-isl.c
2  * Routines for Cisco ISL Ethernet header disassembly
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <glib.h>
30 #include <epan/packet.h>
31 #include "packet-isl.h"
32 #include "packet-eth.h"
33 #include "packet-tr.h"
34 #include "packet-frame.h"
35 #include <epan/etypes.h>
36
37 /*
38  * See
39  *
40  *      http://www.cisco.com/warp/public/473/741_4.html
41  *
42  * and
43  *
44  *      http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
45  *
46  * and
47  *
48  *      http://www.cisco.com/en/US/tech/tk389/tk390/technologies_tech_note09186a0080094665.shtml
49  *
50  * for information on ISL.
51  */
52 static int proto_isl = -1;
53 static int hf_isl_dst = -1;
54 static int hf_isl_type = -1;
55 static int hf_isl_user_eth = -1;
56 static int hf_isl_user = -1;
57 static int hf_isl_src = -1;
58 static int hf_isl_addr = -1;
59 static int hf_isl_len = -1;
60 static int hf_isl_hsa = -1;
61 static int hf_isl_vlan_id = -1;
62 static int hf_isl_bpdu = -1;
63 static int hf_isl_index = -1;
64 static int hf_isl_crc = -1;
65 static int hf_isl_src_vlan_id = -1;
66 static int hf_isl_explorer = -1;
67 static int hf_isl_dst_route_descriptor = -1;
68 static int hf_isl_src_route_descriptor = -1;
69 static int hf_isl_fcs_not_incl = -1;
70 static int hf_isl_esize = -1;
71 static int hf_isl_trailer = -1;
72
73 static gint ett_isl = -1;
74
75 #define ISL_HEADER_SIZE 26
76
77 #define TYPE_ETHER      0x0
78 #define TYPE_TR         0x1
79 #define TYPE_FDDI       0x2
80 #define TYPE_ATM        0x3
81
82 static dissector_handle_t eth_withfcs_handle;
83 static dissector_handle_t tr_handle;
84 static dissector_handle_t data_handle;
85
86 void
87 capture_isl(const guchar *pd, int offset, int len, packet_counts *ld)
88 {
89   guint8 type;
90
91   if (!BYTES_ARE_IN_FRAME(offset, len, ISL_HEADER_SIZE)) {
92     ld->other++;
93     return;
94   }
95
96   type = (pd[offset+5] >> 4)&0x0F;
97
98   switch (type) {
99
100   case TYPE_ETHER:
101     offset += 14+12;    /* skip the header */
102     capture_eth(pd, offset, len, ld);
103     break;
104
105   case TYPE_TR:
106     offset += 14+17;    /* skip the header */
107     capture_tr(pd, offset, len, ld);
108     break;
109
110   default:
111     ld->other++;
112     break;
113   }
114 }
115
116 static const value_string type_vals[] = {
117         {TYPE_ETHER, "Ethernet"},
118         {TYPE_TR,    "Token-Ring"},
119         {TYPE_FDDI,  "FDDI"},
120         {TYPE_ATM,   "ATM"},
121         {0,          NULL}
122 };
123
124 static const value_string ether_user_vals[] = {
125         /* User values are defined by IEEE 802.1D-2004, annex G */
126         {0x0, "Best effort (default priority)"},
127         {0x1, "Background"},
128         {0x2, "[spare priority]"},
129         {0x3, "Excellent effort"},
130         {0x4, "Controlled load"},
131         {0x5, "\"Video\", < 100ms latency and jitter"},
132         {0x6, "\"Voice\", < 10ms latency and jitter"},
133         {0x7, "Network control"},
134         {0,   NULL}
135 };
136
137 static const true_false_string bpdu_tfs = {
138         "Yes",
139         "No"
140 };
141
142 static const true_false_string explorer_tfs = {
143         "Explorer frame",
144         "Data frame"
145 };
146
147 void
148 dissect_isl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int fcs_len)
149 {
150   proto_tree *volatile fh_tree = NULL;
151   proto_item *ti, *hidden_item;
152   volatile guint8 type;
153   volatile guint16 length;
154   gint captured_length;
155   tvbuff_t *volatile payload_tvb = NULL;
156   tvbuff_t *volatile next_tvb;
157   tvbuff_t *volatile trailer_tvb = NULL;
158   const char *saved_proto;
159
160   if (check_col(pinfo->cinfo, COL_PROTOCOL))
161     col_set_str(pinfo->cinfo, COL_PROTOCOL, "ISL");
162   if (check_col(pinfo->cinfo, COL_INFO))
163     col_clear(pinfo->cinfo, COL_INFO);
164
165   type = (tvb_get_guint8(tvb, 5) >> 4)&0x0F;
166
167   if (tree) {
168     ti = proto_tree_add_protocol_format(tree, proto_isl, tvb, 0, ISL_HEADER_SIZE,
169                 "ISL");
170     fh_tree = proto_item_add_subtree(ti, ett_isl);
171     proto_tree_add_item(fh_tree, hf_isl_dst, tvb, 0, 5, FALSE);
172     hidden_item = proto_tree_add_item(fh_tree, hf_isl_addr, tvb, 0, 6, FALSE);
173     PROTO_ITEM_SET_HIDDEN(hidden_item);
174     proto_tree_add_item(fh_tree, hf_isl_type, tvb, 5, 1, FALSE);
175     switch (type) {
176
177     case TYPE_ETHER:
178       proto_tree_add_item(fh_tree, hf_isl_user_eth, tvb, 5, 1, FALSE);
179       break;
180
181     default:
182       /* XXX - the spec appears to indicate that the "User" field is
183          used for TYPE_TR to distinguish between types of packets. */
184       proto_tree_add_item(fh_tree, hf_isl_user, tvb, 5, 1, FALSE);
185       break;
186     }
187     proto_tree_add_item(fh_tree, hf_isl_src, tvb, 6, 6, FALSE);
188     hidden_item = proto_tree_add_item(fh_tree, hf_isl_addr, tvb, 6, 6, FALSE);
189     PROTO_ITEM_SET_HIDDEN(hidden_item);
190   }
191   length = tvb_get_ntohs(tvb, 12);
192   if (tree)
193     proto_tree_add_uint(fh_tree, hf_isl_len, tvb, 12, 2, length);
194
195   if (length != 0) {
196     /* The length field was set; it's like an 802.3 length field, so
197        treat it similarly, by constructing a tvbuff containing only
198        the data specified by the length field. */
199
200     TRY {
201       payload_tvb = tvb_new_subset(tvb, 14, length, length);
202       trailer_tvb = tvb_new_subset(tvb, 14 + length, -1, -1);
203     }
204     CATCH2(BoundsError, ReportedBoundsError) {
205       /* Either:
206
207           the packet doesn't have "length" bytes worth of
208           captured data left in it - or it may not even have
209           "length" bytes worth of data in it, period -
210           so the "tvb_new_subset()" creating "payload_tvb"
211           threw an exception
212
213          or
214
215           the packet has exactly "length" bytes worth of
216           captured data left in it, so the "tvb_new_subset()"
217           creating "trailer_tvb" threw an exception.
218
219          In either case, this means that all the data in the frame
220          is within the length value, so we give all the data to the
221          next protocol and have no trailer. */
222       payload_tvb = tvb_new_subset(tvb, 14, -1, length);
223       trailer_tvb = NULL;
224     }
225     ENDTRY;
226   } else {
227     /* The length field is 0; make it the length remaining in the packet
228        after the first 14 bytes. */
229     length = tvb_reported_length_remaining(tvb, 14);
230     payload_tvb = tvb_new_subset(tvb, 14, -1, -1);
231     trailer_tvb = NULL;
232   }
233
234   if (tree) {
235     tvb_ensure_bytes_exist(payload_tvb, 0, 6);
236     /* This part looks sort of like a SNAP-encapsulated LLC header... */
237     proto_tree_add_text(fh_tree, payload_tvb, 0, 1, "DSAP: 0x%X", tvb_get_guint8(tvb, 14));
238     proto_tree_add_text(fh_tree, payload_tvb, 1, 1, "SSAP: 0x%X", tvb_get_guint8(tvb, 15));
239     proto_tree_add_text(fh_tree, payload_tvb, 2, 1, "Control: 0x%X", tvb_get_guint8(tvb, 16));
240
241     /* ...but this is the manufacturer's ID portion of the source address
242        field (which is, admittedly, an OUI). */
243     proto_tree_add_item(fh_tree, hf_isl_hsa, payload_tvb, 3, 3, FALSE);
244   }
245   if (check_col(pinfo->cinfo, COL_INFO))
246     col_add_fstr(pinfo->cinfo, COL_INFO, "VLAN ID: 0x%04X",
247                  tvb_get_ntohs(tvb, 20) >> 1);
248   if (tree) {
249     proto_tree_add_item(fh_tree, hf_isl_vlan_id, payload_tvb, 6, 2, FALSE);
250     proto_tree_add_item(fh_tree, hf_isl_bpdu, payload_tvb, 6, 2, FALSE);
251     proto_tree_add_item(fh_tree, hf_isl_index, payload_tvb, 8, 2, FALSE);
252   }
253
254   switch (type) {
255
256   case TYPE_ETHER:
257     /* The length of the encapsulated frame is the length from the
258        header, minus 12 bytes for the part of the ISL header that
259        follows the length. */
260     if (length >= 12) {
261       /* Well, we at least had that much data in the frame.  Try
262          dissecting what's left as an Ethernet frame. */
263       length -= 12;
264
265       /* Trim the captured length. */
266       captured_length = tvb_length_remaining(payload_tvb, 12);
267
268       /* Make sure it's not bigger than the actual length. */
269       if (captured_length > length)
270         captured_length = length;
271
272       next_tvb = tvb_new_subset(payload_tvb, 12, captured_length, length);
273
274       /* Dissect the payload as an Etherner frame.
275
276         Catch BoundsError and ReportedBoundsError, so that if the
277         reported length of "next_tvb" was reduced by some dissector
278         before an exception was thrown, we can still put in an item
279         for the trailer. */
280       saved_proto = pinfo->current_proto;
281       TRY {
282         /* Frames encapsulated in ISL include an FCS. */
283         call_dissector(eth_withfcs_handle, next_tvb, pinfo, tree);
284       }
285       CATCH(BoundsError) {
286        /* Somebody threw BoundsError, which means that dissecting the payload
287           found that the packet was cut off by a snapshot length before the
288           end of the payload.  The trailer comes after the payload, so *all*
289           of the trailer is cut off - don't bother adding the trailer, just
290           rethrow the exception so it gets reported. */
291        RETHROW;
292       }
293       CATCH_ALL {
294         /* Well, somebody threw an exception other than BoundsError.
295            Show the exception, and then drive on to show the trailer,
296            restoring the protocol value that was in effect before we
297            called the subdissector. */
298         show_exception(next_tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
299         pinfo->current_proto = saved_proto;
300       }
301       ENDTRY;
302
303       /* Now add the Ethernet trailer and FCS.
304          XXX - do this only if we're encapsulated in Ethernet? */
305       add_ethernet_trailer(pinfo, fh_tree, hf_isl_trailer, tvb, trailer_tvb, fcs_len);
306     }
307     break;
308
309   case TYPE_TR:
310     if (tree) {
311       proto_tree_add_item(fh_tree, hf_isl_src_vlan_id, payload_tvb, 10, 2, FALSE);
312       proto_tree_add_item(fh_tree, hf_isl_explorer, payload_tvb, 10, 2, FALSE);
313       proto_tree_add_item(fh_tree, hf_isl_dst_route_descriptor, payload_tvb, 12, 2, FALSE);
314       proto_tree_add_item(fh_tree, hf_isl_src_route_descriptor, payload_tvb, 14, 2, FALSE);
315       /* This doesn't appear to be present in at least one capture I've seen. */
316       proto_tree_add_item(fh_tree, hf_isl_fcs_not_incl, payload_tvb, 16, 1, FALSE);
317       proto_tree_add_item(fh_tree, hf_isl_esize, payload_tvb, 16, 1, FALSE);
318     }
319     next_tvb = tvb_new_subset(payload_tvb, 17, -1, -1);
320     call_dissector(tr_handle, next_tvb, pinfo, tree);
321     break;
322
323   default:
324     next_tvb = tvb_new_subset(payload_tvb, 12, -1, -1);
325     call_dissector(data_handle, next_tvb, pinfo, tree);
326     break;
327   }
328 }
329
330 void
331 proto_register_isl(void)
332 {
333   static hf_register_info hf[] = {
334         { &hf_isl_dst,
335         { "Destination",        "isl.dst", FT_BYTES, BASE_NONE, NULL, 0x0,
336                 "Destination Address", HFILL }},
337         { &hf_isl_type,
338         { "Type",               "isl.type", FT_UINT8, BASE_DEC,
339                 VALS(type_vals), 0xF0, NULL, HFILL }},
340         { &hf_isl_user_eth,
341         { "User",               "isl.user_eth", FT_UINT8, BASE_DEC,
342                 VALS(ether_user_vals), 0x0F, "Priority (for Ethernet)", HFILL }},
343         { &hf_isl_user,
344         { "User",               "isl.user", FT_UINT8, BASE_HEX, NULL, 0x0F,
345                 "User-defined bits", HFILL }},
346         { &hf_isl_src,
347         { "Source",             "isl.src", FT_ETHER, BASE_NONE, NULL, 0x0,
348                 "Source Hardware Address", HFILL }},
349         { &hf_isl_addr,
350         { "Source or Destination Address", "isl.addr", FT_ETHER, BASE_NONE, NULL, 0x0,
351                 "Source or Destination Hardware Address", HFILL }},
352         { &hf_isl_len,
353         { "Length",             "isl.len", FT_UINT16, BASE_DEC, NULL, 0x0,
354                 NULL, HFILL }},
355         { &hf_isl_hsa,
356         { "HSA",                "isl.hsa", FT_UINT24, BASE_HEX, NULL, 0x0,
357                 "High bits of source address", HFILL }},
358         { &hf_isl_vlan_id,
359         { "VLAN ID",            "isl.vlan_id", FT_UINT16, BASE_HEX, NULL,
360                 0xFFFE, "Virtual LAN ID", HFILL }},
361         { &hf_isl_bpdu,
362         { "BPDU",               "isl.bpdu", FT_BOOLEAN, 16,
363                 TFS(&bpdu_tfs), 0x0001, "BPDU indicator", HFILL }},
364         { &hf_isl_index,
365         { "Index",              "isl.index", FT_UINT16, BASE_DEC, NULL, 0x0,
366                 "Port index of packet source", HFILL }},
367         { &hf_isl_crc,
368         { "CRC",                "isl.crc", FT_UINT32, BASE_HEX, NULL, 0x0,
369                 "CRC field of encapsulated frame", HFILL }},
370         { &hf_isl_src_vlan_id,
371         { "Source VLAN ID",     "isl.src_vlan_id", FT_UINT16, BASE_HEX, NULL,
372                 0xFFFE, "Source Virtual LAN ID", HFILL }},
373         { &hf_isl_explorer,
374         { "Explorer",           "isl.explorer", FT_BOOLEAN, 16,
375                 TFS(&explorer_tfs), 0x0001, NULL, HFILL }},
376         { &hf_isl_dst_route_descriptor,
377         { "Destination route descriptor",       "isl.dst_route_desc",
378                 FT_UINT16, BASE_HEX, NULL, 0x0,
379                 "Route descriptor to be used for forwarding", HFILL }},
380         { &hf_isl_src_route_descriptor,
381         { "Source-route descriptor",    "isl.src_route_desc",
382                 FT_UINT16, BASE_HEX, NULL, 0x0,
383                 "Route descriptor to be used for source learning", HFILL }},
384         { &hf_isl_fcs_not_incl,
385         { "FCS Not Included",   "isl.fcs_not_incl", FT_BOOLEAN, 9,
386                 NULL, 0x40, "FCS not included", HFILL }},
387         { &hf_isl_esize,
388         { "Esize",      "isl.esize", FT_UINT8, BASE_DEC, NULL,
389                 0x3F, "Frame size for frames less than 64 bytes", HFILL }},
390         { &hf_isl_trailer,
391         { "Trailer",    "isl.trailer", FT_BYTES, BASE_NONE, NULL, 0x0,
392                 "Ethernet Trailer or Checksum", HFILL }},
393
394   };
395   static gint *ett[] = {
396         &ett_isl,
397   };
398
399   proto_isl = proto_register_protocol("Cisco ISL", "ISL", "isl");
400   proto_register_field_array(proto_isl, hf, array_length(hf));
401   proto_register_subtree_array(ett, array_length(ett));
402 }
403
404 void
405 proto_reg_handoff_isl(void)
406 {
407   /*
408    * Get handles for the Ethernet and Token Ring dissectors.
409    */
410   eth_withfcs_handle = find_dissector("eth_withfcs");
411   tr_handle = find_dissector("tr");
412   data_handle = find_dissector("data");
413 }