Adjust proto_tree_add_uint_format_value calls to use unit string
[metze/wireshark/wip.git] / epan / dissectors / packet-ip.c
index 22cf9a87720df9510533b528729de5cd711659a0..8961c96e89b51c90e21ffcdec8737e27ddb5c254 100644 (file)
 #include "config.h"
 
 #include <epan/packet.h>
+#include <epan/capture_dissectors.h>
 #include <epan/addr_resolv.h>
 #include <epan/ipproto.h>
 #include <epan/expert.h>
 #include <epan/ip_opts.h>
 #include <epan/prefs.h>
 #include <epan/conversation_table.h>
-#include <epan/color_dissector_filters.h>
+#include <epan/dissector_filters.h>
 #include <epan/reassemble.h>
 #include <epan/etypes.h>
 #include <epan/ppptypes.h>
 #include <epan/nlpid.h>
 #include <epan/ax25_pids.h>
 #include <epan/decode_as.h>
+#include <epan/proto_data.h>
+
 #include <wiretap/erf.h>
+#include <wsutil/str_util.h>
 
 #include "packet-ip.h"
 #include "packet-juniper.h"
 #include "packet-sflow.h"
 #include "packet-gre.h"
+#include "packet-l2tp.h"
+#include "packet-vxlan.h"
+#include "packet-mpls.h"
 
 #ifdef HAVE_GEOIP
 #include <GeoIP.h>
@@ -118,8 +125,7 @@ static int hf_ip_ttl = -1;
 static int hf_ip_proto = -1;
 static int hf_ip_checksum = -1;
 static int hf_ip_checksum_calculated = -1;
-static int hf_ip_checksum_good = -1;
-static int hf_ip_checksum_bad = -1;
+static int hf_ip_checksum_status = -1;
 
 /* IP option fields */
 static int hf_ip_opt_type = -1;
@@ -237,7 +243,6 @@ static gint ett_ip_option_qs = -1;
 static gint ett_ip_option_other = -1;
 static gint ett_ip_fragments = -1;
 static gint ett_ip_fragment  = -1;
-static gint ett_ip_checksum = -1;
 static gint ett_ip_opt_type = -1;
 static gint ett_ip_opt_sec_prot_auth_flags = -1;
 static gint ett_unknown_ip_tcp_opt = -1;
@@ -255,6 +260,7 @@ static expert_field ei_ip_checksum_bad = EI_INIT;
 static expert_field ei_ip_ttl_lncb = EI_INIT;
 static expert_field ei_ip_ttl_too_small = EI_INIT;
 static expert_field ei_ip_cipso_tag = EI_INIT;
+static expert_field ei_ip_bogus_ip_version = EI_INIT;
 
 
 #ifdef HAVE_GEOIP
@@ -283,7 +289,7 @@ static heur_dissector_list_t heur_subdissector_list;
 static dissector_table_t ip_dissector_table;
 
 static dissector_handle_t ipv6_handle;
-static dissector_handle_t data_handle;
+static capture_dissector_handle_t ip_cap_handle;
 
 
 /* IP structs and definitions */
@@ -315,14 +321,7 @@ static dissector_handle_t data_handle;
 #define IP_MF                   0x2000      /* Flag: "More Fragments"   */
 #define IP_OFFSET               0x1FFF      /* "Fragment Offset" part   */
 
-/* Differentiated Services Field. See RFCs 2474, 2597 and 2598. */
-#define IPDSFIELD_DSCP_MASK     0xFC
-#define IPDSFIELD_ECN_MASK      0x03
-#define IPDSFIELD_DSCP_SHIFT    2
-
-#define IPDSFIELD_DSCP(dsfield) (((dsfield)&IPDSFIELD_DSCP_MASK)>>IPDSFIELD_DSCP_SHIFT)
-#define IPDSFIELD_ECN(dsfield)  ((dsfield)&IPDSFIELD_ECN_MASK)
-
+/* Differentiated Services Field. See RFCs 2474, 2597, 2598 and 3168. */
 #define IPDSFIELD_DSCP_DEFAULT  0x00
 #define IPDSFIELD_DSCP_CS1      0x08
 #define IPDSFIELD_DSCP_AF11     0x0A
@@ -479,12 +478,12 @@ static dissector_handle_t data_handle;
 static void ip_prompt(packet_info *pinfo, gchar* result)
 {
     g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "IP protocol %u as",
-        GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, proto_ip, 0)));
+        GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, proto_ip, pinfo->curr_layer_num)));
 }
 
 static gpointer ip_value(packet_info *pinfo)
 {
-    return p_get_proto_data(pinfo->pool, pinfo, proto_ip, 0);
+    return p_get_proto_data(pinfo->pool, pinfo, proto_ip, pinfo->curr_layer_num);
 }
 
 static const char* ip_conv_get_filter_type(conv_item_t* conv, conv_filter_type_e filter)
@@ -509,7 +508,7 @@ ip_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, c
     conv_hash_t *hash = (conv_hash_t*) pct;
     const ws_ip *iph=(const ws_ip *)vip;
 
-    add_conversation_table_data(hash, &iph->ip_src, &iph->ip_dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->rel_ts, &pinfo->fd->abs_ts, &ip_ct_dissector_info, PT_NONE);
+    add_conversation_table_data(hash, &iph->ip_src, &iph->ip_dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->rel_ts, &pinfo->abs_ts, &ip_ct_dissector_info, PT_NONE);
 
     return 1;
 }
@@ -539,13 +538,13 @@ ip_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const
 }
 
 static gboolean
-ip_color_filter_valid(packet_info *pinfo)
+ip_filter_valid(packet_info *pinfo)
 {
     return proto_is_frame_protocol(pinfo->layers, "ip");
 }
 
 static gchar*
-ip_build_color_filter(packet_info *pinfo)
+ip_build_filter(packet_info *pinfo)
 {
     return g_strdup_printf("ip.addr eq %s and ip.addr eq %s",
                 address_to_str(pinfo->pool, &pinfo->net_src),
@@ -564,39 +563,19 @@ ip_defragment_init(void)
                         &addresses_reassembly_table_functions);
 }
 
-void
-capture_ip(const guchar *pd, int offset, int len, packet_counts *ld) {
-  if (!BYTES_ARE_IN_FRAME(offset, len, IPH_MIN_LEN)) {
-    ld->other++;
-    return;
-  }
-  switch (pd[offset + 9]) {
-    case IP_PROTO_TCP:
-      ld->tcp++;
-      break;
-    case IP_PROTO_UDP:
-    case IP_PROTO_UDPLITE:
-      ld->udp++;
-      break;
-    case IP_PROTO_ICMP:
-    case IP_PROTO_ICMPV6:   /* XXX - separate counters? */
-      ld->icmp++;
-      break;
-    case IP_PROTO_SCTP:
-      ld->sctp++;
-      break;
-    case IP_PROTO_OSPF:
-      ld->ospf++;
-      break;
-    case IP_PROTO_GRE:
-      ld->gre++;
-      break;
-    case IP_PROTO_VINES:
-      ld->vines++;
-      break;
-    default:
-      ld->other++;
-  }
+static void
+ip_defragment_cleanup(void)
+{
+  reassembly_table_destroy(&ip_reassembly_table);
+}
+
+static gboolean
+capture_ip(const guchar *pd, int offset, int len, capture_packet_info_t *cpinfo, const union wtap_pseudo_header *pseudo_header _U_) {
+  if (!BYTES_ARE_IN_FRAME(offset, len, IPH_MIN_LEN))
+    return FALSE;
+
+  capture_dissector_increment_count(cpinfo, proto_ip);
+  return try_capture_dissector("ip.proto", pd[offset + 9], pd, offset+IPH_MIN_LEN, len, cpinfo, pseudo_header);
 }
 
 #ifdef HAVE_GEOIP
@@ -1549,9 +1528,8 @@ dissect_ipopt_qs(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
     proto_tree_add_item(field_tree, hf_ip_opt_qs_rate, tvb, offset + 2, 1, ENC_NA);
     proto_tree_add_item(field_tree, hf_ip_opt_qs_ttl, tvb, offset + 3, 1, ENC_NA);
     ttl_diff = (iph->ip_ttl - tvb_get_guint8(tvb, offset + 3) % 256);
-    ti = proto_tree_add_uint_format_value(field_tree, hf_ip_opt_qs_ttl_diff,
-                                          tvb, offset + 3, 1, ttl_diff,
-                                          "%u", ttl_diff);
+    ti = proto_tree_add_uint(field_tree, hf_ip_opt_qs_ttl_diff,
+                                          tvb, offset + 3, 1, ttl_diff);
     PROTO_ITEM_SET_GENERATED(ti);
     proto_item_append_text(tf, ", %s, QS TTL %u, QS TTL diff %u",
                            val_to_str_ext(rate, &qs_rate_vals_ext, "Unknown (%u)"),
@@ -1882,6 +1860,32 @@ local_network_control_block_addr_valid_ttl(guint32 addr)
   return IPLOCAL_NETWRK_CTRL_BLK_DEFAULT_TTL;
 }
 
+static const value_string dscp_short_vals[] = {
+  { IPDSFIELD_DSCP_DEFAULT, "CS0"    },
+  { IPDSFIELD_DSCP_CS1,     "CS1"    },
+  { IPDSFIELD_DSCP_AF11,    "AF11"   },
+  { IPDSFIELD_DSCP_AF12,    "AF12"   },
+  { IPDSFIELD_DSCP_AF13,    "AF13"   },
+  { IPDSFIELD_DSCP_CS2,     "CS2"    },
+  { IPDSFIELD_DSCP_AF21,    "AF21"   },
+  { IPDSFIELD_DSCP_AF22,    "AF22"   },
+  { IPDSFIELD_DSCP_AF23,    "AF23"   },
+  { IPDSFIELD_DSCP_CS3,     "CS3"    },
+  { IPDSFIELD_DSCP_AF31,    "AF31"   },
+  { IPDSFIELD_DSCP_AF32,    "AF32"   },
+  { IPDSFIELD_DSCP_AF33,    "AF33"   },
+  { IPDSFIELD_DSCP_CS4,     "CS4"    },
+  { IPDSFIELD_DSCP_AF41,    "AF41"   },
+  { IPDSFIELD_DSCP_AF42,    "AF42"   },
+  { IPDSFIELD_DSCP_AF43,    "AF43"   },
+  { IPDSFIELD_DSCP_CS5,     "CS5"    },
+  { IPDSFIELD_DSCP_EF,      "EF PHB" },
+  { IPDSFIELD_DSCP_CS6,     "CS6"    },
+  { IPDSFIELD_DSCP_CS7,     "CS7"    },
+  { 0,                      NULL     }};
+value_string_ext dscp_short_vals_ext = VALUE_STRING_EXT_INIT(dscp_short_vals);
+
+
 static const value_string dscp_vals[] = {
   { IPDSFIELD_DSCP_DEFAULT, "Default"               },
   { IPDSFIELD_DSCP_CS1,     "Class Selector 1"      },
@@ -1907,12 +1911,21 @@ static const value_string dscp_vals[] = {
   { 0,                      NULL                    }};
 value_string_ext dscp_vals_ext = VALUE_STRING_EXT_INIT(dscp_vals);
 
-const value_string ecn_vals[] = {
-  { IPDSFIELD_ECT_NOT, "Not-ECT (Not ECN-Capable Transport)" },
-  { IPDSFIELD_ECT_1,   "ECT(1) (ECN-Capable Transport)"      },
-  { IPDSFIELD_ECT_0,   "ECT(0) (ECN-Capable Transport)"      },
-  { IPDSFIELD_CE,      "CE (Congestion Experienced)"         },
-  { 0,                 NULL                                  }};
+static const value_string ecn_short_vals[] = {
+  { IPDSFIELD_ECT_NOT, "Not-ECT" },
+  { IPDSFIELD_ECT_1,   "ECT(1)"  },
+  { IPDSFIELD_ECT_0,   "ECT(0)"  },
+  { IPDSFIELD_CE,      "CE"      },
+  { 0,                 NULL      }};
+value_string_ext ecn_short_vals_ext = VALUE_STRING_EXT_INIT(ecn_short_vals);
+
+static const value_string ecn_vals[] = {
+  { IPDSFIELD_ECT_NOT, "Not ECN-Capable Transport"            },
+  { IPDSFIELD_ECT_1,   "ECN-Capable Transport codepoint '01'" },
+  { IPDSFIELD_ECT_0,   "ECN-Capable Transport codepoint '10'" },
+  { IPDSFIELD_CE,      "Congestion Experienced"               },
+  { 0,                 NULL                                   }};
+value_string_ext ecn_vals_ext = VALUE_STRING_EXT_INIT(ecn_vals);
 
 static const value_string precedence_vals[] = {
   { IPTOS_PREC_ROUTINE,         "routine"              },
@@ -1950,8 +1963,33 @@ static const true_false_string flags_sf_set_evil = {
   "Not evil"
 };
 
-static void
-dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
+gboolean
+ip_try_dissect(gboolean heur_first, guint nxt, tvbuff_t *tvb, packet_info *pinfo,
+               proto_tree *tree, ws_ip *iph)
+{
+  heur_dtbl_entry_t *hdtbl_entry;
+
+  if ((heur_first) && (dissector_try_heuristic(heur_subdissector_list, tvb,
+                       pinfo, tree, &hdtbl_entry, iph))) {
+    return TRUE;
+  }
+
+  if (dissector_try_uint_new(ip_dissector_table, nxt, tvb, pinfo,
+                             tree, TRUE, iph)) {
+    return TRUE;
+  }
+
+  if ((!heur_first) && (dissector_try_heuristic(heur_subdissector_list, tvb,
+                                                 pinfo, tree, &hdtbl_entry,
+                                                 iph))) {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static int
+dissect_ip_v4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
 {
   proto_tree *ip_tree, *field_tree = NULL;
   proto_item *ti, *tf;
@@ -1959,9 +1997,7 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
   int        offset = 0, dst_off;
   guint      hlen, optlen;
   guint16    flags;
-  guint8     nxt;
   guint16    ipsum;
-  guint16    expected_cksum;
   fragment_head *ipfd_head = NULL;
   tvbuff_t   *next_tvb;
   gboolean   update_col_info = TRUE;
@@ -1970,28 +2006,34 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
   guint32    src32, dst32;
   proto_tree *tree;
   proto_item *item = NULL, *ttl_item;
-  proto_tree *checksum_tree;
   guint16 ttl;
-  heur_dtbl_entry_t *hdtbl_entry;
+  int bit_offset;
 
   tree = parent_tree;
-  iph = (ws_ip *)wmem_alloc(wmem_packet_scope(), sizeof(ws_ip));
+  iph = wmem_new0(wmem_packet_scope(), ws_ip);
 
   col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPv4");
   col_clear(pinfo->cinfo, COL_INFO);
 
-  iph->ip_v_hl = tvb_get_guint8(tvb, offset);
-  if ( hi_nibble(iph->ip_v_hl) == 6) {
-    call_dissector(ipv6_handle, tvb, pinfo, parent_tree);
-    return;
-  }
+  iph->ip_ver = tvb_get_bits8(tvb, 0, 4);
 
-  hlen = lo_nibble(iph->ip_v_hl) * 4;   /* IP header length, in bytes */
+  hlen = tvb_get_bits8(tvb, 4, 4) * 4;  /* IP header length, in bytes */
 
   ti = proto_tree_add_item(tree, proto_ip, tvb, offset, hlen, ENC_NA);
   ip_tree = proto_item_add_subtree(ti, ett_ip);
 
-  proto_tree_add_item(ip_tree, hf_ip_version, tvb, offset, 1, ENC_NA);
+  tf = proto_tree_add_item(ip_tree, hf_ip_version, tvb, offset, 1, ENC_NA);
+  if (iph->ip_ver != 4) {
+    col_add_fstr(pinfo->cinfo, COL_INFO,
+                 "Bogus IPv4 version (%u, must be 4)", iph->ip_ver);
+    expert_add_info_format(pinfo, tf, &ei_ip_bogus_ip_version, "Bogus IPv4 version");
+    /* I have a Linux cooked capture with ethertype IPv4 containing an IPv6 packet, continnue dissection in that case*/
+    if (iph->ip_ver == 6) {
+        call_dissector(ipv6_handle, tvb, pinfo, tree);
+    }
+
+    return tvb_captured_length(tvb);
+  }
 
   /* if IP is not referenced from any filters we don't need to worry about
      generating any tree items.  We must do this after we created the actual
@@ -2009,28 +2051,26 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
                  "Bogus IP header length (%u, must be at least %u)",
                  hlen, IPH_MIN_LEN);
 
-    proto_tree_add_uint_format_value(ip_tree, hf_ip_hdr_len, tvb, offset, 1, hlen,
+    proto_tree_add_uint_bits_format_value(ip_tree, hf_ip_hdr_len, tvb, (offset<<3)+4, 4, hlen,
                                  "%u bytes (bogus, must be at least %u)", hlen, IPH_MIN_LEN);
-    return;
+    return tvb_captured_length(tvb);
   }
 
-  proto_tree_add_uint_format_value(ip_tree, hf_ip_hdr_len, tvb, offset, 1, hlen/4,
-                               "%u bytes", hlen);
+  proto_tree_add_uint_bits_format_value(ip_tree, hf_ip_hdr_len, tvb, (offset<<3)+4, 4, hlen,
+                               "%u bytes (%u)", hlen, hlen>>2);
 
   iph->ip_tos = tvb_get_guint8(tvb, offset + 1);
   if (g_ip_dscp_actif) {
-    col_add_fstr(pinfo->cinfo, COL_DSCP_VALUE, "%u",
-                 IPDSFIELD_DSCP(iph->ip_tos));
+    col_add_str(pinfo->cinfo, COL_DSCP_VALUE,
+                val_to_str_ext(IPDSFIELD_DSCP(iph->ip_tos), &dscp_short_vals_ext, "%u"));
   }
 
   if (tree) {
     if (g_ip_dscp_actif) {
-      tf = proto_tree_add_uint_format_value(ip_tree, hf_ip_dsfield, tvb, offset + 1,
-        1, iph->ip_tos, "0x%02x (DSCP 0x%02x: %s; ECN: 0x%02x: %s)", iph->ip_tos,
-        IPDSFIELD_DSCP(iph->ip_tos), val_to_str_ext_const(IPDSFIELD_DSCP(iph->ip_tos),
-                                                          &dscp_vals_ext, "Unknown DSCP"),
-        IPDSFIELD_ECN(iph->ip_tos), val_to_str_const(IPDSFIELD_ECN(iph->ip_tos),
-                                                     ecn_vals, "Unknown ECN"));
+      tf = proto_tree_add_item(ip_tree, hf_ip_dsfield, tvb, offset + 1, 1, ENC_NA);
+      proto_item_append_text(tf, " (DSCP: %s, ECN: %s)",
+            val_to_str_ext_const(IPDSFIELD_DSCP(iph->ip_tos), &dscp_short_vals_ext, "Unknown"),
+            val_to_str_ext_const(IPDSFIELD_ECN(iph->ip_tos), &ecn_short_vals_ext, "Unknown"));
 
       field_tree = proto_item_add_subtree(tf, ett_ip_dsfield);
       proto_tree_add_item(field_tree, hf_ip_dsfield_dscp, tvb, offset + 1, 1, ENC_NA);
@@ -2085,18 +2125,28 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
           iph->ip_len, hlen);
       expert_add_info(pinfo, tf, &ei_ip_bogus_ip_length);
       /* Can't dissect any further */
-      return;
+      return tvb_captured_length(tvb);
     }
   } else {
-    /*
-     * Now that we know that the total length of this IP datagram isn't
-     * obviously bogus, adjust the length of this tvbuff to include only
-     * the IP datagram.
-     */
-    set_actual_length(tvb, iph->ip_len);
-
-    if (tree)
-      proto_tree_add_uint(ip_tree, hf_ip_len, tvb, offset + 2, 2, iph->ip_len);
+    tf = proto_tree_add_uint(ip_tree, hf_ip_len, tvb, offset + 2, 2, iph->ip_len);
+    if (iph->ip_len > tvb_reported_length(tvb)) {
+      /*
+       * Length runs past the data we're given.
+       * Note that if not in a ICMP error packet.
+       */
+      if (!pinfo->flags.in_error_pkt) {
+        expert_add_info_format(pinfo, tf, &ei_ip_bogus_ip_length,
+                               "IPv4 total length exceeds packet length (%u bytes)",
+                               tvb_reported_length(tvb));
+      }
+    } else {
+      /*
+       * Now that we know that the total length of this IP datagram isn't
+       * obviously bogus, adjust the length of this tvbuff to include only
+       * the IP datagram.
+       */
+      set_actual_length(tvb, iph->ip_len);
+    }
   }
 
   iph->ip_id  = tvb_get_ntohs(tvb, offset + 4);
@@ -2104,13 +2154,12 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
     proto_tree_add_uint(ip_tree, hf_ip_id, tvb, offset + 4, 2, iph->ip_id);
 
   iph->ip_off = tvb_get_ntohs(tvb, offset + 6);
-  if (tree) {
-    int bit_offset = (offset + 6) * 8;
+  bit_offset = (offset + 6) * 8;
 
-    flags = (iph->ip_off & (IP_RF | IP_DF | IP_MF)) >> IP_OFFSET_WIDTH;
-    tf = proto_tree_add_uint(ip_tree, hf_ip_flags, tvb, offset + 6, 1, flags);
-    field_tree = proto_item_add_subtree(tf, ett_ip_off);
-    if (ip_security_flag) {
+  flags = (iph->ip_off & (IP_RF | IP_DF | IP_MF)) >> IP_OFFSET_WIDTH;
+  tf = proto_tree_add_uint(ip_tree, hf_ip_flags, tvb, offset + 6, 1, flags);
+  field_tree = proto_item_add_subtree(tf, ett_ip_off);
+  if (ip_security_flag) {
       proto_item *sf;
 
       sf = proto_tree_add_bits_item(field_tree, hf_ip_flags_sf, tvb,
@@ -2119,21 +2168,21 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
         proto_item_append_text(tf, " (Evil packet!)");
         expert_add_info(pinfo, sf, &ei_ip_evil_packet);
       }
-    } else {
+  } else {
       proto_tree_add_bits_item(field_tree, hf_ip_flags_rf, tvb, bit_offset + 0,
                                1, ENC_LITTLE_ENDIAN);
-    }
-    if (iph->ip_off & IP_DF)
-      proto_item_append_text(tf, " (Don't Fragment)");
-    proto_tree_add_bits_item(field_tree, hf_ip_flags_df, tvb, bit_offset + 1,
+  }
+  if (iph->ip_off & IP_DF)
+    proto_item_append_text(tf, " (Don't Fragment)");
+
+  proto_tree_add_bits_item(field_tree, hf_ip_flags_df, tvb, bit_offset + 1,
                              1, ENC_BIG_ENDIAN);
-    if (iph->ip_off & IP_MF)
+  if (iph->ip_off & IP_MF)
       proto_item_append_text(tf, " (More Fragments)");
-    proto_tree_add_bits_item(field_tree, hf_ip_flags_mf, tvb, bit_offset + 2,
+  proto_tree_add_bits_item(field_tree, hf_ip_flags_mf, tvb, bit_offset + 2,
                              1, ENC_BIG_ENDIAN);
-    proto_tree_add_uint(ip_tree, hf_ip_frag_offset, tvb, offset + 6, 2,
+  proto_tree_add_uint(ip_tree, hf_ip_frag_offset, tvb, offset + 6, 2,
                         (iph->ip_off & IP_OFFSET)*8);
-  }
 
   iph->ip_ttl = tvb_get_guint8(tvb, offset + 8);
   if (tree) {
@@ -2142,7 +2191,7 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
     ttl_item = NULL;
   }
 
-  iph->ip_p = tvb_get_guint8(tvb, offset + 9);
+  iph->ip_nxt = tvb_get_guint8(tvb, offset + 9);
   if (tree) {
     proto_tree_add_item(ip_tree, hf_ip_proto, tvb, offset + 9, 1, ENC_BIG_ENDIAN);
   }
@@ -2156,55 +2205,25 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
    */
   if (ip_check_checksum && tvb_bytes_exist(tvb, offset, hlen)&&(!pinfo->flags.in_error_pkt)) {
     ipsum = ip_checksum_tvb(tvb, offset, hlen);
-    if (tree) {
-      if (ipsum == 0) {
-        item = proto_tree_add_uint_format_value(ip_tree, hf_ip_checksum, tvb,
-                                          offset + 10, 2, iph->ip_sum,
-                                          "0x%04x [correct]",
-                                          iph->ip_sum);
-        checksum_tree = proto_item_add_subtree(item, ett_ip_checksum);
-        item = proto_tree_add_uint(checksum_tree, hf_ip_checksum_calculated, tvb,
-                                      offset + 10, 2, iph->ip_sum);
-        PROTO_ITEM_SET_GENERATED(item);
-        item = proto_tree_add_boolean(checksum_tree, hf_ip_checksum_good, tvb,
-                                      offset + 10, 2, TRUE);
-        PROTO_ITEM_SET_GENERATED(item);
-        item = proto_tree_add_boolean(checksum_tree, hf_ip_checksum_bad, tvb,
-                                      offset + 10, 2, FALSE);
-        PROTO_ITEM_SET_GENERATED(item);
-      } else {
-        expected_cksum = in_cksum_shouldbe(iph->ip_sum, ipsum);
-
-        item = proto_tree_add_uint_format_value(ip_tree, hf_ip_checksum, tvb,
-                                          offset + 10, 2, iph->ip_sum,
-                                          "0x%04x"
-                                          "[incorrect, should be 0x%04x "
-                                          "(may be caused by \"IP checksum "
-                                          "offload\"?)]", iph->ip_sum,
-                                          expected_cksum);
-        checksum_tree = proto_item_add_subtree(item, ett_ip_checksum);
-        item = proto_tree_add_uint(checksum_tree, hf_ip_checksum_calculated, tvb,
-                                      offset + 10, 2, expected_cksum);
-        PROTO_ITEM_SET_GENERATED(item);
-        item = proto_tree_add_boolean(checksum_tree, hf_ip_checksum_good, tvb,
-                                      offset + 10, 2, FALSE);
-        PROTO_ITEM_SET_GENERATED(item);
-        item = proto_tree_add_boolean(checksum_tree, hf_ip_checksum_bad, tvb,
-                                      offset + 10, 2, TRUE);
-        PROTO_ITEM_SET_GENERATED(item);
-      }
-    }
-    if (ipsum != 0) {
-      /* Add expert item always (so tap gets called if present);
-         if (tree == NULL) then item will be NULL
-         else item should be from the
-         add_boolean(..., hf_ip_checksum_bad, ...) above */
-      expert_add_info(pinfo, item, &ei_ip_checksum_bad);
+    item = proto_tree_add_checksum(ip_tree, tvb, offset + 10, hf_ip_checksum, hf_ip_checksum_status, &ei_ip_checksum_bad, pinfo, ipsum,
+                                ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY|PROTO_CHECKSUM_IN_CKSUM);
+    if (ipsum == 0) {
+      /* XXX - Keeping hf_ip_checksum_calculated field for now.  Doesn't fit into the
+        proto_tree_add_checksum design, but IP is a popular enough dissector that somebody
+        may have a legitimate reason for wanting it filtered */
+      item = proto_tree_add_uint(ip_tree, hf_ip_checksum_calculated, tvb,
+                                    offset + 10, 2, iph->ip_sum);
+      PROTO_ITEM_SET_GENERATED(item);
+    } else {
+      proto_item_append_text(item, "(may be caused by \"IP checksum offload\"?)");
+
+      item = proto_tree_add_uint(ip_tree, hf_ip_checksum_calculated, tvb,
+                                      offset + 10, 2, in_cksum_shouldbe(iph->ip_sum, ipsum));
+      PROTO_ITEM_SET_GENERATED(item);
     }
   } else {
     ipsum = 0;
-    if (tree) {
-      item = proto_tree_add_uint_format_value(ip_tree, hf_ip_checksum, tvb,
+    proto_tree_add_uint_format_value(ip_tree, hf_ip_checksum, tvb,
                                         offset + 10, 2, iph->ip_sum,
                                         "0x%04x [%s]",
                                         iph->ip_sum,
@@ -2213,19 +2232,14 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
                                              "in ICMP error packet" :
                                              "not all data available") :
                                             "validation disabled");
-      checksum_tree = proto_item_add_subtree(item, ett_ip_checksum);
-      item = proto_tree_add_boolean(checksum_tree, hf_ip_checksum_good, tvb,
-                                    offset + 10, 2, FALSE);
-      PROTO_ITEM_SET_GENERATED(item);
-      item = proto_tree_add_boolean(checksum_tree, hf_ip_checksum_bad, tvb,
-                                    offset + 10, 2, FALSE);
-      PROTO_ITEM_SET_GENERATED(item);
-    }
+    item = proto_tree_add_uint(ip_tree, hf_ip_checksum_status, tvb,
+                                    offset + 10, 0, PROTO_CHECKSUM_E_UNVERIFIED);
+    PROTO_ITEM_SET_GENERATED(item);
   }
   src32 = tvb_get_ntohl(tvb, offset + IPH_SRC);
-  TVB_SET_ADDRESS(&pinfo->net_src, AT_IPv4, tvb, offset + IPH_SRC, 4);
-  COPY_ADDRESS_SHALLOW(&pinfo->src, &pinfo->net_src);
-  COPY_ADDRESS_SHALLOW(&iph->ip_src, &pinfo->src);
+  set_address_tvb(&pinfo->net_src, AT_IPv4, 4, tvb, offset + IPH_SRC);
+  copy_address_shallow(&pinfo->src, &pinfo->net_src);
+  copy_address_shallow(&iph->ip_src, &pinfo->src);
   if (tree) {
     const char *src_host;
 
@@ -2265,9 +2279,9 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
     dst_off = 0;
 
   dst32 = tvb_get_ntohl(tvb, offset + IPH_DST + dst_off);
-  TVB_SET_ADDRESS(&pinfo->net_dst, AT_IPv4, tvb, offset + IPH_DST + dst_off, 4);
-  COPY_ADDRESS_SHALLOW(&pinfo->dst, &pinfo->net_dst);
-  COPY_ADDRESS_SHALLOW(&iph->ip_dst, &pinfo->net_dst);
+  set_address_tvb(&pinfo->net_dst, AT_IPv4, 4, tvb, offset + IPH_DST + dst_off);
+  copy_address_shallow(&pinfo->dst, &pinfo->net_dst);
+  copy_address_shallow(&iph->ip_dst, &pinfo->net_dst);
 
   /* If an IP is destined for an IP address in the Local Network Control Block
    * (e.g. 224.0.0.0/24), the packet should never be routed and the TTL would
@@ -2284,8 +2298,11 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
                              "Local Network Control Block (see RFC 3171)",
                              ttl);
     }
-  } else if (!is_a_multicast_addr(dst32) && iph->ip_ttl < 5 &&
-            (iph->ip_p != IP_PROTO_PIM)) {
+  } else if (!is_a_multicast_addr(dst32) &&
+       /* At least BGP should appear here as well */
+       iph->ip_ttl < 5 &&
+        iph->ip_nxt != IP_PROTO_PIM &&
+        iph->ip_nxt != IP_PROTO_OSPF) {
     expert_add_info_format(pinfo, ttl_item, &ei_ip_ttl_too_small, "\"Time To Live\" only %u", iph->ip_ttl);
   }
 
@@ -2344,12 +2361,11 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
                            IPOPT_EOOL, &IP_OPT_TYPES, &ei_ip_opt_len_invalid, pinfo, field_tree, tf, iph);
   }
 
-  p_add_proto_data(pinfo->pool, pinfo, proto_ip, 0, GUINT_TO_POINTER((guint)iph->ip_p));
+  p_add_proto_data(pinfo->pool, pinfo, proto_ip, pinfo->curr_layer_num, GUINT_TO_POINTER((guint)iph->ip_nxt));
   tap_queue_packet(ip_tap, pinfo, iph);
 
   /* Skip over header + options */
   offset += hlen;
-  nxt = iph->ip_p;  /* XXX - what if this isn't the same for all fragments? */
 
   /* If ip_defragment is on, this is a fragment, we have all the data
    * in the fragment, and the header checksum is valid, then just add
@@ -2361,7 +2377,7 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
       ipsum == 0) {
     ipfd_head = fragment_add_check(&ip_reassembly_table, tvb, offset,
                                    pinfo,
-                                   iph->ip_p ^ iph->ip_id ^ src32 ^ dst32,
+                                   iph->ip_nxt ^ iph->ip_id ^ src32 ^ dst32 ^ pinfo->vlan_id,
                                    NULL,
                                    (iph->ip_off & IP_OFFSET) * 8,
                                    iph->ip_len - hlen,
@@ -2400,19 +2416,20 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
     /* Just show this as a fragment. */
     col_add_fstr(pinfo->cinfo, COL_INFO,
                  "Fragmented IP protocol (proto=%s %u, off=%u, ID=%04x)",
-                 ipprotostr(iph->ip_p), iph->ip_p,
+                 ipprotostr(iph->ip_nxt), iph->ip_nxt,
                  (iph->ip_off & IP_OFFSET) * 8, iph->ip_id);
-    if ( ipfd_head && ipfd_head->reassembled_in != pinfo->fd->num ) {
+    if ( ipfd_head && ipfd_head->reassembled_in != pinfo->num ) {
       col_append_fstr(pinfo->cinfo, COL_INFO, " [Reassembled in #%u]",
                       ipfd_head->reassembled_in);
     }
 
-    call_dissector(data_handle, tvb_new_subset_remaining(tvb, offset), pinfo,
+    call_data_dissector(tvb_new_subset_remaining(tvb, offset), pinfo,
                    parent_tree);
     pinfo->fragmented = save_fragmented;
-    return;
+    return tvb_captured_length(tvb);
   }
 
+  if (tvb_reported_length(next_tvb) > 0) {
     /* Hand off to the next protocol.
 
      XXX - setting the columns only after trying various dissectors means
@@ -2420,20 +2437,47 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
      even be labeled as an IP frame; ideally, if a frame being dissected
      throws an exception, it'll be labeled as a mangled frame of the
      type in question. */
-  if ((try_heuristic_first) && (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, parent_tree, &hdtbl_entry, iph))) {
-    /* We're good */
-  } else if (!dissector_try_uint_new(ip_dissector_table, nxt, next_tvb, pinfo,
-                                 parent_tree, TRUE, iph)) {
-    if ((!try_heuristic_first) && (!dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, parent_tree, &hdtbl_entry, iph))) {
+    if (!ip_try_dissect(try_heuristic_first, iph->ip_nxt, next_tvb, pinfo,
+                        parent_tree, iph)) {
       /* Unknown protocol */
       if (update_col_info) {
         col_add_fstr(pinfo->cinfo, COL_INFO, "%s (%u)",
-                   ipprotostr(iph->ip_p), iph->ip_p);
+                   ipprotostr(iph->ip_nxt), iph->ip_nxt);
       }
-      call_dissector(data_handle,next_tvb, pinfo, parent_tree);
+      call_data_dissector(next_tvb, pinfo, parent_tree);
     }
   }
   pinfo->fragmented = save_fragmented;
+  return tvb_captured_length(tvb);
+}
+
+static int
+dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+{
+  proto_tree *ip_tree;
+  proto_item *ti, *tf;
+  guint8 version;
+
+  version = tvb_get_guint8(tvb, 0) >> 4;
+
+  if(version == 4){
+    dissect_ip_v4(tvb, pinfo, tree, data);
+    return tvb_captured_length(tvb);
+  }
+  if(version == 6){
+    call_dissector(ipv6_handle, tvb, pinfo, tree);
+    return tvb_captured_length(tvb);
+  }
+
+  /* Bogus IP version */
+  ti = proto_tree_add_protocol_format(tree, proto_ip, tvb, 0, 1, "Internet Protocol, bogus version (%u)", version);
+  col_set_str(pinfo->cinfo, COL_PROTOCOL, "IP");
+  col_clear(pinfo->cinfo, COL_INFO);
+  col_add_fstr(pinfo->cinfo, COL_INFO, "Bogus IP version (%u)", version);
+  ip_tree = proto_item_add_subtree(ti, ett_ip);
+  tf = proto_tree_add_item(ip_tree, hf_ip_version, tvb, 0, 1, ENC_NA);
+  expert_add_info(pinfo, tf, &ei_ip_bogus_ip_version);
+  return 1;
 }
 
 static gboolean
@@ -2459,7 +2503,6 @@ dissect_ip_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data
     ihl = oct & 0x0f;
     version = oct >> 4;
     if(version == 6){
-        /* TODO: Add IPv6 checks here */
 /*
     3.  IPv6 Header Format
 
@@ -2526,7 +2569,7 @@ dissect_ip_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data
         return FALSE;
     }
 
-    dissect_ip(tvb, pinfo, tree);
+    dissect_ip_v4(tvb, pinfo, tree, data);
     return TRUE;
 }
 
@@ -2546,19 +2589,19 @@ proto_register_ip(void)
 
     { &hf_ip_hdr_len,
       { "Header Length", "ip.hdr_len", FT_UINT8, BASE_DEC,
-        NULL, 0x0F, NULL, HFILL }},
+        NULL, 0x0, NULL, HFILL }},
 
     { &hf_ip_dsfield,
-      { "Differentiated Services Field", "ip.dsfield", FT_UINT8, BASE_DEC,
+      { "Differentiated Services Field", "ip.dsfield", FT_UINT8, BASE_HEX,
         NULL, 0x0, NULL, HFILL }},
 
     { &hf_ip_dsfield_dscp,
-      { "Differentiated Services Codepoint", "ip.dsfield.dscp", FT_UINT8, BASE_HEX | BASE_EXT_STRING,
+      { "Differentiated Services Codepoint", "ip.dsfield.dscp", FT_UINT8, BASE_DEC | BASE_EXT_STRING,
         &dscp_vals_ext, IPDSFIELD_DSCP_MASK, NULL, HFILL }},
 
     { &hf_ip_dsfield_ecn,
-      { "Explicit Congestion Notification", "ip.dsfield.ecn", FT_UINT8, BASE_HEX,
-        VALS(ecn_vals), IPDSFIELD_ECN_MASK, NULL, HFILL }},
+      { "Explicit Congestion Notification", "ip.dsfield.ecn", FT_UINT8, BASE_DEC | BASE_EXT_STRING,
+        &ecn_vals_ext, IPDSFIELD_ECN_MASK, NULL, HFILL }},
 
     { &hf_ip_tos,
       { "Type of Service", "ip.tos", FT_UINT8, BASE_DEC,
@@ -2722,13 +2765,9 @@ proto_register_ip(void)
     { "Calculated Checksum", "ip.checksum_calculated", FT_UINT16, BASE_HEX, NULL, 0x0,
         "The expected IP checksum field as calculated from the IP datagram", HFILL }},
 
-    { &hf_ip_checksum_good,
-      { "Good", "ip.checksum_good", FT_BOOLEAN, BASE_NONE,  NULL, 0x0,
-        "True: checksum matches packet content; False: doesn't match content or not checked", HFILL }},
-
-    { &hf_ip_checksum_bad,
-      { "Bad", "ip.checksum_bad", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
-        "True: checksum doesn't match packet content; False: matches content or not checked", HFILL }},
+    { &hf_ip_checksum_status,
+      { "Header checksum status", "ip.checksum.status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0,
+        NULL, HFILL }},
 
     /* IP options related fields */
     { &hf_ip_opt_type,
@@ -3001,7 +3040,6 @@ proto_register_ip(void)
     &ett_ip_option_other,
     &ett_ip_fragments,
     &ett_ip_fragment,
-    &ett_ip_checksum,
     &ett_ip_opt_type,
     &ett_ip_opt_sec_prot_auth_flags,
     &ett_unknown_ip_tcp_opt,
@@ -3018,11 +3056,12 @@ proto_register_ip(void)
      { &ei_ip_subopt_too_long, { "ip.subopt_too_long", PI_PROTOCOL, PI_WARN, "Suboption would go past end of option", EXPFILL }},
      { &ei_ip_nop, { "ip.nop", PI_PROTOCOL, PI_WARN, "4 NOP in a row - a router may have removed some options", EXPFILL }},
      { &ei_ip_bogus_ip_length, { "ip.bogus_ip_length", PI_PROTOCOL, PI_ERROR, "Bogus IP length", EXPFILL }},
-     { &ei_ip_evil_packet, { "ip.evil_packet", PI_PROTOCOL, PI_WARN, "Suboption would go past end of option", EXPFILL }},
+     { &ei_ip_evil_packet, { "ip.evil_packet", PI_PROTOCOL, PI_WARN, "Packet has evil intent", EXPFILL }},
      { &ei_ip_checksum_bad, { "ip.checksum_bad.expert", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }},
      { &ei_ip_ttl_lncb, { "ip.ttl.lncb", PI_SEQUENCE, PI_NOTE, "Time To Live", EXPFILL }},
      { &ei_ip_ttl_too_small, { "ip.ttl.too_small", PI_SEQUENCE, PI_NOTE, "Time To Live", EXPFILL }},
      { &ei_ip_cipso_tag, { "ip.cipso.malformed", PI_SEQUENCE, PI_ERROR, "Malformed CIPSO tag", EXPFILL }},
+     { &ei_ip_bogus_ip_version, { "ip.bogus_ip_version", PI_PROTOCOL, PI_ERROR, "Bogus IP version", EXPFILL }},
   };
 
   /* Decode As handling */
@@ -3042,8 +3081,9 @@ proto_register_ip(void)
 
   /* subdissector code */
   ip_dissector_table = register_dissector_table("ip.proto", "IP protocol",
-                                                FT_UINT8, BASE_DEC);
-  heur_subdissector_list = register_heur_dissector_list("ip");
+                                                proto_ip, FT_UINT8, BASE_DEC);
+  heur_subdissector_list = register_heur_dissector_list("ip", proto_ip);
+  register_capture_dissector_table("ip.proto", "IP protocol");
 
   /* Register configuration options */
   ip_module = prefs_register_protocol(proto_ip, NULL);
@@ -3082,23 +3122,29 @@ proto_register_ip(void)
 
   register_dissector("ip", dissect_ip, proto_ip);
   register_init_routine(ip_defragment_init);
+  register_cleanup_routine(ip_defragment_cleanup);
   ip_tap = register_tap("ip");
 
   register_decode_as(&ip_da);
   register_conversation_table(proto_ip, TRUE, ip_conversation_packet, ip_hostlist_packet);
-  register_color_conversation_filter("ip", "IPv4", ip_color_filter_valid, ip_build_color_filter);
+  register_conversation_filter("ip", "IPv4", ip_filter_valid, ip_build_filter);
+
+  ip_cap_handle = register_capture_dissector("ip", capture_ip, proto_ip);
 }
 
 void
 proto_reg_handoff_ip(void)
 {
   dissector_handle_t ip_handle;
+  dissector_handle_t ipv4_handle;
+  capture_dissector_handle_t clip_cap_handle;
+  int proto_clip;
 
   ip_handle = find_dissector("ip");
   ipv6_handle = find_dissector("ipv6");
-  data_handle = find_dissector("data");
+  ipv4_handle = create_dissector_handle(dissect_ip_v4, proto_ip);
 
-  dissector_add_uint("ethertype", ETHERTYPE_IP, ip_handle);
+  dissector_add_uint("ethertype", ETHERTYPE_IP, ipv4_handle);
   dissector_add_uint("erf.types.type", ERF_TYPE_IPV4, ip_handle);
   dissector_add_uint("ppp.protocol", PPP_IP, ip_handle);
   dissector_add_uint("ppp.protocol", ETHERTYPE_IP, ip_handle);
@@ -3116,11 +3162,31 @@ proto_reg_handoff_ip(void)
   dissector_add_uint("ax25.pid", AX25_P_IP, ip_handle);
   dissector_add_uint("juniper.proto", JUNIPER_PROTO_IP, ip_handle);
   dissector_add_uint("juniper.proto", JUNIPER_PROTO_MPLS_IP, ip_handle);
-  dissector_add_uint("pwach.channel_type", 0x21, ip_handle); /* IPv4, RFC4385 clause 6. */
+  dissector_add_uint("pwach.channel_type", PW_ACH_TYPE_IPV4, ip_handle);
   dissector_add_uint("sflow_245.header_protocol", SFLOW_245_HEADER_IPv4, ip_handle);
-  dissector_add_for_decode_as("udp.port", ip_handle);
-
-  heur_dissector_add("tipc", dissect_ip_heur, proto_ip);
+  dissector_add_uint("l2tp.pw_type", L2TPv3_PROTOCOL_IP, ip_handle);
+  dissector_add_for_decode_as_with_preference("udp.port", ip_handle);
+  dissector_add_for_decode_as("pcli.payload", ip_handle);
+  dissector_add_uint("wtap_encap", WTAP_ENCAP_RAW_IP4, ip_handle);
+  dissector_add_uint("enc", BSD_AF_INET, ip_handle);
+  dissector_add_uint("vxlan.next_proto", VXLAN_IPV4, ip_handle);
+
+  heur_dissector_add("tipc", dissect_ip_heur, "IP over TIPC", "ip_tipc", proto_ip, HEURISTIC_ENABLE);
+
+  capture_dissector_add_uint("ethertype", ETHERTYPE_IP, ip_cap_handle);
+  capture_dissector_add_uint("ax25.pid", AX25_P_IP, ip_cap_handle);
+  capture_dissector_add_uint("enc", BSD_AF_INET, ip_cap_handle);
+  capture_dissector_add_uint("ppp_hdlc", PPP_IP, ip_cap_handle);
+  capture_dissector_add_uint("llc.dsap", SAP_IP, ip_cap_handle);
+  capture_dissector_add_uint("null.bsd", BSD_AF_INET, ip_cap_handle);
+  capture_dissector_add_uint("fr.nlpid", NLPID_IP, ip_cap_handle);
+
+  /* Classic IP uses the same capture function, but wants its own
+     protocol associated with it.  To eliminate linking dependencies,
+     just add it here */
+  proto_clip = proto_get_id_by_filter_name( "clip" );
+  clip_cap_handle = register_capture_dissector("clip", capture_ip, proto_clip);
+  capture_dissector_add_uint("wtap_encap", WTAP_ENCAP_LINUX_ATM_CLIP, clip_cap_handle);
 }
 
 /*
@@ -3135,4 +3201,3 @@ proto_reg_handoff_ip(void)
  * vi: set shiftwidth=2 tabstop=8 expandtab:
  * :indentSize=2:tabSize=8:noTabs=true:
  */
-