OSPF database packets and OSPF hellos in DC mode are unicast. At
[metze/wireshark/wip.git] / epan / dissectors / packet-ip.c
index 667bb88cfe3efe37516ab8ec3017f634140db52e..b6b8ab85f7d7a02ab1bcb5cf028c234f0e64141b 100644 (file)
@@ -1,8 +1,6 @@
 /* packet-ip.c
  * Routines for IP and miscellaneous IP protocol packet disassembly
  *
- * $Id$
- *
  * Wireshark - Network traffic analyzer
  * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
 
 #include "config.h"
 
-#include <string.h>
-#include <glib.h>
-
 #include <epan/packet.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/reassemble.h>
 #include <epan/etypes.h>
-#include <epan/greproto.h>
 #include <epan/ppptypes.h>
 #include <epan/llcsaps.h>
 #include <epan/aftypes.h>
 #include <epan/in_cksum.h>
 #include <epan/nlpid.h>
 #include <epan/ax25_pids.h>
-#include <epan/tap.h>
-#include <epan/emem.h>
-#include <epan/expert.h>
+#include <epan/decode_as.h>
+#include <wiretap/erf.h>
 
 #include "packet-ip.h"
-#include "packet-ipsec.h"
+#include "packet-juniper.h"
+#include "packet-sflow.h"
+#include "packet-gre.h"
+#include "packet-l2tp.h"
 
 #ifdef HAVE_GEOIP
 #include <GeoIP.h>
 #include <epan/geoip_db.h>
 #endif /* HAVE_GEOIP */
 
+void proto_register_ip(void);
+void proto_reg_handoff_ip(void);
 
 static int ip_tap = -1;
 
@@ -72,11 +73,14 @@ static gboolean ip_defragment = TRUE;
 static gboolean ip_summary_in_tree = TRUE;
 
 /* Perform IP checksum */
-static gboolean ip_check_checksum = TRUE;
+static gboolean ip_check_checksum = FALSE;
 
 /* Assume TSO and correct zero-length IP packets */
 static gboolean ip_tso_supported = TRUE;
 
+/* Use heuristics to determine subdissector */
+static gboolean try_heuristic_first = FALSE;
+
 #ifdef HAVE_GEOIP
 /* Look up addresses in GeoIP */
 static gboolean ip_use_geoip = TRUE;
@@ -85,7 +89,7 @@ static gboolean ip_use_geoip = TRUE;
 /* Interpret the reserved flag as security flag (RFC 3514) */
 static gboolean ip_security_flag = FALSE;
 
-static int proto_ip = -1;
+int proto_ip = -1;
 static int hf_ip_version = -1;
 static int hf_ip_hdr_len = -1;
 static int hf_ip_dsfield = -1;
@@ -114,6 +118,7 @@ static int hf_ip_frag_offset = -1;
 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;
 
@@ -164,6 +169,7 @@ static int hf_ip_src_rt = -1;
 static int hf_ip_src_rt_host = -1;
 static int hf_ip_empty_rt = -1;
 static int hf_ip_empty_rt_host = -1;
+static int hf_ip_cipso_tag_type = -1;
 
 static int hf_ip_fragments = -1;
 static int hf_ip_fragment = -1;
@@ -177,6 +183,16 @@ static int hf_ip_reassembled_in = -1;
 static int hf_ip_reassembled_length = -1;
 static int hf_ip_reassembled_data = -1;
 
+/* Generated from convert_proto_tree_add_text.pl */
+static int hf_ip_opt_flag = -1;
+static int hf_ip_opt_overflow = -1;
+static int hf_ip_cipso_tag_data = -1;
+static int hf_ip_cipso_sensitivity_level = -1;
+static int hf_ip_cipso_categories = -1;
+static int hf_ip_cipso_doi = -1;
+static int hf_ip_opt_time_stamp = -1;
+static int hf_ip_opt_time_stamp_addr = -1;
+
 #ifdef HAVE_GEOIP
 static int hf_geoip_country = -1;
 static int hf_geoip_city = -1;
@@ -225,6 +241,23 @@ 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;
+
+static expert_field ei_ip_opt_len_invalid = EI_INIT;
+static expert_field ei_ip_opt_sec_prot_auth_fti = EI_INIT;
+static expert_field ei_ip_extraneous_data = EI_INIT;
+static expert_field ei_ip_opt_ptr_before_address = EI_INIT;
+static expert_field ei_ip_opt_ptr_middle_address = EI_INIT;
+static expert_field ei_ip_subopt_too_long = EI_INIT;
+static expert_field ei_ip_nop = EI_INIT;
+static expert_field ei_ip_bogus_ip_length = EI_INIT;
+static expert_field ei_ip_evil_packet = EI_INIT;
+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
 static gint ett_geoip_info = -1;
@@ -247,11 +280,12 @@ static const fragment_items ip_frag_items = {
   "IPv4 fragments"
 };
 
+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 dissector_handle_t tapa_handle;
 
 
 /* IP structs and definitions */
@@ -341,17 +375,13 @@ static dissector_handle_t tapa_handle;
 #define IPTOS_PREC_ROUTINE          0
 
 /* IP options */
-#define IPOPT_COPY_MASK         0x80
 #define IPOPT_COPY              0x80
 
-#define IPOPT_CLASS_MASK        0x60
 #define IPOPT_CONTROL           0x00
 #define IPOPT_RESERVED1         0x20
 #define IPOPT_MEASUREMENT       0x40
 #define IPOPT_RESERVED2         0x60
 
-#define IPOPT_NUMBER_MASK       0x1F
-
 /* REF: http://www.iana.org/assignments/ip-parameters */
 /* TODO: Not all of these are implemented. */
 #define IPOPT_EOOL      (0 |IPOPT_CONTROL)
@@ -448,17 +478,98 @@ static dissector_handle_t tapa_handle;
 #define is_a_multicast_addr(addr) \
   ((addr & 0xf0000000) == 0xe0000000)
 
+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)));
+}
+
+static gpointer ip_value(packet_info *pinfo)
+{
+    return p_get_proto_data(pinfo->pool, pinfo, proto_ip, 0);
+}
+
+static const char* ip_conv_get_filter_type(conv_item_t* conv, conv_filter_type_e filter)
+{
+    if ((filter == CONV_FT_SRC_ADDRESS) && (conv->src_address.type == AT_IPv4))
+        return "ip.src";
+
+    if ((filter == CONV_FT_DST_ADDRESS) && (conv->dst_address.type == AT_IPv4))
+        return "ip.dst";
+
+    if ((filter == CONV_FT_ANY_ADDRESS) && (conv->src_address.type == AT_IPv4))
+        return "ip.addr";
+
+    return CONV_FILTER_INVALID;
+}
+
+static ct_dissector_info_t ip_ct_dissector_info = {&ip_conv_get_filter_type};
+
+static int
+ip_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+    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);
+
+    return 1;
+}
+
+static const char* ip_host_get_filter_type(hostlist_talker_t* host, conv_filter_type_e filter)
+{
+    if ((filter == CONV_FT_ANY_ADDRESS) && (host->myaddress.type == AT_IPv4))
+        return "ip.addr";
+
+    return CONV_FILTER_INVALID;
+}
+
+static hostlist_dissector_info_t ip_host_dissector_info = {&ip_host_get_filter_type};
+
+static int
+ip_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+    conv_hash_t *hash = (conv_hash_t*) pit;
+    const ws_ip *iph=(const ws_ip *)vip;
+
+    /* Take two "add" passes per packet, adding for each direction, ensures that all
+    packets are counted properly (even if address is sending to itself)
+    XXX - this could probably be done more efficiently inside hostlist_table */
+    add_hostlist_table_data(hash, &iph->ip_src, 0, TRUE, 1, pinfo->fd->pkt_len, &ip_host_dissector_info, PT_NONE);
+    add_hostlist_table_data(hash, &iph->ip_dst, 0, FALSE, 1, pinfo->fd->pkt_len, &ip_host_dissector_info, PT_NONE);
+    return 1;
+}
+
+static gboolean
+ip_color_filter_valid(packet_info *pinfo)
+{
+    return proto_is_frame_protocol(pinfo->layers, "ip");
+}
+
+static gchar*
+ip_build_color_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),
+                address_to_str(pinfo->pool, &pinfo->net_dst));
+}
+
 /*
  * defragmentation of IPv4
  */
-static GHashTable *ip_fragment_table = NULL;
-static GHashTable *ip_reassembled_table = NULL;
+static reassembly_table ip_reassembly_table;
 
 static void
 ip_defragment_init(void)
 {
-  fragment_table_init(&ip_fragment_table);
-  reassembled_table_init(&ip_reassembled_table);
+  reassembly_table_init(&ip_reassembly_table,
+                        &addresses_reassembly_table_functions);
+}
+
+static void
+ip_defragment_cleanup(void)
+{
+  reassembly_table_destroy(&ip_reassembly_table);
 }
 
 void
@@ -498,18 +609,14 @@ capture_ip(const guchar *pd, int offset, int len, packet_counts *ld) {
 
 #ifdef HAVE_GEOIP
 static void
-add_geoip_info_entry(proto_item *geoip_info_item, tvbuff_t *tvb, gint offset, guint32 ip, int isdst)
+add_geoip_info_entry(proto_tree *geoip_info_tree, proto_item *geoip_info_item, tvbuff_t *tvb, gint offset, guint32 ip, int isdst)
 {
-  proto_tree *geoip_info_tree;
-
   guint num_dbs = geoip_db_num_dbs();
   guint item_cnt = 0;
   guint dbnum;
 
-  geoip_info_tree = proto_item_add_subtree(geoip_info_item, ett_geoip_info);
-
   for (dbnum = 0; dbnum < num_dbs; dbnum++) {
-    const char *geoip_str = geoip_db_lookup_ipv4(dbnum, ip, NULL);
+    char *geoip_str = geoip_db_lookup_ipv4(dbnum, ip, NULL);
     int db_type = geoip_db_type(dbnum);
 
     int geoip_hf, geoip_local_hf;
@@ -549,7 +656,6 @@ add_geoip_info_entry(proto_item *geoip_info_item, tvbuff_t *tvb, gint offset, gu
         break;
       default:
         continue;
-        break;
     }
 
     if (geoip_str) {
@@ -564,10 +670,10 @@ add_geoip_info_entry(proto_item *geoip_info_item, tvbuff_t *tvb, gint offset, gu
         PROTO_ITEM_SET_GENERATED(item);
         PROTO_ITEM_SET_HIDDEN(item);
       } else {
-        item = proto_tree_add_unicode_string(geoip_info_tree, geoip_local_hf,
+        item = proto_tree_add_string(geoip_info_tree, geoip_local_hf,
           tvb, offset, 4, geoip_str);
         PROTO_ITEM_SET_GENERATED(item);
-        item = proto_tree_add_unicode_string(geoip_info_tree, geoip_hf,
+        item = proto_tree_add_string(geoip_info_tree, geoip_hf,
           tvb, offset, 4, geoip_str);
         PROTO_ITEM_SET_GENERATED(item);
         PROTO_ITEM_SET_HIDDEN(item);
@@ -576,6 +682,7 @@ add_geoip_info_entry(proto_item *geoip_info_item, tvbuff_t *tvb, gint offset, gu
       item_cnt++;
       proto_item_append_text(geoip_info_item, "%s%s",
                              plurality(item_cnt, "", ", "), geoip_str);
+      wmem_free(NULL, geoip_str);
     }
   }
 
@@ -589,23 +696,23 @@ add_geoip_info(proto_tree *tree, tvbuff_t *tvb, gint offset, guint32 src32,
 {
   guint num_dbs;
   proto_item *geoip_info_item;
+  proto_tree *geoip_info_tree;
 
   num_dbs = geoip_db_num_dbs();
   if (num_dbs < 1)
     return;
 
-  geoip_info_item = proto_tree_add_text(tree, tvb, offset + IPH_SRC, 4, "Source GeoIP: ");
+  geoip_info_tree = proto_tree_add_subtree(tree, tvb, offset + IPH_SRC, 4, ett_geoip_info, &geoip_info_item, "Source GeoIP: ");
   PROTO_ITEM_SET_GENERATED(geoip_info_item);
-  add_geoip_info_entry(geoip_info_item, tvb, offset + IPH_SRC, src32, 0);
+  add_geoip_info_entry(geoip_info_tree, geoip_info_item, tvb, offset + IPH_SRC, src32, 0);
 
-  geoip_info_item = proto_tree_add_text(tree, tvb, offset + IPH_DST, 4,
-                                        "Destination GeoIP: ");
+  geoip_info_tree = proto_tree_add_subtree(tree, tvb, offset + IPH_DST, 4, ett_geoip_info, &geoip_info_item, "Destination GeoIP: ");
   PROTO_ITEM_SET_GENERATED(geoip_info_item);
-  add_geoip_info_entry(geoip_info_item, tvb, offset + IPH_DST, dst32, 1);
+  add_geoip_info_entry(geoip_info_tree, geoip_info_item, tvb, offset + IPH_DST, dst32, 1);
 }
 #endif /* HAVE_GEOIP */
 
-static const value_string ipopt_type_class_vals[] = {
+const value_string ipopt_type_class_vals[] = {
   {(IPOPT_CONTROL & IPOPT_CLASS_MASK) >> 5, "Control"},
   {(IPOPT_RESERVED1 & IPOPT_CLASS_MASK) >> 5, "Reserved for future use"},
   {(IPOPT_MEASUREMENT & IPOPT_CLASS_MASK) >> 5, "Debugging and measurement"},
@@ -613,7 +720,7 @@ static const value_string ipopt_type_class_vals[] = {
   {0, NULL}
 };
 
-static const value_string ipopt_type_number_vals[] = {
+const value_string ipopt_type_number_vals[] = {
   {IPOPT_EOOL & IPOPT_NUMBER_MASK, "End of Option List (EOL)"},
   {IPOPT_NOP & IPOPT_NUMBER_MASK, "No-Operation (NOP)"},
   {IPOPT_SEC & IPOPT_NUMBER_MASK, "Security"},
@@ -644,17 +751,20 @@ static const value_string ipopt_type_number_vals[] = {
   {0, NULL}
 };
 
+static ip_tcp_opt_type IP_OPT_TYPES = {&hf_ip_opt_type, &ett_ip_opt_type,
+    &hf_ip_opt_type_copy, &hf_ip_opt_type_class, &hf_ip_opt_type_number};
+
 static void
-dissect_ipopt_type(tvbuff_t *tvb, int offset, proto_tree *tree)
+dissect_ipopt_type(tvbuff_t *tvb, int offset, proto_tree *tree, ip_tcp_opt_type* opttypes)
 {
   proto_tree *type_tree;
   proto_item *ti;
 
-  ti = proto_tree_add_item(tree, hf_ip_opt_type, tvb, offset, 1, ENC_NA);
-  type_tree = proto_item_add_subtree(ti, ett_ip_opt_type);
-  proto_tree_add_item(type_tree, hf_ip_opt_type_copy, tvb, offset, 1, ENC_NA);
-  proto_tree_add_item(type_tree, hf_ip_opt_type_class, tvb, offset, 1, ENC_NA);
-  proto_tree_add_item(type_tree, hf_ip_opt_type_number, tvb, offset, 1, ENC_NA);
+  ti = proto_tree_add_item(tree, *opttypes->phf_opt_type, tvb, offset, 1, ENC_NA);
+  type_tree = proto_item_add_subtree(ti, *opttypes->pett_opt_type);
+  proto_tree_add_item(type_tree, *opttypes->phf_opt_type_copy, tvb, offset, 1, ENC_NA);
+  proto_tree_add_item(type_tree, *opttypes->phf_opt_type_class, tvb, offset, 1, ENC_NA);
+  proto_tree_add_item(type_tree, *opttypes->phf_opt_type_number, tvb, offset, 1, ENC_NA);
 }
 
 static void
@@ -663,11 +773,9 @@ dissect_ipopt_eool(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
                    proto_tree *opt_tree, void * data _U_)
 {
   proto_tree *field_tree;
-  proto_item *tf;
 
-  tf = proto_tree_add_text(opt_tree, tvb, offset,  1, "%s", optp->name);
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, offset, field_tree);
+  field_tree = proto_tree_add_subtree(opt_tree, tvb, offset,  1, *optp->subtree_index, NULL, optp->name);
+  dissect_ipopt_type(tvb, offset, field_tree, &IP_OPT_TYPES);
 }
 
 #define dissect_ipopt_nop   dissect_ipopt_eool
@@ -741,22 +849,20 @@ dissect_ipopt_security(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   guint      val;
   guint      curr_offset = offset;
 
-  tf = proto_tree_add_text(opt_tree, tvb, curr_offset, optlen, "%s (%u bytes)",
-                           optp->name, optlen);
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, curr_offset, field_tree);
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, curr_offset, optlen, *optp->subtree_index, &tf,
+                           "%s (%u bytes)", optp->name, optlen);
+  dissect_ipopt_type(tvb, curr_offset, field_tree, &IP_OPT_TYPES);
   curr_offset++;
   tf_sub = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, curr_offset, 1, ENC_NA);
   if (optlen > IPOLEN_MAX)
-    expert_add_info_format(pinfo, tf_sub, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf_sub, &ei_ip_opt_len_invalid);
   curr_offset++;
 
   if (optlen == 11) {
   /* Analyze payload start to decide whether it should be dissected
      according to RFC 791 or RFC 1108 */
     val = tvb_get_ntohs(tvb, curr_offset);
-    if (match_strval(val, secl_rfc791_vals)) {
+    if (try_val_to_str(val, secl_rfc791_vals)) {
       /* Dissect as RFC 791 */
       proto_tree_add_item(field_tree, hf_ip_opt_sec_rfc791_sec,
                           tvb, curr_offset, 2, ENC_BIG_ENDIAN);
@@ -786,8 +892,7 @@ dissect_ipopt_security(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   curr_offset++;
   while (val & 0x01) {
     if ((val & 0x01) && ((curr_offset - offset) == optlen)) {
-      expert_add_info_format(pinfo, tf_sub, PI_PROTOCOL, PI_WARN,
-                             "Field Termination Indicator set to 1 for last byte of option");
+      expert_add_info(pinfo, tf_sub, &ei_ip_opt_sec_prot_auth_fti);
       break;
     }
     val = tvb_get_guint8(tvb, curr_offset);
@@ -797,8 +902,7 @@ dissect_ipopt_security(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
     curr_offset++;
   }
   if ((curr_offset - offset) < optlen) {
-    expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                           "Extraneous data in option");
+    expert_add_info(pinfo, tf, &ei_ip_extraneous_data);
   }
 }
 
@@ -808,20 +912,17 @@ dissect_ipopt_ext_security(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
                            void * data _U_)
 {
   proto_tree *field_tree;
-  proto_item *tf;
   proto_item *tf_sub;
   guint      curr_offset = offset;
   gint      remaining;
 
-  tf = proto_tree_add_text(opt_tree, tvb, curr_offset, optlen, "%s (%u bytes)",
-                           optp->name, optlen);
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, curr_offset, field_tree);
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, curr_offset, optlen,
+                           *optp->subtree_index, NULL, "%s (%u bytes)", optp->name, optlen);
+  dissect_ipopt_type(tvb, curr_offset, field_tree, &IP_OPT_TYPES);
   curr_offset++;
   tf_sub = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, curr_offset, 1, ENC_NA);
   if (optlen > IPOLEN_MAX)
-    expert_add_info_format(pinfo, tf_sub, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf_sub, &ei_ip_opt_len_invalid);
   curr_offset++;
   proto_tree_add_item(field_tree, hf_ip_opt_ext_sec_add_sec_info_format_code, tvb, curr_offset, 1, ENC_BIG_ENDIAN);
   curr_offset++;
@@ -842,34 +943,43 @@ dissect_ipopt_ext_security(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
  * draft-ietf-cipso-ipsecurity-01.txt and FIPS 188, a copy of both documents
  * can be found at the NetLabel project page, http://netlabel.sf.net or at
  * http://tools.ietf.org/html/draft-ietf-cipso-ipsecurity-01 */
+static const value_string cipso_tag_type_vals[] = {
+   {0,   "Padding"},
+   {1,   "Restrictive Category Bitmap"},
+   {2,   "Enumerated Categories"},
+   {5,   "Ranged Categories"},
+   {6,   "Permissive Categories"},
+   {7,   "Free Form"},
+
+   { 0,                          NULL }
+};
+
 static void
 dissect_ipopt_cipso(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
                     guint optlen, packet_info *pinfo, proto_tree *opt_tree,
                     void * data _U_)
 {
   proto_tree *field_tree;
-  proto_item *tf;
+  proto_item *tf, *tag_item;
   guint      tagtype, taglen;
   int        offset_max = offset + optlen;
 
-  tf = proto_tree_add_text(opt_tree, tvb, offset, optlen, "%s (%u bytes)",
-                           optp->name, optlen);
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, offset, field_tree);
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, offset, optlen, *optp->subtree_index, &tf,
+                           "%s (%u bytes)", optp->name, optlen);
+  dissect_ipopt_type(tvb, offset, field_tree, &IP_OPT_TYPES);
   tf = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, offset + 1, 1, ENC_NA);
   if (optlen > IPOLEN_MAX)
-    expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf, &ei_ip_opt_len_invalid);
 
   offset += 2;
 
-  proto_tree_add_text(field_tree, tvb, offset, 4, "DOI: %u",
-                      tvb_get_ntohl(tvb, offset));
+  proto_tree_add_item(field_tree, hf_ip_cipso_doi, tvb, offset, 4, ENC_BIG_ENDIAN);
   offset += 4;
 
   /* loop through all of the tags in the CIPSO option */
   while (offset < offset_max) {
     tagtype = tvb_get_guint8(tvb, offset);
+    tag_item = proto_tree_add_item(field_tree, hf_ip_cipso_tag_type, tvb, offset, 1, ENC_NA);
 
     if ((offset + 1) < offset_max)
       taglen = tvb_get_guint8(tvb, offset + 1);
@@ -885,20 +995,14 @@ dissect_ipopt_cipso(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
       /* restrictive bitmap, see CIPSO draft section 3.4.2 for tag format */
       if ((taglen < 4) || (taglen > CIPSO_TAG_LEN_MAX) ||
          ((offset + (int)taglen - 1) > offset_max)) {
-        proto_tree_add_text(field_tree, tvb, offset, offset_max - offset,
-                            "Malformed CIPSO tag");
+        expert_add_info(pinfo, tag_item, &ei_ip_cipso_tag);
         return;
       }
 
-      proto_tree_add_text(field_tree, tvb, offset, 1,
-                          "Tag Type: Restrictive Category Bitmap (%u)",
-                          tagtype);
-
       /* skip past alignment octet */
       offset += 3;
 
-      proto_tree_add_text(field_tree, tvb, offset, 1, "Sensitivity Level: %u",
-                          tvb_get_guint8(tvb, offset));
+      proto_tree_add_item(field_tree, hf_ip_cipso_sensitivity_level, tvb, offset, 1, ENC_NA);
       offset += 1;
 
       if (taglen > 4) {
@@ -906,14 +1010,14 @@ dissect_ipopt_cipso(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
         guint byte_spot = 0;
         unsigned char bitmask;
         char *cat_str;
-        char *cat_str_tmp = ep_alloc(USHRT_MAX_STRLEN);
+        char *cat_str_tmp = (char *)wmem_alloc(wmem_packet_scope(), USHRT_MAX_STRLEN);
         size_t cat_str_len;
         const guint8 *val_ptr = tvb_get_ptr(tvb, offset, taglen - 4);
 
         /* this is just a guess regarding string size, but we grow it below
          * if needed */
         cat_str_len = 256;
-        cat_str = ep_alloc0(cat_str_len);
+        cat_str = (char *)wmem_alloc0(wmem_packet_scope(), cat_str_len);
 
         /* we checked the length above so the highest category value
          * possible here is 240 */
@@ -929,7 +1033,7 @@ dissect_ipopt_cipso(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
 
                 while (cat_str_len < (strlen(cat_str) + 2 + USHRT_MAX_STRLEN))
                   cat_str_len += cat_str_len;
-                cat_str_new = ep_alloc(cat_str_len);
+                cat_str_new = (char *)wmem_alloc(wmem_packet_scope(), cat_str_len);
                 g_strlcpy(cat_str_new, cat_str, cat_str_len);
                 cat_str_new[cat_str_len - 1] = '\0';
                 cat_str = cat_str_new;
@@ -945,11 +1049,9 @@ dissect_ipopt_cipso(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
         }
 
         if (cat_str)
-          proto_tree_add_text(field_tree, tvb, offset, taglen - 4,
-                              "Categories: %s", cat_str);
+          proto_tree_add_string(field_tree, hf_ip_cipso_categories, tvb, offset, taglen - 4, cat_str);
         else
-          proto_tree_add_text(field_tree, tvb, offset, taglen - 4,
-                              "Categories: ERROR PARSING CATEGORIES");
+          proto_tree_add_string(field_tree, hf_ip_cipso_categories, tvb, offset, taglen - 4, "ERROR PARSING CATEGORIES");
         offset += taglen - 4;
       }
       break;
@@ -957,26 +1059,21 @@ dissect_ipopt_cipso(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
       /* enumerated categories, see CIPSO draft section 3.4.3 for tag format */
       if ((taglen < 4) || (taglen > CIPSO_TAG_LEN_MAX) ||
          ((offset + (int)taglen - 1) > offset_max)) {
-        proto_tree_add_text(field_tree, tvb, offset, offset_max - offset,
-                            "Malformed CIPSO tag");
+        expert_add_info(pinfo, tag_item, &ei_ip_cipso_tag);
         return;
       }
 
-      proto_tree_add_text(field_tree, tvb, offset, 1,
-                          "Tag Type: Enumerated Categories (%u)", tagtype);
-
       /* skip past alignment octet */
       offset += 3;
 
       /* sensitivity level */
-      proto_tree_add_text(field_tree, tvb, offset, 1, "Sensitivity Level: %u",
-                          tvb_get_guint8(tvb, offset));
+      proto_tree_add_item(field_tree, hf_ip_cipso_sensitivity_level, tvb, offset, 1, ENC_NA);
       offset += 1;
 
       if (taglen > 4) {
         int offset_max_cat = offset + taglen - 4;
-        char *cat_str = ep_alloc0(USHRT_MAX_STRLEN * 15);
-        char *cat_str_tmp = ep_alloc(USHRT_MAX_STRLEN);
+        char *cat_str = (char *)wmem_alloc0(wmem_packet_scope(), USHRT_MAX_STRLEN * 15);
+        char *cat_str_tmp = (char *)wmem_alloc(wmem_packet_scope(), USHRT_MAX_STRLEN);
 
         while ((offset + 2) <= offset_max_cat) {
           g_snprintf(cat_str_tmp, USHRT_MAX_STRLEN, "%u",
@@ -987,35 +1084,29 @@ dissect_ipopt_cipso(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
           g_strlcat(cat_str, cat_str_tmp, USHRT_MAX_STRLEN * 15);
         }
 
-        proto_tree_add_text(field_tree, tvb, offset - taglen + 4, taglen - 4,
-                            "Categories: %s", cat_str);
+        proto_tree_add_string(field_tree, hf_ip_cipso_categories, tvb, offset - taglen + 4, taglen - 4, cat_str);
       }
       break;
     case 5:
       /* ranged categories, see CIPSO draft section 3.4.4 for tag format */
       if ((taglen < 4) || (taglen > CIPSO_TAG_LEN_MAX) ||
          ((offset + (int)taglen - 1) > offset_max)) {
-        proto_tree_add_text(field_tree, tvb, offset, offset_max - offset,
-                            "Malformed CIPSO tag");
+        expert_add_info(pinfo, tag_item, &ei_ip_cipso_tag);
         return;
       }
 
-      proto_tree_add_text(field_tree, tvb, offset, 1,
-                          "Tag Type: Ranged Categories (%u)", tagtype);
-
       /* skip past alignment octet */
       offset += 3;
 
       /* sensitivity level */
-      proto_tree_add_text(field_tree, tvb, offset, 1, "Sensitivity Level: %u",
-                          tvb_get_guint8(tvb, offset));
+      proto_tree_add_item(field_tree, hf_ip_cipso_sensitivity_level, tvb, offset, 1, ENC_NA);
       offset += 1;
 
       if (taglen > 4) {
         guint16 cat_low, cat_high;
         int offset_max_cat = offset + taglen - 4;
-        char *cat_str = ep_alloc0(USHRT_MAX_STRLEN * 16);
-        char *cat_str_tmp = ep_alloc(USHRT_MAX_STRLEN * 2);
+        char *cat_str = (char *)wmem_alloc0(wmem_packet_scope(), USHRT_MAX_STRLEN * 16);
+        char *cat_str_tmp = (char *)wmem_alloc(wmem_packet_scope(), USHRT_MAX_STRLEN * 2);
 
         while ((offset + 2) <= offset_max_cat) {
           cat_high = tvb_get_ntohs(tvb, offset);
@@ -1037,49 +1128,38 @@ dissect_ipopt_cipso(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
           g_strlcat(cat_str, cat_str_tmp, USHRT_MAX_STRLEN * 16);
         }
 
-        proto_tree_add_text(field_tree, tvb, offset - taglen + 4, taglen - 4,
-                            "Categories: %s", cat_str);
+        proto_tree_add_string(field_tree, hf_ip_cipso_categories, tvb, offset - taglen + 4, taglen - 4, cat_str);
       }
       break;
     case 6:
       /* permissive categories, see FIPS 188 section 6.9 for tag format */
       if ((taglen < 4) || (taglen > CIPSO_TAG_LEN_MAX) ||
          ((offset + (int)taglen - 1) > offset_max)) {
-        proto_tree_add_text(field_tree, tvb, offset, offset_max - offset,
-                            "Malformed CIPSO tag");
+        expert_add_info(pinfo, tag_item, &ei_ip_cipso_tag);
         return;
       }
 
-      proto_tree_add_text(field_tree, tvb, offset, 1,
-                          "Tag Type: Permissive Categories (%u)", tagtype);
-      proto_tree_add_text(field_tree, tvb, offset + 2, taglen - 2, "Tag data");
+      proto_tree_add_item(field_tree, hf_ip_cipso_tag_data, tvb, offset + 2, taglen - 2, ENC_NA);
       offset += taglen;
       break;
     case 7:
       /* free form, see FIPS 188 section 6.10 for tag format */
       if ((taglen < 2) || (taglen > CIPSO_TAG_LEN_MAX) ||
          ((offset + (int)taglen - 1) > offset_max)) {
-        proto_tree_add_text(field_tree, tvb, offset, offset_max - offset,
-                            "Malformed CIPSO tag");
+        expert_add_info(pinfo, tag_item, &ei_ip_cipso_tag);
         return;
       }
 
-      proto_tree_add_text(field_tree, tvb, offset, 1,
-                          "Tag Type: Free Form (%u)", tagtype);
-      proto_tree_add_text(field_tree, tvb, offset + 2, taglen - 2, "Tag data");
+      proto_tree_add_item(field_tree, hf_ip_cipso_tag_data, tvb, offset + 2, taglen - 2, ENC_NA);
       offset += taglen;
       break;
     default:
       /* unknown tag - stop parsing this IPv4 option */
       if ((offset + 1) <= offset_max) {
         taglen = tvb_get_guint8(tvb, offset + 1);
-        proto_tree_add_text(field_tree, tvb, offset, 1,
-                            "Tag Type: Unknown (%u) (%u bytes)",
-                            tagtype, taglen);
+        proto_item_append_text(tag_item, " (%u bytes)", taglen);
         return;
       }
-      proto_tree_add_text(field_tree, tvb, offset, 1,
-                          "Tag Type: Unknown (%u) (invalid format)", tagtype);
       return;
     }
   }
@@ -1096,7 +1176,7 @@ dissect_option_route(proto_tree *tree, tvbuff_t *tvb, int offset, int hf,
   if (next)
     proto_tree_add_ipv4_format_value(tree, hf, tvb, offset, 4, route,
                                      "%s <- (next)",
-                                     ip_to_str((gchar *)&route));
+                                     tvb_ip_to_str(tvb, offset));
   else
     proto_tree_add_ipv4(tree, hf, tvb, offset, 4, route);
   ti = proto_tree_add_string(tree, hf_host, tvb, offset, 4, get_hostname(route));
@@ -1114,24 +1194,20 @@ dissect_ipopt_route(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   guint8 len, ptr;
   int optoffset = 0;
 
-  tf = proto_tree_add_text(opt_tree, tvb, offset, optlen, "%s (%u bytes)",
-                           optp->name, optlen);
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, offset, field_tree);
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, offset, optlen, *optp->subtree_index, NULL,
+                           "%s (%u bytes)", optp->name, optlen);
+  dissect_ipopt_type(tvb, offset, field_tree, &IP_OPT_TYPES);
   tf = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, offset + 1, 1, ENC_NA);
   if (optlen > IPOLEN_MAX)
-    expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf, &ei_ip_opt_len_invalid);
   ptr = tvb_get_guint8(tvb, offset + 2);
   tf = proto_tree_add_item(field_tree, hf_ip_opt_ptr, tvb, offset + 2, 1, ENC_NA);
   if ((ptr < (optp->optlen + 1)) || (ptr & 3)) {
     if (ptr < (optp->optlen + 1)) {
-      expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                             "Pointer points before first address");
+      expert_add_info(pinfo, tf, &ei_ip_opt_ptr_before_address);
     }
     else {
-      expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                             "Pointer points to middle of address");
+      expert_add_info(pinfo, tf, &ei_ip_opt_ptr_middle_address);
     }
     return;
   }
@@ -1140,8 +1216,7 @@ dissect_ipopt_route(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   optoffset = 3;    /* skip past type, length and pointer */
   for (optlen -= 3; optlen > 0; optlen -= 4, optoffset += 4) {
     if (optlen < 4) {
-      expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                             "Suboption would go past end of option");
+      expert_add_info(pinfo, tf, &ei_ip_subopt_too_long);
       break;
     }
 
@@ -1198,25 +1273,21 @@ dissect_ipopt_record_route(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   guint8 len, ptr;
   int optoffset = 0;
 
-  tf = proto_tree_add_text(opt_tree, tvb, offset, optlen, "%s (%u bytes)",
-                           optp->name, optlen);
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, offset, field_tree);
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, offset, optlen, *optp->subtree_index, NULL,
+                           "%s (%u bytes)", optp->name, optlen);
+  dissect_ipopt_type(tvb, offset, field_tree, &IP_OPT_TYPES);
   tf = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, offset + 1, 1, ENC_NA);
   if (optlen > IPOLEN_MAX)
-    expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf, &ei_ip_opt_len_invalid);
   ptr = tvb_get_guint8(tvb, offset + 2);
   tf = proto_tree_add_item(field_tree, hf_ip_opt_ptr, tvb, offset + 2, 1, ENC_NA);
 
   if ((ptr < (optp->optlen + 1)) || (ptr & 3)) {
     if (ptr < (optp->optlen + 1)) {
-      expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                             "Pointer points before first address");
+      expert_add_info(pinfo, tf, &ei_ip_opt_ptr_before_address);
     }
     else {
-      expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                             "Pointer points to middle of address");
+      expert_add_info(pinfo, tf, &ei_ip_opt_ptr_middle_address);
     }
     return;
   }
@@ -1225,8 +1296,7 @@ dissect_ipopt_record_route(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   optoffset = 3;    /* skip past type, length and pointer */
   for (optlen -= 3; optlen > 0; optlen -= 4, optoffset += 4) {
     if (optlen < 4) {
-      expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                             "Suboption would go past end of option");
+      expert_add_info(pinfo, tf, &ei_ip_subopt_too_long);
       break;
     }
 
@@ -1261,14 +1331,12 @@ dissect_ipopt_sid(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   proto_tree *field_tree;
   proto_item *tf;
 
-  tf = proto_tree_add_text(opt_tree, tvb, offset, optlen, "%s (%u bytes): %u",
-                           optp->name, optlen, tvb_get_ntohs(tvb, offset + 2));
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, offset, field_tree);
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, offset, optlen, *optp->subtree_index, NULL,
+                           "%s (%u bytes): %u", optp->name, optlen, tvb_get_ntohs(tvb, offset + 2));
+  dissect_ipopt_type(tvb, offset, field_tree, &IP_OPT_TYPES);
   tf = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, offset + 1, 1, ENC_NA);
   if (optlen != (guint)optp->optlen)
-    expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf, &ei_ip_opt_len_invalid);
   proto_tree_add_item(field_tree, hf_ip_opt_sid, tvb, offset + 2, 2, ENC_BIG_ENDIAN);
 }
 
@@ -1281,14 +1349,12 @@ dissect_ipopt_mtu(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   proto_tree *field_tree;
   proto_item *tf;
 
-  tf = proto_tree_add_text(opt_tree, tvb, offset, optlen, "%s (%u bytes): %u",
-                           optp->name, optlen, tvb_get_ntohs(tvb, offset + 2));
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, offset, field_tree);
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, offset, optlen, *optp->subtree_index, NULL,
+                           "%s (%u bytes): %u", optp->name, optlen, tvb_get_ntohs(tvb, offset + 2));
+  dissect_ipopt_type(tvb, offset, field_tree, &IP_OPT_TYPES);
   tf = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, offset + 1, 1, ENC_NA);
   if (optlen != (guint)optp->optlen)
-    expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf, &ei_ip_opt_len_invalid);
   proto_tree_add_item(field_tree, hf_ip_opt_mtu, tvb, offset + 2, 2, ENC_BIG_ENDIAN);
 }
 
@@ -1301,14 +1367,12 @@ dissect_ipopt_tr(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   proto_tree *field_tree;
   proto_item *tf;
 
-  tf = proto_tree_add_text(opt_tree, tvb, offset, optlen, "%s (%u bytes)",
-                           optp->name, optlen);
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, offset, field_tree);
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, offset, optlen, *optp->subtree_index, NULL,
+                           "%s (%u bytes)", optp->name, optlen);
+  dissect_ipopt_type(tvb, offset, field_tree, &IP_OPT_TYPES);
   tf = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, offset + 1, 1, ENC_NA);
   if (optlen != (guint)optp->optlen)
-    expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf, &ei_ip_opt_len_invalid);
 
   proto_tree_add_item(field_tree, hf_ip_opt_id_number, tvb, offset + 2, 2, ENC_BIG_ENDIAN);
   proto_tree_add_item(field_tree, hf_ip_opt_ohc, tvb, offset + 4, 2, ENC_BIG_ENDIAN);
@@ -1316,6 +1380,12 @@ dissect_ipopt_tr(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   proto_tree_add_item(field_tree, hf_ip_opt_originator, tvb, offset + 8, 4, ENC_BIG_ENDIAN);
 }
 
+static const value_string ipopt_timestamp_flag_vals[] = {
+    {IPOPT_TS_TSONLY,    "Time stamps only"                      },
+    {IPOPT_TS_TSANDADDR, "Time stamp and address"                },
+    {IPOPT_TS_PRESPEC,   "Time stamps for prespecified addresses"},
+    {0,                  NULL                                    }};
+
 static void
 dissect_ipopt_timestamp(const ip_tcp_opt *optp, tvbuff_t *tvb,
                         int offset, guint optlen, packet_info *pinfo,
@@ -1326,27 +1396,19 @@ dissect_ipopt_timestamp(const ip_tcp_opt *optp, tvbuff_t *tvb,
   int        ptr;
   int        optoffset = 0;
   int        flg;
-  static const value_string flag_vals[] = {
-    {IPOPT_TS_TSONLY,    "Time stamps only"                      },
-    {IPOPT_TS_TSANDADDR, "Time stamp and address"                },
-    {IPOPT_TS_PRESPEC,   "Time stamps for prespecified addresses"},
-    {0,                  NULL                                    }};
   guint32 addr;
-  guint ts;
 
-  tf = proto_tree_add_text(opt_tree, tvb, offset, optlen, "%s (%u bytes)",
-                           optp->name, optlen);
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, offset, field_tree);
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, offset, optlen, *optp->subtree_index, NULL,
+                           "%s (%u bytes)", optp->name, optlen);
+  dissect_ipopt_type(tvb, offset, field_tree, &IP_OPT_TYPES);
   tf = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, offset + 1, 1, ENC_NA);
   if (optlen > IPOLEN_MAX)
-    expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf, &ei_ip_opt_len_invalid);
   optoffset += 2;   /* skip past type and length */
   optlen -= 2;      /* subtract size of type and length */
 
   ptr = tvb_get_guint8(tvb, offset + optoffset);
-  proto_tree_add_text(field_tree, tvb, offset + optoffset, 1, "Pointer: %d%s",
+  proto_tree_add_uint_format_value(field_tree, hf_ip_opt_ptr, tvb, offset + optoffset, 1, ptr, "%d%s",
                       ptr, ((ptr == 1) ? " (header is full)" :
                       (ptr < 5) ? " (points before first address)" :
                       (((ptr - 1) & 3) ? " (points to middle of field)" : "")));
@@ -1355,40 +1417,35 @@ dissect_ipopt_timestamp(const ip_tcp_opt *optp, tvbuff_t *tvb,
   ptr--;    /* ptr is 1-origin */
 
   flg = tvb_get_guint8(tvb, offset + optoffset);
-  proto_tree_add_text(field_tree, tvb, offset + optoffset, 1, "Overflow: %u",
-                      flg >> 4);
+  proto_tree_add_item(field_tree, hf_ip_opt_overflow, tvb, offset + optoffset, 1, ENC_NA);
   flg &= 0xF;
-  proto_tree_add_text(field_tree, tvb, offset + optoffset, 1, "Flag: %s",
-                      val_to_str(flg, flag_vals, "Unknown (0x%x)"));
+  proto_tree_add_item(field_tree, hf_ip_opt_flag, tvb, offset + optoffset, 1, ENC_NA);
   optoffset++;
   optlen--;
 
   while (optlen > 0) {
     if (flg == IPOPT_TS_TSANDADDR || flg == IPOPT_TS_PRESPEC) {
       if (optlen < 8) {
-        proto_tree_add_text(field_tree, tvb, offset + optoffset, optlen,
-                            "(suboption would go past end of option)");
+        proto_tree_add_expert(field_tree, pinfo, &ei_ip_subopt_too_long, tvb, offset + optoffset, optlen);
         break;
       }
       addr = tvb_get_ipv4(tvb, offset + optoffset);
-      ts = tvb_get_ntohl(tvb, offset + optoffset + 4);
-      optlen -= 8;
-      proto_tree_add_text(field_tree, tvb, offset + optoffset, 8,
-                          "Address = %s, time stamp = %u",
-                          ((addr == 0) ? "-" :
-                          get_hostname(addr)), ts);
-      optoffset += 8;
+      proto_tree_add_ipv4_format_value(field_tree, hf_ip_opt_time_stamp_addr, tvb, offset + optoffset, 4, addr,
+            "%s", ((addr == 0) ? "-" : get_hostname(addr)));
+      optoffset += 4;
+      optlen -= 4;
+
+      proto_tree_add_item(field_tree, hf_ip_opt_time_stamp, tvb, offset + optoffset, 4, ENC_BIG_ENDIAN);
+      optoffset += 4;
+      optlen -= 4;
     } else {
       if (optlen < 4) {
-        proto_tree_add_text(field_tree, tvb, offset + optoffset, optlen,
-                            "(suboption would go past end of option)");
+        proto_tree_add_expert(field_tree, pinfo, &ei_ip_subopt_too_long, tvb, offset + optoffset, optlen);
         break;
       }
-      ts = tvb_get_ntohl(tvb, offset + optoffset);
-      optlen -= 4;
-      proto_tree_add_text(field_tree, tvb, offset + optoffset, 4,
-                          "Time stamp = %u", ts);
+      proto_tree_add_item(field_tree, hf_ip_opt_time_stamp, tvb, offset + optoffset, 4, ENC_BIG_ENDIAN);
       optoffset += 4;
+      optlen -= 4;
     }
   }
 }
@@ -1410,16 +1467,14 @@ dissect_ipopt_ra(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   proto_item *tf;
   guint16 value = tvb_get_ntohs(tvb, offset + 2);
 
-  tf = proto_tree_add_text(opt_tree, tvb, offset, optlen,
-                           "%s (%u bytes): %s (%u)", optp->name, optlen,
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, offset, optlen,
+                           *optp->subtree_index, NULL, "%s (%u bytes): %s (%u)", optp->name, optlen,
                            rval_to_str(value, ra_rvals, "Unknown (%u)"),
                            value);
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, offset, field_tree);
+  dissect_ipopt_type(tvb, offset, field_tree, &IP_OPT_TYPES);
   tf = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, offset + 1, 1, ENC_NA);
   if (optlen != (guint)optp->optlen)
-    expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf, &ei_ip_opt_len_invalid);
   proto_tree_add_item(field_tree, hf_ip_opt_ra, tvb, offset + 2, 2, ENC_BIG_ENDIAN);
 }
 
@@ -1432,14 +1487,12 @@ dissect_ipopt_sdb(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
   proto_tree *field_tree;
   proto_item *tf;
 
-  tf = proto_tree_add_text(opt_tree, tvb, offset, optlen, "%s (%u bytes)",
-                           optp->name, optlen);
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, offset, field_tree);
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, offset, optlen, *optp->subtree_index, NULL,
+                           "%s (%u bytes)", optp->name, optlen);
+  dissect_ipopt_type(tvb, offset, field_tree, &IP_OPT_TYPES);
   tf = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, offset + 1, 1, ENC_NA);
   if (optlen > IPOLEN_MAX)
-    expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf, &ei_ip_opt_len_invalid);
   for (offset += 2, optlen -= 2; optlen >= 4; offset += 4, optlen -= 4)
     proto_tree_add_item(field_tree, hf_ip_opt_addr, tvb, offset, 4, ENC_BIG_ENDIAN);
 
@@ -1477,33 +1530,33 @@ value_string_ext qs_rate_vals_ext = VALUE_STRING_EXT_INIT(qs_rate_vals);
 static void
 dissect_ipopt_qs(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
                  guint optlen, packet_info *pinfo, proto_tree *opt_tree,
-                 void * data _U_)
+                 void * data)
 {
   proto_tree *field_tree;
   proto_item *tf;
   proto_item *ti;
+  ws_ip *iph = (ws_ip*)data;
 
   guint8 command = tvb_get_guint8(tvb, offset + 2);
   guint8 function = command >> 4;
   guint8 rate = command & QS_RATE_MASK;
   guint8 ttl_diff;
 
-  tf = proto_tree_add_text(opt_tree, tvb, offset, optlen,
-                           "%s (%u bytes): %s (%u)", optp->name, optlen,
+  field_tree = proto_tree_add_subtree_format(opt_tree, tvb, offset, optlen,
+                           *optp->subtree_index, NULL, "%s (%u bytes): %s (%u)", optp->name, optlen,
                            val_to_str(function, qs_func_vals, "Unknown (%u)"),
                            function);
-  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
-  dissect_ipopt_type(tvb, offset, field_tree);
-  ti = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, offset + 1, 1, ENC_NA);
+
+  dissect_ipopt_type(tvb, offset, field_tree, &IP_OPT_TYPES);
+  tf = proto_tree_add_item(field_tree, hf_ip_opt_len, tvb, offset + 1, 1, ENC_NA);
   if (optlen != (guint)optp->optlen)
-    expert_add_info_format(pinfo, ti, PI_PROTOCOL, PI_WARN,
-                           "Invalid length for option");
+    expert_add_info(pinfo, tf, &ei_ip_opt_len_invalid);
   proto_tree_add_item(field_tree, hf_ip_opt_qs_func, tvb, offset + 2, 1, ENC_NA);
 
   if (function == QS_RATE_REQUEST) {
     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 = (pinfo->ip_ttl - tvb_get_guint8(tvb, offset + 3) % 256);
+    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);
@@ -1511,15 +1564,15 @@ dissect_ipopt_qs(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
     proto_item_append_text(tf, ", %s, QS TTL %u, QS TTL diff %u",
                            val_to_str_ext(rate, &qs_rate_vals_ext, "Unknown (%u)"),
                            tvb_get_guint8(tvb, offset + 3), ttl_diff);
-    proto_tree_add_item(field_tree, hf_ip_opt_qs_nonce, tvb, offset + 4, 4, ENC_NA);
-    proto_tree_add_item(field_tree, hf_ip_opt_qs_reserved, tvb, offset + 4, 4, ENC_NA);
+    proto_tree_add_item(field_tree, hf_ip_opt_qs_nonce, tvb, offset + 4, 4, ENC_BIG_ENDIAN);
+    proto_tree_add_item(field_tree, hf_ip_opt_qs_reserved, tvb, offset + 4, 4, ENC_BIG_ENDIAN);
   } else if (function == QS_RATE_REPORT) {
     proto_tree_add_item(field_tree, hf_ip_opt_qs_rate, tvb, offset + 2, 1, ENC_NA);
     proto_item_append_text(tf, ", %s",
                            val_to_str_ext(rate, &qs_rate_vals_ext, "Unknown (%u)"));
     proto_tree_add_item(field_tree, hf_ip_opt_qs_unused, tvb, offset + 3, 1, ENC_NA);
-    proto_tree_add_item(field_tree, hf_ip_opt_qs_nonce, tvb, offset + 4, 4, ENC_NA);
-    proto_tree_add_item(field_tree, hf_ip_opt_qs_reserved, tvb, offset + 4, 4, ENC_NA);
+    proto_tree_add_item(field_tree, hf_ip_opt_qs_nonce, tvb, offset + 4, 4, ENC_BIG_ENDIAN);
+    proto_tree_add_item(field_tree, hf_ip_opt_qs_reserved, tvb, offset + 4, 4, ENC_BIG_ENDIAN);
   }
 }
 
@@ -1597,6 +1650,7 @@ static const ip_tcp_opt ipopts[] = {
 void
 dissect_ip_tcp_options(tvbuff_t *tvb, int offset, guint length,
                        const ip_tcp_opt *opttab, int nopts, int eol,
+                       ip_tcp_opt_type* opttypes, expert_field* ei_bad,
                        packet_info *pinfo, proto_tree *opt_tree,
                        proto_item *opt_item, void * data)
 {
@@ -1624,7 +1678,7 @@ dissect_ip_tcp_options(tvbuff_t *tvb, int offset, guint length,
       optp = NULL;  /* indicate that we don't know this option */
       len_type = OPT_LEN_VARIABLE_LENGTH;
       optlen = 2;
-      name = ep_strdup_printf("Unknown (0x%02x)", opt);
+      name = wmem_strdup_printf(wmem_packet_scope(), "Unknown (0x%02x)", opt);
       dissect = NULL;
       nop_count = 0;
     } else {
@@ -1646,8 +1700,8 @@ dissect_ip_tcp_options(tvbuff_t *tvb, int offset, guint length,
       if (length == 0) {
         /* Bogus - packet must at least include option code byte and
            length byte! */
-        proto_tree_add_text(opt_tree, tvb, offset, 1,
-                            "%s (length byte past end of options)", name);
+        proto_tree_add_expert_format(opt_tree, pinfo, ei_bad, tvb, offset, 1,
+                                     "%s (length byte past end of options)", name);
         return;
       }
       len = tvb_get_guint8(tvb, offset + 1);  /* total including type, len */
@@ -1655,35 +1709,33 @@ dissect_ip_tcp_options(tvbuff_t *tvb, int offset, guint length,
       if (len < 2) {
         /* Bogus - option length is too short to include option code and
            option length. */
-        proto_tree_add_text(opt_tree, tvb, offset, 2,
+        proto_tree_add_expert_format(opt_tree, pinfo, ei_bad, tvb, offset, 2,
                             "%s (with too-short option length = %u byte%s)",
                             name, len, plurality(len, "", "s"));
         return;
       } else if (len - 2 > length) {
         /* Bogus - option goes past the end of the header. */
-        proto_tree_add_text(opt_tree, tvb, offset, length,
-                            "%s (option length = %u byte%s says option goes "
-                            "past end of options)",
+        proto_tree_add_expert_format(opt_tree, pinfo, ei_bad, tvb, offset, length,
+                            "%s (option length = %u byte%s says option goes past end of options)",
                             name, len, plurality(len, "", "s"));
         return;
       } else if (len_type == OPT_LEN_FIXED_LENGTH && len != optlen) {
         /* Bogus - option length isn't what it's supposed to be for this
            option. */
-        proto_tree_add_text(opt_tree, tvb, offset, len,
+        proto_tree_add_expert_format(opt_tree, pinfo, ei_bad, tvb, offset, len,
                             "%s (with option length = %u byte%s; should be %u)",
                             name, len, plurality(len, "", "s"), optlen);
         return;
       } else if (len_type == OPT_LEN_VARIABLE_LENGTH && len < optlen) {
         /* Bogus - option length is less than what it's supposed to be for
            this option. */
-        proto_tree_add_text(opt_tree, tvb, offset, len,
-                            "%s (with option length = %u byte%s; "
-                            "should be >= %u)",
+        proto_tree_add_expert_format(opt_tree, pinfo, ei_bad, tvb, offset, len,
+                            "%s (with option length = %u byte%s; should be >= %u)",
                             name, len, plurality(len, "", "s"), optlen);
         return;
       } else {
         if (optp == NULL) {
-          proto_tree_add_text(opt_tree, tvb, offset, len, "%s (%u byte%s)",
+          proto_tree_add_subtree_format(opt_tree, tvb, offset, len, ett_unknown_ip_tcp_opt, NULL, "%s (%u byte%s)",
                               name, len, plurality(len, "", "s"));
         } else {
           if (dissect != NULL) {
@@ -1693,14 +1745,12 @@ dissect_ip_tcp_options(tvbuff_t *tvb, int offset, guint length,
             (*dissect)(optp, tvb, offset, len, pinfo, opt_tree, data);
           } else {
             proto_tree *field_tree;
-            proto_item *tf;
 
             /* Option has no data, hence no dissector. */
             proto_item_append_text(proto_tree_get_parent(opt_tree), ", %s",
                                    name);
-            tf = proto_tree_add_text(opt_tree, tvb, offset,  len, "%s", name);
-            field_tree = proto_item_add_subtree(tf, ett_ip_option_other);
-            dissect_ipopt_type(tvb, offset, field_tree);
+            field_tree = proto_tree_add_subtree(opt_tree, tvb, offset, len, ett_ip_option_other, NULL, name);
+            dissect_ipopt_type(tvb, offset, field_tree, opttypes);
           }
         }
         len -= 2;   /* subtract size of type and length */
@@ -1714,20 +1764,16 @@ dissect_ip_tcp_options(tvbuff_t *tvb, int offset, guint length,
         (*dissect)(optp, tvb, offset, 1, pinfo, opt_tree, data);
       } else {
         proto_tree *field_tree;
-        proto_item *tf;
 
         /* Option has no data, hence no dissector. */
         proto_item_append_text(proto_tree_get_parent(opt_tree), ", %s", name);
-        tf = proto_tree_add_text(opt_tree, tvb, offset,  1, "%s", name);
-        field_tree = proto_item_add_subtree(tf, ett_ip_option_other);
-        dissect_ipopt_type(tvb, offset, field_tree);
+        field_tree = proto_tree_add_subtree(opt_tree, tvb, offset, 1, ett_ip_option_other, NULL, name);
+        dissect_ipopt_type(tvb, offset, field_tree, opttypes);
       }
       offset += 1;
 
       if (nop_count == 4 && strcmp (name, "No-Operation (NOP)") == 0) {
-        expert_add_info_format(pinfo, opt_item, PI_PROTOCOL, PI_WARN,
-                               "4 NOP in a row - a router may have removed "
-                               "some options");
+        expert_add_info(pinfo, opt_item, &ei_ip_nop);
       }
     }
     if (opt == eol)
@@ -1912,33 +1958,47 @@ static const true_false_string flags_sf_set_evil = {
   "Not evil"
 };
 
-guint16
-ip_checksum(const guint8 *ptr, int len)
+gboolean
+ip_try_dissect(gboolean heur_first, tvbuff_t *tvb, packet_info *pinfo,
+               proto_tree *tree, ws_ip *iph)
 {
-  vec_t cksum_vec[1];
+  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, iph->ip_p, tvb, pinfo,
+                             tree, TRUE, iph)) {
+    return TRUE;
+  }
+
+  if ((!heur_first) && (!dissector_try_heuristic(heur_subdissector_list, tvb,
+                                                 pinfo, tree, &hdtbl_entry,
+                                                 iph))) {
+    return TRUE;
+  }
 
-  cksum_vec[0].ptr = ptr;
-  cksum_vec[0].len = len;
-  return in_cksum(&cksum_vec[0], 1);
+  return FALSE;
 }
 
 static void
-dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
+dissect_ip_v4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
 {
-  proto_tree *ip_tree = NULL, *field_tree = NULL;
-  proto_item *ti = NULL, *tf;
+  proto_tree *ip_tree, *field_tree = NULL;
+  proto_item *ti, *tf;
   guint32    addr;
   int        offset = 0, dst_off;
   guint      hlen, optlen;
   guint16    flags;
-  guint8     nxt;
   guint16    ipsum;
-  fragment_data *ipfd_head = NULL;
+  guint16    expected_cksum;
+  fragment_head *ipfd_head = NULL;
   tvbuff_t   *next_tvb;
   gboolean   update_col_info = TRUE;
   gboolean   save_fragmented;
   ws_ip *iph;
-  const guchar *src_addr, *dst_addr;
   guint32    src32, dst32;
   proto_tree *tree;
   proto_item *item = NULL, *ttl_item;
@@ -1946,26 +2006,19 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
   guint16 ttl;
 
   tree = parent_tree;
-  iph = ep_alloc(sizeof(ws_ip));
+  iph = (ws_ip *)wmem_alloc(wmem_packet_scope(), sizeof(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;
-  }
 
   hlen = lo_nibble(iph->ip_v_hl) * 4;   /* IP header length, in bytes */
 
-  if (tree) {
-    ti = proto_tree_add_item(tree, proto_ip, tvb, offset, hlen, ENC_NA);
-    ip_tree = proto_item_add_subtree(ti, ett_ip);
+  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_uint(ip_tree, hf_ip_version, tvb, offset, 1,
-                        hi_nibble(iph->ip_v_hl));
-  }
+  proto_tree_add_item(ip_tree, hf_ip_version, tvb, offset, 1, ENC_NA);
 
   /* 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
@@ -1982,18 +2035,14 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
     col_add_fstr(pinfo->cinfo, COL_INFO,
                  "Bogus IP header length (%u, must be at least %u)",
                  hlen, IPH_MIN_LEN);
-    if (tree) {
-      proto_tree_add_uint_format(ip_tree, hf_ip_hdr_len, tvb, offset, 1, hlen,
-                                 "Header length: %u bytes (bogus, 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,
+                                 "%u bytes (bogus, must be at least %u)", hlen, IPH_MIN_LEN);
     return;
   }
 
-  if (tree) {
-    proto_tree_add_uint_format(ip_tree, hf_ip_hdr_len, tvb, offset, 1, hlen,
-                               "Header length: %u bytes", hlen);
-  }
+  proto_tree_add_uint_format_value(ip_tree, hf_ip_hdr_len, tvb, offset, 1, hlen/4,
+                               "%u bytes", hlen);
 
   iph->ip_tos = tvb_get_guint8(tvb, offset + 1);
   if (g_ip_dscp_actif) {
@@ -2003,9 +2052,8 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
 
   if (tree) {
     if (g_ip_dscp_actif) {
-      tf = proto_tree_add_uint_format(ip_tree, hf_ip_dsfield, tvb, offset + 1,
-        1, iph->ip_tos, "Differentiated Services Field: 0x%02x "
-        "(DSCP 0x%02x: %s; ECN: 0x%02x: %s)", iph->ip_tos,
+      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),
@@ -2015,9 +2063,9 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
       proto_tree_add_item(field_tree, hf_ip_dsfield_dscp, tvb, offset + 1, 1, ENC_NA);
       proto_tree_add_item(field_tree, hf_ip_dsfield_ecn, tvb, offset + 1, 1, ENC_NA);
     } else {
-      tf = proto_tree_add_uint_format(ip_tree, hf_ip_tos, tvb, offset + 1, 1,
+      tf = proto_tree_add_uint_format_value(ip_tree, hf_ip_tos, tvb, offset + 1, 1,
                                       iph->ip_tos,
-                                      "Type of service: 0x%02x (%s)",
+                                      "0x%02x (%s)",
                                       iph->ip_tos,
                                       val_to_str_const(IPTOS_TOS(iph->ip_tos),
                                                        iptos_vals, "Unknown"));
@@ -2058,14 +2106,11 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
       col_add_fstr(pinfo->cinfo, COL_INFO,
                    "Bogus IP length (%u, less than header length %u)",
                    iph->ip_len, hlen);
-      tf = NULL;
-      if (tree) {
-        tf = proto_tree_add_uint_format(ip_tree, hf_ip_len, tvb, offset + 2, 2,
+      tf = proto_tree_add_uint_format_value(ip_tree, hf_ip_len, tvb, offset + 2, 2,
           iph->ip_len,
-          "Total length: %u bytes (bogus, less than header length %u)",
+          "%u bytes (bogus, less than header length %u)",
           iph->ip_len, hlen);
-      }
-      expert_add_info_format(pinfo, tf, PI_PROTOCOL, PI_ERROR, "Bogus IP length");
+      expert_add_info(pinfo, tf, &ei_ip_bogus_ip_length);
       /* Can't dissect any further */
       return;
     }
@@ -2099,8 +2144,7 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
                                     bit_offset + 0, 1, ENC_BIG_ENDIAN);
       if (iph->ip_off & IP_RF) {
         proto_item_append_text(tf, " (Evil packet!)");
-        expert_add_info_format(pinfo, sf, PI_SECURITY, PI_WARN,
-                               "This is an Evil packet (RFC 3514)");
+        expert_add_info(pinfo, sf, &ei_ip_evil_packet);
       }
     } else {
       proto_tree_add_bits_item(field_tree, hf_ip_flags_rf, tvb, bit_offset + 0,
@@ -2119,7 +2163,6 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
   }
 
   iph->ip_ttl = tvb_get_guint8(tvb, offset + 8);
-  pinfo->ip_ttl = iph->ip_ttl;
   if (tree) {
     ttl_item = proto_tree_add_item(ip_tree, hf_ip_ttl, tvb, offset + 8, 1, ENC_BIG_ENDIAN);
   } else {
@@ -2134,17 +2177,22 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
   iph->ip_sum = tvb_get_ntohs(tvb, offset + 10);
 
   /*
-   * If we have the entire IP header available, check the checksum.
+   * If checksum checking is enabled, and we have the entire IP header
+   * available, and this isn't inside an ICMP error packet, check the
+   * checksum.
    */
-  if (ip_check_checksum && tvb_bytes_exist(tvb, offset, hlen)) {
-    ipsum = ip_checksum(tvb_get_ptr(tvb, offset, hlen), hlen);
+  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(ip_tree, hf_ip_checksum, tvb,
+        item = proto_tree_add_uint_format_value(ip_tree, hf_ip_checksum, tvb,
                                           offset + 10, 2, iph->ip_sum,
-                                          "Header checksum: 0x%04x [correct]",
+                                          "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);
@@ -2152,15 +2200,19 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
                                       offset + 10, 2, FALSE);
         PROTO_ITEM_SET_GENERATED(item);
       } else {
-        item = proto_tree_add_uint_format(ip_tree, hf_ip_checksum, tvb,
+        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,
-                                          "Header checksum: 0x%04x "
+                                          "0x%04x"
                                           "[incorrect, should be 0x%04x "
                                           "(may be caused by \"IP checksum "
                                           "offload\"?)]", iph->ip_sum,
-                                          in_cksum_shouldbe(iph->ip_sum,
-                                          ipsum));
+                                          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);
@@ -2174,18 +2226,20 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
          if (tree == NULL) then item will be NULL
          else item should be from the
          add_boolean(..., hf_ip_checksum_bad, ...) above */
-      expert_add_info_format(pinfo, item, PI_CHECKSUM, PI_ERROR,
-                             "Bad checksum");
+      expert_add_info(pinfo, item, &ei_ip_checksum_bad);
     }
   } else {
     ipsum = 0;
     if (tree) {
-      item = proto_tree_add_uint_format(ip_tree, hf_ip_checksum, tvb,
+      item = proto_tree_add_uint_format_value(ip_tree, hf_ip_checksum, tvb,
                                         offset + 10, 2, iph->ip_sum,
-                                        "Header checksum: 0x%04x [%s]",
-                                        iph->ip_sum, ip_check_checksum ?
-                                        "not all data available" :
-                                        "validation disabled");
+                                        "0x%04x [%s]",
+                                        iph->ip_sum,
+                                        ip_check_checksum ?
+                                            (pinfo->flags.in_error_pkt ?
+                                             "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);
@@ -2195,19 +2249,17 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
       PROTO_ITEM_SET_GENERATED(item);
     }
   }
-  src_addr = tvb_get_ptr(tvb, offset + IPH_SRC, 4);
   src32 = tvb_get_ntohl(tvb, offset + IPH_SRC);
-  SET_ADDRESS(&pinfo->net_src, AT_IPv4, 4, src_addr);
-  SET_ADDRESS(&pinfo->src, AT_IPv4, 4, src_addr);
-  SET_ADDRESS(&iph->ip_src, AT_IPv4, 4, src_addr);
+  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);
   if (tree) {
     const char *src_host;
 
     memcpy(&addr, iph->ip_src.data, 4);
     src_host = get_hostname(addr);
     if (ip_summary_in_tree) {
-      proto_item_append_text(ti, ", Src: %s (%s)", src_host,
-                             ip_to_str(iph->ip_src.data));
+      proto_item_append_text(ti, ", Src: %s", address_with_resolution_to_str(wmem_packet_scope(), &iph->ip_src));
     }
     proto_tree_add_ipv4(ip_tree, hf_ip_src, tvb, offset + 12, 4, addr);
     item = proto_tree_add_ipv4(ip_tree, hf_ip_addr, tvb, offset + 12, 4, addr);
@@ -2239,11 +2291,10 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
   else
     dst_off = 0;
 
-  dst_addr = tvb_get_ptr(tvb, offset + IPH_DST + dst_off, 4);
   dst32 = tvb_get_ntohl(tvb, offset + IPH_DST + dst_off);
-  SET_ADDRESS(&pinfo->net_dst, AT_IPv4, 4, dst_addr);
-  SET_ADDRESS(&pinfo->dst, AT_IPv4, 4, dst_addr);
-  SET_ADDRESS(&iph->ip_dst, AT_IPv4, 4, dst_addr);
+  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);
 
   /* 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
@@ -2256,34 +2307,35 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
   if (is_a_local_network_control_block_addr(dst32)) {
     ttl = local_network_control_block_addr_valid_ttl(dst32);
     if (ttl != iph->ip_ttl && ttl != IPLOCAL_NETWRK_CTRL_BLK_ANY_TTL) {
-      expert_add_info_format(pinfo, ttl_item, PI_SEQUENCE, PI_NOTE,
-                             "\"Time To Live\" != %d for a packet sent to the "
+      expert_add_info_format(pinfo, ttl_item, &ei_ip_ttl_lncb, "\"Time To Live\" != %d for a packet sent to the "
                              "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)) {
-    expert_add_info_format(pinfo, ttl_item, PI_SEQUENCE, PI_NOTE,
-                           "\"Time To Live\" only %u", iph->ip_ttl);
+  } else if (!is_a_multicast_addr(dst32) &&
+       /* At least BGP should appear here as well */
+       iph->ip_ttl < 5 &&
+        iph->ip_p != IP_PROTO_PIM &&
+        iph->ip_p != IP_PROTO_OSPF) {
+    expert_add_info_format(pinfo, ttl_item, &ei_ip_ttl_too_small, "\"Time To Live\" only %u", iph->ip_ttl);
   }
 
   if (tree) {
     const char *dst_host;
-    guint32 cur_rt;
 
     memcpy(&addr, iph->ip_dst.data, 4);
     dst_host = get_hostname(addr);
-    if (dst_off)
-      cur_rt = tvb_get_ipv4(tvb, offset + 16);
     if (ip_summary_in_tree) {
-      proto_item_append_text(ti, ", Dst: %s (%s)", dst_host,
-                             ip_to_str(iph->ip_dst.data));
-      if (dst_off)
-        proto_item_append_text(ti, ", Via: %s (%s)", get_hostname(cur_rt),
-                               ip_to_str((gchar *)&cur_rt));
+      proto_item_append_text(ti, ", Dst: %s", address_with_resolution_to_str(wmem_packet_scope(), &iph->ip_dst));
     }
 
     if (dst_off) {
+      guint32 cur_rt;
+
+      cur_rt = tvb_get_ipv4(tvb, offset + 16);
+      if (ip_summary_in_tree) {
+        proto_item_append_text(ti, ", Via: %s",
+            tvb_address_with_resolution_to_str(wmem_packet_scope(), tvb, AT_IPv4, offset + 16));
+      }
       proto_tree_add_ipv4(ip_tree, hf_ip_cur_rt, tvb, offset + 16, 4, cur_rt);
       item = proto_tree_add_string(ip_tree, hf_ip_cur_rt_host, tvb,
                                    offset + 16, 4, get_hostname(cur_rt));
@@ -2316,21 +2368,17 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
   if (hlen > IPH_MIN_LEN) {
     /* There's more than just the fixed-length header.  Decode the options. */
     optlen = hlen - IPH_MIN_LEN;  /* length of options, in bytes */
-    tf = proto_tree_add_text(ip_tree, tvb, offset + 20, optlen,
-                             "Options: (%u bytes)", optlen);
-    field_tree = proto_item_add_subtree(tf, ett_ip_options);
+    field_tree = proto_tree_add_subtree_format(ip_tree, tvb, offset + 20, optlen,
+                             ett_ip_options, &tf, "Options: (%u bytes)", optlen);
     dissect_ip_tcp_options(tvb, offset + 20, optlen, ipopts, N_IP_OPTS,
-                           IPOPT_EOOL, pinfo, field_tree, tf, NULL);
+                           IPOPT_EOOL, &IP_OPT_TYPES, &ei_ip_opt_len_invalid, pinfo, field_tree, tf, iph);
   }
 
-  pinfo->ipproto = iph->ip_p;
-  pinfo->iplen = iph->ip_len;
-  pinfo->iphdrlen = hlen;
+  p_add_proto_data(pinfo->pool, pinfo, proto_ip, 0, GUINT_TO_POINTER((guint)iph->ip_p));
   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
@@ -2338,13 +2386,14 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
    */
   save_fragmented = pinfo->fragmented;
   if (ip_defragment && (iph->ip_off & (IP_MF|IP_OFFSET)) &&
-      tvb_bytes_exist(tvb, offset, pinfo->iplen - pinfo->iphdrlen) &&
+      tvb_bytes_exist(tvb, offset, iph->ip_len - hlen) &&
       ipsum == 0) {
-    ipfd_head = fragment_add_check(tvb, offset, pinfo,
+    ipfd_head = fragment_add_check(&ip_reassembly_table, tvb, offset,
+                                   pinfo,
                                    iph->ip_p ^ iph->ip_id ^ src32 ^ dst32,
-                                   ip_fragment_table, ip_reassembled_table,
+                                   NULL,
                                    (iph->ip_off & IP_OFFSET) * 8,
-                                   pinfo->iplen - pinfo->iphdrlen,
+                                   iph->ip_len - hlen,
                                    iph->ip_off & IP_MF);
 
     next_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled IPv4",
@@ -2393,16 +2442,7 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
     return;
   }
 
-  /* XXX This is an ugly hack because I didn't manage to make the IPIP
-   * dissector a heuristic one [JMayer]
-   * The TAPA protocol also uses IP protocol number 4 but it isn't really
-   * IPIP, so try to detect it first and call it explicitly before calling
-   * the generic ip.proto dispatcher
-   */
-  if (nxt == IP_PROTO_IPIP && (tvb_get_guint8(next_tvb, 0) & 0xF0) != 0x40 &&
-      tvb_get_ntohs(next_tvb, 2) < 20) {
-    call_dissector(tapa_handle,next_tvb, pinfo, parent_tree);
-
+  if (tvb_reported_length(next_tvb) > 0) {
     /* Hand off to the next protocol.
 
      XXX - setting the columns only after trying various dissectors means
@@ -2410,33 +2450,61 @@ 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. */
-  } else if (!dissector_try_uint(ip_dissector_table, nxt, next_tvb, pinfo,
-                                 parent_tree)) {
-    /* Unknown protocol */
-    if (update_col_info) {
-      col_add_fstr(pinfo->cinfo, COL_INFO, "%s (%u)",
+    if (!ip_try_dissect(try_heuristic_first, 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);
+      }
+      call_dissector(data_handle,next_tvb, pinfo, parent_tree);
     }
-    call_dissector(data_handle,next_tvb, pinfo, parent_tree);
   }
   pinfo->fragmented = save_fragmented;
 }
 
+static void
+dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+  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);
+    return;
+  }
+  if(version == 6){
+    call_dissector(ipv6_handle, tvb, pinfo, tree);
+    return;
+  }
+
+  /* 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);
+}
+
 static gboolean
 dissect_ip_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
 {
     int length, tot_length;
     guint8 oct, version, ihl;
-        
+
 /*
-    0                   1                   2                   3   
-    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |Version|  IHL  |Type of Service|          Total Length         |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 */
-    length = tvb_length(tvb);
+    length = tvb_captured_length(tvb);
     if(length<4){
         /* Need at least 4 bytes to make some sort of decision */
         return FALSE;
@@ -2445,12 +2513,11 @@ 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
 
-         0                   1                   2                   3   
-         0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
+         0                   1                   2                   3
+         0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         |Version| Traffic Class |           Flow Label                  |
         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -2512,7 +2579,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);
     return TRUE;
 }
 
@@ -2528,14 +2595,14 @@ proto_register_ip(void)
   static hf_register_info hf[] = {
     { &hf_ip_version,
       { "Version", "ip.version", FT_UINT8, BASE_DEC,
-        NULL, 0x0, NULL, HFILL }},
+        NULL, 0xF0, NULL, HFILL }},
 
     { &hf_ip_hdr_len,
       { "Header Length", "ip.hdr_len", FT_UINT8, BASE_DEC,
-        NULL, 0x0, NULL, HFILL }},
+        NULL, 0x0F, NULL, HFILL }},
 
     { &hf_ip_dsfield,
-      { "Differentiated Services field", "ip.dsfield", FT_UINT8, BASE_DEC,
+      { "Differentiated Services Field", "ip.dsfield", FT_UINT8, BASE_DEC,
         NULL, 0x0, NULL, HFILL }},
 
     { &hf_ip_dsfield_dscp,
@@ -2605,19 +2672,19 @@ proto_register_ip(void)
 #ifdef HAVE_GEOIP
     { &hf_geoip_country,
       { "Source or Destination GeoIP Country", "ip.geoip.country",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_city,
       { "Source or Destination GeoIP City", "ip.geoip.city",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_org,
       { "Source or Destination GeoIP Organization", "ip.geoip.org",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_isp,
       { "Source or Destination GeoIP ISP", "ip.geoip.isp",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_asnum,
       { "Source or Destination GeoIP AS Number", "ip.geoip.asnum",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_lat,
       { "Source or Destination GeoIP Latitude", "ip.geoip.lat",
         FT_DOUBLE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
@@ -2626,19 +2693,19 @@ proto_register_ip(void)
         FT_DOUBLE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_src_country,
       { "Source GeoIP Country", "ip.geoip.src_country",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_src_city,
       { "Source GeoIP City", "ip.geoip.src_city",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_src_org,
       { "Source GeoIP Organization", "ip.geoip.src_org",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_src_isp,
       { "Source GeoIP ISP", "ip.geoip.src_isp",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_src_asnum,
       { "Source GeoIP AS Number", "ip.geoip.src_asnum",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_src_lat,
       { "Source GeoIP Latitude", "ip.geoip.src_lat",
         FT_DOUBLE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
@@ -2647,19 +2714,19 @@ proto_register_ip(void)
         FT_DOUBLE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_dst_country,
       { "Destination GeoIP Country", "ip.geoip.dst_country",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_dst_city,
       { "Destination GeoIP City", "ip.geoip.dst_city",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_dst_org,
       { "Destination GeoIP Organization", "ip.geoip.dst_org",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_dst_isp,
       { "Destination GeoIP ISP", "ip.geoip.dst_isp",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_dst_asnum,
       { "Destination GeoIP AS Number", "ip.geoip.dst_asnum",
-        FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+        FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL }},
     { &hf_geoip_dst_lat,
       { "Destination GeoIP Latitude", "ip.geoip.dst_lat",
         FT_DOUBLE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
@@ -2704,6 +2771,10 @@ proto_register_ip(void)
       { "Header checksum", "ip.checksum", FT_UINT16, BASE_HEX,
         NULL, 0x0, NULL, HFILL }},
 
+    { &hf_ip_checksum_calculated,
+    { "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 }},
@@ -2897,6 +2968,11 @@ proto_register_ip(void)
       { "Empty Route Host", "ip.empty_rt_host", FT_STRING, BASE_NONE,
         NULL, 0x0, NULL, HFILL }},
 
+    { &hf_ip_cipso_tag_type,
+      { "Tag Type", "ip.cipso.tag_type", FT_UINT8, BASE_DEC,
+        VALS(cipso_tag_type_vals), 0x0, NULL, HFILL }},
+
+
     { &hf_ip_fragment_overlap,
       { "Fragment overlap", "ip.fragment.overlap", FT_BOOLEAN, BASE_NONE,
         NULL, 0x0, "Fragment overlaps with other fragments", HFILL }},
@@ -2942,7 +3018,18 @@ proto_register_ip(void)
 
     { &hf_ip_reassembled_data,
       { "Reassembled IPv4 data", "ip.reassembled.data", FT_BYTES, BASE_NONE,
-        NULL, 0x0, "The reassembled payload", HFILL }}
+        NULL, 0x0, "The reassembled payload", HFILL }},
+
+      /* Generated from convert_proto_tree_add_text.pl */
+      { &hf_ip_cipso_doi, { "DOI", "ip.cipso.doi", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
+      { &hf_ip_cipso_sensitivity_level, { "Sensitivity Level", "ip.cipso.sensitivity_level", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
+      { &hf_ip_cipso_categories, { "Categories", "ip.cipso.categories", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+      { &hf_ip_cipso_tag_data, { "Tag data", "ip.cipso.tag_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+      { &hf_ip_opt_overflow, { "Overflow", "ip.opt.overflow", FT_UINT8, BASE_DEC, NULL, 0xF0, NULL, HFILL }},
+      { &hf_ip_opt_flag, { "Flag", "ip.opt.flag", FT_UINT8, BASE_HEX, VALS(ipopt_timestamp_flag_vals), 0x0F, NULL, HFILL }},
+      { &hf_ip_opt_time_stamp, { "Time stamp", "ip.opt.time_stamp", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
+      { &hf_ip_opt_time_stamp_addr, { "Address", "ip.opt.time_stamp_addr", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+
 };
 
   static gint *ett[] = {
@@ -2970,19 +3057,47 @@ proto_register_ip(void)
     &ett_ip_checksum,
     &ett_ip_opt_type,
     &ett_ip_opt_sec_prot_auth_flags,
+    &ett_unknown_ip_tcp_opt,
 #ifdef HAVE_GEOIP
     &ett_geoip_info
 #endif
   };
+  static ei_register_info ei[] = {
+     { &ei_ip_opt_len_invalid, { "ip.opt.len.invalid", PI_PROTOCOL, PI_WARN, "Invalid length for option", EXPFILL }},
+     { &ei_ip_opt_sec_prot_auth_fti, { "ip.opt.len.invalid", PI_PROTOCOL, PI_WARN, "Field Termination Indicator set to 1 for last byte of option", EXPFILL }},
+     { &ei_ip_extraneous_data, { "ip.opt.len.invalid", PI_PROTOCOL, PI_WARN, "Extraneous data in option", EXPFILL }},
+     { &ei_ip_opt_ptr_before_address, { "ip.opt.ptr.before_address", PI_PROTOCOL, PI_WARN, "Pointer points before first address", EXPFILL }},
+     { &ei_ip_opt_ptr_middle_address, { "ip.opt.ptr.middle_address", PI_PROTOCOL, PI_WARN, "Pointer points to middle of address", EXPFILL }},
+     { &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_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 */
+  static build_valid_func ip_da_build_value[1] = {ip_value};
+  static decode_as_value_t ip_da_values = {ip_prompt, 1, ip_da_build_value};
+  static decode_as_t ip_da = {"ip", "Network", "ip.proto", 1, 0, &ip_da_values, NULL, NULL,
+                              decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL};
+
   module_t *ip_module;
+  expert_module_t* expert_ip;
 
   proto_ip = proto_register_protocol("Internet Protocol Version 4", "IPv4", "ip");
   proto_register_field_array(proto_ip, hf, array_length(hf));
   proto_register_subtree_array(ett, array_length(ett));
+  expert_ip = expert_register_protocol(proto_ip);
+  expert_register_field_array(expert_ip, ei, array_length(ei));
 
   /* subdissector code */
-  ip_dissector_table = register_dissector_table("ip.proto", "IPv4 protocol",
+  ip_dissector_table = register_dissector_table("ip.proto", "IP protocol",
                                                 FT_UINT8, BASE_DEC);
+  heur_subdissector_list = register_heur_dissector_list("ip");
 
   /* Register configuration options */
   ip_module = prefs_register_protocol(proto_ip, NULL);
@@ -3014,10 +3129,19 @@ proto_register_ip(void)
     "Interpret Reserved flag as Security flag (RFC 3514)",
     "Whether to interpret the originally reserved flag as security flag",
     &ip_security_flag);
+  prefs_register_bool_preference(ip_module, "try_heuristic_first",
+    "Try heuristic sub-dissectors first",
+    "Try to decode a packet using an heuristic sub-dissector before using a sub-dissector registered to a specific port",
+    &try_heuristic_first);
 
   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);
 }
 
 void
@@ -3027,10 +3151,10 @@ proto_reg_handoff_ip(void)
 
   ip_handle = find_dissector("ip");
   ipv6_handle = find_dissector("ipv6");
-  tapa_handle = find_dissector("tapa");
   data_handle = find_dissector("data");
 
   dissector_add_uint("ethertype", ETHERTYPE_IP, ip_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);
   dissector_add_uint("gre.proto", ETHERTYPE_IP, ip_handle);
@@ -3038,16 +3162,23 @@ proto_reg_handoff_ip(void)
   dissector_add_uint("llc.dsap", SAP_IP, ip_handle);
   dissector_add_uint("ip.proto", IP_PROTO_IPIP, ip_handle);
   dissector_add_uint("null.type", BSD_AF_INET, ip_handle);
-  dissector_add_uint("chdlctype", ETHERTYPE_IP, ip_handle);
+  dissector_add_uint("chdlc.protocol", ETHERTYPE_IP, ip_handle);
   dissector_add_uint("osinl.excl", NLPID_IP, ip_handle);
-  dissector_add_uint("fr.ietf", NLPID_IP, ip_handle);
+  dissector_add_uint("fr.nlpid", NLPID_IP, ip_handle);
   dissector_add_uint("x.25.spi", NLPID_IP, ip_handle);
   dissector_add_uint("arcnet.protocol_id", ARCNET_PROTO_IP_1051, ip_handle);
   dissector_add_uint("arcnet.protocol_id", ARCNET_PROTO_IP_1201, ip_handle);
   dissector_add_uint("ax25.pid", AX25_P_IP, ip_handle);
-  dissector_add_handle("udp.port", ip_handle);
-
-  heur_dissector_add("tipc", dissect_ip_heur, proto_ip);
+  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("sflow_245.header_protocol", SFLOW_245_HEADER_IPv4, ip_handle);
+  dissector_add_uint("l2tp.pw_type", L2TPv3_PROTOCOL_IP, ip_handle);
+  dissector_add_for_decode_as("udp.port", ip_handle);
+  dissector_add_for_decode_as("pcli.payload", ip_handle);
+  dissector_add_uint("wtap_encap", WTAP_ENCAP_RAW_IP4, ip_handle);
+
+  heur_dissector_add("tipc", dissect_ip_heur, "IP over TIPC", "ip_tipc", proto_ip, HEURISTIC_ENABLE);
 }
 
 /*
@@ -3062,4 +3193,3 @@ proto_reg_handoff_ip(void)
  * vi: set shiftwidth=2 tabstop=8 expandtab:
  * :indentSize=2:tabSize=8:noTabs=true:
  */
-