Get rid of a debugging fprintf.
[obnox/wireshark/wip.git] / packet-arp.c
index f8144b5e3d47a2bca277ff4d5a2dce0311a35bde..4e05d1f5f080b2e2f2250ff0c09fc6c59d29b5de 100644 (file)
@@ -1,7 +1,7 @@
 /* packet-arp.c
  * Routines for ARP packet disassembly
  *
- * $Id: packet-arp.c,v 1.4 1998/10/10 03:32:10 gerald Exp $
+ * $Id: packet-arp.c,v 1.30 2000/05/31 05:06:50 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@zing.org>
 # include "config.h"
 #endif
 
-#include <gtk/gtk.h>
-#include <pcap.h>
-
-#include <stdio.h>
-
 #ifdef HAVE_SYS_TYPES_H
 # include <sys/types.h>
 #endif
 
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-
-#include "ethereal.h"
+#include <glib.h>
 #include "packet.h"
+#include "packet-arp.h"
 #include "etypes.h"
 
+static int proto_arp = -1;
+static int hf_arp_hard_type = -1;
+static int hf_arp_proto_type = -1;
+static int hf_arp_hard_size = -1;
+static int hf_atmarp_shtl = -1;
+static int hf_atmarp_ssl = -1;
+static int hf_arp_proto_size = -1;
+static int hf_arp_opcode = -1;
+static int hf_atmarp_spln = -1;
+static int hf_atmarp_thtl = -1;
+static int hf_atmarp_tsl = -1;
+static int hf_atmarp_tpln = -1;
+static int hf_arp_src_ether = -1;
+static int hf_arp_src_proto = -1;
+static int hf_arp_dst_ether = -1;
+static int hf_arp_dst_proto = -1;
+static int hf_atmarp_src_atm_num_e164 = -1;
+static int hf_atmarp_src_atm_num_nsap = -1;
+static int hf_atmarp_src_atm_subaddr = -1;
+static int hf_atmarp_dst_atm_num_e164 = -1;
+static int hf_atmarp_dst_atm_num_nsap = -1;
+static int hf_atmarp_dst_atm_subaddr = -1;
+
+static gint ett_arp = -1;
+static gint ett_atmarp_nsap = -1;
+
+/* Definitions taken from Linux "linux/if_arp.h" header file, and from
+
+       http://www.isi.edu/in-notes/iana/assignments/arp-parameters
+
+ */
+
+/* ARP protocol HARDWARE identifiers. */
+#define ARPHRD_NETROM  0               /* from KA9Q: NET/ROM pseudo    */
+#define ARPHRD_ETHER   1               /* Ethernet 10Mbps              */
+#define        ARPHRD_EETHER   2               /* Experimental Ethernet        */
+#define        ARPHRD_AX25     3               /* AX.25 Level 2                */
+#define        ARPHRD_PRONET   4               /* PROnet token ring            */
+#define        ARPHRD_CHAOS    5               /* Chaosnet                     */
+#define        ARPHRD_IEEE802  6               /* IEEE 802.2 Ethernet/TR/TB    */
+#define        ARPHRD_ARCNET   7               /* ARCnet                       */
+#define        ARPHRD_HYPERCH  8               /* Hyperchannel                 */
+#define        ARPHRD_LANSTAR  9               /* Lanstar                      */
+#define        ARPHRD_AUTONET  10              /* Autonet Short Address        */
+#define        ARPHRD_LOCALTLK 11              /* Localtalk                    */
+#define        ARPHRD_LOCALNET 12              /* LocalNet (IBM PCNet/Sytek LocalNET) */
+#define        ARPHRD_ULTRALNK 13              /* Ultra link                   */
+#define        ARPHRD_SMDS     14              /* SMDS                         */
+#define ARPHRD_DLCI    15              /* Frame Relay DLCI             */
+#define ARPHRD_ATM     16              /* ATM                          */
+#define ARPHRD_HDLC    17              /* HDLC                         */
+#define ARPHRD_FIBREC  18              /* Fibre Channel                */
+#define ARPHRD_ATM2225 19              /* ATM (RFC 2225)               */
+#define ARPHRD_SERIAL  20              /* Serial Line                  */
+#define ARPHRD_ATM2    21              /* ATM                          */
+#define ARPHRD_MS188220        22              /* MIL-STD-188-220              */
+#define ARPHRD_METRICOM        23              /* Metricom STRIP               */
+#define ARPHRD_IEEE1394        24              /* IEEE 1394.1995               */
+#define ARPHRD_MAPOS   25              /* MAPOS                        */
+#define ARPHRD_TWINAX  26              /* Twinaxial                    */
+#define ARPHRD_EUI_64  27              /* EUI-64                       */
+
+/* ARP / RARP structs and definitions */
+#ifndef ARPOP_REQUEST
+#define ARPOP_REQUEST  1       /* ARP request.  */
+#endif
+#ifndef ARPOP_REPLY
+#define ARPOP_REPLY    2       /* ARP reply.  */
+#endif
+/* Some OSes have different names, or don't define these at all */
+#ifndef ARPOP_RREQUEST
+#define ARPOP_RREQUEST 3       /* RARP request.  */
+#endif
+#ifndef ARPOP_RREPLY
+#define ARPOP_RREPLY   4       /* RARP reply.  */
+#endif
+#ifndef ARPOP_IREQUEST
+#define ARPOP_IREQUEST 8       /* Inverse ARP (RFC 1293) request.  */
+#endif
+#ifndef ARPOP_IREPLY
+#define ARPOP_IREPLY   9       /* Inverse ARP reply.  */
+#endif
+#ifndef ATMARPOP_NAK
+#define ATMARPOP_NAK   10      /* ATMARP NAK.  */
+#endif
+
+static const value_string op_vals[] = {
+  {ARPOP_REQUEST,  "request" },
+  {ARPOP_REPLY,    "reply"   },
+  {ARPOP_RREQUEST, "reverse request"},
+  {ARPOP_RREPLY,   "reverse reply"  },
+  {ARPOP_IREQUEST, "inverse request"},
+  {ARPOP_IREPLY,   "inverse reply"  },
+  {0,              NULL          } };
+
+static const value_string atmop_vals[] = {
+  {ARPOP_REQUEST,  "request" },
+  {ARPOP_REPLY,    "reply"   },
+  {ARPOP_IREQUEST, "inverse request"},
+  {ARPOP_IREPLY,   "inverse reply"  },
+  {ATMARPOP_NAK,   "nak"  },
+  {0,              NULL          } };
+
+#define        ATMARP_IS_E164  0x40    /* bit in shtl/thtl for E.164 format */
+#define        ATMARP_LEN_MASK 0x3F    /* length of address in shtl/thtl */
+
+gchar *
+arphrdaddr_to_str(guint8 *ad, int ad_len, guint16 type)
+{
+  if (ad_len == 0)
+    return "<No address>";
+  if ((type == ARPHRD_ETHER || type == ARPHRD_EETHER || type == ARPHRD_IEEE802)
+                               && ad_len == 6) {
+    /* Ethernet address (or Experimental 3Mb Ethernet, or IEEE 802.x
+       address, which are the same type of address). */
+    return ether_to_str(ad);
+  }
+  return bytes_to_str(ad, ad_len);
+}
+
+static gchar *
+arpproaddr_to_str(guint8 *ad, int ad_len, guint16 type)
+{
+  if (ad_len == 0)
+    return "<No address>";
+  if (type == ETHERTYPE_IP && ad_len == 4) {
+    /* IPv4 address.  */
+    return ip_to_str(ad);
+  }
+  return bytes_to_str(ad, ad_len);
+}
+
+#define        N_ATMARPNUM_TO_STR_STRINGS      2
+#define        MAX_E164_STR_LEN                20
+static gchar *
+atmarpnum_to_str(guint8 *ad, int ad_tl)
+{
+  int           ad_len = ad_tl & ATMARP_LEN_MASK;
+  static gchar  str[N_ATMARPNUM_TO_STR_STRINGS][MAX_E164_STR_LEN+3+1];
+  static int    cur_idx;
+  gchar        *cur;
+
+  if (ad_len == 0)
+    return "<No address>";
+
+  if (ad_tl & ATMARP_IS_E164) {
+    /*
+     * I'm assuming this means it's an ASCII (IA5) string.
+     */
+    cur_idx++;
+    if (cur_idx >= N_ATMARPNUM_TO_STR_STRINGS)
+      cur_idx = 0;
+    cur = &str[cur_idx][0];
+    if (ad_len > MAX_E164_STR_LEN) {
+      /* Can't show it all. */
+      memcpy(cur, ad, MAX_E164_STR_LEN);
+      strcpy(&cur[MAX_E164_STR_LEN], "...");
+    } else {
+      memcpy(cur, ad, ad_len);
+      cur[ad_len + 1] = '\0';
+    }
+    return cur;
+  } else {
+    /*
+     * NSAP.
+     *
+     * XXX - break down into subcomponents.
+     */
+    return bytes_to_str(ad, ad_len);
+  }
+}
+
+static gchar *
+atmarpsubaddr_to_str(guint8 *ad, int ad_len)
+{
+  if (ad_len == 0)
+    return "<No address>";
+
+  /*
+   * XXX - break down into subcomponents?
+   */
+  return bytes_to_str(ad, ad_len);
+}
+
+static const value_string hrd_vals[] = {
+  {ARPHRD_NETROM,   "NET/ROM pseudo"       },
+  {ARPHRD_ETHER,    "Ethernet"             },
+  {ARPHRD_EETHER,   "Experimental Ethernet"},
+  {ARPHRD_AX25,     "AX.25"                },
+  {ARPHRD_PRONET,   "ProNET"               },
+  {ARPHRD_CHAOS,    "Chaos"                },
+  {ARPHRD_IEEE802,  "IEEE 802"             },
+  {ARPHRD_ARCNET,   "ARCNET"               },
+  {ARPHRD_HYPERCH,  "Hyperchannel"         },
+  {ARPHRD_LANSTAR,  "Lanstar"              },
+  {ARPHRD_AUTONET,  "Autonet Short Address"},
+  {ARPHRD_LOCALTLK, "Localtalk"            },
+  {ARPHRD_LOCALNET, "LocalNet"             },
+  {ARPHRD_ULTRALNK, "Ultra link"           },
+  {ARPHRD_SMDS,     "SMDS"                 },
+  {ARPHRD_DLCI,     "Frame Relay DLCI"     },
+  {ARPHRD_ATM,      "ATM"                  },
+  {ARPHRD_HDLC,     "HDLC"                 },
+  {ARPHRD_FIBREC,   "Fibre Channel"        },
+  {ARPHRD_ATM2225,  "ATM (RFC 2225)"       },
+  {ARPHRD_SERIAL,   "Serial Line"          },
+  {ARPHRD_ATM2,     "ATM"                  },
+  {ARPHRD_MS188220, "MIL-STD-188-220"      },
+  {ARPHRD_METRICOM, "Metricom STRIP"       },
+  {ARPHRD_IEEE1394, "IEEE 1394.1995"       },
+  {ARPHRD_MAPOS,    "MAPOS"                },
+  {ARPHRD_TWINAX,   "Twinaxial"            },
+  {ARPHRD_EUI_64,   "EUI-64"               },
+  {0,                NULL                  } };
+
+gchar *
+arphrdtype_to_str(guint16 hwtype, const char *fmt) {
+    return val_to_str(hwtype, hrd_vals, fmt);
+}
+
+/* Offsets of fields within an ARP packet. */
+#define        AR_HRD          0
+#define        AR_PRO          2
+#define        AR_HLN          4
+#define        AR_PLN          5
+#define        AR_OP           6
+#define MIN_ARP_HEADER_SIZE    8
+
+/* Offsets of fields within an ATMARP packet. */
+#define        ATM_AR_HRD      0
+#define        ATM_AR_PRO      2
+#define        ATM_AR_SHTL     4
+#define        ATM_AR_SSL      5
+#define        ATM_AR_OP       6
+#define        ATM_AR_SPLN     8
+#define        ATM_AR_THTL     9
+#define        ATM_AR_TSL      10
+#define        ATM_AR_TPLN     11
+#define MIN_ATMARP_HEADER_SIZE 12
+
+static void
+dissect_atm_number(const u_char *pd, int offset, int tl, int hf_e164,
+    int hf_nsap, proto_tree *tree)
+{
+       int len = tl & ATMARP_LEN_MASK;
+       proto_item *ti;
+       proto_tree *nsap_tree;
+
+       if (tl & ATMARP_IS_E164)
+               proto_tree_add_string(tree, hf_e164, NullTVB, offset, len, &pd[offset]);
+       else {
+               ti = proto_tree_add_bytes(tree, hf_nsap, NullTVB, offset, len,
+                   &pd[offset]);
+               if (len >= 20) {
+                       nsap_tree = proto_item_add_subtree(ti, ett_atmarp_nsap);
+                       dissect_atm_nsap(pd, offset, len, nsap_tree);
+               }
+       }
+}
+
 void
-dissect_arp(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
-  e_ether_arp *ea;
-  guint16      ar_hrd, ar_pro, ar_op;
-  GtkWidget   *arp_tree, *ti;
+dissect_atm_nsap(const u_char *pd, int offset, int len, proto_tree *tree)
+{
+       switch (pd[offset]) {
+
+       case 0x39:      /* DCC ATM format */
+       case 0xBD:      /* DCC ATM group format */
+               proto_tree_add_text(tree, NullTVB, offset + 0, 3,
+                   "Data Country Code%s: 0x%04X",
+                   (pd[offset] == 0xBD) ? " (group)" : "",
+                   pntohs(&pd[offset + 1]));
+               proto_tree_add_text(tree, NullTVB, offset + 3, 10,
+                   "High Order DSP: %s",
+                   bytes_to_str(&pd[offset + 3], 10));
+               proto_tree_add_text(tree, NullTVB, offset + 13, 6,
+                   "End System Identifier: %s",
+                   bytes_to_str(&pd[offset + 13], 6));
+               proto_tree_add_text(tree, NullTVB, offset + 19, 1,
+                   "Selector: 0x%02X", pd[offset + 19]);
+               break;
+
+       case 0x47:      /* ICD ATM format */
+       case 0xC5:      /* ICD ATM group format */
+               proto_tree_add_text(tree, NullTVB, offset + 0, 3,
+                   "International Code Designator%s: 0x%04X",
+                   (pd[offset] == 0xC5) ? " (group)" : "",
+                   pntohs(&pd[offset + 1]));
+               proto_tree_add_text(tree, NullTVB, offset + 3, 10,
+                   "High Order DSP: %s",
+                   bytes_to_str(&pd[offset + 3], 10));
+               proto_tree_add_text(tree, NullTVB, offset + 13, 6,
+                   "End System Identifier: %s",
+                   bytes_to_str(&pd[offset + 13], 6));
+               proto_tree_add_text(tree, NullTVB, offset + 19, 1,
+                   "Selector: 0x%02X", pd[offset + 19]);
+               break;
+
+       case 0x45:      /* E.164 ATM format */
+       case 0xC3:      /* E.164 ATM group format */
+               proto_tree_add_text(tree, NullTVB, offset + 0, 9,
+                   "E.164 ISDN%s: %s",
+                   (pd[offset] == 0xC3) ? " (group)" : "",
+                   bytes_to_str(&pd[offset + 1], 8));
+               proto_tree_add_text(tree, NullTVB, offset + 9, 4,
+                   "High Order DSP: %s",
+                   bytes_to_str(&pd[offset + 3], 10));
+               proto_tree_add_text(tree, NullTVB, offset + 13, 6,
+                   "End System Identifier: %s",
+                   bytes_to_str(&pd[offset + 13], 6));
+               proto_tree_add_text(tree, NullTVB, offset + 19, 1,
+                   "Selector: 0x%02X", pd[offset + 19]);
+               break;
+
+       default:
+               proto_tree_add_text(tree, NullTVB, offset, 1,
+                   "Unknown AFI: 0x%02X", pd[offset]);
+               proto_tree_add_text(tree, NullTVB, offset + 1, len - 1,
+                   "Rest of address: %s",
+                   bytes_to_str(&pd[offset + 1], len - 1));
+               break;
+       }
+}
+
+/*
+ * RFC 2225 ATMARP - it's just like ARP, except where it isn't.
+ */
+static void
+dissect_atmarp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
+{
+  guint16     ar_hrd;
+  guint16     ar_pro;
+  guint8      ar_shtl;
+  guint8      ar_sht;
+  guint8      ar_shl;
+  guint8      ar_ssl;
+  guint16     ar_op;
+  guint8      ar_spln;
+  guint8      ar_thtl;
+  guint8      ar_tht;
+  guint8      ar_thl;
+  guint8      ar_tsl;
+  guint8      ar_tpln;
+  int         tot_len;
+  proto_tree  *arp_tree;
+  proto_item  *ti;
   gchar       *op_str;
-  value_string op_vals[] = { ARPOP_REQUEST,  "ARP request",
-                             ARPOP_REPLY,    "ARP reply",
-                             ARPOP_RREQUEST, "RARP request",
-                             ARPOP_RREPLY,   "RARP reply" };
-
-  /* To do: Check for {cap len,pkt len} < struct len */
-  ea = (e_ether_arp *) &pd[offset];
-  ar_hrd = ntohs(ea->ar_hrd);
-  ar_pro = ntohs(ea->ar_pro);
-  /* To do: Check for bounds on ar_op */
-  ar_op  = ntohs(ea->ar_op);
-  
-  if (fd->win_info[COL_NUM]) { strcpy(fd->win_info[COL_PROTOCOL], "ARP"); }
+  int         sha_offset, ssa_offset, spa_offset;
+  int         tha_offset, tsa_offset, tpa_offset;
+  gchar       *sha_str, *ssa_str, *spa_str;
+  gchar       *tha_str, *tsa_str, *tpa_str;
+
+  if (!BYTES_ARE_IN_FRAME(offset, MIN_ATMARP_HEADER_SIZE)) {
+    dissect_data(pd, offset, fd, tree);
+    return;
+  }
+
+  ar_hrd = pntohs(&pd[offset + ATM_AR_HRD]);
+  ar_pro = pntohs(&pd[offset + ATM_AR_PRO]);
+  ar_shtl = (guint8) pd[offset + ATM_AR_SHTL];
+  ar_sht = ar_shtl & ATMARP_IS_E164;
+  ar_shl = ar_shtl & ATMARP_LEN_MASK;
+  ar_ssl = (guint8) pd[offset + ATM_AR_SSL];
+  ar_op  = pntohs(&pd[offset + AR_OP]);
+  ar_spln = (guint8) pd[offset + ATM_AR_SPLN];
+  ar_thtl = (guint8) pd[offset + ATM_AR_THTL];
+  ar_tht = ar_thtl & ATMARP_IS_E164;
+  ar_thl = ar_thtl & ATMARP_LEN_MASK;
+  ar_tsl = (guint8) pd[offset + ATM_AR_TSL];
+  ar_tpln = (guint8) pd[offset + ATM_AR_TPLN];
+
+  tot_len = MIN_ATMARP_HEADER_SIZE + ar_shtl + ar_ssl + ar_spln +
+                               ar_thtl + ar_tsl + ar_tpln;
+  if (!BYTES_ARE_IN_FRAME(offset, tot_len)) {
+    dissect_data(pd, offset, fd, tree);
+    return;
+  }
+
+  /* Extract the addresses.  */
+  sha_offset = offset + MIN_ATMARP_HEADER_SIZE;
+  if (ar_shl != 0)
+    sha_str = atmarpnum_to_str((guint8 *) &pd[sha_offset], ar_shtl);
+  else
+    sha_str = "<No address>";
+  ssa_offset = sha_offset + ar_shl;
+  if (ar_ssl != 0)
+    ssa_str = atmarpsubaddr_to_str((guint8 *) &pd[ssa_offset], ar_ssl);
+  else
+    ssa_str = NULL;
+  spa_offset = ssa_offset + ar_ssl;
+  spa_str = arpproaddr_to_str((guint8 *) &pd[spa_offset], ar_spln, ar_pro);
+  tha_offset = spa_offset + ar_spln;
+  if (ar_thl != 0)
+    tha_str = atmarpnum_to_str((guint8 *) &pd[tha_offset], ar_thtl);
+  else
+    tha_str = "<No address>";
+  tsa_offset = tha_offset + ar_thl;
+  if (ar_tsl != 0)
+    tsa_str = atmarpsubaddr_to_str((guint8 *) &pd[tsa_offset], ar_tsl);
+  else
+    tsa_str = NULL;
+  tpa_offset = tsa_offset + ar_tsl;
+  tpa_str = arpproaddr_to_str((guint8 *) &pd[tpa_offset], ar_tpln, ar_pro);
   
+  if (check_col(fd, COL_PROTOCOL)) {
+    switch (ar_op) {
+
+    case ARPOP_REQUEST:
+    case ARPOP_REPLY:
+    case ATMARPOP_NAK:
+    default:
+      col_add_str(fd, COL_PROTOCOL, "ATMARP");
+      break;
+
+    case ARPOP_RREQUEST:
+    case ARPOP_RREPLY:
+      col_add_str(fd, COL_PROTOCOL, "ATMRARP");
+      break;
+
+    case ARPOP_IREQUEST:
+    case ARPOP_IREPLY:
+      col_add_str(fd, COL_PROTOCOL, "Inverse ATMARP");
+      break;
+    }
+  }
+
+  if (check_col(fd, COL_INFO)) {
+    switch (ar_op) {
+      case ARPOP_REQUEST:
+        col_add_fstr(fd, COL_INFO, "Who has %s?  Tell %s", tpa_str, spa_str);
+        break;
+      case ARPOP_REPLY:
+        col_add_fstr(fd, COL_INFO, "%s is at %s%s%s", spa_str, sha_str,
+               ((ssa_str != NULL) ? "," : ""),
+               ((ssa_str != NULL) ? ssa_str : ""));
+        break;
+      case ARPOP_IREQUEST:
+        col_add_fstr(fd, COL_INFO, "Who is %s%s%s?  Tell %s%s%s", tha_str,
+               ((tsa_str != NULL) ? "," : ""),
+               ((tsa_str != NULL) ? tsa_str : ""),
+               sha_str,
+               ((ssa_str != NULL) ? "," : ""),
+               ((ssa_str != NULL) ? ssa_str : ""));
+        break;
+      case ARPOP_IREPLY:
+        col_add_fstr(fd, COL_INFO, "%s%s%s is at %s", sha_str,
+               ((ssa_str != NULL) ? "," : ""),
+               ((ssa_str != NULL) ? ssa_str : ""),
+               spa_str);
+        break;
+      case ATMARPOP_NAK:
+        col_add_fstr(fd, COL_INFO, "I don't know where %s is", spa_str);
+        break;
+      default:
+        col_add_fstr(fd, COL_INFO, "Unknown ATMARP opcode 0x%04x", ar_op);
+        break;
+    }
+  }
+
   if (tree) {
-    if (op_str = match_strval(ar_op, op_vals, 4))
-      ti = add_item_to_tree(GTK_WIDGET(tree), offset, 28, op_str);
+    if ((op_str = match_strval(ar_op, atmop_vals)))
+      ti = proto_tree_add_protocol_format(tree, proto_arp, NullTVB, offset, tot_len,
+                                       "ATM Address Resolution Protocol (%s)", 
+                                       op_str);
     else
-      ti = add_item_to_tree(GTK_WIDGET(tree), offset, 28,
-        "Unkown ARP (opcode 0x%04x)", ar_op);
-    arp_tree = gtk_tree_new();
-    add_subtree(ti, arp_tree, ETT_ARP);
-    add_item_to_tree(arp_tree, offset,      2,
-      "Hardware type: 0x%04x", ar_hrd);
-    add_item_to_tree(arp_tree, offset +  2, 2,
-      "Protocol type: 0x%04x", ar_pro);
-    add_item_to_tree(arp_tree, offset +  4, 1,
-      "Hardware size: 0x%02x", ea->ar_hln);
-    add_item_to_tree(arp_tree, offset +  5, 1,
-      "Protocol size: 0x%02x", ea->ar_pln);
-    add_item_to_tree(arp_tree, offset +  6, 2,
-      "Opcode: 0x%04x (%s)", ar_op, op_str ? op_str : "Unknown");
-    add_item_to_tree(arp_tree, offset +  8, 6,
-      "Sender ether: %s", ether_to_str((guint8 *) ea->arp_sha));
-    add_item_to_tree(arp_tree, offset + 14, 4,
-      "Sender IP: %s", ip_to_str((guint8 *) ea->arp_spa));
-    add_item_to_tree(arp_tree, offset + 18, 6,
-      "Target ether: %s", ether_to_str((guint8 *) ea->arp_tha));
-    add_item_to_tree(arp_tree, offset + 24, 4,
-      "Target IP: %s", ip_to_str((guint8 *) ea->arp_tpa));
+      ti = proto_tree_add_protocol_format(tree, proto_arp, NullTVB, offset, tot_len,
+                                     "ATM Address Resolution Protocol (opcode 0x%04x)", ar_op);
+    arp_tree = proto_item_add_subtree(ti, ett_arp);
+    proto_tree_add_uint(arp_tree, hf_arp_hard_type, NullTVB, offset + ATM_AR_HRD, 2,
+                              ar_hrd);
+    proto_tree_add_uint(arp_tree, hf_arp_proto_type, NullTVB, offset + ATM_AR_PRO, 2,
+                              ar_pro);
+    proto_tree_add_uint(arp_tree, hf_atmarp_shtl, NullTVB, offset + ATM_AR_SHTL, 1,
+                              ar_shtl);
+    proto_tree_add_uint(arp_tree, hf_atmarp_ssl, NullTVB, offset + ATM_AR_SSL, 1,
+                              ar_ssl);
+    proto_tree_add_uint(arp_tree, hf_arp_opcode, NullTVB, offset + AR_OP,  2,
+                              ar_op);
+    proto_tree_add_uint(arp_tree, hf_atmarp_spln, NullTVB, offset + ATM_AR_SPLN, 1,
+                              ar_spln);
+    proto_tree_add_uint(arp_tree, hf_atmarp_thtl, NullTVB, offset + ATM_AR_THTL, 1,
+                              ar_thtl);
+    proto_tree_add_uint(arp_tree, hf_atmarp_tsl, NullTVB, offset + ATM_AR_TSL, 1,
+                              ar_tsl);
+    proto_tree_add_uint(arp_tree, hf_atmarp_tpln, NullTVB, offset + ATM_AR_TPLN, 1,
+                              ar_tpln);
+    if (ar_shl != 0)
+      dissect_atm_number(pd, sha_offset, ar_shtl, hf_atmarp_src_atm_num_e164,
+                              hf_atmarp_src_atm_num_nsap, arp_tree);
+    if (ar_ssl != 0)
+      proto_tree_add_bytes_format(arp_tree, hf_atmarp_src_atm_subaddr, NullTVB, ssa_offset,
+                              ar_ssl,
+                              &pd[ssa_offset],
+                              "Sender ATM subaddress: %s", ssa_str);
+    if (ar_spln != 0)
+      proto_tree_add_bytes_format(arp_tree, hf_arp_src_proto, NullTVB, spa_offset, ar_spln,
+                              &pd[spa_offset],
+                              "Sender protocol address: %s", spa_str);
+    if (ar_thl != 0)
+      dissect_atm_number(pd, tha_offset, ar_thtl, hf_atmarp_dst_atm_num_e164,
+                              hf_atmarp_dst_atm_num_nsap, arp_tree);
+    if (ar_tsl != 0)
+      proto_tree_add_bytes_format(arp_tree, hf_atmarp_dst_atm_subaddr, NullTVB, tsa_offset,
+                              ar_tsl,
+                              &pd[tsa_offset],
+                              "Target ATM subaddress: %s", tsa_str);
+    if (ar_tpln != 0)
+      proto_tree_add_bytes_format(arp_tree, hf_arp_dst_proto, NullTVB, tpa_offset, ar_tpln,
+                              &pd[tpa_offset],
+                              "Target protocol address: %s", tpa_str);
   }
+}
+
+static void
+dissect_arp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
+{
+  guint16     ar_hrd;
+  guint16     ar_pro;
+  guint8      ar_hln;
+  guint8      ar_pln;
+  guint16     ar_op;
+  int         tot_len;
+  proto_tree  *arp_tree;
+  proto_item  *ti;
+  gchar       *op_str;
+  int         sha_offset, spa_offset, tha_offset, tpa_offset;
+  gchar       *sha_str, *spa_str, *tha_str, *tpa_str;
 
-  if (ar_pro != ETHERTYPE_IP && fd->win_info[COL_NUM]) {
-    sprintf(fd->win_info[COL_INFO], "h/w %d (%d) prot %d (%d) op 0x%04x",
-      ar_hrd, ea->ar_hln, ar_pro, ea->ar_pln, ar_op);
+  if (!BYTES_ARE_IN_FRAME(offset, MIN_ARP_HEADER_SIZE)) {
+    dissect_data(pd, offset, fd, tree);
     return;
   }
-  switch (ar_op) {
+
+  ar_hrd = pntohs(&pd[offset + AR_HRD]);
+  if (ar_hrd == ARPHRD_ATM2225) {
+    dissect_atmarp(pd, offset, fd, tree);
+    return;
+  }
+  ar_pro = pntohs(&pd[offset + AR_PRO]);
+  ar_hln = (guint8) pd[offset + AR_HLN];
+  ar_pln = (guint8) pd[offset + AR_PLN];
+  ar_op  = pntohs(&pd[offset + AR_OP]);
+
+  tot_len = MIN_ARP_HEADER_SIZE + ar_hln*2 + ar_pln*2;
+  if (!BYTES_ARE_IN_FRAME(offset, tot_len)) {
+    dissect_data(pd, offset, fd, tree);
+    return;
+  }
+
+  /* Extract the addresses.  */
+  sha_offset = offset + MIN_ARP_HEADER_SIZE;
+  sha_str = arphrdaddr_to_str((guint8 *) &pd[sha_offset], ar_hln, ar_hrd);
+  spa_offset = sha_offset + ar_hln;
+  spa_str = arpproaddr_to_str((guint8 *) &pd[spa_offset], ar_pln, ar_pro);
+  tha_offset = spa_offset + ar_pln;
+  tha_str = arphrdaddr_to_str((guint8 *) &pd[tha_offset], ar_hln, ar_hrd);
+  tpa_offset = tha_offset + ar_hln;
+  tpa_str = arpproaddr_to_str((guint8 *) &pd[tpa_offset], ar_pln, ar_pro);
+  
+  if (check_col(fd, COL_PROTOCOL)) {
+    switch (ar_op) {
+
     case ARPOP_REQUEST:
-      if (fd->win_info[COL_NUM]) {
-        sprintf(fd->win_info[COL_INFO], "Who has %s?  Tell %s",
-          ip_to_str((guint8 *) ea->arp_tpa), ip_to_str((guint8 *) ea->arp_spa));
-      }
-      break;
     case ARPOP_REPLY:
-      if (fd->win_info[COL_NUM]) {
-        sprintf(fd->win_info[COL_INFO], "%s is at %s",
-          ip_to_str((guint8 *) ea->arp_spa),
-          ether_to_str((guint8 *) ea->arp_sha));
-      }
+    default:
+      col_add_str(fd, COL_PROTOCOL, "ARP");
       break;
+
     case ARPOP_RREQUEST:
-      if (fd->win_info[COL_NUM]) {
-        strcpy(fd->win_info[COL_PROTOCOL], "RARP");
-        sprintf(fd->win_info[COL_INFO], "Who is %s?  Tell %s",
-          ether_to_str((guint8 *) ea->arp_tha), 
-          ether_to_str((guint8 *) ea->arp_sha));
-      }
-      break;
     case ARPOP_RREPLY:
-      if (fd->win_info[COL_NUM]) {
-        strcpy(fd->win_info[COL_PROTOCOL], "RARP");
-        sprintf(fd->win_info[COL_INFO], "%s is at %s",
-          ether_to_str((guint8 *) ea->arp_sha),
-          ip_to_str((guint8 *) ea->arp_spa));
-      }
+      col_add_str(fd, COL_PROTOCOL, "RARP");
       break;
+
+    case ARPOP_IREQUEST:
+    case ARPOP_IREPLY:
+      col_add_str(fd, COL_PROTOCOL, "Inverse ARP");
+      break;
+    }
+  }
+
+  if (check_col(fd, COL_INFO)) {
+    switch (ar_op) {
+      case ARPOP_REQUEST:
+        col_add_fstr(fd, COL_INFO, "Who has %s?  Tell %s", tpa_str, spa_str);
+        break;
+      case ARPOP_REPLY:
+        col_add_fstr(fd, COL_INFO, "%s is at %s", spa_str, sha_str);
+        break;
+      case ARPOP_RREQUEST:
+      case ARPOP_IREQUEST:
+        col_add_fstr(fd, COL_INFO, "Who is %s?  Tell %s", tha_str, sha_str);
+        break;
+      case ARPOP_RREPLY:
+      case ARPOP_IREPLY:
+        col_add_fstr(fd, COL_INFO, "%s is at %s", sha_str, spa_str);
+        break;
+      default:
+        col_add_fstr(fd, COL_INFO, "Unknown ARP opcode 0x%04x", ar_op);
+        break;
+    }
   }
+
+  if (tree) {
+    if ((op_str = match_strval(ar_op, op_vals)))
+      ti = proto_tree_add_protocol_format(tree, proto_arp, NullTVB, offset, tot_len,
+                                       "Address Resolution Protocol (%s)", op_str);
+    else
+      ti = proto_tree_add_protocol_format(tree, proto_arp, NullTVB, offset, tot_len,
+                                     "Address Resolution Protocol (opcode 0x%04x)", ar_op);
+    arp_tree = proto_item_add_subtree(ti, ett_arp);
+    proto_tree_add_uint(arp_tree, hf_arp_hard_type, NullTVB, offset + AR_HRD, 2,
+                              ar_hrd);
+    proto_tree_add_uint(arp_tree, hf_arp_proto_type, NullTVB, offset + AR_PRO, 2,
+                              ar_pro);
+    proto_tree_add_uint(arp_tree, hf_arp_hard_size, NullTVB, offset + AR_HLN, 1,
+                              ar_hln);
+    proto_tree_add_uint(arp_tree, hf_arp_proto_size, NullTVB, offset + AR_PLN, 1,
+                              ar_pln);
+    proto_tree_add_uint(arp_tree, hf_arp_opcode, NullTVB, offset + AR_OP,  2,
+                              ar_op);
+    if (ar_hln != 0)
+      proto_tree_add_bytes_format(arp_tree, hf_arp_src_ether, NullTVB, sha_offset, ar_hln,
+                              &pd[sha_offset],
+                              "Sender hardware address: %s", sha_str);
+    if (ar_pln != 0)
+      proto_tree_add_bytes_format(arp_tree, hf_arp_src_proto, NullTVB, spa_offset, ar_pln,
+                              &pd[spa_offset],
+                              "Sender protocol address: %s", spa_str);
+    if (ar_hln != 0)
+      proto_tree_add_bytes_format(arp_tree, hf_arp_dst_ether, NullTVB, tha_offset, ar_hln,
+                              &pd[tha_offset],
+                              "Target hardware address: %s", tha_str);
+    if (ar_pln != 0)
+      proto_tree_add_bytes_format(arp_tree, hf_arp_dst_proto, NullTVB, tpa_offset, ar_pln,
+                              &pd[tpa_offset],
+                              "Target protocol address: %s", tpa_str);
+  }
+}
+
+void
+proto_register_arp(void)
+{
+  static hf_register_info hf[] = {
+    { &hf_arp_hard_type,
+      { "Hardware type",               "arp.hw.type",   
+       FT_UINT16,      BASE_HEX,       VALS(hrd_vals), 0x0,
+       "" }},
+
+    { &hf_arp_proto_type,
+      { "Protocol type",               "arp.proto.type",
+       FT_UINT16,      BASE_HEX,       VALS(etype_vals),       0x0,
+       "" }},
+
+    { &hf_arp_hard_size,
+      { "Hardware size",               "arp.hw.size",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "" }},
+
+    { &hf_atmarp_shtl,
+      { "Sender ATM number type and length",   "arp.src.htl",  
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "" }},
+
+    { &hf_atmarp_ssl,
+      { "Sender ATM subaddress length",        "arp.src.slen",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "" }},
+
+    { &hf_arp_proto_size,
+      { "Protocol size",               "arp.proto.size",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "" }},
+
+    { &hf_arp_opcode,
+      { "Opcode",                      "arp.opcode",
+       FT_UINT16,      BASE_HEX,       VALS(op_vals),  0x0,
+       "" }},
+
+    { &hf_atmarp_spln,
+      { "Sender protocol size",                "arp.src.pln",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "" }},
+
+    { &hf_atmarp_thtl,
+      { "Target ATM number type and length",   "arp.dst.htl",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "" }},
+
+    { &hf_atmarp_tsl,
+      { "Target ATM subaddress length",        "arp.dst.slen",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "" }},
+
+    { &hf_atmarp_tpln,
+      { "Target protocol size",                "arp.dst.pln",
+       FT_UINT8,       BASE_DEC,       NULL,   0x0,
+       "" }},
+
+    { &hf_arp_src_ether,
+      { "Sender hardware address",     "arp.src.hw",
+       FT_BYTES,       BASE_NONE,      NULL,   0x0,
+       "" }},
+
+    { &hf_atmarp_src_atm_num_e164,
+      { "Sender ATM number (E.164)",   "arp.src.atm_num_e164",
+       FT_STRING,      BASE_NONE,      NULL,   0x0,
+       "" }},
+
+    { &hf_atmarp_src_atm_num_nsap,
+      { "Sender ATM number (NSAP)",    "arp.src.atm_num_nsap",
+       FT_BYTES,       BASE_NONE,      NULL,   0x0,
+       "" }},
+
+    { &hf_atmarp_src_atm_subaddr,
+      { "Sender ATM subaddress",       "arp.src.atm_subaddr",
+       FT_BYTES,       BASE_NONE,      NULL,   0x0,
+       "" }},
+
+    { &hf_arp_src_proto,
+      { "Sender protocol address",     "arp.src.proto", 
+       FT_BYTES,       BASE_NONE,      NULL,   0x0,
+       "" }},
+
+    { &hf_arp_dst_ether,
+      { "Target hardware address",     "arp.dst.hw",
+       FT_BYTES,       BASE_NONE,      NULL,   0x0,
+       "" }},
+
+    { &hf_atmarp_dst_atm_num_e164,
+      { "Target ATM number (E.164)",   "arp.dst.atm_num_e164",
+       FT_STRING,      BASE_NONE,      NULL,   0x0,
+       "" }},
+
+    { &hf_atmarp_dst_atm_num_nsap,
+      { "Target ATM number (NSAP)",    "arp.dst.atm_num_nsap",
+       FT_BYTES,       BASE_NONE,      NULL,   0x0,
+       "" }},
+
+    { &hf_atmarp_dst_atm_subaddr,
+      { "Target ATM subaddress",       "arp.dst.atm_subaddr",
+       FT_BYTES,       BASE_NONE,      NULL,   0x0,
+       "" }},
+
+    { &hf_arp_dst_proto,
+      { "Target protocol address",     "arp.dst.proto", 
+       FT_BYTES,       BASE_NONE,      NULL,   0x0,
+      "" }}
+  };
+  static gint *ett[] = {
+    &ett_arp,
+    &ett_atmarp_nsap,
+  };
+
+  proto_arp = proto_register_protocol("Address Resolution Protocol", "arp");
+  proto_register_field_array(proto_arp, hf, array_length(hf));
+  proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_arp(void)
+{
+       dissector_add("ethertype", ETHERTYPE_ARP, dissect_arp);
+       dissector_add("ethertype", ETHERTYPE_REVARP, dissect_arp);
 }