Include files from the "epan" directory and subdirectories thereof with
[obnox/wireshark/wip.git] / packet-tr.c
index 62ab02f32e96eed145ad12ce0f3d73dacc8892b3..465d6535859f59ddb979286fe8c790b749c9401f 100644 (file)
@@ -1,13 +1,12 @@
 /* packet-tr.c
  * Routines for Token-Ring packet disassembly
- * Gilbert Ramirez <gram@verdict.uthscsa.edu>
+ * Gilbert Ramirez <gram@alumni.rice.edu>
  *
- * $Id: packet-tr.c,v 1.22 1999/08/27 19:15:38 gram Exp $
+ * $Id: packet-tr.c,v 1.69 2002/01/21 07:36:44 guy Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@unicom.net>
+ * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1998 Gerald Combs
- *
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 # include <sys/types.h>
 #endif
 
+#include <string.h>
 #include <stdio.h>
 #include <glib.h>
-#include "packet.h"
+#include <epan/packet.h>
+#include "packet-tr.h"
+#include "packet-llc.h"
        
 static int proto_tr = -1;
 static int hf_tr_dst = -1;
 static int hf_tr_src = -1;
+static int hf_tr_addr = -1;
 static int hf_tr_sr = -1;
 static int hf_tr_ac = -1;
 static int hf_tr_priority = -1;
@@ -55,12 +58,15 @@ static int hf_tr_direction = -1;
 static int hf_tr_rif = -1;
 static int hf_tr_rif_ring = -1;
 static int hf_tr_rif_bridge = -1;
-       
-static const value_string ac_vals[] = {
-       { 0,    "Token" },
-       { 0x10, "Frame" },
-       { 0,    NULL }
-};
+
+static gint ett_token_ring = -1;
+static gint ett_token_ring_ac = -1;
+static gint ett_token_ring_fc = -1;
+
+#define TR_MIN_HEADER_LEN 14
+#define TR_MAX_HEADER_LEN 32
+
+static const true_false_string ac_truth = { "Frame", "Token" };
 
 static const value_string pcf_vals[] = {
        { 0,    "Normal buffer" },
@@ -75,8 +81,8 @@ static const value_string pcf_vals[] = {
 
 static const value_string frame_vals[] = {
        { 0,    "MAC" },
-       { 64,   "LLC" },
-       { 128,  "Reserved" },
+       { 1,    "LLC" },
+       { 2,    "Reserved" },
        { 0,    NULL },
 };
 
@@ -93,14 +99,15 @@ static const value_string broadcast_vals[] = {
 };
 
 static const value_string max_frame_size_vals[] = {
-       { 0,    "516" },
-       { 1,    "1500" },
-       { 2,    "2052" },
-       { 3,    "4472" },
-       { 4,    "8144" },
-       { 5,    "11407" },
-       { 6,    "17800" },
-       { 0,    NULL }
+       { 0 << 4,       "516" },
+       { 1 << 4,       "1500" },
+       { 2 << 4,       "2052" },
+       { 3 << 4,       "4472" },
+       { 4 << 4,       "8144" },
+       { 5 << 4,       "11407" },
+       { 6 << 4,       "17800" },
+       { 7 << 4,       "65535" },
+       { 0,            NULL }
 };
 
 static const value_string direction_vals[] = {
@@ -109,6 +116,10 @@ static const value_string direction_vals[] = {
        { 0,    NULL }
 };
 
+static dissector_handle_t trmac_handle;
+static dissector_handle_t llc_handle;
+static dissector_handle_t data_handle;
+
 /*
  * DODGY LINUX HACK DODGY LINUX HACK
  * Linux 2.0.x always passes frames to the Token Ring driver for transmission with 
@@ -128,6 +139,29 @@ static const value_string direction_vals[] = {
  * (And this real frame x bytes in looks like a proper TR frame that goes on the wire
  * with none of the Linux idiosyncrasies).
  */
+int check_for_old_linux_tvb(tvbuff_t *tvb)
+{
+       const guint8    *data;
+       int             x, bytes;
+
+       /* Restrict our looping to the boundaries of the frame */
+       bytes = tvb_length(tvb);
+       if (bytes > 19) {
+               bytes = 19;
+       }
+
+       data = tvb_get_ptr(tvb, 0, bytes);
+
+       for(x = 1; x <= bytes-1 ;x++)
+       {
+               if (memcmp(&data[0], &data[x], x) == 0)
+               {
+                       return x;
+               }
+       }
+       return 0;               
+}
+
 int check_for_old_linux(const u_char * pd)
 {
        int x;
@@ -141,23 +175,28 @@ int check_for_old_linux(const u_char * pd)
        return 0;               
 }
 
+
 static void
-add_ring_bridge_pairs(int rcf_len, const u_char *pd, int offset, proto_tree *tree);
+add_ring_bridge_pairs(int rcf_len, tvbuff_t*, proto_tree *tree);
 
 void
-capture_tr(const u_char *pd, guint32 cap_len, packet_counts *ld) {
-
-       int                     offset = 0;
+capture_tr(const u_char *pd, int offset, int len, packet_counts *ld) {
 
        int                     source_routed = 0;
        int                     frame_type;
        int                     x;
        guint8                  trn_rif_bytes;
        guint8                  actual_rif_bytes;
+       guint16                 first2_sr;
 
        /* The trn_hdr struct, as separate variables */
        guint8                  trn_fc;         /* field control field */
-       guint8                  trn_shost[6];   /* source host */
+       const guint8            *trn_shost;     /* source host */
+
+       if (!BYTES_ARE_IN_FRAME(offset, len, TR_MIN_HEADER_LEN)) {
+               ld->other++;
+               return;
+       }
 
        if ((x = check_for_old_linux(pd)))
        {
@@ -168,8 +207,8 @@ capture_tr(const u_char *pd, guint32 cap_len, packet_counts *ld) {
        }
 
        /* get the data */
-       memcpy(&trn_fc, &pd[offset + 1], sizeof(guint8));
-       memcpy(trn_shost, &pd[offset + 8], 6 * sizeof(guint8));
+       trn_fc = pd[offset + 1];
+       trn_shost = &pd[offset + 8];
 
        frame_type = (trn_fc & 192) >> 6;
 
@@ -179,30 +218,26 @@ capture_tr(const u_char *pd, guint32 cap_len, packet_counts *ld) {
 
        trn_rif_bytes = pd[offset + 14] & 31;
 
-       /* sometimes we have a RCF but no RIF... half source-routed? */
-       /* I'll check for 2 bytes of RIF and the 0x70 byte */
+       /* the Linux 2.0 TR code strips source-route bits in
+        * order to test for SR. This can be removed from most
+        * packets with oltr, but not all. So, I try to figure out
+        * which packets should have been SR here. I'll check to
+        * see if there's a SNAP or IPX field right after
+        * my RIF fields.
+        */
        if (!source_routed && trn_rif_bytes > 0) {
-               if (trn_rif_bytes == 2) {
-                       source_routed = 1;
-               }
-               /* the Linux 2.0 TR code strips source-route bits in
-                * order to test for SR. This can be removed from most
-                * packets with oltr, but not all. So, I try to figure out
-                * which packets should have been SR here. I'll check to
-                * see if there's a SNAP or IPX field right after
-                * my RIF fields.
-                */
-               else if ( (
-                       pd[offset + 0x0e + trn_rif_bytes] == 0xaa &&
-                       pd[offset + 0x0f + trn_rif_bytes] == 0xaa &&
-                       pd[offset + 0x10 + trn_rif_bytes] == 0x03) ||
-                         (
-                       pd[offset + 0x0e + trn_rif_bytes] == 0xe0 &&
-                       pd[offset + 0x0f + trn_rif_bytes] == 0xe0) ) {
-
-                       source_routed = 1;
-               }
+               if (pd[offset + 0x0e] != pd[offset + 0x0f]) {
+                       first2_sr = pntohs(&pd[offset + 0xe0 + trn_rif_bytes]);
+                       if (
+                               (first2_sr == 0xaaaa &&
+                               pd[offset + 0x10 + trn_rif_bytes] == 0x03) ||
 
+                               first2_sr == 0xe0e0 ||
+                               first2_sr == 0xe0aa ) {
+
+                               source_routed = 1;
+                       }
+               }
        }
 
        if (source_routed) {
@@ -241,7 +276,7 @@ capture_tr(const u_char *pd, guint32 cap_len, packet_counts *ld) {
                 }
        }
        
-       offset += actual_rif_bytes + 14;
+       offset += actual_rif_bytes + TR_MIN_HEADER_LEN;
 
        /* The package is either MAC or LLC */
        switch (frame_type) {
@@ -250,7 +285,7 @@ capture_tr(const u_char *pd, guint32 cap_len, packet_counts *ld) {
                        ld->other++;
                        break;
                case 1:
-                       capture_llc(pd, offset, cap_len, ld);
+                       capture_llc(pd, offset, len, ld);
                        break;
                default:
                        /* non-MAC, non-LLC, i.e., "Reserved" */
@@ -260,89 +295,101 @@ capture_tr(const u_char *pd, guint32 cap_len, packet_counts *ld) {
 }
 
 
-void
-dissect_tr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
-
+static void
+dissect_tr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
        proto_tree      *tr_tree, *bf_tree;
        proto_item      *ti;
-       int             fixoffset = 0;
-       int                     source_routed = 0;
-       int                     frame_type;
-       guint8          trn_rif_bytes;
-       guint8          actual_rif_bytes;
+       int             frame_type;
+       guint8          rcf1, rcf2;
+       tvbuff_t        *next_tvb;
+
+       volatile int            fixoffset = 0;
+       volatile int            source_routed = 0;
+       volatile guint8         trn_rif_bytes;
+       volatile guint8         actual_rif_bytes;
+       volatile guint8         c1_nonsr;
+       volatile guint8         c2_nonsr;
+       volatile guint16        first2_sr;
+       tvbuff_t                *volatile tr_tvb;
 
        /* The trn_hdr struct, as separate variables */
        guint8                  trn_ac;         /* access control field */
        guint8                  trn_fc;         /* field control field */
-       guint8                  trn_dhost[6];   /* destination host */
-       guint8                  trn_shost[6];   /* source host */
-       guint16                 trn_rcf;        /* routing control field */
-       guint16                 trn_rseg[8];    /* routing registers */
+       const guint8            *trn_dhost;     /* destination host */
+       const guint8            *trn_shost;     /* source host */
 
        /* non-source-routed version of source addr */
-       guint8                  trn_shost_nonsr[6];
+       static guint8           trn_shost_nonsr[6];
        int                     x;
        
        /* Token-Ring Strings */
        char *fc[] = { "MAC", "LLC", "Reserved", "Unknown" };
 
-       if ((x = check_for_old_linux(pd)))
-       {
+       if (check_col(pinfo->cinfo, COL_PROTOCOL))
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, "TR");
+
+       if ((x = check_for_old_linux_tvb((tvbuff_t*) tvb))) {
                /* Actually packet starts x bytes into what we have got but with all
                   source routing compressed. See comment above */
-               offset += x;
-               /* pd = &pd[x]; */
+               tr_tvb = tvb_new_subset((tvbuff_t*) tvb, x, -1, -1);
+       }
+       else {
+               tr_tvb = tvb;
        }
 
+       /* Get the data */
+       trn_fc          = tvb_get_guint8(tr_tvb, 1);
+       trn_dhost       = tvb_get_ptr(tr_tvb, 2, 6);
+       trn_shost       = tvb_get_ptr(tr_tvb, 8, 6);
 
-       /* get the data */
-       memcpy(&trn_ac, &pd[offset+0], sizeof(guint8));
-       memcpy(&trn_fc, &pd[offset+1], sizeof(guint8));
-       memcpy(trn_dhost, &pd[offset+2], 6 * sizeof(guint8));
-       memcpy(trn_shost, &pd[offset+8], 6 * sizeof(guint8));
-       memcpy(&trn_rcf, &pd[offset+14], sizeof(guint16));
-       memcpy(trn_rseg, &pd[offset+16], 8 * sizeof(guint16));
-
-       memcpy(trn_shost_nonsr, &pd[offset+8], 6 * sizeof(guint8));
+
+       memcpy(trn_shost_nonsr, trn_shost, 6);
        trn_shost_nonsr[0] &= 127;
        frame_type = (trn_fc & 192) >> 6;
 
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_add_fstr(pinfo->cinfo, COL_INFO, "Token-Ring %s", fc[frame_type]);
+
        /* if the high bit on the first byte of src hwaddr is 1, then
                this packet is source-routed */
        source_routed = trn_shost[0] & 128;
 
-       trn_rif_bytes = pd[offset+14] & 31;
+       trn_rif_bytes = tvb_get_guint8(tr_tvb, 14) & 31;
 
-       /* sometimes we have a RCF but no RIF... half source-routed? */
-       /* I'll check for 2 bytes of RIF and the 0x70 byte */
-       if (!source_routed && trn_rif_bytes > 0) {
-               if (trn_rif_bytes == 2) {
-                       source_routed = 1;
+       /* the Linux 2.0 TR code strips source-route bits in
+        * order to test for SR. This can be removed from most
+        * packets with oltr, but not all. So, I try to figure out
+        * which packets should have been SR here. I'll check to
+        * see if there's a SNAP or IPX field right after
+        * my RIF fields.
+        */
+       if (frame_type == 1 && !source_routed && trn_rif_bytes > 0) {
+               TRY {
+
+                       c1_nonsr = tvb_get_guint8(tr_tvb, 14);
+                       c2_nonsr = tvb_get_guint8(tr_tvb, 15);
+
+                       if (c1_nonsr != c2_nonsr) {
+
+                               first2_sr = tvb_get_ntohs(tr_tvb, trn_rif_bytes + 0x0e);
+
+                               if ( ( first2_sr == 0xaaaa &&
+                                       tvb_get_guint8(tr_tvb, trn_rif_bytes + 0x10) == 0x03)   ||
+                                       
+                                       first2_sr == 0xe0e0 || 
+                                       first2_sr == 0xe0aa ) {
+
+                                       source_routed = 1;
+                               }
+                       }
                }
-               /* the Linux 2.0 TR code strips source-route bits in
-                * order to test for SR. This can be removed from most
-                * packets with oltr, but not all. So, I try to figure out
-                * which packets should have been SR here. I'll check to
-                * see if there's a SNAP or IPX field right after
-                * my RIF fields.
-                */
-               else if ( (
-                       pd[offset + 0x0e + trn_rif_bytes] == 0xaa &&
-                       pd[offset + 0x0f + trn_rif_bytes] == 0xaa &&
-                       pd[offset + 0x10 + trn_rif_bytes] == 0x03) ||
-                         (
-                       pd[offset + 0x0e + trn_rif_bytes] == 0xe0 &&
-                       pd[offset + 0x0f + trn_rif_bytes] == 0xe0) ) {
-
-                       source_routed = 1;
+               CATCH(BoundsError) {
+                       /* We had no information beyond the TR header. Just assume
+                        * this is a normal (non-Linux) TR header. */
+                       ;
                }
-/*             else {
-                       printf("0e+%d = %02X   0f+%d = %02X\n", trn_rif_bytes,
-                                       pd[offset + 0x0e + trn_rif_bytes],
-                                       trn_rif_bytes,
-                                       pd[offset + 0x0f + trn_rif_bytes]);
-               } */
-
+               ENDTRY;
        }
 
        if (source_routed) {
@@ -356,103 +403,96 @@ dissect_tr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
        /* this is a silly hack for Linux 2.0.x. Read the comment below,
        in front of the other #ifdef linux. If we're sniffing our own NIC,
         we get a full RIF, sometimes with garbage */
-       if ((source_routed && trn_rif_bytes == 2 && frame_type == 1) ||
-               (!source_routed && frame_type == 1)) {
-               /* look for SNAP or IPX only */
-               if (    (pd[offset + 0x20] == 0xaa &&
-                       pd[offset + 0x21] == 0xaa &&
-                       pd[offset + 0x22] == 03)
-                ||
-                       (pd[offset + 0x20] == 0xe0 &&
-                       pd[offset + 0x21] == 0xe0) ) {
-
-                       actual_rif_bytes = 18;
-               }
-               else if (
-                       pd[0x23] == 0 &&
-                       pd[0x24] == 0 &&
-                       pd[0x25] == 0 &&
-                       pd[0x26] == 0x00 &&
-                       pd[0x27] == 0x11) {
-
-                        actual_rif_bytes = 18;
-
-                       /* Linux 2.0.x also requires drivers pass up a fake SNAP and LLC header before th
-                          real LLC hdr for all Token Ring frames that arrive with DSAP and SSAP != 0xAA
-                          (i.e. for non SNAP frames e.g. for Netware frames)
-                          the fake SNAP header has the ETH_P_TR_802_2 ether type (0x0011) and the protocol id
-                          bytes as zero frame looks like :-
-                          TR Header | Fake LLC | Fake SNAP | Wire LLC | Rest of data */
-                       fixoffset += 8; /* Skip fake LLC and SNAP */
-                }
+       TRY {
+               if (frame_type == 1 && ( (source_routed && trn_rif_bytes == 2) ||
+                                        !source_routed) ) {
+                       /* look for SNAP or IPX only */
+                       if (    
+                               (tvb_get_ntohs(tr_tvb, 0x20) == 0xaaaa &&
+                               tvb_get_guint8(tr_tvb, 0x22) == 0x03)
+                        ||
+                               tvb_get_ntohs(tr_tvb, 0x20) == 0xe0e0 ) { 
+
+                               actual_rif_bytes = 18;
+                      }
+                       else if (
+                                       tvb_get_ntohl(tr_tvb, 0x23) == 0 &&
+                                       tvb_get_guint8(tr_tvb, 0x27) == 0x11) {
+
+                               actual_rif_bytes = 18;
+
+                              /* Linux 2.0.x also requires drivers pass up a fake SNAP and LLC header before th
+                                 real LLC hdr for all Token Ring frames that arrive with DSAP and SSAP != 0xAA
+                                 (i.e. for non SNAP frames e.g. for Netware frames)
+                                 the fake SNAP header has the ETH_P_TR_802_2 ether type (0x0011) and the protocol id
+                                 bytes as zero frame looks like :-
+                                 TR Header | Fake LLC | Fake SNAP | Wire LLC | Rest of data */
+                              fixoffset += 8; /* Skip fake LLC and SNAP */
+                       }
+               }
        }
+       CATCH(BoundsError) {
+               /* We had no information beyond the TR header. Just assume
+                * this is a normal (non-Linux) TR header. */
+               ;
+       }
+       ENDTRY;
+
 
-       /* information window */
-       if (check_col(fd, COL_RES_DL_DST))
-               col_add_str(fd, COL_RES_DL_DST,
-                 ether_to_str((guint8 *)&pd[offset + 2]));
-       if (check_col(fd, COL_RES_DL_SRC))
-               col_add_str(fd, COL_RES_DL_SRC, ether_to_str(trn_shost_nonsr));
-       if (check_col(fd, COL_PROTOCOL))
-               col_add_str(fd, COL_PROTOCOL, "TR");
-       if (check_col(fd, COL_INFO))
-               col_add_fstr(fd, COL_INFO, "Token-Ring %s", fc[frame_type]);
+       /* XXX - copy it to some buffer associated with "*pinfo", rather than
+          just making "trn_shost_nonsr" static? */
+       SET_ADDRESS(&pinfo->dl_src,     AT_ETHER, 6, trn_shost_nonsr);
+       SET_ADDRESS(&pinfo->src,        AT_ETHER, 6, trn_shost_nonsr);
+       SET_ADDRESS(&pinfo->dl_dst,     AT_ETHER, 6, trn_dhost);
+       SET_ADDRESS(&pinfo->dst,        AT_ETHER, 6, trn_dhost);
 
        /* protocol analysis tree */
        if (tree) {
                /* Create Token-Ring Tree */
-               ti = proto_tree_add_item(tree, proto_tr, offset, 14 + actual_rif_bytes, NULL);
-               tr_tree = proto_item_add_subtree(ti, ETT_TOKEN_RING);
+               ti = proto_tree_add_item(tree, proto_tr, tr_tvb, 0, TR_MIN_HEADER_LEN + actual_rif_bytes, FALSE);
+               tr_tree = proto_item_add_subtree(ti, ett_token_ring);
 
                /* Create the Access Control bitfield tree */
-               ti = proto_tree_add_item_format(tr_tree, hf_tr_ac, offset, 1, trn_ac,
-                       "Access Control (0x%02x)", trn_ac);
-               bf_tree = proto_item_add_subtree(ti, ETT_TOKEN_RING_AC);
-
-               proto_tree_add_item_format(bf_tree, hf_tr_priority, offset, 1, trn_ac & 0xe0,
-                       decode_numeric_bitfield(trn_ac, 0xe0, 8, "Priority = %d"));
+               trn_ac = tvb_get_guint8(tr_tvb, 0);
+               ti = proto_tree_add_uint(tr_tree, hf_tr_ac, tr_tvb, 0, 1, trn_ac);
+               bf_tree = proto_item_add_subtree(ti, ett_token_ring_ac);
 
-               proto_tree_add_item_format(bf_tree, hf_tr_frame, offset, 1, trn_ac & 0x10,
-                       decode_enumerated_bitfield(trn_ac, 0x10, 8, ac_vals, "%s"));
-
-               proto_tree_add_item_format(bf_tree, hf_tr_monitor_cnt, offset, 1, trn_ac & 0x08,
-                       decode_numeric_bitfield(trn_ac, 0x08, 8, "Monitor Count"));
-
-               proto_tree_add_item_format(bf_tree, hf_tr_priority_reservation, offset, 1, trn_ac & 0x07,
-                       decode_numeric_bitfield(trn_ac, 0x07, 8, "Priority Reservation = %d"));
+               proto_tree_add_uint(bf_tree, hf_tr_priority, tr_tvb, 0, 1, trn_ac);
+               proto_tree_add_boolean(bf_tree, hf_tr_frame, tr_tvb, 0, 1, trn_ac);
+               proto_tree_add_uint(bf_tree, hf_tr_monitor_cnt, tr_tvb, 0, 1, trn_ac);
+               proto_tree_add_uint(bf_tree, hf_tr_priority_reservation, tr_tvb, 0, 1, trn_ac);
 
                /* Create the Frame Control bitfield tree */
-               ti = proto_tree_add_item_format(tr_tree, hf_tr_fc, offset + 1, 1, trn_fc,
-                       "Frame Control (0x%02x)", trn_fc);
-               bf_tree = proto_item_add_subtree(ti, ETT_TOKEN_RING_FC);
-
-               proto_tree_add_item_format(bf_tree, hf_tr_fc_type, offset + 1, 1, trn_fc & 0xc0,
-                       decode_enumerated_bitfield(trn_fc, 0xc0, 8, frame_vals, "%s"));
+               ti = proto_tree_add_uint(tr_tree, hf_tr_fc, tr_tvb, 1, 1, trn_fc);
+               bf_tree = proto_item_add_subtree(ti, ett_token_ring_fc);
 
-               proto_tree_add_item_format(bf_tree, hf_tr_fc_pcf, offset + 1, 1, trn_fc & 0x0f,
-                       decode_enumerated_bitfield(trn_fc, 0x0f, 8, pcf_vals, "%s"));
+               proto_tree_add_uint(bf_tree, hf_tr_fc_type, tr_tvb, 1, 1, trn_fc);
+               proto_tree_add_uint(bf_tree, hf_tr_fc_pcf, tr_tvb,  1, 1, trn_fc);
+               proto_tree_add_ether(tr_tree, hf_tr_dst, tr_tvb, 2, 6, trn_dhost);
+               proto_tree_add_ether(tr_tree, hf_tr_src, tr_tvb, 8, 6, trn_shost);
+               proto_tree_add_ether_hidden(tr_tree, hf_tr_addr, tr_tvb, 2, 6, trn_dhost);
+               proto_tree_add_ether_hidden(tr_tree, hf_tr_addr, tr_tvb, 8, 6, trn_shost);
 
-               proto_tree_add_item(tr_tree, hf_tr_dst, offset + 2, 6, trn_dhost);
-               proto_tree_add_item(tr_tree, hf_tr_src, offset + 8, 6, trn_shost);
-               proto_tree_add_item_hidden(tr_tree, hf_tr_sr, offset + 8, 1, source_routed);
+               proto_tree_add_boolean(tr_tree, hf_tr_sr, tr_tvb, 8, 1, source_routed);
 
                /* non-source-routed version of src addr */
-               proto_tree_add_item_hidden(tr_tree, hf_tr_src, offset + 8, 6, trn_shost_nonsr);
+               proto_tree_add_ether_hidden(tr_tree, hf_tr_src, tr_tvb, 8, 6, trn_shost_nonsr);
 
                if (source_routed) {
                        /* RCF Byte 1 */
-                       proto_tree_add_item(tr_tree, hf_tr_rif_bytes, offset + 14, 1, trn_rif_bytes);
-                       proto_tree_add_item(tr_tree, hf_tr_broadcast, offset + 14, 1, pd[offset + 14] & 224);
+                       rcf1 = tvb_get_guint8(tr_tvb, 14);
+                       proto_tree_add_uint(tr_tree, hf_tr_rif_bytes, tr_tvb, 14, 1, trn_rif_bytes);
+                       proto_tree_add_uint(tr_tree, hf_tr_broadcast, tr_tvb, 14, 1, rcf1 & 224);
 
                        /* RCF Byte 2 */
-                       proto_tree_add_item(tr_tree, hf_tr_max_frame_size, offset + 15, 1, pd[offset + 15] & 112);
-                       proto_tree_add_item(tr_tree, hf_tr_direction, offset + 15, 1, pd[offset + 15] & 128);
+                       rcf2 = tvb_get_guint8(tr_tvb, 15);
+                       proto_tree_add_uint(tr_tree, hf_tr_max_frame_size, tr_tvb, 15, 1, rcf2 & 112);
+                       proto_tree_add_uint(tr_tree, hf_tr_direction, tr_tvb, 15, 1, rcf2 & 128);
 
                        /* if we have more than 2 bytes of RIF, then we have
                                ring/bridge pairs */
                        if (trn_rif_bytes > 2) {
-                               add_ring_bridge_pairs(trn_rif_bytes,
-                                       pd + offset, offset, tr_tree);
+                               add_ring_bridge_pairs(trn_rif_bytes, tr_tvb, tr_tree);
                        }
                }
 
@@ -464,30 +504,31 @@ dissect_tr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
                are filled with garbage. The best way to detect this problem is
                to know the src hwaddr of the machine from which you were running
                tcpdump. W/o that, however, I'm guessing that DSAP == SSAP if the
-               frame type is LLC.  It's very much a hack. -- Gilbert Ramirez */
+               frame type is LLC.  It's very much a hack. */
                if (actual_rif_bytes > trn_rif_bytes) {
-                       proto_tree_add_text(tr_tree, 14 + trn_rif_bytes, actual_rif_bytes - trn_rif_bytes,
+                       proto_tree_add_text(tr_tree, tr_tvb, TR_MIN_HEADER_LEN + trn_rif_bytes, actual_rif_bytes - trn_rif_bytes,
                                "Empty RIF from Linux 2.0.x driver. The sniffing NIC "
                                "is also running a protocol stack.");
                }
                if (fixoffset) {
-                       proto_tree_add_text(tr_tree, 14 + 18,8,"Linux 2.0.x fake LLC and SNAP header");
+                       proto_tree_add_text(tr_tree, tr_tvb, TR_MIN_HEADER_LEN + 18,8,"Linux 2.0.x fake LLC and SNAP header");
                }
        }
-       offset += 14 + actual_rif_bytes + fixoffset;
+
+       next_tvb = tvb_new_subset(tr_tvb, TR_MIN_HEADER_LEN + actual_rif_bytes + fixoffset, -1, -1);
 
        /* The package is either MAC or LLC */
        switch (frame_type) {
                /* MAC */
                case 0:
-                       dissect_trmac(pd, offset, fd, tree);
+                       call_dissector(trmac_handle, next_tvb, pinfo, tree);
                        break;
                case 1:
-                       dissect_llc(pd, offset, fd, tree);
+                       call_dissector(llc_handle, next_tvb, pinfo, tree);
                        break;
                default:
                        /* non-MAC, non-LLC, i.e., "Reserved" */
-                       dissect_data(pd, offset, fd, tree);
+                       call_dissector(data_handle,next_tvb, pinfo, tree);
                        break;
        }
 }
@@ -495,30 +536,45 @@ dissect_tr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
 /* this routine is taken from the Linux net/802/tr.c code, which shows
 ring-bridge pairs in the /proc/net/tr_rif virtual file. */
 static void
-add_ring_bridge_pairs(int rcf_len, const u_char *pd, int offset, proto_tree *tree)
+add_ring_bridge_pairs(int rcf_len, tvbuff_t *tvb, proto_tree *tree)
 {
        int     j, size;
-       int     segment, brdgnmb;
-       char    buffer[50];
+       int     segment, brdgnmb, unprocessed_rif;
        int     buff_offset=0;
 
+#define RIF_OFFSET             16
+#define RIF_BYTES_TO_PROCESS   30
+
+       char    buffer[3 + (RIF_BYTES_TO_PROCESS / 2) * 6 + 1];
+
+       /* Only process so many  bytes of RIF, as per TR spec, and not overflow
+        * static buffer above */
+       unprocessed_rif = rcf_len - RIF_BYTES_TO_PROCESS;
+       rcf_len = MIN(rcf_len, RIF_BYTES_TO_PROCESS);
+
+       /* Ignore the 2 RCF bytes, since they don't make up the ring/bride pairs */
        rcf_len -= 2;
 
        for(j = 1; j < rcf_len - 1; j += 2) {
                if (j==1) {
-                       segment=pntohs(&pd[offset + 16]) >> 4;
+                       segment = tvb_get_ntohs(tvb, RIF_OFFSET) >> 4;
                        size = sprintf(buffer, "%03X",segment);
-                       proto_tree_add_item_hidden(tree, hf_tr_rif_ring, offset + 16, 2, segment);
+                       proto_tree_add_uint_hidden(tree, hf_tr_rif_ring, tvb, TR_MIN_HEADER_LEN + 2, 2, segment);
                        buff_offset += size;
                }
-               segment=pntohs(&pd[offset+17+j]) >> 4;
-               brdgnmb=pd[offset+16+j] & 0x0f;
+               segment = tvb_get_ntohs(tvb, RIF_OFFSET + 1 + j) >> 4;
+               brdgnmb = tvb_get_guint8(tvb, RIF_OFFSET + j) & 0x0f;
                size = sprintf(buffer+buff_offset, "-%01X-%03X",brdgnmb,segment);
-               proto_tree_add_item_hidden(tree, hf_tr_rif_ring, offset+17+j, 2, segment);
-               proto_tree_add_item_hidden(tree, hf_tr_rif_bridge, offset+16+j, 1, brdgnmb);
+               proto_tree_add_uint_hidden(tree, hf_tr_rif_ring, tvb, TR_MIN_HEADER_LEN + 3 + j, 2, segment);
+               proto_tree_add_uint_hidden(tree, hf_tr_rif_bridge, tvb, TR_MIN_HEADER_LEN + 2 + j, 1, brdgnmb);
                buff_offset += size;    
        }
-       proto_tree_add_item(tree, hf_tr_rif, offset+16, rcf_len, buffer);
+       proto_tree_add_string(tree, hf_tr_rif, tvb, TR_MIN_HEADER_LEN + 2, rcf_len, buffer);
+
+       if (unprocessed_rif > 0) {
+               proto_tree_add_text(tree, tvb, TR_MIN_HEADER_LEN + RIF_BYTES_TO_PROCESS, unprocessed_rif,
+                               "Extra RIF bytes beyond spec: %d", unprocessed_rif);
+       }
 }
 
 void
@@ -526,61 +582,107 @@ proto_register_tr(void)
 {
        static hf_register_info hf[] = {
                { &hf_tr_ac,
-               { "Access Control",     "tr.ac", FT_UINT8, NULL }},
+               { "Access Control",     "tr.ac", FT_UINT8, BASE_HEX, NULL, 0x0,
+                       "", HFILL }},
 
                { &hf_tr_priority,
-               { "Priority",           "tr.priority", FT_UINT8, NULL }},
+               { "Priority",           "tr.priority", FT_UINT8, BASE_DEC, NULL, 0xe0,
+                       "", HFILL }},
 
                { &hf_tr_frame,
-               { "Frame",              "tr.frame", FT_VALS_UINT8, VALS(ac_vals) }},
+               { "Frame",              "tr.frame", FT_BOOLEAN, 8, TFS(&ac_truth), 0x10,
+                       "", HFILL }},
 
                { &hf_tr_monitor_cnt,
-               { "Monitor Count",      "tr.monitor_cnt", FT_UINT8, NULL }},
+               { "Monitor Count",      "tr.monitor_cnt", FT_UINT8, BASE_DEC, NULL, 0x08,
+                       "", HFILL }},
 
                { &hf_tr_priority_reservation,
-               { "Priority Reservation","tr.priority_reservation", FT_UINT8, NULL }},
+               { "Priority Reservation","tr.priority_reservation", FT_UINT8, BASE_DEC, NULL, 0x07,
+                       "", HFILL }},
 
                { &hf_tr_fc,
-               { "Frame Control",      "tr.fc", FT_UINT8, NULL }},
+               { "Frame Control",      "tr.fc", FT_UINT8, BASE_HEX, NULL, 0x0,
+                       "", HFILL }},
 
                { &hf_tr_fc_type,
-               { "Frame Type",         "tr.frame_type", FT_VALS_UINT8, VALS(frame_vals) }},
+               { "Frame Type",         "tr.frame_type", FT_UINT8, BASE_DEC, VALS(frame_vals), 0xc0,
+                       "", HFILL }},
 
                { &hf_tr_fc_pcf,
-               { "Frame PCF",          "tr.frame_pcf", FT_VALS_UINT8, VALS(pcf_vals) }},
+               { "Frame PCF",          "tr.frame_pcf", FT_UINT8, BASE_DEC, VALS(pcf_vals), 0x0f,
+                       "", HFILL }},
 
                { &hf_tr_dst,
-               { "Destination",        "tr.dst", FT_ETHER, NULL }},
+               { "Destination",        "tr.dst", FT_ETHER, BASE_NONE,  NULL, 0x0,
+                       "Destination Hardware Address", HFILL }},
 
                { &hf_tr_src,
-               { "Source",             "tr.src", FT_ETHER, NULL }},
+               { "Source",             "tr.src", FT_ETHER, BASE_NONE, NULL, 0x0,
+                       "Source Hardware Address", HFILL }},
+
+               { &hf_tr_addr,
+               { "Source or Destination Address", "tr.addr", FT_ETHER, BASE_NONE, NULL, 0x0,
+                       "Source or Destination Hardware Address", HFILL }},
 
                { &hf_tr_sr,
-               { "Source Routed",      "tr.sr", FT_BOOLEAN, NULL }},
+               { "Source Routed",      "tr.sr", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+                       "Source Routed", HFILL }},
 
                { &hf_tr_rif_bytes,
-               { "RIF Bytes",          "tr.rif_bytes", FT_UINT8, NULL }},
+               { "RIF Bytes",          "tr.rif_bytes", FT_UINT8, BASE_DEC, NULL, 0x0,
+                       "Number of bytes in Routing Information Fields, including "
+                       "the two bytes of Routing Control Field", HFILL }},
 
                { &hf_tr_broadcast,
-               { "Broadcast Type",     "tr.broadcast", FT_VALS_UINT8, VALS(broadcast_vals) }},
+               { "Broadcast Type",     "tr.broadcast", FT_UINT8, BASE_DEC, VALS(broadcast_vals), 0x0,
+                       "Type of Token-Ring Broadcast", HFILL }},
 
                { &hf_tr_max_frame_size,
-               { "Maximum Frame Size", "tr.max_frame_size", FT_VALS_UINT8, VALS(max_frame_size_vals) }},
+               { "Maximum Frame Size", "tr.max_frame_size", FT_UINT8, BASE_DEC, VALS(max_frame_size_vals),
+                       0x0,
+                       "", HFILL }},
 
                { &hf_tr_direction,
-               { "Direction",          "tr.direction", FT_VALS_UINT8, VALS(direction_vals) }},
+               { "Direction",          "tr.direction", FT_UINT8, BASE_DEC, VALS(direction_vals), 0x0,
+                       "Direction of RIF", HFILL }},
 
                { &hf_tr_rif,
-               { "Ring-Bridge Pairs",  "tr.rif", FT_STRING, NULL }},
+               { "Ring-Bridge Pairs",  "tr.rif", FT_STRING, BASE_NONE, NULL, 0x0,
+                       "String representing Ring-Bridge Pairs", HFILL }},
 
                { &hf_tr_rif_ring,
-               { "RIF Ring",           "tr.rif.ring", FT_UINT16, NULL }},
+               { "RIF Ring",           "tr.rif.ring", FT_UINT16, BASE_HEX, NULL, 0x0,
+                       "", HFILL }},
 
                { &hf_tr_rif_bridge,
-               { "RIF Bridge",         "tr.rif.bridge", FT_UINT8, NULL }}
+               { "RIF Bridge",         "tr.rif.bridge", FT_UINT8, BASE_HEX, NULL, 0x0,
+                       "", HFILL }},
+       };
+       static gint *ett[] = {
+               &ett_token_ring,
+               &ett_token_ring_ac,
+               &ett_token_ring_fc,
        };
 
-       proto_tr = proto_register_protocol("Token-Ring", "tr");
+       proto_tr = proto_register_protocol("Token-Ring", "Token-Ring", "tr");
        proto_register_field_array(proto_tr, hf, array_length(hf));
+       proto_register_subtree_array(ett, array_length(ett));
+       register_dissector("tr", dissect_tr, proto_tr);
 }
 
+void
+proto_reg_handoff_tr(void)
+{
+       dissector_handle_t tr_handle;
+
+       /*
+        * Get handles for the TR MAC and LLC dissectors.
+        */
+       trmac_handle = find_dissector("trmac");
+       llc_handle = find_dissector("llc");
+       data_handle = find_dissector("data");
+
+       tr_handle = find_dissector("tr");
+       dissector_add("wtap_encap", WTAP_ENCAP_TOKEN_RING, tr_handle);
+}