ip: Use .api declared error counters

Type: improvement

Signed-off-by: Neale Ranns <neale@graphiant.com>
Change-Id: I822ead1495edb96ee62e53dc5920aa6c565e3621
diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt
index 76d5c06..54c986f 100644
--- a/src/vnet/CMakeLists.txt
+++ b/src/vnet/CMakeLists.txt
@@ -444,13 +444,11 @@
   ip/icmp4.h
   ip/icmp6.h
   ip/igmp_packet.h
-  ip/ip4_error.h
   ip/ip4.h
   ip/ip4_mtrie.h
   ip/ip4_inlines.h
   ip/ip4_packet.h
   ip/ip46_address.h
-  ip/ip6_error.h
   ip/ip6.h
   ip/ip6_hop_by_hop.h
   ip/ip6_hop_by_hop_packet.h
diff --git a/src/vnet/dpo/mpls_disposition.c b/src/vnet/dpo/mpls_disposition.c
index 7bc2cb6..2f99672 100644
--- a/src/vnet/dpo/mpls_disposition.c
+++ b/src/vnet/dpo/mpls_disposition.c
@@ -431,14 +431,14 @@
                                           FIB_MPLS_LSP_MODE_PIPE));
 }
 
-VLIB_REGISTER_NODE(ip4_mpls_label_disposition_pipe_node) = {
-    .name = "ip4-mpls-label-disposition-pipe",
-    .vector_size = sizeof(u32),
+VLIB_REGISTER_NODE (ip4_mpls_label_disposition_pipe_node) = {
+  .name = "ip4-mpls-label-disposition-pipe",
+  .vector_size = sizeof (u32),
 
-    .format_trace = format_mpls_label_disposition_trace,
-    .sibling_of = "ip4-input",
-    .n_errors = IP4_N_ERROR,
-    .error_strings = ip4_error_strings,
+  .format_trace = format_mpls_label_disposition_trace,
+  .sibling_of = "ip4-input",
+  .n_errors = IP4_N_ERROR,
+  .error_counters = ip4_error_counters,
 };
 
 VLIB_NODE_FN (ip6_mpls_label_disposition_pipe_node) (vlib_main_t * vm,
@@ -449,14 +449,14 @@
                                           FIB_MPLS_LSP_MODE_PIPE));
 }
 
-VLIB_REGISTER_NODE(ip6_mpls_label_disposition_pipe_node) = {
-    .name = "ip6-mpls-label-disposition-pipe",
-    .vector_size = sizeof(u32),
+VLIB_REGISTER_NODE (ip6_mpls_label_disposition_pipe_node) = {
+  .name = "ip6-mpls-label-disposition-pipe",
+  .vector_size = sizeof (u32),
 
-    .format_trace = format_mpls_label_disposition_trace,
-    .sibling_of = "ip6-input",
-    .n_errors = IP6_N_ERROR,
-    .error_strings = ip6_error_strings,
+  .format_trace = format_mpls_label_disposition_trace,
+  .sibling_of = "ip6-input",
+  .n_errors = IP6_N_ERROR,
+  .error_counters = ip6_error_counters,
 };
 
 VLIB_NODE_FN (ip4_mpls_label_disposition_uniform_node) (vlib_main_t * vm,
@@ -467,14 +467,14 @@
                                           FIB_MPLS_LSP_MODE_UNIFORM));
 }
 
-VLIB_REGISTER_NODE(ip4_mpls_label_disposition_uniform_node) = {
-    .name = "ip4-mpls-label-disposition-uniform",
-    .vector_size = sizeof(u32),
+VLIB_REGISTER_NODE (ip4_mpls_label_disposition_uniform_node) = {
+  .name = "ip4-mpls-label-disposition-uniform",
+  .vector_size = sizeof (u32),
 
-    .format_trace = format_mpls_label_disposition_trace,
-    .sibling_of = "ip4-input",
-    .n_errors = IP4_N_ERROR,
-    .error_strings = ip4_error_strings,
+  .format_trace = format_mpls_label_disposition_trace,
+  .sibling_of = "ip4-input",
+  .n_errors = IP4_N_ERROR,
+  .error_counters = ip4_error_counters,
 };
 
 VLIB_NODE_FN (ip6_mpls_label_disposition_uniform_node) (vlib_main_t * vm,
@@ -485,14 +485,14 @@
                                           FIB_MPLS_LSP_MODE_UNIFORM));
 }
 
-VLIB_REGISTER_NODE(ip6_mpls_label_disposition_uniform_node) = {
-    .name = "ip6-mpls-label-disposition-uniform",
-    .vector_size = sizeof(u32),
+VLIB_REGISTER_NODE (ip6_mpls_label_disposition_uniform_node) = {
+  .name = "ip6-mpls-label-disposition-uniform",
+  .vector_size = sizeof (u32),
 
-    .format_trace = format_mpls_label_disposition_trace,
-    .sibling_of = "ip6-input",
-    .n_errors = IP6_N_ERROR,
-    .error_strings = ip6_error_strings,
+  .format_trace = format_mpls_label_disposition_trace,
+  .sibling_of = "ip6-input",
+  .n_errors = IP6_N_ERROR,
+  .error_counters = ip6_error_counters,
 };
 
 #ifndef CLIB_MARCH_VARIANT
diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api
index cd18098..aa30350 100644
--- a/src/vnet/ip/ip.api
+++ b/src/vnet/ip/ip.api
@@ -917,6 +917,572 @@
   u32 context;
 };
 
+counters ip_frag {
+  none {
+    severity info;
+    type counter64;
+    units "packets";
+    description "packet fragmented";
+  };
+  small_packet {
+    severity error;
+    type counter64;
+    units "packets";
+    description "packet smaller than MTU";
+  };
+  fragment_sent {
+    severity info;
+    type counter64;
+    units "packets";
+    description "number of sent fragments";
+  };
+  cant_fragment_header {
+    severity error;
+    type counter64;
+    units "packets";
+    description "can't fragment header";
+  };
+  dont_fragment_set {
+    severity error;
+    type counter64;
+    units "packets";
+    description "can't fragment this packet";
+  };
+  malformed {
+    severity error;
+    type counter64;
+    units "packets";
+    description "malformed packet";
+  };
+  memory {
+    severity error;
+    type counter64;
+    units "packets";
+    description "could not allocate buffer";
+  };
+  unknown {
+    severity error;
+    type counter64;
+    units "packets";
+    description "unknown error";
+  };
+};
+
+counters ip4 {
+  /* Must be first. */
+  none {
+    severity info;
+    type counter64;
+    units "packets";
+    description "valid ip4 packets";
+  };
+
+  /* Errors signalled by ip4-input */
+  too_short {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 length < 20 bytes";
+  };
+  bad_length {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 length > l2 length";
+  };
+  bad_checksum {
+    severity error;
+    type counter64;
+    units "packets";
+    description "bad ip4 checksum";
+  };
+  version {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 version != 4";
+  };
+  options {
+    severity info;
+    type counter64;
+    units "packets";
+    description "ip4 options present";
+  };
+  fragment_offset_one {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 fragment offset == 1";
+  };
+  time_expired {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 ttl <= 1";
+  };
+
+  /* Errors signalled by ip4-rewrite. */
+  mtu_exceeded {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 MTU exceeded and DF set";
+  };
+  dst_lookup_miss {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 destination lookup miss";
+  };
+  src_lookup_miss {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 source lookup miss";
+  };
+  drop {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 drop";
+  };
+  punt {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 punt";
+  };
+  same_interface {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 egress interface same as ingress";
+  };
+
+  /* errors signalled by ip4-local. */
+  unknown_protocol {
+    severity error;
+    type counter64;
+    units "packets";
+    description "unknown ip protocol";
+  };
+  tcp_checksum {
+    severity error;
+    type counter64;
+    units "packets";
+    description "bad tcp checksum";
+  };
+  udp_checksum {
+    severity error;
+    type counter64;
+    units "packets";
+    description "bad udp checksum";
+  };
+  udp_length {
+    severity error;
+    type counter64;
+    units "packets";
+    description "inconsistent udp/ip lengths";
+  };
+
+  /* spoofed packets in ip4-rewrite-local */
+  spoofed_local_packets {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip4 spoofed local-address packet drops";
+  };
+
+  /* Errors signalled by ip4-inacl */
+  inacl_table_miss {
+    severity error;
+    type counter64;
+    units "packets";
+    description "input ACL table-miss drops";
+  };
+  inacl_session_deny {
+    severity error;
+    type counter64;
+    units "packets";
+    description "input ACL session deny drops";
+  };
+
+  /* Errors singalled by ip4-outacl */
+  outacl_table_miss {
+    severity error;
+    type counter64;
+    units "packets";
+    description "output ACL table-miss drops";
+  };
+  outacl_session_deny {
+    severity error;
+    type counter64;
+    units "packets";
+    description "output ACL session deny drops";
+  };
+
+  /* Errors from mfib-forward */
+  rpf_failure {
+    severity error;
+    type counter64;
+    units "packets";
+    description "Multicast RPF check failed";
+  };
+
+  /* Errors signalled by ip4-reassembly */
+  reass_duplicate_fragment {
+    severity error;
+    type counter64;
+    units "packets";
+    description "duplicate/overlapping fragments";
+  };
+  reass_limit_reached {
+    severity error;
+    type counter64;
+    units "packets";
+    description "drops due to concurrent reassemblies limit";
+  };
+  reass_fragment_chain_too_long {
+    severity error;
+    type counter64;
+    units "packets";
+    description "fragment chain too long (drop)";
+  };
+  reass_no_buf {
+    severity error;
+    type counter64;
+    units "packets";
+    description "out of buffers (drop)";
+  };
+  reass_malformed_packet {
+    severity error;
+    type counter64;
+    units "packets";
+    description "malformed packets";
+  };
+  reass_internal_error {
+    severity error;
+    type counter64;
+    units "packets";
+    description "drops due to internal reassembly error";
+  };
+  reass_timeout {
+    severity error;
+    type counter64;
+    units "packets";
+    description "fragments dropped due to reassembly timeout";
+  };
+  reass_to_custom_app {
+    severity error;
+    type counter64;
+    units "packets";
+    description "send to custom drop app";
+  };
+  reass_success {
+    severity info;
+    type counter64;
+    units "packets";
+    description "successful reassemblies";
+  };
+  reass_fragments_reassembled {
+    severity info;
+    type counter64;
+    units "packets";
+    description "fragments reassembled";
+  };
+  reass_fragments_rcvd {
+    severity info;
+    type counter64;
+    units "packets";
+    description "fragments received";
+  };
+  reass_unsupp_ip_prot {
+    severity error;
+    type counter64;
+    units "packets";
+    description "unsupported ip protocol";
+  };
+};
+
+/**
+ * IPv6 Error/info counters
+ */
+counters ip6 {
+  /* Must be first. */
+  none {
+    severity info;
+    type counter64;
+    units "packets";
+    description "valid ip6 packets";
+  };
+
+  /* Errors signalled by ip6-input */
+  too_short {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip6 length < 40 bytes";
+  };
+  bad_length {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip6 length > l2 length";
+  };
+  version {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip6 version != 6";
+  };
+  time_expired {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip6 ttl <= 1";
+  };
+
+  /* Errors signalled by ip6-rewrite. */
+  mtu_exceeded {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip6 MTU exceeded";
+  };
+  dst_lookup_miss {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip6 destination lookup miss";
+  };
+  src_lookup_miss {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip6 source lookup miss";
+  };
+  drop {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip6 drop";
+  };
+  punt {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip6 punt";
+  };
+
+  /* errors signalled by ip6-local. */
+  unknown_protocol {
+    severity error;
+    type counter64;
+    units "packets";
+    description "unknown ip protocol";
+  };
+  udp_checksum {
+    severity error;
+    type counter64;
+    units "packets";
+    description "bad udp checksum";
+  };
+  icmp_checksum {
+    severity error;
+    type counter64;
+    units "packets";
+    description "bad icmp checksum";
+  };
+  udp_length {
+    severity error;
+    type counter64;
+    units "packets";
+    description "inconsistent udp/ip lengths";
+  };
+  /* Errors signalled by udp6-lookup. */
+  unknown_udp_port {
+    severity error;
+    type counter64;
+    units "packets";
+    description "no listener for udp port";
+  };
+
+  /* spoofed packets in ip6-rewrite-local */
+  spoofed_local_packets {
+    severity error;
+    type counter64;
+    units "packets";
+    description "ip6 spoofed local-address packet drops";
+  };
+
+  /* Errors signalled by ip6-inacl */
+  inacl_table_miss {
+    severity error;
+    type counter64;
+    units "packets";
+    description "input ACL table-miss drops";
+  };
+  inacl_session_deny {
+    severity error;
+    type counter64;
+    units "packets";
+    description "input ACL session deny drops";
+  };
+
+  /* Errors singalled by ip6-outacl */
+  outacl_table_miss {
+    severity error;
+    type counter64;
+    units "packets";
+    description "output ACL table-miss drops";
+  };
+  outacl_session_deny {
+    severity error;
+    type counter64;
+    units "packets";
+    description "output ACL session deny drops";
+  };
+
+  /* Errors from mfib-forward */
+  rpf_failure {
+    severity error;
+    type counter64;
+    units "packets";
+    description "Multicast RPF check failed";
+  };
+
+  /* Errors signalled by ip6-reassembly */
+  reass_missing_upper {
+    severity error;
+    type counter64;
+    units "packets";
+    description "missing-upper layer drops";
+  };
+  reass_duplicate_fragment {
+    severity error;
+    type counter64;
+    units "packets";
+    description "duplicate fragments";
+  };
+  reass_overlapping_fragment {
+    severity error;
+    type counter64;
+    units "packets";
+    description "overlapping fragments";
+  };
+  reass_limit_reached {
+    severity error;
+    type counter64;
+    units "packets";
+    description "drops due to concurrent reassemblies limit";
+  };
+  reass_fragment_chain_too_long {
+    severity error;
+    type counter64;
+    units "packets";
+    description "fragment chain too long (drop)";
+  };
+  reass_no_buf {
+    severity error;
+    type counter64;
+    units "packets";
+    description "out of buffers (drop)";
+  };
+  reass_timeout {
+    severity error;
+    type counter64;
+    units "packets";
+    description "fragments dropped due to reassembly timeout";
+  };
+  reass_internal_error {
+    severity error;
+    type counter64;
+    units "packets";
+    description "drops due to internal reassembly error";
+  };
+  reass_invalid_frag_len {
+    severity error;
+    type counter64;
+    units "packets";
+    description "invalid fragment length";
+  };
+  reass_to_custom_app {
+    severity error;
+    type counter64;
+    units "packets";
+    description "send to custom drop app";
+  };
+  reass_no_frag_hdr {
+    severity error;
+    type counter64;
+    units "packets";
+    description "no fragmentation header";
+  };
+  reass_invalid_frag_size {
+    severity error;
+    type counter64;
+    units "packets";
+    description "drop due to invalid fragment size";
+  };
+  reass_success {
+    severity info;
+    type counter64;
+    units "packets";
+    description "successful reassemblies";
+  };
+  reass_fragments_reassembled {
+    severity info;
+    type counter64;
+    units "packets";
+    description "fragments reassembled";
+  };
+  reass_fragments_rcvd {
+    severity info;
+    type counter64;
+    units "packets";
+    description "fragments received";
+  };
+  reass_unsupp_ip_proto {
+    severity error;
+    type counter64;
+    units "packets";
+    description "unsupported ip protocol";
+  };
+};
+
+paths {
+  "/err/ip-frag" "ip_frag";
+  "/err/mpls-frag" "ip_frag";
+  "/err/ip4-mpls-label-disposition-pipe" "ip4";
+  "/err/ip4-mpls-label-disposition-uniform" "ip4";
+  "/err/ip4-local" "ip4";
+  "/err/ip4-input" "ip4";
+  "/err/ip4-full-reassembly" "ip4";
+  "/err/ip4-local-full-reassembly" "ip4";
+  "/err/ip4-full-reassembly-feature" "ip4";
+  "/err/ip4-full-reassembly-custom" "ip4";
+  "/err/ip4-full-reassembly-expire-walk" "ip4";
+  "/err/ip4-sv-reassembly" "ip4";
+  "/err/ip4-sv-reassembly-feature" "ip4";
+  "/err/ip4-sv-reassembly-output-feature" "ip4";
+  "/err/ip4-sv-reassembly-custom-next" "ip4";
+  "/err/ip4-sv-reassembly-expire-walk" "ip4";
+  "/err/ip6-mpls-label-disposition-pipe" "ip6";
+  "/err/ip6-mpls-label-disposition-uniform" "ip6";
+  "/err/ip6-local" "ip6";
+  "/err/ip6-input" "ip6";
+  "/err/ip6-full-reassembly" "ip6";
+  "/err/ip6-local-full-reassembly" "ip6";
+  "/err/ip6-full-reassembly-feature" "ip6";
+  "/err/ip6-full-reassembly-custom" "ip6";
+  "/err/ip6-full-reassembly-expire-walk" "ip6";
+  "/err/ip6-sv-reassembly" "ip6";
+  "/err/ip6-sv-reassembly-feature" "ip6";
+  "/err/ip6-sv-reassembly-output-feature" "ip6";
+  "/err/ip6-sv-reassembly-custom-next" "ip6";
+  "/err/ip6-sv-reassembly-expire-walk" "ip6";
+};
+
 /*
  * Local Variables:
  * eval: (c-set-style "gnu")
diff --git a/src/vnet/ip/ip.h b/src/vnet/ip/ip.h
index c2a3801..9ebefa0 100644
--- a/src/vnet/ip/ip.h
+++ b/src/vnet/ip/ip.h
@@ -51,19 +51,18 @@
 #include <vnet/ip/ip_packet.h>
 #include <vnet/ip/lookup.h>
 #include <vnet/ip/ip_interface.h>
+#include <vnet/ip/ip.api_enum.h>
 
 #include <vnet/tcp/tcp_packet.h>
 #include <vnet/udp/udp_packet.h>
 #include <vnet/ip/icmp46_packet.h>
 
 #include <vnet/ip/ip4.h>
-#include <vnet/ip/ip4_error.h>
 #include <vnet/ip/ip4_packet.h>
 #include <vnet/ip/icmp4.h>
 
 #include <vnet/ip/ip6.h>
 #include <vnet/ip/ip6_packet.h>
-#include <vnet/ip/ip6_error.h>
 #include <vnet/ip/icmp6.h>
 
 /* Per protocol info. */
diff --git a/src/vnet/ip/ip4.h b/src/vnet/ip/ip4.h
index dde7b7b..e969594 100644
--- a/src/vnet/ip/ip4.h
+++ b/src/vnet/ip/ip4.h
@@ -169,7 +169,6 @@
 
 /** Global ip4 main structure. */
 extern ip4_main_t ip4_main;
-extern char *ip4_error_strings[];
 
 /** Global ip4 input node.  Errors get attached to ip4 input node. */
 extern vlib_node_registration_t ip4_input_node;
diff --git a/src/vnet/ip/ip4_error.h b/src/vnet/ip/ip4_error.h
deleted file mode 100644
index 187b079..0000000
--- a/src/vnet/ip/ip4_error.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * ip/ip4_error.h: ip4 fast path errors
- *
- * Copyright (c) 2008 Eliot Dresselhaus
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef included_ip_ip4_error_h
-#define included_ip_ip4_error_h
-
-#define foreach_ip4_error                                                     \
-  /* Must be first. */                                                        \
-  _ (NONE, "valid ip4 packets")                                               \
-                                                                              \
-  /* Errors signalled by ip4-input */                                         \
-  _ (TOO_SHORT, "ip4 length < 20 bytes")                                      \
-  _ (BAD_LENGTH, "ip4 length > l2 length")                                    \
-  _ (BAD_CHECKSUM, "bad ip4 checksum")                                        \
-  _ (VERSION, "ip4 version != 4")                                             \
-  _ (OPTIONS, "ip4 options present")                                          \
-  _ (FRAGMENT_OFFSET_ONE, "ip4 fragment offset == 1")                         \
-  _ (TIME_EXPIRED, "ip4 ttl <= 1")                                            \
-                                                                              \
-  /* Errors signalled by ip4-rewrite. */                                      \
-  _ (MTU_EXCEEDED, "ip4 MTU exceeded and DF set")                             \
-  _ (DST_LOOKUP_MISS, "ip4 destination lookup miss")                          \
-  _ (SRC_LOOKUP_MISS, "ip4 source lookup miss")                               \
-  _ (DROP, "ip4 drop")                                                        \
-  _ (PUNT, "ip4 punt")                                                        \
-  _ (SAME_INTERFACE, "ip4 egress interface same as ingress")                  \
-                                                                              \
-  /* Errors signalled by ip4-local. */                                        \
-  _ (UNKNOWN_PROTOCOL, "unknown ip protocol")                                 \
-  _ (TCP_CHECKSUM, "bad tcp checksum")                                        \
-  _ (UDP_CHECKSUM, "bad udp checksum")                                        \
-  _ (UDP_LENGTH, "inconsistent udp/ip lengths")                               \
-                                                                              \
-  /* Spoofed packets in ip4-rewrite-local */                                  \
-  _ (SPOOFED_LOCAL_PACKETS, "ip4 spoofed local-address packet drops")         \
-                                                                              \
-  /* Errors signalled by ip4-inacl */                                         \
-  _ (INACL_TABLE_MISS, "input ACL table-miss drops")                          \
-  _ (INACL_SESSION_DENY, "input ACL session deny drops")                      \
-  /* Errors singalled by ip4-outacl */                                        \
-  _ (OUTACL_TABLE_MISS, "output ACL table-miss drops")                        \
-  _ (OUTACL_SESSION_DENY, "output ACL session deny drops")                    \
-                                                                              \
-  /* Errors from mfib-forward */                                              \
-  _ (RPF_FAILURE, "Multicast RPF check failed")                               \
-                                                                              \
-  /* Errors signalled by ip4-reassembly */                                    \
-  _ (REASS_DUPLICATE_FRAGMENT, "duplicate/overlapping fragments")             \
-  _ (REASS_LIMIT_REACHED, "drops due to concurrent reassemblies limit")       \
-  _ (REASS_FRAGMENT_CHAIN_TOO_LONG, "fragment chain too long (drop)")         \
-  _ (REASS_NO_BUF, "out of buffers (drop)")                                   \
-  _ (REASS_MALFORMED_PACKET, "malformed packets")                             \
-  _ (REASS_INTERNAL_ERROR, "drops due to internal reassembly error")          \
-  _ (REASS_TIMEOUT, "fragments dropped due to reassembly timeout")            \
-  _ (REASS_TO_CUSTOM_APP, "send to custom drop app")                          \
-  _ (REASS_SUCCESS, "successful reassemblies")                                \
-  _ (REASS_FRAGMENTS_REASSEMBLED, "fragments reassembled")                    \
-  _ (REASS_FRAGMENTS_RCVD, "fragments received")                              \
-  _ (REASS_UNSUPP_IP_PROT, "unsupported ip protocol")
-
-typedef enum
-{
-#define _(sym,str) IP4_ERROR_##sym,
-  foreach_ip4_error
-#undef _
-    IP4_N_ERROR,
-} ip4_error_t;
-
-#endif /* included_ip_ip4_error_h */
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/vnet/ip/ip4_forward.c b/src/vnet/ip/ip4_forward.c
index 33ab341..826fa57 100644
--- a/src/vnet/ip/ip4_forward.c
+++ b/src/vnet/ip/ip4_forward.c
@@ -1880,7 +1880,7 @@
   .vector_size = sizeof (u32),
   .format_trace = format_ip4_forward_next_trace,
   .n_errors = IP4_N_ERROR,
-  .error_strings = ip4_error_strings,
+  .error_counters = ip4_error_counters,
   .n_next_nodes = IP_LOCAL_N_NEXT,
   .next_nodes =
   {
diff --git a/src/vnet/ip/ip4_input.c b/src/vnet/ip/ip4_input.c
index 0e8d22e..436e52f 100644
--- a/src/vnet/ip/ip4_input.c
+++ b/src/vnet/ip/ip4_input.c
@@ -374,14 +374,6 @@
   return ip4_input_inline (vm, node, frame, /* verify_checksum */ 0);
 }
 
-#ifndef CLIB_MARCH_VARIANT
-char *ip4_error_strings[] = {
-#define _(sym,string) string,
-  foreach_ip4_error
-#undef _
-};
-#endif
-
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (ip4_input_node) = {
   .name = "ip4-input",
@@ -389,7 +381,7 @@
   .protocol_hint = VLIB_NODE_PROTO_HINT_IP4,
 
   .n_errors = IP4_N_ERROR,
-  .error_strings = ip4_error_strings,
+  .error_counters = ip4_error_counters,
 
   .n_next_nodes = IP4_INPUT_N_NEXT,
   .next_nodes = {
diff --git a/src/vnet/ip/ip6_error.h b/src/vnet/ip/ip6_error.h
deleted file mode 100644
index 207d040..0000000
--- a/src/vnet/ip/ip6_error.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * ip/ip6_error.h: ip6 fast path errors
- *
- * Copyright (c) 2008 Eliot Dresselhaus
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef included_ip_ip6_error_h
-#define included_ip_ip6_error_h
-
-// clang-format off
-#define foreach_ip6_error                                               \
-  /* Must be first. */                                                  \
-  _ (NONE, "valid ip6 packets")                                         \
-                                                                        \
-  /* Errors signalled by ip6-input */                                   \
-  _ (TOO_SHORT, "ip6 length < 40 bytes")                                \
-  _ (BAD_LENGTH, "ip6 length > l2 length")                              \
-  _ (VERSION, "ip6 version != 6")                                       \
-  _ (TIME_EXPIRED, "ip6 ttl <= 1")                                      \
-                                                                        \
-  /* Errors signalled by ip6-rewrite. */                                \
-  _ (MTU_EXCEEDED, "ip6 MTU exceeded")                                  \
-  _ (DST_LOOKUP_MISS, "ip6 destination lookup miss")                    \
-  _ (SRC_LOOKUP_MISS, "ip6 source lookup miss")                         \
-  _ (DROP, "ip6 drop")                                                  \
-  _ (PUNT, "ip6 punt")                                                  \
-                                                                        \
-  /* Errors signalled by ip6-local. */                                  \
-  _ (UNKNOWN_PROTOCOL, "unknown ip protocol")                           \
-  _ (UDP_CHECKSUM, "bad udp checksum")                                  \
-  _ (ICMP_CHECKSUM, "bad icmp checksum")                                \
-  _ (UDP_LENGTH, "inconsistent udp/ip lengths")                         \
-                                                                        \
-  /* Errors signalled by udp6-lookup. */				\
-  _ (UNKNOWN_UDP_PORT, "no listener for udp port")                      \
-                                                                        \
-  /* Spoofed packets in ip6-rewrite-local */                            \
-  _(SPOOFED_LOCAL_PACKETS, "ip4 spoofed local-address packet drops")    \
-                                                                        \
- /* Erros singalled by ip6-inacl */                                     \
-  _ (INACL_TABLE_MISS, "input ACL table-miss drops")                    \
-  _ (INACL_SESSION_DENY, "input ACL session deny drops")                \
- /* Erros singalled by ip6-outacl */                                    \
-  _ (OUTACL_TABLE_MISS, "output ACL table-miss drops")                  \
-  _ (OUTACL_SESSION_DENY, "output ACL session deny drops")              \
-                                                                        \
-  /* Errors from mfib-forward */                                        \
-  _ (RPF_FAILURE, "Multicast RPF check failed")                         \
-                                                                        \
-  /* Errors signalled by ip6-reassembly */                              \
-  _ (REASS_MISSING_UPPER, "missing-upper layer drops")                  \
-  _ (REASS_DUPLICATE_FRAGMENT, "duplicate fragments")                   \
-  _ (REASS_OVERLAPPING_FRAGMENT, "overlapping fragments")               \
-  _ (REASS_LIMIT_REACHED, "drops due to concurrent reassemblies limit") \
-  _ (REASS_FRAGMENT_CHAIN_TOO_LONG, "fragment chain too long (drop)")   \
-  _ (REASS_NO_BUF, "out of buffers (drop)")                             \
-  _ (REASS_TIMEOUT, "fragments dropped due to reassembly timeout")      \
-  _ (REASS_INTERNAL_ERROR, "drops due to internal reassembly error")    \
-  _ (REASS_INVALID_FRAG_LEN, "invalid fragment length")                 \
-  _ (REASS_TO_CUSTOM_APP, "send to custom drop app")                    \
-  _ (REASS_NO_FRAG_HDR, "no fragmentation header")                      \
-  _ (REASS_INVALID_FRAG_SIZE, "drop due to invalid fragment size")      \
-  _ (REASS_SUCCESS, "successful reassemblies")                          \
-  _ (REASS_FRAGMENTS_REASSEMBLED, "fragments reassembled")              \
-  _ (REASS_FRAGMENTS_RCVD, "fragments received")                        \
-  _ (REASS_UNSUPP_IP_PROTO, "unsupported ip protocol")
-
-// clang-format on
-
-typedef enum
-{
-#define _(sym,str) IP6_ERROR_##sym,
-  foreach_ip6_error
-#undef _
-    IP6_N_ERROR,
-} ip6_error_t;
-
-#endif /* included_ip_ip6_error_h */
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/vnet/ip/ip6_forward.c b/src/vnet/ip/ip6_forward.c
index 44b0052..84ea5a0 100644
--- a/src/vnet/ip/ip6_forward.c
+++ b/src/vnet/ip/ip6_forward.c
@@ -1319,7 +1319,7 @@
 	  vlib_prefetch_buffer_data (b[3], LOAD);
 	}
 
-      ip6_error_t error[2];
+      vl_counter_ip6_enum_t error[2];
       error[0] = IP6_ERROR_UNKNOWN_PROTOCOL;
       error[1] = IP6_ERROR_UNKNOWN_PROTOCOL;
 
@@ -1671,6 +1671,8 @@
   .name = "ip6-local",
   .vector_size = sizeof (u32),
   .format_trace = format_ip6_forward_next_trace,
+  .n_errors = IP6_N_ERROR,
+  .error_counters = ip6_error_counters,
   .n_next_nodes = IP_LOCAL_N_NEXT,
   .next_nodes =
   {
@@ -2242,19 +2244,20 @@
 }
 
 /* *INDENT-OFF* */
-VLIB_REGISTER_NODE (ip6_midchain_node) =
-{
+VLIB_REGISTER_NODE (ip6_midchain_node) = {
   .name = "ip6-midchain",
   .vector_size = sizeof (u32),
   .format_trace = format_ip6_forward_next_trace,
   .sibling_of = "ip6-rewrite",
-  };
+};
 
 VLIB_REGISTER_NODE (ip6_rewrite_node) =
 {
   .name = "ip6-rewrite",
   .vector_size = sizeof (u32),
   .format_trace = format_ip6_rewrite_trace,
+  .n_errors = IP6_N_ERROR,
+  .error_counters = ip6_error_counters,
   .n_next_nodes = IP6_REWRITE_N_NEXT,
   .next_nodes =
   {
diff --git a/src/vnet/ip/ip6_input.c b/src/vnet/ip/ip6_input.c
index 01b8f46..8d89890 100644
--- a/src/vnet/ip/ip6_input.c
+++ b/src/vnet/ip/ip6_input.c
@@ -219,21 +219,13 @@
   return frame->n_vectors;
 }
 
-#ifndef CLIB_MARCH_VARIANT
-char *ip6_error_strings[] = {
-#define _(sym,string) string,
-  foreach_ip6_error
-#undef _
-};
-#endif /* CLIB_MARCH_VARIANT */
-
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (ip6_input_node) = {
   .name = "ip6-input",
   .vector_size = sizeof (u32),
 
   .n_errors = IP6_N_ERROR,
-  .error_strings = ip6_error_strings,
+  .error_counters = ip6_error_counters,
 
   .n_next_nodes = IP6_INPUT_N_NEXT,
   .next_nodes = {
diff --git a/src/vnet/ip/ip6_input.h b/src/vnet/ip/ip6_input.h
index fe993ca..49e37ec 100644
--- a/src/vnet/ip/ip6_input.h
+++ b/src/vnet/ip/ip6_input.h
@@ -43,8 +43,6 @@
 #include <vnet/ip/ip.h>
 #include <vnet/ip/icmp6.h>
 
-extern char *ip6_error_strings[];
-
 typedef enum
 {
   IP6_INPUT_NEXT_DROP,
diff --git a/src/vnet/ip/ip_frag.c b/src/vnet/ip/ip_frag.c
index 9560011..5e8d368 100644
--- a/src/vnet/ip/ip_frag.c
+++ b/src/vnet/ip/ip_frag.c
@@ -500,12 +500,6 @@
   return IP_FRAG_ERROR_NONE;
 }
 
-char *ip4_frag_error_strings[] = {
-#define _(sym,string) string,
-  foreach_ip_frag_error
-#undef _
-};
-
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (ip4_frag_node) = {
   .function = ip4_frag,
@@ -515,17 +509,15 @@
   .type = VLIB_NODE_TYPE_INTERNAL,
 
   .n_errors = IP_FRAG_N_ERROR,
-  .error_strings = ip4_frag_error_strings,
+  .error_counters = ip_frag_error_counters,
 
   .n_next_nodes = IP_FRAG_N_NEXT,
-  .next_nodes = {
-    [IP_FRAG_NEXT_IP_REWRITE] = "ip4-rewrite",
-    [IP_FRAG_NEXT_IP_REWRITE_MIDCHAIN] = "ip4-midchain",
-    [IP_FRAG_NEXT_IP4_LOOKUP] = "ip4-lookup",
-    [IP_FRAG_NEXT_IP6_LOOKUP] = "ip6-lookup",
-    [IP_FRAG_NEXT_ICMP_ERROR] = "ip4-icmp-error",
-    [IP_FRAG_NEXT_DROP] = "ip4-drop"
-  },
+  .next_nodes = { [IP_FRAG_NEXT_IP_REWRITE] = "ip4-rewrite",
+		  [IP_FRAG_NEXT_IP_REWRITE_MIDCHAIN] = "ip4-midchain",
+		  [IP_FRAG_NEXT_IP4_LOOKUP] = "ip4-lookup",
+		  [IP_FRAG_NEXT_IP6_LOOKUP] = "ip6-lookup",
+		  [IP_FRAG_NEXT_ICMP_ERROR] = "ip4-icmp-error",
+		  [IP_FRAG_NEXT_DROP] = "ip4-drop" },
 };
 /* *INDENT-ON* */
 
@@ -538,17 +530,15 @@
   .type = VLIB_NODE_TYPE_INTERNAL,
 
   .n_errors = IP_FRAG_N_ERROR,
-  .error_strings = ip4_frag_error_strings,
+  .error_counters = ip_frag_error_counters,
 
   .n_next_nodes = IP_FRAG_N_NEXT,
-  .next_nodes = {
-    [IP_FRAG_NEXT_IP_REWRITE] = "ip6-rewrite",
-    [IP_FRAG_NEXT_IP_REWRITE_MIDCHAIN] = "ip6-midchain",
-    [IP_FRAG_NEXT_IP4_LOOKUP] = "ip4-lookup",
-    [IP_FRAG_NEXT_IP6_LOOKUP] = "ip6-lookup",
-    [IP_FRAG_NEXT_ICMP_ERROR] = "error-drop",
-    [IP_FRAG_NEXT_DROP] = "ip6-drop"
-  },
+  .next_nodes = { [IP_FRAG_NEXT_IP_REWRITE] = "ip6-rewrite",
+		  [IP_FRAG_NEXT_IP_REWRITE_MIDCHAIN] = "ip6-midchain",
+		  [IP_FRAG_NEXT_IP4_LOOKUP] = "ip4-lookup",
+		  [IP_FRAG_NEXT_IP6_LOOKUP] = "ip6-lookup",
+		  [IP_FRAG_NEXT_ICMP_ERROR] = "error-drop",
+		  [IP_FRAG_NEXT_DROP] = "ip6-drop" },
 };
 /* *INDENT-ON* */
 
diff --git a/src/vnet/ip/ip_frag.h b/src/vnet/ip/ip_frag.h
index ac562c9..4ddd62b 100644
--- a/src/vnet/ip/ip_frag.h
+++ b/src/vnet/ip/ip_frag.h
@@ -36,6 +36,7 @@
 #define IP_FRAG_H
 
 #include <vnet/vnet.h>
+#include <vnet/ip/ip.api_enum.h>
 
 #define IP_FRAG_FLAG_IP4_HEADER 0x01	//Encapsulating IPv4 header
 #define IP_FRAG_FLAG_IP6_HEADER 0x02	//Encapsulating IPv6 header
@@ -57,26 +58,7 @@
   IP_FRAG_N_NEXT
 } ip_frag_next_t;
 
-#define foreach_ip_frag_error				\
-  /* Must be first. */					\
- _(NONE, "packet fragmented")				\
- _(SMALL_PACKET, "packet smaller than MTU")             \
- _(FRAGMENT_SENT, "number of sent fragments")           \
- _(CANT_FRAGMENT_HEADER, "can't fragment header")	\
- _(DONT_FRAGMENT_SET, "can't fragment this packet")	\
- _(MALFORMED, "malformed packet")                       \
- _(MEMORY, "could not allocate buffer")                 \
- _(UNKNOWN, "unknown error")
-
-typedef enum
-{
-#define _(sym,str) IP_FRAG_ERROR_##sym,
-  foreach_ip_frag_error
-#undef _
-    IP_FRAG_N_ERROR,
-} ip_frag_error_t;
-
-extern char *ip4_frag_error_strings[];
+typedef vl_counter_ip_frag_enum_t ip_frag_error_t;
 
 void ip_frag_set_vnet_buffer (vlib_buffer_t * b, u16 mtu,
 			      u8 next_index, u8 flags);
diff --git a/src/vnet/ip/ip_path_mtu_node.c b/src/vnet/ip/ip_path_mtu_node.c
index b3681b9..cadf1cb 100644
--- a/src/vnet/ip/ip_path_mtu_node.c
+++ b/src/vnet/ip/ip_path_mtu_node.c
@@ -174,7 +174,7 @@
   .vector_size = sizeof (u32),
   .format_trace = format_ip_pmtu_trace,
   .n_errors = IP_FRAG_N_ERROR,
-  .error_strings = ip4_frag_error_strings,
+  .error_counters = ip_frag_error_counters,
   .n_next_nodes = IP_PMTU_N_NEXT,
   .next_nodes =
   {
@@ -186,7 +186,7 @@
   .vector_size = sizeof (u32),
   .format_trace = format_ip_pmtu_trace,
   .n_errors = IP_FRAG_N_ERROR,
-  .error_strings = ip4_frag_error_strings,
+  .error_counters = ip_frag_error_counters,
   .n_next_nodes = IP_PMTU_N_NEXT,
   .next_nodes =
   {
diff --git a/src/vnet/ip/reass/ip4_full_reass.c b/src/vnet/ip/reass/ip4_full_reass.c
index f6e9e36..3183560 100644
--- a/src/vnet/ip/reass/ip4_full_reass.c
+++ b/src/vnet/ip/reass/ip4_full_reass.c
@@ -23,6 +23,7 @@
 #include <vppinfra/vec.h>
 #include <vnet/vnet.h>
 #include <vnet/ip/ip.h>
+#include <vnet/ip/ip.api_enum.h>
 #include <vppinfra/fifo.h>
 #include <vppinfra/bihash_16_8.h>
 #include <vnet/ip/reass/ip4_full_reass.h>
@@ -1387,12 +1388,6 @@
   return frame->n_vectors;
 }
 
-static char *ip4_full_reass_error_strings[] = {
-#define _(sym, string) string,
-  foreach_ip4_error
-#undef _
-};
-
 VLIB_NODE_FN (ip4_full_reass_node) (vlib_main_t * vm,
 				    vlib_node_runtime_t * node,
 				    vlib_frame_t * frame)
@@ -1404,8 +1399,8 @@
     .name = "ip4-full-reassembly",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
-    .error_strings = ip4_full_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_FULL_REASS_N_NEXT,
     .next_nodes =
         {
@@ -1426,8 +1421,8 @@
     .name = "ip4-local-full-reassembly",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
-    .error_strings = ip4_full_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_FULL_REASS_N_NEXT,
     .next_nodes =
         {
@@ -1450,8 +1445,8 @@
     .name = "ip4-full-reassembly-feature",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
-    .error_strings = ip4_full_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_FULL_REASS_N_NEXT,
     .next_nodes =
         {
@@ -1480,8 +1475,8 @@
     .name = "ip4-full-reassembly-custom",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
-    .error_strings = ip4_full_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_FULL_REASS_N_NEXT,
     .next_nodes =
         {
@@ -1760,13 +1755,12 @@
 }
 
 VLIB_REGISTER_NODE (ip4_full_reass_expire_node) = {
-    .function = ip4_full_reass_walk_expired,
-    .type = VLIB_NODE_TYPE_PROCESS,
-    .name = "ip4-full-reassembly-expire-walk",
-    .format_trace = format_ip4_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
-    .error_strings = ip4_full_reass_error_strings,
-
+  .function = ip4_full_reass_walk_expired,
+  .type = VLIB_NODE_TYPE_PROCESS,
+  .name = "ip4-full-reassembly-expire-walk",
+  .format_trace = format_ip4_full_reass_trace,
+  .n_errors = IP4_N_ERROR,
+  .error_counters = ip4_error_counters,
 };
 
 static u8 *
diff --git a/src/vnet/ip/reass/ip4_sv_reass.c b/src/vnet/ip/reass/ip4_sv_reass.c
index dfe3f57..4ef144e 100644
--- a/src/vnet/ip/reass/ip4_sv_reass.c
+++ b/src/vnet/ip/reass/ip4_sv_reass.c
@@ -955,12 +955,6 @@
   return frame->n_vectors;
 }
 
-static char *ip4_sv_reass_error_strings[] = {
-#define _(sym, string) string,
-  foreach_ip4_error
-#undef _
-};
-
 VLIB_NODE_FN (ip4_sv_reass_node) (vlib_main_t * vm,
 				  vlib_node_runtime_t * node,
 				  vlib_frame_t * frame)
@@ -975,8 +969,8 @@
     .name = "ip4-sv-reassembly",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_sv_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_sv_reass_error_strings),
-    .error_strings = ip4_sv_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_SV_REASSEMBLY_N_NEXT,
     .next_nodes =
         {
@@ -1002,8 +996,8 @@
     .name = "ip4-sv-reassembly-feature",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_sv_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_sv_reass_error_strings),
-    .error_strings = ip4_sv_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_SV_REASSEMBLY_N_NEXT,
     .next_nodes =
         {
@@ -1038,8 +1032,8 @@
     .name = "ip4-sv-reassembly-output-feature",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_sv_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_sv_reass_error_strings),
-    .error_strings = ip4_sv_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_SV_REASSEMBLY_N_NEXT,
     .next_nodes =
         {
@@ -1064,8 +1058,8 @@
     .name = "ip4-sv-reassembly-custom-next",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_sv_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_sv_reass_error_strings),
-    .error_strings = ip4_sv_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_SV_REASSEMBLY_N_NEXT,
     .next_nodes =
         {
@@ -1313,13 +1307,12 @@
 
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (ip4_sv_reass_expire_node) = {
-    .function = ip4_sv_reass_walk_expired,
-    .type = VLIB_NODE_TYPE_PROCESS,
-    .name = "ip4-sv-reassembly-expire-walk",
-    .format_trace = format_ip4_sv_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_sv_reass_error_strings),
-    .error_strings = ip4_sv_reass_error_strings,
-
+  .function = ip4_sv_reass_walk_expired,
+  .type = VLIB_NODE_TYPE_PROCESS,
+  .name = "ip4-sv-reassembly-expire-walk",
+  .format_trace = format_ip4_sv_reass_trace,
+  .n_errors = IP4_N_ERROR,
+  .error_counters = ip4_error_counters,
 };
 /* *INDENT-ON* */
 
diff --git a/src/vnet/ip/reass/ip6_full_reass.c b/src/vnet/ip/reass/ip6_full_reass.c
index 7419334..9781557 100644
--- a/src/vnet/ip/reass/ip6_full_reass.c
+++ b/src/vnet/ip/reass/ip6_full_reass.c
@@ -1448,12 +1448,6 @@
   return frame->n_vectors;
 }
 
-static char *ip6_full_reassembly_error_strings[] = {
-#define _(sym, string) string,
-  foreach_ip6_error
-#undef _
-};
-
 VLIB_NODE_FN (ip6_full_reass_node) (vlib_main_t * vm,
 				    vlib_node_runtime_t * node,
 				    vlib_frame_t * frame)
@@ -1467,8 +1461,8 @@
     .name = "ip6-full-reassembly",
     .vector_size = sizeof (u32),
     .format_trace = format_ip6_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip6_full_reassembly_error_strings),
-    .error_strings = ip6_full_reassembly_error_strings,
+    .n_errors = IP6_N_ERROR,
+    .error_counters = ip6_error_counters,
     .n_next_nodes = IP6_FULL_REASSEMBLY_N_NEXT,
     .next_nodes =
         {
@@ -1491,8 +1485,8 @@
     .name = "ip6-local-full-reassembly",
     .vector_size = sizeof (u32),
     .format_trace = format_ip6_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip6_full_reassembly_error_strings),
-    .error_strings = ip6_full_reassembly_error_strings,
+    .n_errors = IP6_N_ERROR,
+    .error_counters = ip6_error_counters,
     .n_next_nodes = IP6_FULL_REASSEMBLY_N_NEXT,
     .next_nodes =
         {
@@ -1516,8 +1510,8 @@
     .name = "ip6-full-reassembly-feature",
     .vector_size = sizeof (u32),
     .format_trace = format_ip6_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip6_full_reassembly_error_strings),
-    .error_strings = ip6_full_reassembly_error_strings,
+    .n_errors = IP6_N_ERROR,
+    .error_counters = ip6_error_counters,
     .n_next_nodes = IP6_FULL_REASSEMBLY_N_NEXT,
     .next_nodes =
         {
@@ -1548,8 +1542,8 @@
     .name = "ip6-full-reassembly-custom",
     .vector_size = sizeof (u32),
     .format_trace = format_ip6_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip6_full_reassembly_error_strings),
-    .error_strings = ip6_full_reassembly_error_strings,
+    .n_errors = IP6_N_ERROR,
+    .error_counters = ip6_error_counters,
     .n_next_nodes = IP6_FULL_REASSEMBLY_N_NEXT,
     .next_nodes =
         {
@@ -1851,14 +1845,13 @@
 }
 
 VLIB_REGISTER_NODE (ip6_full_reass_expire_node) = {
-    .function = ip6_full_reass_walk_expired,
-    .format_trace = format_ip6_full_reass_trace,
-    .type = VLIB_NODE_TYPE_PROCESS,
-    .name = "ip6-full-reassembly-expire-walk",
+  .function = ip6_full_reass_walk_expired,
+  .format_trace = format_ip6_full_reass_trace,
+  .type = VLIB_NODE_TYPE_PROCESS,
+  .name = "ip6-full-reassembly-expire-walk",
 
-    .n_errors = ARRAY_LEN (ip6_full_reassembly_error_strings),
-    .error_strings = ip6_full_reassembly_error_strings,
-
+  .n_errors = IP6_N_ERROR,
+  .error_counters = ip6_error_counters,
 };
 
 static u8 *
diff --git a/src/vnet/ip/reass/ip6_sv_reass.c b/src/vnet/ip/reass/ip6_sv_reass.c
index e1493c9..c7f64ca 100644
--- a/src/vnet/ip/reass/ip6_sv_reass.c
+++ b/src/vnet/ip/reass/ip6_sv_reass.c
@@ -763,12 +763,6 @@
   return frame->n_vectors;
 }
 
-static char *ip6_sv_reassembly_error_strings[] = {
-#define _(sym, string) string,
-  foreach_ip6_error
-#undef _
-};
-
 VLIB_NODE_FN (ip6_sv_reass_node) (vlib_main_t * vm,
 				  vlib_node_runtime_t * node,
 				  vlib_frame_t * frame)
@@ -781,8 +775,8 @@
     .name = "ip6-sv-reassembly",
     .vector_size = sizeof (u32),
     .format_trace = format_ip6_sv_reass_trace,
-    .n_errors = ARRAY_LEN (ip6_sv_reassembly_error_strings),
-    .error_strings = ip6_sv_reassembly_error_strings,
+    .n_errors = IP6_N_ERROR,
+    .error_counters = ip6_error_counters,
     .n_next_nodes = IP6_SV_REASSEMBLY_N_NEXT,
     .next_nodes =
         {
@@ -806,8 +800,8 @@
     .name = "ip6-sv-reassembly-feature",
     .vector_size = sizeof (u32),
     .format_trace = format_ip6_sv_reass_trace,
-    .n_errors = ARRAY_LEN (ip6_sv_reassembly_error_strings),
-    .error_strings = ip6_sv_reassembly_error_strings,
+    .n_errors = IP6_N_ERROR,
+    .error_counters = ip6_error_counters,
     .n_next_nodes = IP6_SV_REASSEMBLY_N_NEXT,
     .next_nodes =
         {
@@ -1060,14 +1054,13 @@
 
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (ip6_sv_reass_expire_node) = {
-    .function = ip6_sv_reass_walk_expired,
-    .format_trace = format_ip6_sv_reass_trace,
-    .type = VLIB_NODE_TYPE_PROCESS,
-    .name = "ip6-sv-reassembly-expire-walk",
+  .function = ip6_sv_reass_walk_expired,
+  .format_trace = format_ip6_sv_reass_trace,
+  .type = VLIB_NODE_TYPE_PROCESS,
+  .name = "ip6-sv-reassembly-expire-walk",
 
-    .n_errors = ARRAY_LEN (ip6_sv_reassembly_error_strings),
-    .error_strings = ip6_sv_reassembly_error_strings,
-
+  .n_errors = IP6_N_ERROR,
+  .error_counters = ip6_error_counters,
 };
 /* *INDENT-ON* */
 
diff --git a/src/vnet/mpls/mpls_output.c b/src/vnet/mpls/mpls_output.c
index d606360..3ea6ce5 100644
--- a/src/vnet/mpls/mpls_output.c
+++ b/src/vnet/mpls/mpls_output.c
@@ -363,12 +363,6 @@
   .format_trace = format_mpls_output_trace,
 };
 
-static char *mpls_frag_error_strings[] = {
-#define _(sym,string) string,
-  foreach_ip_frag_error
-#undef _
-};
-
 typedef struct mpls_frag_trace_t_
 {
     u16 pkt_size;
@@ -541,7 +535,7 @@
   .type = VLIB_NODE_TYPE_INTERNAL,
 
   .n_errors = IP_FRAG_N_ERROR,
-  .error_strings = mpls_frag_error_strings,
+  .error_counters = ip_frag_error_counters,
 
   .n_next_nodes = MPLS_FRAG_N_NEXT,
   .next_nodes = { [MPLS_FRAG_NEXT_REWRITE] = "mpls-output",
diff --git a/test/test_gre.py b/test/test_gre.py
index a79819e..cfba559 100644
--- a/test/test_gre.py
+++ b/test/test_gre.py
@@ -58,7 +58,7 @@
         self.pg0.add_stream(pkt)
         self.pg_start()
         # no tunnel created, gre-input not registered
-        err = self.statistics.get_counter("/err/ip4-local/unknown ip protocol")[0]
+        err = self.statistics.get_counter("/err/ip4-local/unknown_protocol")[0]
         self.assertEqual(err, 1)
         err_count = err
 
@@ -69,7 +69,7 @@
         self.pg0.add_stream(pkt)
         self.pg_start()
         # tunnel created, gre-input registered
-        err = self.statistics.get_counter("/err/ip4-local/unknown ip protocol")[0]
+        err = self.statistics.get_counter("/err/ip4-local/unknown_protocol")[0]
         # expect no new errors
         self.assertEqual(err, err_count)
 
diff --git a/test/test_ip4.py b/test/test_ip4.py
index 9079e54..736d8f7 100644
--- a/test/test_ip4.py
+++ b/test/test_ip4.py
@@ -1793,9 +1793,7 @@
         self.send_and_assert_no_replies(self.pg0, [pkts[0]])
         self.send_and_assert_no_replies(self.pg0, pkts)
 
-        self.assert_error_counter_equal(
-            "/err/ip4-local/ip4 source lookup miss", len(pkts) + 1
-        )
+        self.assert_error_counter_equal("/err/ip4-local/src_lookup_miss", len(pkts) + 1)
 
         # using the same source in different tables, should reject
         # for the table that the source is not present in
@@ -1855,9 +1853,7 @@
         self.send_and_assert_no_replies(self.pg0, [pkts[0]])
         self.send_and_assert_no_replies(self.pg0, pkts)
 
-        self.assert_error_counter_equal(
-            "/err/ip6-input/ip6 source lookup miss", len(pkts) + 1
-        )
+        self.assert_error_counter_equal("/err/ip6-input/src_lookup_miss", len(pkts) + 1)
 
         # using the same source in different tables, should reject
         # for the table that the source is not present in
diff --git a/test/test_ip_mcast.py b/test/test_ip_mcast.py
index c3ac16c..b060e97 100644
--- a/test/test_ip_mcast.py
+++ b/test/test_ip_mcast.py
@@ -222,9 +222,7 @@
         self.pg0.assert_nothing_captured(
             remark="IP multicast packets forwarded on default route"
         )
-        count = self.statistics.get_err_counter(
-            "/err/ip4-input/Multicast RPF check failed"
-        )
+        count = self.statistics.get_err_counter("/err/ip4-input/rpf_failure")
         self.assertEqual(count, len(tx))
 
         #
@@ -577,9 +575,7 @@
         self.vapi.cli("clear trace")
         tx = self.create_stream_ip6(self.pg1, "2002::1", "ff01:2::255")
         self.send_and_assert_no_replies(self.pg1, tx, "RPF miss")
-        count = self.statistics.get_err_counter(
-            "/err/ip6-input/Multicast RPF check failed"
-        )
+        count = self.statistics.get_err_counter("/err/ip6-input/rpf_failure")
         self.assertEqual(count, 2 * len(tx))
 
         #
diff --git a/test/test_mpls.py b/test/test_mpls.py
index 6e01a03..8461797 100644
--- a/test/test_mpls.py
+++ b/test/test_mpls.py
@@ -1148,9 +1148,7 @@
             self.assertEqual(rx[ICMP].nexthopmtu, 9000 - 4)
 
         self.assertEqual(
-            self.statistics.get_err_counter(
-                "/err/mpls-frag/can't fragment this packet"
-            ),
+            self.statistics.get_err_counter("/err/mpls-frag/dont_fragment_set"),
             len(tx),
         )
         #
diff --git a/test/test_reassembly.py b/test/test_reassembly.py
index cebe583..cef94f3 100644
--- a/test/test_reassembly.py
+++ b/test/test_reassembly.py
@@ -235,9 +235,7 @@
     def test_long_fragment_chain(self):
         """long fragment chain"""
 
-        error_cnt_str = (
-            "/err/ip4-full-reassembly-feature/fragment chain too long (drop)"
-        )
+        error_cnt_str = "/err/ip4-full-reassembly-feature/reass_fragment_chain_too_long"
 
         error_cnt = self.statistics.get_err_counter(error_cnt_str)
 
@@ -286,7 +284,7 @@
         )
         valid_fragments = fragment_rfc791(p, 400)
 
-        counter = "/err/ip4-full-reassembly-feature/malformed packets"
+        counter = "/err/ip4-full-reassembly-feature/reass_malformed_packet"
         error_counter = self.statistics.get_err_counter(counter)
         self.pg_enable_capture()
         self.src_if.add_stream([malformed_packet] + valid_fragments)
@@ -394,7 +392,7 @@
         # TODO remove above, uncomment below once clearing of counters
         # is supported
         # self.assert_packet_counter_equal(
-        #     "/err/ip4-full-reassembly-feature/malformed packets", 1)
+        #     "/err/ip4-full-reassembly-feature/reass_malformed_packet", 1)
 
     def test_random(self):
         """random order reassembly"""
@@ -1394,9 +1392,7 @@
     def test_long_fragment_chain(self):
         """long fragment chain"""
 
-        error_cnt_str = (
-            "/err/ip6-full-reassembly-feature/fragment chain too long (drop)"
-        )
+        error_cnt_str = "/err/ip6-full-reassembly-feature/reass_fragment_chain_too_long"
 
         error_cnt = self.statistics.get_err_counter(error_cnt_str)