From Richard Urwin a great enhancement to the color filter dialogue to
[obnox/wireshark/wip.git] / packet-isl.c
1 /* packet-isl.c
2  * Routines for Cisco ISL Ethernet header disassembly
3  *
4  * $Id: packet-isl.c,v 1.33 2003/08/08 17:57:44 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
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 "etypes.h"
35
36 /*
37  * See
38  *
39  *      http://www.cisco.com/warp/public/473/741_4.html
40  *
41  * and
42  *
43  *      http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
44  *
45  * for information on ISL.
46  */
47 static int proto_isl = -1;
48 static int hf_isl_dst = -1;
49 static int hf_isl_type = -1;
50 static int hf_isl_user_eth = -1;
51 static int hf_isl_user = -1;
52 static int hf_isl_src = -1;
53 static int hf_isl_addr = -1;
54 static int hf_isl_len = -1;
55 static int hf_isl_hsa = -1;
56 static int hf_isl_vlan_id = -1;
57 static int hf_isl_bpdu = -1;
58 static int hf_isl_index = -1;
59 static int hf_isl_crc = -1;
60 static int hf_isl_src_vlan_id = -1;
61 static int hf_isl_explorer = -1;
62 static int hf_isl_dst_route_descriptor = -1;
63 static int hf_isl_src_route_descriptor = -1;
64 static int hf_isl_fcs_not_incl = -1;
65 static int hf_isl_esize = -1;
66
67 static gint ett_isl = -1;
68
69 #define ISL_HEADER_SIZE 26
70
71 #define TYPE_ETHER      0x0
72 #define TYPE_TR         0x1
73 #define TYPE_FDDI       0x2
74 #define TYPE_ATM        0x3
75
76 static dissector_handle_t eth_handle;
77 static dissector_handle_t tr_handle;
78 static dissector_handle_t data_handle;
79
80 void
81 capture_isl(const guchar *pd, int offset, int len, packet_counts *ld)
82 {
83   guint8 type;
84
85   if (!BYTES_ARE_IN_FRAME(offset, len, ISL_HEADER_SIZE)) {
86     ld->other++;
87     return;
88   }
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     capture_eth(pd, offset, len, ld);
97     break;
98
99   case TYPE_TR:
100     offset += 14+17;    /* skip the header */
101     capture_tr(pd, offset, len, ld);
102     break;
103
104   default:
105     ld->other++;
106     break;
107   }
108 }
109
110 static const value_string type_vals[] = {
111         {TYPE_ETHER, "Ethernet"},
112         {TYPE_TR,    "Token-Ring"},
113         {TYPE_FDDI,  "FDDI"},
114         {TYPE_ATM,   "ATM"},
115         {0,          NULL}
116 };
117
118 static const value_string ether_user_vals[] = {
119         {0x0, "Normal priority"},
120         {0x1, "Priority 1"},
121         {0x2, "Priority 2"},
122         {0x3, "Highest priority"},
123         {0,   NULL}
124 };
125
126 static const true_false_string bpdu_tfs = {
127         "Yes",
128         "No"
129 };
130
131 static const true_false_string explorer_tfs = {
132         "Explorer frame",
133         "Data frame"
134 };
135
136 static void
137 dissect_isl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
138 {
139   proto_tree *fh_tree = NULL;
140   proto_item *ti;
141   guint8 type;
142   guint16 length;
143   gint crc_offset;
144   gint captured_length;
145   tvbuff_t *next_tvb;
146
147   if (check_col(pinfo->cinfo, COL_PROTOCOL))
148     col_set_str(pinfo->cinfo, COL_PROTOCOL, "ISL");
149   if (check_col(pinfo->cinfo, COL_INFO))
150     col_clear(pinfo->cinfo, COL_INFO);
151
152   type = (tvb_get_guint8(tvb, 5) >> 4)&0x0F;
153
154   if (tree) {
155     ti = proto_tree_add_protocol_format(tree, proto_isl, tvb, 0, ISL_HEADER_SIZE,
156                 "ISL");
157     fh_tree = proto_item_add_subtree(ti, ett_isl);
158     proto_tree_add_item(fh_tree, hf_isl_dst, tvb, 0, 6, FALSE);
159     proto_tree_add_item_hidden(fh_tree, hf_isl_addr, tvb, 0, 6, FALSE);
160     proto_tree_add_item(fh_tree, hf_isl_type, tvb, 5, 1, FALSE);
161     switch (type) {
162
163     case TYPE_ETHER:
164       proto_tree_add_item(fh_tree, hf_isl_user_eth, tvb, 5, 1, FALSE);
165       break;
166
167     default:
168       /* XXX - the spec appears to indicate that the "User" field is
169          used for TYPE_TR to distinguish between types of packets. */
170       proto_tree_add_item(fh_tree, hf_isl_user, tvb, 5, 1, FALSE);
171       break;
172     }
173     proto_tree_add_item(fh_tree, hf_isl_src, tvb, 6, 6, FALSE);
174     proto_tree_add_item_hidden(fh_tree, hf_isl_addr, tvb, 6, 6, FALSE);
175   }
176   length = tvb_get_ntohs(tvb, 12);
177   if (tree) {
178     proto_tree_add_uint(fh_tree, hf_isl_len, tvb, 12, 2, length);
179
180     /* This part looks sort of like a SNAP-encapsulated LLC header... */
181     proto_tree_add_text(fh_tree, tvb, 14, 1, "DSAP: 0x%X", tvb_get_guint8(tvb, 14));
182     proto_tree_add_text(fh_tree, tvb, 15, 1, "SSAP: 0x%X", tvb_get_guint8(tvb, 15));
183     proto_tree_add_text(fh_tree, tvb, 16, 1, "Control: 0x%X", tvb_get_guint8(tvb, 16));
184
185     /* ...but this is the manufacturer's ID portion of the source address
186        field (which is, admittedly, an OUI). */
187     proto_tree_add_item(fh_tree, hf_isl_hsa, tvb, 17, 3, FALSE);
188   }
189   if (check_col(pinfo->cinfo, COL_INFO))
190     col_add_fstr(pinfo->cinfo, COL_INFO, "VLAN ID: 0x%04X",
191                  tvb_get_ntohs(tvb, 20) >> 1);
192   if (tree) {
193     proto_tree_add_item(fh_tree, hf_isl_vlan_id, tvb, 20, 2, FALSE);
194     proto_tree_add_item(fh_tree, hf_isl_bpdu, tvb, 20, 2, FALSE);
195     proto_tree_add_item(fh_tree, hf_isl_index, tvb, 22, 2, FALSE);
196
197     /* Now for the encapsulated frame's CRC, which is at the *end* of the
198        packet; "length" is the length of the frame, not including the
199        first 14 bytes of the frame, but including the encapsulated
200        frame's CRC, which is 4 bytes long, so the offset of the
201        encapsulated CRC is "length + 14 - 4".
202
203        We check for the CRC and display it only if we have that data,
204        rather than throwing an exception before we've dissected any
205        of the rest of the frame. */
206     crc_offset = length + 14 - 4;
207     if (tvb_bytes_exist(tvb, crc_offset, 4))
208       proto_tree_add_item(fh_tree, hf_isl_crc, tvb, crc_offset, 4, FALSE);
209   }
210
211   switch (type) {
212
213   case TYPE_ETHER:
214     /* The length of the encapsulated frame is the length from the
215        header, minus 12 bytes for the part of the ISL header that
216        follows the length and 4 bytes for the encapsulated frame
217        CRC. */
218     if (length >= 12+4) {
219       /* Well, we at least had that much data in the frame.  Try
220          dissecting what's left as an Ethernet frame. */
221       length -= 12+4;
222
223       /* Trim the captured length. */
224       captured_length = tvb_length_remaining(tvb, ISL_HEADER_SIZE);
225       if (captured_length > 4) {
226         /* Subtract the encapsulated frame CRC. */
227         captured_length -= 4;
228
229         /* Make sure it's not bigger than the actual length. */
230         if (captured_length > length)
231           captured_length = length;
232
233         next_tvb = tvb_new_subset(tvb, ISL_HEADER_SIZE, captured_length, length);
234
235         call_dissector(eth_handle, next_tvb, pinfo, tree);
236       }
237     }
238     break;
239
240   case TYPE_TR:
241     if (tree) {
242       proto_tree_add_item(fh_tree, hf_isl_src_vlan_id, tvb, 24, 2, FALSE);
243       proto_tree_add_item(fh_tree, hf_isl_explorer, tvb, 24, 2, FALSE);
244       proto_tree_add_item(fh_tree, hf_isl_dst_route_descriptor, tvb, 26, 2, FALSE);
245       proto_tree_add_item(fh_tree, hf_isl_src_route_descriptor, tvb, 28, 2, FALSE);
246       proto_tree_add_item(fh_tree, hf_isl_fcs_not_incl, tvb, 30, 1, FALSE);
247       proto_tree_add_item(fh_tree, hf_isl_esize, tvb, 30, 1, FALSE);
248     }
249     next_tvb = tvb_new_subset(tvb, 31, -1, -1);
250     call_dissector(tr_handle, next_tvb, pinfo, tree);
251     break;
252
253   default:
254     next_tvb = tvb_new_subset(tvb, ISL_HEADER_SIZE, -1, -1);
255     call_dissector(data_handle,next_tvb, pinfo, tree);
256     break;
257   }
258 }
259
260 void
261 proto_register_isl(void)
262 {
263   static hf_register_info hf[] = {
264         { &hf_isl_dst,
265         { "Destination",        "isl.dst", FT_ETHER, BASE_NONE, NULL, 0x0,
266                 "Destination Address", HFILL }},
267         { &hf_isl_type,
268         { "Type",               "isl.type", FT_UINT8, BASE_DEC,
269                 VALS(type_vals), 0xF0, "Type", HFILL }},
270         { &hf_isl_user_eth,
271         { "User",               "isl.user_eth", FT_UINT8, BASE_DEC,
272                 VALS(ether_user_vals), 0x0F, "Priority (for Ethernet)", HFILL }},
273         { &hf_isl_user,
274         { "User",               "isl.user", FT_UINT8, BASE_HEX, NULL, 0x0F,
275                 "User-defined bits", HFILL }},
276         { &hf_isl_src,
277         { "Source",             "isl.src", FT_ETHER, BASE_NONE, NULL, 0x0,
278                 "Source Hardware Address", HFILL }},
279         { &hf_isl_addr,
280         { "Source or Destination Address", "isl.addr", FT_ETHER, BASE_NONE, NULL, 0x0,
281                 "Source or Destination Hardware Address", HFILL }},
282         { &hf_isl_len,
283         { "Length",             "isl.len", FT_UINT16, BASE_DEC, NULL, 0x0,
284                 "", HFILL }},
285         { &hf_isl_hsa,
286         { "HSA",                "isl.hsa", FT_UINT24, BASE_HEX, NULL, 0x0,
287                 "High bits of source address", HFILL }},
288         { &hf_isl_vlan_id,
289         { "VLAN ID",            "isl.vlan_id", FT_UINT16, BASE_HEX, NULL,
290                 0xFFFE, "Virtual LAN ID", HFILL }},
291         { &hf_isl_bpdu,
292         { "BPDU",               "isl.bpdu", FT_BOOLEAN, 16,
293                 TFS(&bpdu_tfs), 0x0001, "BPDU indicator", HFILL }},
294         { &hf_isl_index,
295         { "Index",              "isl.index", FT_UINT16, BASE_DEC, NULL, 0x0,
296                 "Port index of packet source", HFILL }},
297         { &hf_isl_crc,
298         { "CRC",                "isl.crc", FT_UINT32, BASE_HEX, NULL, 0x0,
299                 "CRC field of encapsulated frame", HFILL }},
300         { &hf_isl_src_vlan_id,
301         { "Source VLAN ID",     "isl.src_vlan_id", FT_UINT16, BASE_HEX, NULL,
302                 0xFFFE, "Source Virtual LAN ID", HFILL }},
303         { &hf_isl_explorer,
304         { "Explorer",           "isl.explorer", FT_BOOLEAN, 16,
305                 TFS(&explorer_tfs), 0x0001, "Explorer", HFILL }},
306         { &hf_isl_dst_route_descriptor,
307         { "Destination route descriptor",       "isl.dst_route_desc",
308                 FT_UINT16, BASE_HEX, NULL, 0x0,
309                 "Route descriptor to be used for forwarding", HFILL }},
310         { &hf_isl_src_route_descriptor,
311         { "Source-route descriptor",    "isl.src_route_desc",
312                 FT_UINT16, BASE_HEX, NULL, 0x0,
313                 "Route descriptor to be used for source learning", HFILL }},
314         { &hf_isl_fcs_not_incl,
315         { "FCS Not Included",   "isl.fcs_not_incl", FT_BOOLEAN, 9,
316                 NULL, 0x40, "FCS not included", HFILL }},
317         { &hf_isl_esize,
318         { "Esize",      "isl.esize", FT_UINT8, BASE_DEC, NULL,
319                 0x3F, "Frame size for frames less than 64 bytes", HFILL }},
320   };
321   static gint *ett[] = {
322         &ett_isl,
323   };
324
325   proto_isl = proto_register_protocol("Cisco ISL", "ISL", "isl");
326   proto_register_field_array(proto_isl, hf, array_length(hf));
327   proto_register_subtree_array(ett, array_length(ett));
328
329   register_dissector("isl", dissect_isl, proto_isl);
330 }
331
332 void
333 proto_reg_handoff_isl(void)
334 {
335   /*
336    * Get handles for the Ethernet and Token Ring dissectors.
337    */
338   eth_handle = find_dissector("eth");
339   tr_handle = find_dissector("tr");
340   data_handle = find_dissector("data");
341 }