Slightly reword the preference text about CCSDS packets.
[obnox/wireshark/wip.git] / epan / dissectors / packet-eth.c
index d0dfa6667e2871b90705fad2d16ad200bba03966..4ebd848d7b6e1354fa787f24bcb99fafe2919bb2 100644 (file)
@@ -3,8 +3,8 @@
  *
  * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
  * This program is free software; you can redistribute it and/or
@@ -19,7 +19,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -29,7 +29,7 @@
 #include <glib.h>
 #include <epan/packet.h>
 #include <epan/prefs.h>
-#include "etypes.h"
+#include <epan/etypes.h>
 #include <epan/addr_resolv.h>
 #include "packet-eth.h"
 #include "packet-ieee8023.h"
 #include <epan/crc32.h>
 #include <epan/tap.h>
 
+/* Assume all packets have an FCS */
+static gboolean eth_assume_fcs = FALSE;
 /* Interpret packets as FW1 monitor file packets if they look as if they are */
 static gboolean eth_interpret_as_fw1_monitor = FALSE;
+/* Preference settings defining conditions for which the CCSDS dissector is called */
+static gboolean ccsds_heuristic_length = FALSE;
+static gboolean ccsds_heuristic_version = FALSE;
+static gboolean ccsds_heuristic_header = FALSE;
+static gboolean ccsds_heuristic_bit = FALSE;
 
 /* protocols and header fields */
 static int proto_eth = -1;
@@ -49,18 +56,31 @@ static int hf_eth_src = -1;
 static int hf_eth_len = -1;
 static int hf_eth_type = -1;
 static int hf_eth_addr = -1;
+static int hf_eth_ig = -1;
+static int hf_eth_lg = -1;
 static int hf_eth_trailer = -1;
 
 static gint ett_ieee8023 = -1;
 static gint ett_ether2 = -1;
+static gint ett_addr = -1;
 
 static dissector_handle_t fw1_handle;
 static heur_dissector_list_t heur_subdissector_list;
+static heur_dissector_list_t eth_trailer_subdissector_list;
 
 static int eth_tap = -1;
 
 #define ETH_HEADER_SIZE        14
 
+static const true_false_string ig_tfs = {
+       "Group address (multicast/broadcast)",
+       "Individual address (unicast)"
+};
+static const true_false_string lg_tfs = {
+       "Locally administered address (this is NOT the factory default)",
+       "Globally unique address (factory default)"
+};
+
 /* These are the Netware-ish names for the different Ethernet frame types.
        EthernetII: The ethernet with a Type field instead of a length field
        Ethernet802.2: An 802.3 header followed by an 802.2 header
@@ -159,18 +179,22 @@ capture_eth(const guchar *pd, int offset, int len, packet_counts *ld)
   }
 }
 
+static gboolean check_is_802_2(tvbuff_t *tvb, int fcs_len);
+
 static void
 dissect_eth_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree,
        int fcs_len)
 {
   proto_item           *ti;
-  eth_hdr              *volatile ehdr;
-  volatile gboolean    is_802_2;
-  proto_tree           *volatile fh_tree = NULL;
-  const char           *src_addr, *dst_addr;
+  eth_hdr              *ehdr;
+  gboolean             is_802_2;
+  proto_tree           *fh_tree = NULL;
+  const guint8         *src_addr, *dst_addr;
   static eth_hdr       ehdrs[4];
   static int           ehdr_num=0;
-  proto_tree           *volatile tree;
+  proto_tree           *tree;
+  proto_item           *addr_item;
+  proto_tree           *addr_tree=NULL;
 
   ehdr_num++;
   if(ehdr_num>=4){
@@ -180,8 +204,7 @@ dissect_eth_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree,
 
   tree=parent_tree;
 
-  if (check_col(pinfo->cinfo, COL_PROTOCOL))
-    col_set_str(pinfo->cinfo, COL_PROTOCOL, "Ethernet");
+  col_set_str(pinfo->cinfo, COL_PROTOCOL, "Ethernet");
 
   src_addr=tvb_get_ptr(tvb, 6, 6);
   SET_ADDRESS(&pinfo->dl_src,  AT_ETHER, 6, src_addr);
@@ -194,6 +217,8 @@ dissect_eth_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree,
 
   ehdr->type = tvb_get_ntohs(tvb, 12);
 
+  tap_queue_packet(eth_tap, pinfo, ehdr);
+
   /*
    * In case the packet is a non-Ethernet packet inside
    * Ethernet framing, allow heuristic dissectors to take
@@ -201,7 +226,7 @@ dissect_eth_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree,
    * Ethernet packet.
    */
   if (dissector_try_heuristic(heur_subdissector_list, tvb, pinfo, parent_tree))
-    goto end_of_eth;
+    return;
 
   if (ehdr->type <= IEEE_802_3_MAX_LEN) {
     /* Oh, yuck.  Cisco ISL frames require special interpretation of the
@@ -215,7 +240,7 @@ dissect_eth_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree,
                tvb_get_guint8(tvb, 3) == 0x00 &&
                tvb_get_guint8(tvb, 4) == 0x00 ) {
       dissect_isl(tvb, pinfo, parent_tree, fcs_len);
-      goto end_of_eth;
+      return;
     }
   }
 
@@ -236,28 +261,11 @@ dissect_eth_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree,
    * an ethernet type of ETHERTYPE_UNK.
    */
   if (ehdr->type <= IEEE_802_3_MAX_LEN && ehdr->type != ETHERTYPE_UNK) {
-    /* Is there an 802.2 layer? I can tell by looking at the first 2
-       bytes after the 802.3 header. If they are 0xffff, then what
-       follows the 802.3 header is an IPX payload, meaning no 802.2.
-       (IPX/SPX is they only thing that can be contained inside a
-       straight 802.3 packet). A non-0xffff value means that there's an
-       802.2 layer inside the 802.3 layer */
-    is_802_2 = TRUE;
-    TRY {
-           if (tvb_get_ntohs(tvb, 14) == 0xffff) {
-             is_802_2 = FALSE;
-           }
-    }
-    CATCH2(BoundsError, ReportedBoundsError) {
-           ; /* do nothing */
 
-    }
-    ENDTRY;
+    is_802_2 = check_is_802_2(tvb, fcs_len);
 
-    if (check_col(pinfo->cinfo, COL_INFO)) {
-      col_add_fstr(pinfo->cinfo, COL_INFO, "IEEE 802.3 Ethernet %s",
+     col_add_fstr(pinfo->cinfo, COL_INFO, "IEEE 802.3 Ethernet %s",
                (is_802_2 ? "" : "Raw "));
-    }
     if (tree) {
       ti = proto_tree_add_protocol_format(tree, proto_eth, tvb, 0, ETH_HEADER_SIZE,
                "IEEE 802.3 Ethernet %s", (is_802_2 ? "" : "Raw "));
@@ -274,12 +282,21 @@ dissect_eth_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree,
       fh_tree=NULL;
     }
 
-    proto_tree_add_ether(fh_tree, hf_eth_dst, tvb, 0, 6, dst_addr);
-    proto_tree_add_ether(fh_tree, hf_eth_src, tvb, 6, 6, src_addr);
+    addr_item=proto_tree_add_ether(fh_tree, hf_eth_dst, tvb, 0, 6, dst_addr);
+    if(addr_item){
+        addr_tree = proto_item_add_subtree(addr_item, ett_addr);
+    }
+    proto_tree_add_ether(addr_tree, hf_eth_addr, tvb, 0, 6, dst_addr);
+    proto_tree_add_item(addr_tree, hf_eth_ig, tvb, 0, 3, FALSE);
+    proto_tree_add_item(addr_tree, hf_eth_lg, tvb, 0, 3, FALSE);
 
-/* add items for eth.addr filter */
-    proto_tree_add_ether_hidden(fh_tree, hf_eth_addr, tvb, 0, 6, dst_addr);
-    proto_tree_add_ether_hidden(fh_tree, hf_eth_addr, tvb, 6, 6, src_addr);
+    addr_item=proto_tree_add_ether(fh_tree, hf_eth_src, tvb, 6, 6, src_addr);
+    if(addr_item){
+        addr_tree = proto_item_add_subtree(addr_item, ett_addr);
+    }
+    proto_tree_add_ether(addr_tree, hf_eth_addr, tvb, 6, 6, src_addr);
+    proto_tree_add_item(addr_tree, hf_eth_ig, tvb, 6, 3, FALSE);
+    proto_tree_add_item(addr_tree, hf_eth_lg, tvb, 6, 3, FALSE);
 
     dissect_802_3(ehdr->type, is_802_2, tvb, ETH_HEADER_SIZE, pinfo, parent_tree, fh_tree,
                  hf_eth_len, hf_eth_trailer, fcs_len);
@@ -288,35 +305,124 @@ dissect_eth_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree,
        if ((dst_addr[0] == 'i') || (dst_addr[0] == 'I') ||
            (dst_addr[0] == 'o') || (dst_addr[0] == 'O')) {
          call_dissector(fw1_handle, tvb, pinfo, parent_tree);
-         goto end_of_eth;
+         return;
        }
     }
 
-    if (check_col(pinfo->cinfo, COL_INFO))
-      col_set_str(pinfo->cinfo, COL_INFO, "Ethernet II");
+    col_set_str(pinfo->cinfo, COL_INFO, "Ethernet II");
     if (parent_tree) {
-      ti = proto_tree_add_protocol_format(parent_tree, proto_eth, tvb, 0, ETH_HEADER_SIZE,
+      if (PTREE_DATA(parent_tree)->visible) {
+          ti = proto_tree_add_protocol_format(parent_tree, proto_eth, tvb, 0, ETH_HEADER_SIZE,
                "Ethernet II, Src: %s (%s), Dst: %s (%s)",
                get_ether_name(src_addr), ether_to_str(src_addr), get_ether_name(dst_addr), ether_to_str(dst_addr));
-
+      }
+      else {
+           ti = proto_tree_add_item(parent_tree, proto_eth, tvb, 0, ETH_HEADER_SIZE, FALSE);
+      }
       fh_tree = proto_item_add_subtree(ti, ett_ether2);
     }
 
-    proto_tree_add_ether(fh_tree, hf_eth_dst, tvb, 0, 6, dst_addr);
-    proto_tree_add_ether(fh_tree, hf_eth_src, tvb, 6, 6, src_addr);
-/* add items for eth.addr filter */
-    proto_tree_add_ether_hidden(fh_tree, hf_eth_addr, tvb, 0, 6, dst_addr);
-    proto_tree_add_ether_hidden(fh_tree, hf_eth_addr, tvb, 6, 6, src_addr);
+    addr_item=proto_tree_add_ether(fh_tree, hf_eth_dst, tvb, 0, 6, dst_addr);
+    if(addr_item){
+        addr_tree = proto_item_add_subtree(addr_item, ett_addr);
+    }
+    proto_tree_add_ether(addr_tree, hf_eth_addr, tvb, 0, 6, dst_addr);
+    proto_tree_add_item(addr_tree, hf_eth_ig, tvb, 0, 3, FALSE);
+    proto_tree_add_item(addr_tree, hf_eth_lg, tvb, 0, 3, FALSE);
+
+    addr_item=proto_tree_add_ether(fh_tree, hf_eth_src, tvb, 6, 6, src_addr);
+    if(addr_item){
+        addr_tree = proto_item_add_subtree(addr_item, ett_addr);
+    }
+    proto_tree_add_ether(addr_tree, hf_eth_addr, tvb, 6, 6, src_addr);
+    proto_tree_add_item(addr_tree, hf_eth_ig, tvb, 6, 3, FALSE);
+    proto_tree_add_item(addr_tree, hf_eth_lg, tvb, 6, 3, FALSE);
 
     ethertype(ehdr->type, tvb, ETH_HEADER_SIZE, pinfo, parent_tree, fh_tree, hf_eth_type,
           hf_eth_trailer, fcs_len);
   }
+}
 
-end_of_eth:
-  tap_queue_packet(eth_tap, pinfo, ehdr);
-  return;
+/* -------------- */
+static gboolean check_is_802_2(tvbuff_t *tvb, int fcs_len)
+{
+  volatile gboolean    is_802_2;
+  volatile int length;
+  gint captured_length, reported_length;
+  
+  is_802_2 = TRUE;
+
+    /* Is there an 802.2 layer? I can tell by looking at the first 2
+       bytes after the 802.3 header. If they are 0xffff, then what
+       follows the 802.3 header is an IPX payload, meaning no 802.2.
+       A non-0xffff value means that there's an 802.2 layer or CCSDS
+       layer inside the 802.3 layer */
+
+  TRY {
+       if (tvb_get_ntohs(tvb, 14) == 0xffff) {
+           is_802_2 = FALSE;
+       }
+       /* Is this a CCSDS payload instead of an 802.2 (LLC)?
+          Check the conditions enabled by the user for CCSDS presence */
+       else if (ccsds_heuristic_length || ccsds_heuristic_version ||
+                ccsds_heuristic_header || ccsds_heuristic_bit) {
+         gboolean CCSDS_len = TRUE;
+         gboolean CCSDS_ver = TRUE;
+         gboolean CCSDS_head = TRUE;
+         gboolean CCSDS_bit = TRUE;
+         /* See if the reported payload size matches the
+            size contained in the CCSDS header. */
+         if (ccsds_heuristic_length) {
+           /* The following technique to account for FCS
+              is copied from packet-ieee8023.c dissect_802_3() */
+           length = tvb_get_ntohs(tvb, 12);
+           reported_length = tvb_reported_length_remaining(tvb, ETH_HEADER_SIZE);
+           if (fcs_len > 0) {
+                 if (reported_length >= fcs_len)
+                   reported_length -= fcs_len;
+               }
+               /* Make sure the length in the 802.3 header doesn't go past the end of
+                  the payload. */
+               if (length > reported_length) {
+                 length = reported_length;
+               }
+               /* Only allow inspection of 'length' number of bytes. */
+               captured_length = tvb_length_remaining(tvb, ETH_HEADER_SIZE);
+               if (captured_length > length)
+                 captured_length = length;
+                 
+           /* Check if payload is large enough to contain a CCSDS header */
+           if (captured_length >= 6) {
+             /* Compare length to packet length contained in CCSDS header. */
+             if (length != 7 + tvb_get_ntohs(tvb, ETH_HEADER_SIZE + 4))
+               CCSDS_len = FALSE;
+           }
+         }
+         /* Check if CCSDS Version number (first 3 bits of payload) is zero */
+         if ((ccsds_heuristic_version) && (tvb_get_bits8(tvb, 8*ETH_HEADER_SIZE, 3)!=0))
+           CCSDS_ver = FALSE;
+         /* Check if Secondary Header Flag (4th bit of payload) is set to one. */
+         if ((ccsds_heuristic_header) && (tvb_get_bits8(tvb, 8*ETH_HEADER_SIZE + 4, 1)!=1))
+           CCSDS_head = FALSE;
+         /* Check if spare bit (1st bit of 7th word of payload) is zero. */
+         if ((ccsds_heuristic_bit) && (tvb_get_bits8(tvb, 8*ETH_HEADER_SIZE + 16*6, 1)!=0))
+           CCSDS_bit = FALSE;
+         /* If all the conditions are true, don't interpret payload as an 802.2 (LLC).
+          * Additional check in packet-802.3.c will distinguish between
+          * IPX and CCSDS packets*/
+         if (CCSDS_len && CCSDS_ver && CCSDS_head && CCSDS_bit)
+           is_802_2 = FALSE;
+       }
+  }
+  CATCH2(BoundsError, ReportedBoundsError) {
+           ; /* do nothing */
+
+  }
+  ENDTRY;
+  return is_802_2;
 }
 
+
 /*
  * Add an Ethernet trailer - which, for some captures, might be the FCS
  * rather than a pad-to-60-bytes trailer.
@@ -326,8 +432,8 @@ end_of_eth:
  * it does, maybe it doesn't"), we try to infer whether it has an FCS.
  */
 void
-add_ethernet_trailer(proto_tree *fh_tree, int trailer_id, tvbuff_t *tvb,
-                    tvbuff_t *trailer_tvb, int fcs_len)
+add_ethernet_trailer(packet_info *pinfo, proto_tree *fh_tree, int trailer_id,
+                    tvbuff_t *tvb, tvbuff_t *trailer_tvb, int fcs_len)
 {
   /* If there're some bytes left over, show those bytes as a trailer.
 
@@ -339,8 +445,14 @@ add_ethernet_trailer(proto_tree *fh_tree, int trailer_id, tvbuff_t *tvb,
     guint trailer_length, trailer_reported_length;
     gboolean has_fcs = FALSE;
 
+    if (dissector_try_heuristic(eth_trailer_subdissector_list, trailer_tvb,
+               pinfo, fh_tree)) {
+      return;
+    }
+
     trailer_length = tvb_length(trailer_tvb);
     trailer_reported_length = tvb_reported_length(trailer_tvb);
+
     if (fcs_len != 0) {
       /* If fcs_len is 4, we assume we definitely have an FCS.
          Otherwise, then, if the frame is big enough that, if we
@@ -395,16 +507,20 @@ add_ethernet_trailer(proto_tree *fh_tree, int trailer_id, tvbuff_t *tvb,
                            "Frame check sequence: 0x%08x [incorrect, should be 0x%08x]",
                            sent_fcs, fcs);
       }
+      trailer_length += 4;
     }
+    proto_tree_set_appendix(fh_tree, tvb, tvb_length(tvb) - trailer_length, trailer_length);
   }
 }
 
 /* Called for the Ethernet Wiretap encapsulation type; pass the FCS length
-   reported to us. */
+   reported to us, or, if the "assume_fcs" preference is set, pass 4. */
 static void
 dissect_eth_maybefcs(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-  dissect_eth_common(tvb, pinfo, tree, pinfo->pseudo_header->eth.fcs_len);
+  dissect_eth_common(tvb, pinfo, tree,
+                     eth_assume_fcs ? 4 :
+                     pinfo->pseudo_header->eth.fcs_len);
 }
 
 /* Called by other dissectors  This one's for encapsulated Ethernet
@@ -437,24 +553,35 @@ proto_register_eth(void)
 
                { &hf_eth_len,
                { "Length",             "eth.len", FT_UINT16, BASE_DEC, NULL, 0x0,
-                       "", HFILL }},
+                       NULL, HFILL }},
 
-               /* registered here but handled in ethertype.c */
+               /* registered here but handled in packet-ethertype.c */
                { &hf_eth_type,
                { "Type",               "eth.type", FT_UINT16, BASE_HEX, VALS(etype_vals), 0x0,
-                       "", HFILL }},
+                       NULL, HFILL }},
                { &hf_eth_addr,
-               { "Source or Destination Address", "eth.addr", FT_ETHER, BASE_NONE, NULL, 0x0,
+               { "Address", "eth.addr", FT_ETHER, BASE_NONE, NULL, 0x0,
                        "Source or Destination Hardware Address", HFILL }},
 
                 { &hf_eth_trailer,
                { "Trailer", "eth.trailer", FT_BYTES, BASE_NONE, NULL, 0x0,
                        "Ethernet Trailer or Checksum", HFILL }},
 
+                { &hf_eth_ig,
+               { "IG bit", "eth.ig", FT_BOOLEAN, 24,
+                 TFS(&ig_tfs), 0x010000,
+                 "Specifies if this is an individual (unicast) or group (broadcast/multicast) address", HFILL }},
+
+                { &hf_eth_lg,
+               { "LG bit", "eth.lg", FT_BOOLEAN, 24,
+                 TFS(&lg_tfs), 0x020000,
+                 "Specifies if this is a locally administered or globally unique (IEEE assigned) address", HFILL }}
+
        };
        static gint *ett[] = {
                &ett_ieee8023,
                &ett_ether2,
+               &ett_addr
        };
        module_t *eth_module;
 
@@ -464,16 +591,53 @@ proto_register_eth(void)
 
        /* subdissector code */
        register_heur_dissector_list("eth", &heur_subdissector_list);
+       register_heur_dissector_list("eth.trailer", &eth_trailer_subdissector_list);
 
        /* Register configuration preferences */
        eth_module = prefs_register_protocol(proto_eth, NULL);
+
+       prefs_register_bool_preference(eth_module, "assume_fcs",
+           "Assume packets have FCS",
+           "Some Ethernet adapters and drivers include the FCS at the end of a packet, others do not.  "
+           "The Ethernet dissector attempts to guess whether a captured packet has an FCS, "
+           "but it cannot always guess correctly.",
+           &eth_assume_fcs);
+
        prefs_register_bool_preference(eth_module, "interpret_as_fw1_monitor",
             "Attempt to interpret as FireWall-1 monitor file",
             "Whether packets should be interpreted as coming from CheckPoint FireWall-1 monitor file if they look as if they do",
             &eth_interpret_as_fw1_monitor);
 
+       prefs_register_static_text_preference(eth_module, "ccsds_heuristic",
+            "These are the conditions to match a payload against in order to determine if this\n"
+            "is a CCSDS (Consultative Committee for Space Data Systems) packet within\n"
+            "an 802.3 packet. A packet is considered as a possible CCSDS packet only if\n"
+            "one or more of the conditions are checked.",
+            "Describe the conditions that must be true for the CCSDS dissector to be called");
+
+       prefs_register_bool_preference(eth_module, "ccsds_heuristic_length",
+            "CCSDS Length in header matches payload size",
+            "Set the condition that must be true for the CCSDS dissector to be called",
+            &ccsds_heuristic_length);
+
+       prefs_register_bool_preference(eth_module, "ccsds_heuristic_version",
+            "CCSDS Version # is zero",
+            "Set the condition that must be true for the CCSDS dissector to be called",
+            &ccsds_heuristic_version);
+
+       prefs_register_bool_preference(eth_module, "ccsds_heuristic_header",
+            "CCSDS Secondary Header Flag is set",
+            "Set the condition that must be true for the CCSDS dissector to be called",
+            &ccsds_heuristic_header);
+
+       prefs_register_bool_preference(eth_module, "ccsds_heuristic_bit",
+            "CCSDS Spare bit is cleared",
+            "Set the condition that must be true for the CCSDS dissector to be called",
+            &ccsds_heuristic_bit);
+
        register_dissector("eth_withoutfcs", dissect_eth_withoutfcs, proto_eth);
        register_dissector("eth_withfcs", dissect_eth_withfcs, proto_eth);
+       register_dissector("eth", dissect_eth_maybefcs, proto_eth);
        eth_tap = register_tap("eth");
 }
 
@@ -487,8 +651,7 @@ proto_reg_handoff_eth(void)
         */
        fw1_handle = find_dissector("fw1");
 
-       eth_maybefcs_handle = create_dissector_handle(dissect_eth_maybefcs,
-           proto_eth);
+       eth_maybefcs_handle = find_dissector("eth");
        dissector_add("wtap_encap", WTAP_ENCAP_ETHERNET, eth_maybefcs_handle);
 
        eth_withoutfcs_handle = find_dissector("eth_withoutfcs");