smb2-dissector: learn the "REPLAY_OPERATION" flag
[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/en/US/tech/tk389/tk689/technologies_tech_note09186a0080094665.shtml
41  *
42  * and
43  *
44  *  http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
45  *
46  * for information on ISL.
47  */
48 static int proto_isl = -1;
49 static int hf_isl_dst = -1;
50 static int hf_isl_type = -1;
51 static int hf_isl_user_eth = -1;
52 static int hf_isl_user = -1;
53 static int hf_isl_src = -1;
54 static int hf_isl_addr = -1;
55 static int hf_isl_len = -1;
56 static int hf_isl_hsa = -1;
57 static int hf_isl_vlan_id = -1;
58 static int hf_isl_bpdu = -1;
59 static int hf_isl_index = -1;
60 static int hf_isl_crc = -1;
61 static int hf_isl_src_vlan_id = -1;
62 static int hf_isl_explorer = -1;
63 static int hf_isl_dst_route_descriptor = -1;
64 static int hf_isl_src_route_descriptor = -1;
65 static int hf_isl_fcs_not_incl = -1;
66 static int hf_isl_esize = -1;
67 static int hf_isl_trailer = -1;
68
69 static gint ett_isl = -1;
70 static gint ett_isl_dst = -1;
71
72 #define ISL_HEADER_SIZE 26
73
74 #define TYPE_ETHER      0x0
75 #define TYPE_TR         0x1
76 #define TYPE_FDDI       0x2
77 #define TYPE_ATM        0x3
78
79 #define USER_PRIORITY_NORMAL    0x0
80 #define USER_PRIORITY_1         0x1
81 #define USER_PRIORITY_2         0x2
82 #define USER_PRIORITY_HIGHEST   0x3
83
84 static dissector_handle_t eth_withfcs_handle;
85 static dissector_handle_t tr_handle;
86 static dissector_handle_t data_handle;
87
88 void
89 capture_isl(const guchar *pd, int offset, int len, packet_counts *ld)
90 {
91   guint8 type;
92
93   if (!BYTES_ARE_IN_FRAME(offset, len, 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, len, ld);
105     break;
106
107   case TYPE_TR:
108     offset += 14+17;    /* skip the header */
109     capture_tr(pd, offset, len, 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 user_vals[] = {
127         {USER_PRIORITY_NORMAL,  "Normal Priority"},
128         {USER_PRIORITY_1,       "Priority 1"},
129         {USER_PRIORITY_2,       "Priority 2"},
130         {USER_PRIORITY_HIGHEST, "Highest Priority"},
131         {0,                     NULL}
132 };
133
134 static const true_false_string explorer_tfs = {
135         "Explorer frame",
136         "Data frame"
137 };
138
139 void
140 dissect_isl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int fcs_len)
141 {
142   proto_tree *volatile fh_tree = NULL;
143   proto_tree *dst_tree;
144   proto_item *ti, *hidden_item;
145   volatile guint8 type;
146   volatile guint16 length;
147   gint captured_length;
148   tvbuff_t *volatile payload_tvb = NULL;
149   tvbuff_t *volatile next_tvb;
150   tvbuff_t *volatile trailer_tvb = NULL;
151   const char *saved_proto;
152   void *pd_save;
153
154   col_set_str(pinfo->cinfo, COL_PROTOCOL, "ISL");
155   col_clear(pinfo->cinfo, COL_INFO);
156
157   type = (tvb_get_guint8(tvb, 5) >> 4)&0x0F;
158
159   if (tree) {
160     ti = proto_tree_add_protocol_format(tree, proto_isl, tvb, 0, ISL_HEADER_SIZE,
161                 "ISL");
162     fh_tree = proto_item_add_subtree(ti, ett_isl);
163
164     ti = proto_tree_add_item(fh_tree, hf_isl_dst, tvb, 0, 6, ENC_NA);
165     hidden_item = proto_tree_add_item(fh_tree, hf_isl_addr, tvb, 0, 6, ENC_NA);
166     PROTO_ITEM_SET_HIDDEN(hidden_item);
167     dst_tree = proto_item_add_subtree(ti, ett_isl_dst);
168     proto_tree_add_item(dst_tree, hf_isl_type, tvb, 5, 1, ENC_BIG_ENDIAN);
169
170     switch (type) {
171
172     case TYPE_ETHER:
173       proto_tree_add_item(dst_tree, hf_isl_user_eth, tvb, 5, 1, ENC_BIG_ENDIAN);
174       break;
175
176     default:
177       /* XXX - the spec appears to indicate that the "User" field is
178          used for TYPE_TR to distinguish between types of packets. */
179       proto_tree_add_item(dst_tree, hf_isl_user, tvb, 5, 1, ENC_BIG_ENDIAN);
180       break;
181     }
182     proto_tree_add_item(fh_tree, hf_isl_src, tvb, 6, 6, ENC_NA);
183     hidden_item = proto_tree_add_item(fh_tree, hf_isl_addr, tvb, 6, 6, ENC_NA);
184     PROTO_ITEM_SET_HIDDEN(hidden_item);
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   if (length != 0) {
191     /* The length field was set; it's like an 802.3 length field, so
192        treat it similarly, by constructing a tvbuff containing only
193        the data specified by the length field. */
194
195     TRY {
196       payload_tvb = tvb_new_subset(tvb, 14, length, length);
197       trailer_tvb = tvb_new_subset_remaining(tvb, 14 + length);
198     }
199     CATCH2(BoundsError, ReportedBoundsError) {
200       /* Either:
201
202           the packet doesn't have "length" bytes worth of
203           captured data left in it - or it may not even have
204           "length" bytes worth of data in it, period -
205           so the "tvb_new_subset()" creating "payload_tvb"
206           threw an exception
207
208          or
209
210           the packet has exactly "length" bytes worth of
211           captured data left in it, so the "tvb_new_subset()"
212           creating "trailer_tvb" threw an exception.
213
214          In either case, this means that all the data in the frame
215          is within the length value, so we give all the data to the
216          next protocol and have no trailer. */
217       payload_tvb = tvb_new_subset(tvb, 14, -1, length);
218       trailer_tvb = NULL;
219     }
220     ENDTRY;
221   } else {
222     /* The length field is 0; make it the length remaining in the packet
223        after the first 14 bytes. */
224     length = tvb_reported_length_remaining(tvb, 14);
225     payload_tvb = tvb_new_subset_remaining(tvb, 14);
226     trailer_tvb = NULL;
227   }
228
229   if (tree) {
230     tvb_ensure_bytes_exist(payload_tvb, 0, 6);
231     /* This part looks sort of like a SNAP-encapsulated LLC header... */
232     proto_tree_add_text(fh_tree, payload_tvb, 0, 1, "DSAP: 0x%X", tvb_get_guint8(tvb, 14));
233     proto_tree_add_text(fh_tree, payload_tvb, 1, 1, "SSAP: 0x%X", tvb_get_guint8(tvb, 15));
234     proto_tree_add_text(fh_tree, payload_tvb, 2, 1, "Control: 0x%X", tvb_get_guint8(tvb, 16));
235
236     /* ...but this is the manufacturer's ID portion of the source address
237        field (which is, admittedly, an OUI). */
238     proto_tree_add_item(fh_tree, hf_isl_hsa, payload_tvb, 3, 3, ENC_BIG_ENDIAN);
239   }
240   col_add_fstr(pinfo->cinfo, COL_INFO, "VLAN ID: %u",
241                tvb_get_ntohs(tvb, 20) >> 1);
242   if (tree) {
243     proto_tree_add_item(fh_tree, hf_isl_vlan_id, payload_tvb, 6, 2, ENC_BIG_ENDIAN);
244     proto_tree_add_item(fh_tree, hf_isl_bpdu, payload_tvb, 6, 2, ENC_BIG_ENDIAN);
245     proto_tree_add_item(fh_tree, hf_isl_index, payload_tvb, 8, 2, ENC_BIG_ENDIAN);
246   }
247
248   switch (type) {
249
250   case TYPE_ETHER:
251     /* The length of the encapsulated frame is the length from the
252        header, minus 12 bytes for the part of the ISL header that
253        follows the length. */
254     if (length >= 12) {
255       /* Well, we at least had that much data in the frame.  Try
256          dissecting what's left as an Ethernet frame. */
257       length -= 12;
258
259       /* Trim the captured length. */
260       captured_length = tvb_length_remaining(payload_tvb, 12);
261
262       /* Make sure it's not bigger than the actual length. */
263       if (captured_length > length)
264         captured_length = length;
265
266       next_tvb = tvb_new_subset(payload_tvb, 12, captured_length, length);
267
268       /* Dissect the payload as an Etherner frame.
269
270         Catch BoundsError and ReportedBoundsError, so that if the
271         reported length of "next_tvb" was reduced by some dissector
272         before an exception was thrown, we can still put in an item
273         for the trailer. */
274       saved_proto = pinfo->current_proto;
275       pd_save = pinfo->private_data;
276       TRY {
277         /* Frames encapsulated in ISL include an FCS. */
278         call_dissector(eth_withfcs_handle, next_tvb, pinfo, tree);
279       }
280       CATCH(BoundsError) {
281        /* Somebody threw BoundsError, which means that dissecting the payload
282           found that the packet was cut off by a snapshot length before the
283           end of the payload.  The trailer comes after the payload, so *all*
284           of the trailer is cut off - don't bother adding the trailer, just
285           rethrow the exception so it gets reported. */
286        RETHROW;
287       }
288       CATCH_ALL {
289         /* Well, somebody threw an exception other than BoundsError.
290            Show the exception, and then drive on to show the trailer,
291            restoring the protocol value that was in effect before we
292            called the subdissector. */
293
294         /*  Restore the private_data structure in case one of the
295          *  called dissectors modified it (and, due to the exception,
296          *  was unable to restore it).
297          */
298         pinfo->private_data = pd_save;
299
300         show_exception(next_tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
301         pinfo->current_proto = saved_proto;
302       }
303       ENDTRY;
304
305       /* Now add the Ethernet trailer and FCS.
306          XXX - do this only if we're encapsulated in Ethernet? */
307       add_ethernet_trailer(pinfo, tree, fh_tree, hf_isl_trailer, tvb, trailer_tvb, fcs_len);
308     }
309     break;
310
311   case TYPE_TR:
312     if (tree) {
313       proto_tree_add_item(fh_tree, hf_isl_src_vlan_id, payload_tvb, 10, 2, ENC_BIG_ENDIAN);
314       proto_tree_add_item(fh_tree, hf_isl_explorer, payload_tvb, 10, 2, ENC_BIG_ENDIAN);
315       proto_tree_add_item(fh_tree, hf_isl_dst_route_descriptor, payload_tvb, 12, 2, ENC_BIG_ENDIAN);
316       proto_tree_add_item(fh_tree, hf_isl_src_route_descriptor, payload_tvb, 14, 2, ENC_BIG_ENDIAN);
317       /* This doesn't appear to be present in at least one capture I've seen. */
318       proto_tree_add_item(fh_tree, hf_isl_fcs_not_incl, payload_tvb, 16, 1, ENC_BIG_ENDIAN);
319       proto_tree_add_item(fh_tree, hf_isl_esize, payload_tvb, 16, 1, ENC_BIG_ENDIAN);
320     }
321     next_tvb = tvb_new_subset_remaining(payload_tvb, 17);
322     call_dissector(tr_handle, next_tvb, pinfo, tree);
323     break;
324
325   default:
326     next_tvb = tvb_new_subset_remaining(payload_tvb, 12);
327     call_dissector(data_handle, next_tvb, pinfo, tree);
328     break;
329   }
330 }
331
332 void
333 proto_register_isl(void)
334 {
335   static hf_register_info hf[] = {
336         { &hf_isl_dst,
337         { "Destination",        "isl.dst", FT_ETHER, BASE_NONE, NULL, 0x0,
338                 "Destination Address", HFILL }},
339         { &hf_isl_type,
340         { "Type",               "isl.type", FT_UINT8, BASE_DEC,
341                 VALS(type_vals), 0xF0, NULL, HFILL }},
342         { &hf_isl_user_eth,
343         { "User",               "isl.user_eth", FT_UINT8, BASE_DEC,
344                 VALS(user_vals), 0x03, "Priority while passing through switch", HFILL }},
345         { &hf_isl_user,
346         { "User",               "isl.user", FT_UINT8, BASE_HEX, NULL, 0x0F,
347                 "User-defined bits", HFILL }},
348         { &hf_isl_src,
349         { "Source",             "isl.src", FT_ETHER, BASE_NONE, NULL, 0x0,
350                 "Source Hardware Address", HFILL }},
351         { &hf_isl_addr,
352         { "Source or Destination Address", "isl.addr", FT_ETHER, BASE_NONE, NULL, 0x0,
353                 "Source or Destination Hardware Address", HFILL }},
354         { &hf_isl_len,
355         { "Length",             "isl.len", FT_UINT16, BASE_DEC, NULL, 0x0,
356                 NULL, HFILL }},
357         { &hf_isl_hsa,
358         { "HSA",                "isl.hsa", FT_UINT24, BASE_HEX, NULL, 0x0,
359                 "High bits of source address", HFILL }},
360         { &hf_isl_vlan_id,
361         { "VLAN ID",            "isl.vlan_id", FT_UINT16, BASE_DEC, NULL,
362                 0xFFFE, "Virtual LAN ID (Color)", HFILL }},
363         { &hf_isl_bpdu,
364         { "BPDU/CDP/VTP",               "isl.bpdu", FT_BOOLEAN, 16,
365                 TFS(&tfs_yes_no), 0x0001, "BPDU/CDP/VTP indicator", HFILL }},
366         { &hf_isl_index,
367         { "Index",              "isl.index", FT_UINT16, BASE_DEC, NULL, 0x0,
368                 "Port index of packet source", HFILL }},
369         { &hf_isl_crc,
370         { "CRC",                "isl.crc", FT_UINT32, BASE_HEX, NULL, 0x0,
371                 "CRC field of encapsulated frame", HFILL }},
372         { &hf_isl_src_vlan_id,
373         { "Source VLAN ID",     "isl.src_vlan_id", FT_UINT16, BASE_DEC, NULL,
374                 0xFFFE, "Source Virtual LAN ID (Color)", HFILL }},
375         { &hf_isl_explorer,
376         { "Explorer",           "isl.explorer", FT_BOOLEAN, 16,
377                 TFS(&explorer_tfs), 0x0001, NULL, HFILL }},
378         { &hf_isl_dst_route_descriptor,
379         { "Destination route descriptor",       "isl.dst_route_desc",
380                 FT_UINT16, BASE_HEX, NULL, 0x0,
381                 "Route descriptor to be used for forwarding", HFILL }},
382         { &hf_isl_src_route_descriptor,
383         { "Source-route descriptor",    "isl.src_route_desc",
384                 FT_UINT16, BASE_HEX, NULL, 0x0,
385                 "Route descriptor to be used for source learning", HFILL }},
386         { &hf_isl_fcs_not_incl,
387         { "FCS Not Included",   "isl.fcs_not_incl", FT_BOOLEAN, 9,
388                 NULL, 0x40, NULL, HFILL }},
389         { &hf_isl_esize,
390         { "Esize",      "isl.esize", FT_UINT8, BASE_DEC, NULL,
391                 0x3F, "Frame size for frames less than 64 bytes", HFILL }},
392         { &hf_isl_trailer,
393         { "Trailer",    "isl.trailer", FT_BYTES, BASE_NONE, NULL, 0x0,
394                 "Ethernet Trailer or Checksum", HFILL }},
395
396   };
397   static gint *ett[] = {
398         &ett_isl,
399         &ett_isl_dst,
400   };
401
402   proto_isl = proto_register_protocol("Cisco ISL", "ISL", "isl");
403   proto_register_field_array(proto_isl, hf, array_length(hf));
404   proto_register_subtree_array(ett, array_length(ett));
405 }
406
407 void
408 proto_reg_handoff_isl(void)
409 {
410   /*
411    * Get handles for the Ethernet and Token Ring dissectors.
412    */
413   eth_withfcs_handle = find_dissector("eth_withfcs");
414   tr_handle = find_dissector("tr");
415   data_handle = find_dissector("data");
416 }