Have the Etherenet and PPP dissectors register themselves, and have
[obnox/wireshark/wip.git] / packet-gre.c
index 5dc32047971255a721695ec69b83a28f5f606025..1012a5cf51f125cc306c2f0767aabfcad1c77c8c 100644 (file)
@@ -2,10 +2,10 @@
  * Routines for the Generic Routing Encapsulation (GRE) protocol
  * Brad Robel-Forrest <brad.robel-forrest@watchguard.com>
  *
- * $Id: packet-gre.c,v 1.3 1999/07/13 02:52:51 gram Exp $
+ * $Id: packet-gre.c,v 1.28 2000/11/19 02:00:02 guy Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@unicom.net>
+ * By Gerald Combs <gerald@zing.org>
  * Copyright 1998 Gerald Combs
  *
  * 
 #endif
 #include <glib.h>
 #include "packet.h"
+#include "packet-ip.h"
+#include "packet-ipx.h"
+
+static int proto_gre = -1;
+static int hf_gre_proto = -1;
+
+static gint ett_gre = -1;
+static gint ett_gre_flags = -1;
 
 /* bit positions for flags in header */
 #define GH_B_C         0x8000
 #define GH_B_VER       0x0007
 
 #define GRE_PPP                0x880B
+#define        GRE_IP          0x0800
+#define GRE_WCCP       0x883E
+#define GRE_IPX                0x8137
 
-static int calc_len(guint16, int);
 static void add_flags_and_ver(proto_tree *, guint16, int, int);
 
-void
+static const value_string typevals[] = {
+       { GRE_PPP,  "PPP" },
+       { GRE_IP,   "IP" },
+       { GRE_WCCP, "WCCP"},
+       { GRE_IPX,  "IPX"},
+       { 0,        NULL  }
+};
+
+static dissector_handle_t ip_handle;
+static dissector_handle_t ppp_handle;
+
+static void
 dissect_gre(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
   
   guint16      flags_and_ver = pntohs(pd + offset);
   guint16      type          = pntohs(pd + offset + sizeof(flags_and_ver));
-  static const value_string typevals[] = {
-    { GRE_PPP, "PPP" },
-    { 0,       NULL  }
-  };
+  guint16      sre_af;
+  guint8       sre_length;
+  tvbuff_t     *next_tvb;
+
+  OLD_CHECK_DISPLAY_AS_DATA(proto_gre, pd, offset, fd, tree);
 
   if (check_col(fd, COL_PROTOCOL))
     col_add_str(fd, COL_PROTOCOL, "GRE");
        
   if (check_col(fd, COL_INFO)) {
-    if (type == GRE_PPP)
-      col_add_str(fd, COL_INFO, "Encapsulated PPP");
-    else
-      col_add_str(fd, COL_INFO, "Encapsulated unknown");
+    col_add_fstr(fd, COL_INFO, "Encapsulated %s",
+        val_to_str(type, typevals, "0x%04X (unknown)"));
   }
                
-  if (fd->cap_len > offset && tree) {
-    int                        is_ppp;
+  if (IS_DATA_IN_FRAME(offset) && tree) {
+    gboolean           is_ppp = FALSE;
+    gboolean           is_wccp2 = FALSE;
     proto_item *       ti;
     proto_tree *       gre_tree;
+    guint              len = 4;
 
-    if (type == GRE_PPP) {
-      is_ppp = 1;
-      ti = proto_tree_add_text(tree, offset, calc_len(flags_and_ver, 1),
-       "Generic Routing Encapsulation (PPP)");
-      gre_tree = proto_item_add_subtree(ti, ETT_GRE);
-      add_flags_and_ver(gre_tree, flags_and_ver, offset, 1);
-    }
-    else {
-      is_ppp = 0;
-      ti = proto_tree_add_text(tree, offset, calc_len(flags_and_ver, 1),
-       "Generic Routing Encapsulation");
-      gre_tree = proto_item_add_subtree(ti, ETT_GRE);
-      add_flags_and_ver(gre_tree, flags_and_ver, offset, 0);
+    if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R)
+      len += 4;
+    if (flags_and_ver & GH_B_K)
+      len += 4;
+    if (flags_and_ver & GH_B_S)
+      len += 4;
+    switch (type) {
+
+    case GRE_PPP:
+      if (flags_and_ver & GH_P_A)
+        len += 4;
+      is_ppp = TRUE;
+      break;
+
+    case GRE_WCCP:
+      /* WCCP2 apparently puts an extra 4 octets into the header, but uses
+         the same encapsulation type; if it looks as if the first octet of
+        the packet isn't the beginning of an IPv4 header, assume it's
+        WCCP2. */
+      if ((pd[offset + sizeof(flags_and_ver) + sizeof(type)] & 0xF0) != 0x40) {
+       len += 4;
+       is_wccp2 = TRUE;
+      }
+      break;
     }
 
+    ti = proto_tree_add_protocol_format(tree, proto_gre, NullTVB, offset, len,
+      "Generic Routing Encapsulation (%s)",
+      val_to_str(type, typevals, "0x%04X - unknown"));
+    gre_tree = proto_item_add_subtree(ti, ett_gre);
+    add_flags_and_ver(gre_tree, flags_and_ver, offset, is_ppp);
+
     offset += sizeof(flags_and_ver);
 
-    proto_tree_add_text(gre_tree, offset, sizeof(type),
-                       "Protocol Type: %s (%#04x)",
-                       val_to_str(type, typevals, "Unknown"), type);
-    offset += sizeof(type);    
+    proto_tree_add_uint(gre_tree, hf_gre_proto, NullTVB, offset, sizeof(type), type);
+    offset += sizeof(type);
 
     if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R) {
       guint16 checksum = pntohs(pd + offset);
-      proto_tree_add_text(gre_tree, offset, sizeof(checksum),
+      proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(checksum),
                          "Checksum: %u", checksum);
       offset += sizeof(checksum);
     }
     
     if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R) {
       guint16 rtoffset = pntohs(pd + offset);
-      proto_tree_add_text(gre_tree, offset, sizeof(rtoffset),
+      proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(rtoffset),
                          "Offset: %u", rtoffset);
       offset += sizeof(rtoffset);
     }
@@ -122,18 +159,18 @@ dissect_gre(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
        guint16 callid;
        
        paylen = pntohs(pd + offset);
-       proto_tree_add_text(gre_tree, offset, sizeof(paylen),
+       proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(paylen),
                            "Payload length: %u", paylen);
        offset += sizeof(paylen);
 
        callid = pntohs(pd + offset);
-       proto_tree_add_text(gre_tree, offset, sizeof(callid),
+       proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(callid),
                            "Call ID: %u", callid);
        offset += sizeof(callid);
       }
       else {
        guint32 key = pntohl(pd + offset);
-       proto_tree_add_text(gre_tree, offset, sizeof(key),
+       proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(key),
                            "Key: %u", key);
        offset += sizeof(key);
       }
@@ -141,51 +178,62 @@ dissect_gre(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
     
     if (flags_and_ver & GH_B_S) {
       guint32 seqnum = pntohl(pd + offset);
-      proto_tree_add_text(gre_tree, offset, sizeof(seqnum),
+      proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(seqnum),
                          "Sequence number: %u", seqnum);
       offset += sizeof(seqnum);
     }
 
     if (is_ppp && flags_and_ver & GH_P_A) {
       guint32 acknum = pntohl(pd + offset);
-      proto_tree_add_text(gre_tree, offset, sizeof(acknum),
+      proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(acknum),
                          "Acknowledgement number: %u", acknum);
       offset += sizeof(acknum);
     }
 
     if (flags_and_ver & GH_B_R) {
-      proto_tree_add_text(gre_tree, offset, sizeof(guint16),
-                         "Address family: %u", pntohs(pd + offset));
-      offset += sizeof(guint16);
-      proto_tree_add_text(gre_tree, offset, 1,
+      for (;;) {
+       sre_af = pntohs(pd + offset);
+        proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(guint16),
+                         "Address family: %u", sre_af);
+        offset += sizeof(guint16);
+        proto_tree_add_text(gre_tree, NullTVB, offset, 1,
                          "SRE offset: %u", pd[offset++]);
-      proto_tree_add_text(gre_tree, offset, 1,
-                         "SRE length: %u", pd[offset++]);
+       sre_length = pd[offset];
+        proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(guint8),
+                         "SRE length: %u", sre_length);
+       offset += sizeof(guint8);
+       if (sre_af == 0 && sre_length == 0)
+         break;
+       offset += sre_length;
+      }
     }
 
     switch (type) {
-       case GRE_PPP:
-       dissect_payload_ppp(pd, offset, fd, tree);
+      case GRE_PPP:
+        old_call_dissector(ppp_handle, pd, offset, fd, tree);
        break;
+      case GRE_IP:
+        old_call_dissector(ip_handle, pd, offset, fd, tree);
+        break;
+      case GRE_WCCP:
+        if (is_wccp2) {
+          proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(guint32), "WCCPv2 Data");
+          offset += 4;
+        }
+        old_call_dissector(ip_handle, pd, offset, fd, tree);
+        break;
+      case GRE_IPX:
+       next_tvb = tvb_create_from_top(offset);
+        dissect_ipx(next_tvb, &pi, tree);
+        break;
       default:
-       dissect_data(pd, offset, fd, gre_tree);
+       next_tvb = tvb_create_from_top(offset);
+       dissect_data(next_tvb, 0, &pi, gre_tree);
+       break;
     }
   }
 }
 
-static int
-calc_len(guint16 flags_and_ver, int is_ppp) {
-  
-  int  len = 4;
-  
-  if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R) len += 4;
-  if (flags_and_ver & GH_B_K) len += 4;
-  if (flags_and_ver & GH_B_S) len += 4;
-  if (is_ppp && flags_and_ver & GH_P_A) len += 4;
-  
-  return len;
-}
-
 static void
 add_flags_and_ver(proto_tree *tree, guint16 flags_and_ver, int offset, int is_ppp) {
 
@@ -193,44 +241,74 @@ add_flags_and_ver(proto_tree *tree, guint16 flags_and_ver, int offset, int is_pp
   proto_tree * fv_tree;
   int          nbits = sizeof(flags_and_ver) * 8;
   
-  ti = proto_tree_add_text(tree, offset, 2, 
-                          "Flags and version: %#08x", flags_and_ver);
-  fv_tree = proto_item_add_subtree(ti, ETT_GRE_FLAGS);
+  ti = proto_tree_add_text(tree, NullTVB, offset, 2, 
+                          "Flags and version: %#04x", flags_and_ver);
+  fv_tree = proto_item_add_subtree(ti, ett_gre_flags);
   
-  proto_tree_add_text(fv_tree, offset, sizeof(flags_and_ver), "%s",
+  proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
                      decode_boolean_bitfield(flags_and_ver, GH_B_C, nbits,
                                              "Checksum", "No checksum"));
-  proto_tree_add_text(fv_tree, offset, sizeof(flags_and_ver), "%s",
+  proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
                      decode_boolean_bitfield(flags_and_ver, GH_B_R, nbits,
                                              "Routing", "No routing"));
-  proto_tree_add_text(fv_tree, offset, sizeof(flags_and_ver), "%s",
+  proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
                      decode_boolean_bitfield(flags_and_ver, GH_B_K, nbits,
                                              "Key", "No key"));
-  proto_tree_add_text(fv_tree, offset, sizeof(flags_and_ver), "%s",
+  proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
                      decode_boolean_bitfield(flags_and_ver, GH_B_S, nbits,
                                              "Sequence number", "No sequence number"));
-  proto_tree_add_text(fv_tree, offset, sizeof(flags_and_ver), "%s",
+  proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
                      decode_boolean_bitfield(flags_and_ver, GH_B_s, nbits,
                                              "Strict source route", "No strict source route"));
-  proto_tree_add_text(fv_tree, offset, sizeof(flags_and_ver), "%s",
+  proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
                      decode_numeric_bitfield(flags_and_ver, GH_B_RECUR, nbits,
                                              "Recursion control: %u"));
   if (is_ppp) {
-    proto_tree_add_text(fv_tree, offset, sizeof(flags_and_ver), "%s",
+    proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
                        decode_boolean_bitfield(flags_and_ver, GH_P_A, nbits,
                                                "Acknowledgment number", "No acknowledgment number"));
-    proto_tree_add_text(fv_tree, offset, sizeof(flags_and_ver), "%s",
+    proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
                        decode_numeric_bitfield(flags_and_ver, GH_P_FLAGS, nbits,
                                                "Flags: %u"));
   }
   else {
-    proto_tree_add_text(fv_tree, offset, sizeof(flags_and_ver), "%s",
+    proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
                        decode_numeric_bitfield(flags_and_ver, GH_R_FLAGS, nbits,
                                                "Flags: %u"));
   }
 
-  proto_tree_add_text(fv_tree, offset, sizeof(flags_and_ver), "%s",
+  proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
                      decode_numeric_bitfield(flags_and_ver, GH_B_VER, nbits,
                                              "Version: %u"));
  }
  
+void
+proto_register_gre(void)
+{
+       static hf_register_info hf[] = {
+               { &hf_gre_proto,
+                       { "Protocol Type", "gre.proto", FT_UINT16, BASE_HEX, VALS(typevals), 0x0,
+                               "The protocol that is GRE encapsulated"}
+               },
+       };
+       static gint *ett[] = {
+               &ett_gre,
+               &ett_gre_flags,
+       };
+
+        proto_gre = proto_register_protocol("Generic Routing Encapsulation", "gre");
+        proto_register_field_array(proto_gre, hf, array_length(hf));
+       proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_gre(void)
+{
+       old_dissector_add("ip.proto", IP_PROTO_GRE, dissect_gre);
+
+       /*
+        * Get handles for the IP and PPP dissectors.
+        */
+       ip_handle = find_dissector("ip");
+       ppp_handle = find_dissector("ppp");
+}