Document the new Copy Profile button.
[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   col_set_str(pinfo->cinfo, COL_PROTOCOL, "ISL");
161   col_clear(pinfo->cinfo, COL_INFO);
162
163   type = (tvb_get_guint8(tvb, 5) >> 4)&0x0F;
164
165   if (tree) {
166     ti = proto_tree_add_protocol_format(tree, proto_isl, tvb, 0, ISL_HEADER_SIZE,
167                 "ISL");
168     fh_tree = proto_item_add_subtree(ti, ett_isl);
169     proto_tree_add_item(fh_tree, hf_isl_dst, tvb, 0, 5, FALSE);
170     hidden_item = proto_tree_add_item(fh_tree, hf_isl_addr, tvb, 0, 6, FALSE);
171     PROTO_ITEM_SET_HIDDEN(hidden_item);
172     proto_tree_add_item(fh_tree, hf_isl_type, tvb, 5, 1, FALSE);
173     switch (type) {
174
175     case TYPE_ETHER:
176       proto_tree_add_item(fh_tree, hf_isl_user_eth, tvb, 5, 1, FALSE);
177       break;
178
179     default:
180       /* XXX - the spec appears to indicate that the "User" field is
181          used for TYPE_TR to distinguish between types of packets. */
182       proto_tree_add_item(fh_tree, hf_isl_user, tvb, 5, 1, FALSE);
183       break;
184     }
185     proto_tree_add_item(fh_tree, hf_isl_src, tvb, 6, 6, FALSE);
186     hidden_item = proto_tree_add_item(fh_tree, hf_isl_addr, tvb, 6, 6, FALSE);
187     PROTO_ITEM_SET_HIDDEN(hidden_item);
188   }
189   length = tvb_get_ntohs(tvb, 12);
190   if (tree)
191     proto_tree_add_uint(fh_tree, hf_isl_len, tvb, 12, 2, length);
192
193   if (length != 0) {
194     /* The length field was set; it's like an 802.3 length field, so
195        treat it similarly, by constructing a tvbuff containing only
196        the data specified by the length field. */
197
198     TRY {
199       payload_tvb = tvb_new_subset(tvb, 14, length, length);
200       trailer_tvb = tvb_new_subset_remaining(tvb, 14 + length);
201     }
202     CATCH2(BoundsError, ReportedBoundsError) {
203       /* Either:
204
205           the packet doesn't have "length" bytes worth of
206           captured data left in it - or it may not even have
207           "length" bytes worth of data in it, period -
208           so the "tvb_new_subset()" creating "payload_tvb"
209           threw an exception
210
211          or
212
213           the packet has exactly "length" bytes worth of
214           captured data left in it, so the "tvb_new_subset()"
215           creating "trailer_tvb" threw an exception.
216
217          In either case, this means that all the data in the frame
218          is within the length value, so we give all the data to the
219          next protocol and have no trailer. */
220       payload_tvb = tvb_new_subset(tvb, 14, -1, length);
221       trailer_tvb = NULL;
222     }
223     ENDTRY;
224   } else {
225     /* The length field is 0; make it the length remaining in the packet
226        after the first 14 bytes. */
227     length = tvb_reported_length_remaining(tvb, 14);
228     payload_tvb = tvb_new_subset_remaining(tvb, 14);
229     trailer_tvb = NULL;
230   }
231
232   if (tree) {
233     tvb_ensure_bytes_exist(payload_tvb, 0, 6);
234     /* This part looks sort of like a SNAP-encapsulated LLC header... */
235     proto_tree_add_text(fh_tree, payload_tvb, 0, 1, "DSAP: 0x%X", tvb_get_guint8(tvb, 14));
236     proto_tree_add_text(fh_tree, payload_tvb, 1, 1, "SSAP: 0x%X", tvb_get_guint8(tvb, 15));
237     proto_tree_add_text(fh_tree, payload_tvb, 2, 1, "Control: 0x%X", tvb_get_guint8(tvb, 16));
238
239     /* ...but this is the manufacturer's ID portion of the source address
240        field (which is, admittedly, an OUI). */
241     proto_tree_add_item(fh_tree, hf_isl_hsa, payload_tvb, 3, 3, FALSE);
242   }
243   if (check_col(pinfo->cinfo, COL_INFO))
244     col_add_fstr(pinfo->cinfo, COL_INFO, "VLAN ID: 0x%04X",
245                  tvb_get_ntohs(tvb, 20) >> 1);
246   if (tree) {
247     proto_tree_add_item(fh_tree, hf_isl_vlan_id, payload_tvb, 6, 2, FALSE);
248     proto_tree_add_item(fh_tree, hf_isl_bpdu, payload_tvb, 6, 2, FALSE);
249     proto_tree_add_item(fh_tree, hf_isl_index, payload_tvb, 8, 2, FALSE);
250   }
251
252   switch (type) {
253
254   case TYPE_ETHER:
255     /* The length of the encapsulated frame is the length from the
256        header, minus 12 bytes for the part of the ISL header that
257        follows the length. */
258     if (length >= 12) {
259       /* Well, we at least had that much data in the frame.  Try
260          dissecting what's left as an Ethernet frame. */
261       length -= 12;
262
263       /* Trim the captured length. */
264       captured_length = tvb_length_remaining(payload_tvb, 12);
265
266       /* Make sure it's not bigger than the actual length. */
267       if (captured_length > length)
268         captured_length = length;
269
270       next_tvb = tvb_new_subset(payload_tvb, 12, captured_length, length);
271
272       /* Dissect the payload as an Etherner frame.
273
274         Catch BoundsError and ReportedBoundsError, so that if the
275         reported length of "next_tvb" was reduced by some dissector
276         before an exception was thrown, we can still put in an item
277         for the trailer. */
278       saved_proto = pinfo->current_proto;
279       TRY {
280         /* Frames encapsulated in ISL include an FCS. */
281         call_dissector(eth_withfcs_handle, next_tvb, pinfo, tree);
282       }
283       CATCH(BoundsError) {
284        /* Somebody threw BoundsError, which means that dissecting the payload
285           found that the packet was cut off by a snapshot length before the
286           end of the payload.  The trailer comes after the payload, so *all*
287           of the trailer is cut off - don't bother adding the trailer, just
288           rethrow the exception so it gets reported. */
289        RETHROW;
290       }
291       CATCH_ALL {
292         /* Well, somebody threw an exception other than BoundsError.
293            Show the exception, and then drive on to show the trailer,
294            restoring the protocol value that was in effect before we
295            called the subdissector. */
296         show_exception(next_tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
297         pinfo->current_proto = saved_proto;
298       }
299       ENDTRY;
300
301       /* Now add the Ethernet trailer and FCS.
302          XXX - do this only if we're encapsulated in Ethernet? */
303       add_ethernet_trailer(pinfo, fh_tree, hf_isl_trailer, tvb, trailer_tvb, fcs_len);
304     }
305     break;
306
307   case TYPE_TR:
308     if (tree) {
309       proto_tree_add_item(fh_tree, hf_isl_src_vlan_id, payload_tvb, 10, 2, FALSE);
310       proto_tree_add_item(fh_tree, hf_isl_explorer, payload_tvb, 10, 2, FALSE);
311       proto_tree_add_item(fh_tree, hf_isl_dst_route_descriptor, payload_tvb, 12, 2, FALSE);
312       proto_tree_add_item(fh_tree, hf_isl_src_route_descriptor, payload_tvb, 14, 2, FALSE);
313       /* This doesn't appear to be present in at least one capture I've seen. */
314       proto_tree_add_item(fh_tree, hf_isl_fcs_not_incl, payload_tvb, 16, 1, FALSE);
315       proto_tree_add_item(fh_tree, hf_isl_esize, payload_tvb, 16, 1, FALSE);
316     }
317     next_tvb = tvb_new_subset_remaining(payload_tvb, 17);
318     call_dissector(tr_handle, next_tvb, pinfo, tree);
319     break;
320
321   default:
322     next_tvb = tvb_new_subset_remaining(payload_tvb, 12);
323     call_dissector(data_handle, next_tvb, pinfo, tree);
324     break;
325   }
326 }
327
328 void
329 proto_register_isl(void)
330 {
331   static hf_register_info hf[] = {
332         { &hf_isl_dst,
333         { "Destination",        "isl.dst", FT_BYTES, BASE_NONE, NULL, 0x0,
334                 "Destination Address", HFILL }},
335         { &hf_isl_type,
336         { "Type",               "isl.type", FT_UINT8, BASE_DEC,
337                 VALS(type_vals), 0xF0, NULL, HFILL }},
338         { &hf_isl_user_eth,
339         { "User",               "isl.user_eth", FT_UINT8, BASE_DEC,
340                 VALS(ether_user_vals), 0x0F, "Priority (for Ethernet)", HFILL }},
341         { &hf_isl_user,
342         { "User",               "isl.user", FT_UINT8, BASE_HEX, NULL, 0x0F,
343                 "User-defined bits", HFILL }},
344         { &hf_isl_src,
345         { "Source",             "isl.src", FT_ETHER, BASE_NONE, NULL, 0x0,
346                 "Source Hardware Address", HFILL }},
347         { &hf_isl_addr,
348         { "Source or Destination Address", "isl.addr", FT_ETHER, BASE_NONE, NULL, 0x0,
349                 "Source or Destination Hardware Address", HFILL }},
350         { &hf_isl_len,
351         { "Length",             "isl.len", FT_UINT16, BASE_DEC, NULL, 0x0,
352                 NULL, HFILL }},
353         { &hf_isl_hsa,
354         { "HSA",                "isl.hsa", FT_UINT24, BASE_HEX, NULL, 0x0,
355                 "High bits of source address", HFILL }},
356         { &hf_isl_vlan_id,
357         { "VLAN ID",            "isl.vlan_id", FT_UINT16, BASE_HEX, NULL,
358                 0xFFFE, "Virtual LAN ID", HFILL }},
359         { &hf_isl_bpdu,
360         { "BPDU",               "isl.bpdu", FT_BOOLEAN, 16,
361                 TFS(&bpdu_tfs), 0x0001, "BPDU indicator", HFILL }},
362         { &hf_isl_index,
363         { "Index",              "isl.index", FT_UINT16, BASE_DEC, NULL, 0x0,
364                 "Port index of packet source", HFILL }},
365         { &hf_isl_crc,
366         { "CRC",                "isl.crc", FT_UINT32, BASE_HEX, NULL, 0x0,
367                 "CRC field of encapsulated frame", HFILL }},
368         { &hf_isl_src_vlan_id,
369         { "Source VLAN ID",     "isl.src_vlan_id", FT_UINT16, BASE_HEX, NULL,
370                 0xFFFE, "Source Virtual LAN ID", HFILL }},
371         { &hf_isl_explorer,
372         { "Explorer",           "isl.explorer", FT_BOOLEAN, 16,
373                 TFS(&explorer_tfs), 0x0001, NULL, HFILL }},
374         { &hf_isl_dst_route_descriptor,
375         { "Destination route descriptor",       "isl.dst_route_desc",
376                 FT_UINT16, BASE_HEX, NULL, 0x0,
377                 "Route descriptor to be used for forwarding", HFILL }},
378         { &hf_isl_src_route_descriptor,
379         { "Source-route descriptor",    "isl.src_route_desc",
380                 FT_UINT16, BASE_HEX, NULL, 0x0,
381                 "Route descriptor to be used for source learning", HFILL }},
382         { &hf_isl_fcs_not_incl,
383         { "FCS Not Included",   "isl.fcs_not_incl", FT_BOOLEAN, 9,
384                 NULL, 0x40, "FCS not included", HFILL }},
385         { &hf_isl_esize,
386         { "Esize",      "isl.esize", FT_UINT8, BASE_DEC, NULL,
387                 0x3F, "Frame size for frames less than 64 bytes", HFILL }},
388         { &hf_isl_trailer,
389         { "Trailer",    "isl.trailer", FT_BYTES, BASE_NONE, NULL, 0x0,
390                 "Ethernet Trailer or Checksum", HFILL }},
391
392   };
393   static gint *ett[] = {
394         &ett_isl,
395   };
396
397   proto_isl = proto_register_protocol("Cisco ISL", "ISL", "isl");
398   proto_register_field_array(proto_isl, hf, array_length(hf));
399   proto_register_subtree_array(ett, array_length(ett));
400 }
401
402 void
403 proto_reg_handoff_isl(void)
404 {
405   /*
406    * Get handles for the Ethernet and Token Ring dissectors.
407    */
408   eth_withfcs_handle = find_dissector("eth_withfcs");
409   tr_handle = find_dissector("tr");
410   data_handle = find_dissector("data");
411 }