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