IPv6 Classify Forwarding Graph errors
[vpp.git] / vnet / vnet / ip / ip4_forward.c
index 4c49d0e..d69a3c3 100644 (file)
 #include <vnet/api_errno.h>     /* for API error numbers */
 #include <vnet/fib/fib_table.h> /* for FIB table and entry creation */
 #include <vnet/fib/fib_entry.h> /* for FIB table and entry creation */
+#include <vnet/fib/fib_urpf_list.h> /* for FIB uRPF check */
 #include <vnet/fib/ip4_fib.h>
 #include <vnet/dpo/load_balance.h>
 #include <vnet/dpo/classify_dpo.h>
 
+/**
+ * @file
+ * @brief IPv4 Forwarding.
+ *
+ * This file contains the source code for IPv4 forwarding.
+ */
+
 void
 ip4_forward_next_trace (vlib_main_t * vm,
                         vlib_node_runtime_t * node,
@@ -77,144 +85,222 @@ ip4_lookup_inline (vlib_main_t * vm,
       vlib_get_next_frame (vm, node, next,
                           to_next, n_left_to_next);
 
-      while (n_left_from >= 4 && n_left_to_next >= 2)
+      while (n_left_from >= 8 && n_left_to_next >= 4)
        {
-         vlib_buffer_t * p0, * p1;
-         ip4_header_t * ip0, * ip1;
-         __attribute__((unused)) tcp_header_t * tcp0, * tcp1;
-         ip_lookup_next_t next0, next1;
-         const load_balance_t * lb0, * lb1;
-         ip4_fib_mtrie_t * mtrie0, * mtrie1;
-         ip4_fib_mtrie_leaf_t leaf0, leaf1;
-         ip4_address_t * dst_addr0, *dst_addr1;
+         vlib_buffer_t * p0, * p1, * p2, * p3;
+         ip4_header_t * ip0, * ip1, * ip2, * ip3;
+         __attribute__((unused)) tcp_header_t * tcp0, * tcp1, * tcp2, * tcp3;
+         ip_lookup_next_t next0, next1, next2, next3;
+         const load_balance_t * lb0, * lb1, * lb2, * lb3;
+         ip4_fib_mtrie_t * mtrie0, * mtrie1, * mtrie2, * mtrie3;
+         ip4_fib_mtrie_leaf_t leaf0, leaf1, leaf2, leaf3;
+         ip4_address_t * dst_addr0, *dst_addr1, *dst_addr2, *dst_addr3;
          __attribute__((unused)) u32 pi0, fib_index0, lb_index0, is_tcp_udp0;
          __attribute__((unused)) u32 pi1, fib_index1, lb_index1, is_tcp_udp1;
+         __attribute__((unused)) u32 pi2, fib_index2, lb_index2, is_tcp_udp2;
+         __attribute__((unused)) u32 pi3, fib_index3, lb_index3, is_tcp_udp3;
           flow_hash_config_t flow_hash_config0, flow_hash_config1;
-          u32 hash_c0, hash_c1;
-         u32 wrong_next;
-         const dpo_id_t *dpo0, *dpo1;
+          flow_hash_config_t flow_hash_config2, flow_hash_config3;
+          u32 hash_c0, hash_c1, hash_c2, hash_c3;
+         const dpo_id_t *dpo0, *dpo1, *dpo2, *dpo3;
 
          /* Prefetch next iteration. */
          {
-           vlib_buffer_t * p2, * p3;
-
-           p2 = vlib_get_buffer (vm, from[2]);
-           p3 = vlib_get_buffer (vm, from[3]);
-
-           vlib_prefetch_buffer_header (p2, LOAD);
-           vlib_prefetch_buffer_header (p3, LOAD);
-
-           CLIB_PREFETCH (p2->data, sizeof (ip0[0]), LOAD);
-           CLIB_PREFETCH (p3->data, sizeof (ip0[0]), LOAD);
+            vlib_buffer_t * p4, * p5, * p6, * p7;
+
+           p4 = vlib_get_buffer (vm, from[4]);
+           p5 = vlib_get_buffer (vm, from[5]);
+           p6 = vlib_get_buffer (vm, from[6]);
+           p7 = vlib_get_buffer (vm, from[7]);
+
+           vlib_prefetch_buffer_header (p4, LOAD);
+           vlib_prefetch_buffer_header (p5, LOAD);
+           vlib_prefetch_buffer_header (p6, LOAD);
+           vlib_prefetch_buffer_header (p7, LOAD);
+
+           CLIB_PREFETCH (p4->data, sizeof (ip0[0]), LOAD);
+           CLIB_PREFETCH (p5->data, sizeof (ip0[0]), LOAD);
+           CLIB_PREFETCH (p6->data, sizeof (ip0[0]), LOAD);
+           CLIB_PREFETCH (p7->data, sizeof (ip0[0]), LOAD);
          }
 
          pi0 = to_next[0] = from[0];
          pi1 = to_next[1] = from[1];
+         pi2 = to_next[2] = from[2];
+         pi3 = to_next[3] = from[3];
+
+         from += 4;
+         to_next += 4;
+         n_left_to_next -= 4;
+         n_left_from -= 4;
 
          p0 = vlib_get_buffer (vm, pi0);
          p1 = vlib_get_buffer (vm, pi1);
+         p2 = vlib_get_buffer (vm, pi2);
+         p3 = vlib_get_buffer (vm, pi3);
 
          ip0 = vlib_buffer_get_current (p0);
          ip1 = vlib_buffer_get_current (p1);
+         ip2 = vlib_buffer_get_current (p2);
+         ip3 = vlib_buffer_get_current (p3);
 
          dst_addr0 = &ip0->dst_address;
          dst_addr1 = &ip1->dst_address;
+         dst_addr2 = &ip2->dst_address;
+         dst_addr3 = &ip3->dst_address;
 
          fib_index0 = vec_elt (im->fib_index_by_sw_if_index, vnet_buffer (p0)->sw_if_index[VLIB_RX]);
          fib_index1 = vec_elt (im->fib_index_by_sw_if_index, vnet_buffer (p1)->sw_if_index[VLIB_RX]);
+         fib_index2 = vec_elt (im->fib_index_by_sw_if_index, vnet_buffer (p2)->sw_if_index[VLIB_RX]);
+         fib_index3 = vec_elt (im->fib_index_by_sw_if_index, vnet_buffer (p3)->sw_if_index[VLIB_RX]);
           fib_index0 = (vnet_buffer(p0)->sw_if_index[VLIB_TX] == (u32)~0) ?
             fib_index0 : vnet_buffer(p0)->sw_if_index[VLIB_TX];
           fib_index1 = (vnet_buffer(p1)->sw_if_index[VLIB_TX] == (u32)~0) ?
             fib_index1 : vnet_buffer(p1)->sw_if_index[VLIB_TX];
+          fib_index2 = (vnet_buffer(p2)->sw_if_index[VLIB_TX] == (u32)~0) ?
+            fib_index2 : vnet_buffer(p2)->sw_if_index[VLIB_TX];
+          fib_index3 = (vnet_buffer(p3)->sw_if_index[VLIB_TX] == (u32)~0) ?
+            fib_index3 : vnet_buffer(p3)->sw_if_index[VLIB_TX];
 
 
          if (! lookup_for_responses_to_locally_received_packets)
            {
              mtrie0 = &ip4_fib_get (fib_index0)->mtrie;
              mtrie1 = &ip4_fib_get (fib_index1)->mtrie;
+             mtrie2 = &ip4_fib_get (fib_index2)->mtrie;
+             mtrie3 = &ip4_fib_get (fib_index3)->mtrie;
 
-             leaf0 = leaf1 = IP4_FIB_MTRIE_LEAF_ROOT;
+             leaf0 = leaf1 = leaf2 = leaf3 = IP4_FIB_MTRIE_LEAF_ROOT;
 
              leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, dst_addr0, 0);
              leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, dst_addr1, 0);
+             leaf2 = ip4_fib_mtrie_lookup_step (mtrie2, leaf2, dst_addr2, 0);
+             leaf3 = ip4_fib_mtrie_lookup_step (mtrie3, leaf3, dst_addr3, 0);
            }
 
          tcp0 = (void *) (ip0 + 1);
          tcp1 = (void *) (ip1 + 1);
+         tcp2 = (void *) (ip2 + 1);
+         tcp3 = (void *) (ip3 + 1);
 
          is_tcp_udp0 = (ip0->protocol == IP_PROTOCOL_TCP
                         || ip0->protocol == IP_PROTOCOL_UDP);
          is_tcp_udp1 = (ip1->protocol == IP_PROTOCOL_TCP
                         || ip1->protocol == IP_PROTOCOL_UDP);
+         is_tcp_udp2 = (ip2->protocol == IP_PROTOCOL_TCP
+                        || ip2->protocol == IP_PROTOCOL_UDP);
+         is_tcp_udp3 = (ip1->protocol == IP_PROTOCOL_TCP
+                        || ip1->protocol == IP_PROTOCOL_UDP);
 
          if (! lookup_for_responses_to_locally_received_packets)
            {
              leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, dst_addr0, 1);
              leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, dst_addr1, 1);
+             leaf2 = ip4_fib_mtrie_lookup_step (mtrie2, leaf2, dst_addr2, 1);
+             leaf3 = ip4_fib_mtrie_lookup_step (mtrie3, leaf3, dst_addr3, 1);
            }
 
          if (! lookup_for_responses_to_locally_received_packets)
            {
              leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, dst_addr0, 2);
              leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, dst_addr1, 2);
+             leaf2 = ip4_fib_mtrie_lookup_step (mtrie2, leaf2, dst_addr2, 2);
+             leaf3 = ip4_fib_mtrie_lookup_step (mtrie3, leaf3, dst_addr3, 2);
            }
 
          if (! lookup_for_responses_to_locally_received_packets)
            {
              leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, dst_addr0, 3);
              leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1, dst_addr1, 3);
+             leaf2 = ip4_fib_mtrie_lookup_step (mtrie2, leaf2, dst_addr2, 3);
+             leaf3 = ip4_fib_mtrie_lookup_step (mtrie3, leaf3, dst_addr3, 3);
            }
 
          if (lookup_for_responses_to_locally_received_packets)
            {
              lb_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_RX];
              lb_index1 = vnet_buffer (p1)->ip.adj_index[VLIB_RX];
+             lb_index2 = vnet_buffer (p2)->ip.adj_index[VLIB_RX];
+             lb_index3 = vnet_buffer (p3)->ip.adj_index[VLIB_RX];
            }
          else
            {
              /* Handle default route. */
              leaf0 = (leaf0 == IP4_FIB_MTRIE_LEAF_EMPTY ? mtrie0->default_leaf : leaf0);
              leaf1 = (leaf1 == IP4_FIB_MTRIE_LEAF_EMPTY ? mtrie1->default_leaf : leaf1);
-
+             leaf2 = (leaf2 == IP4_FIB_MTRIE_LEAF_EMPTY ? mtrie2->default_leaf : leaf2);
+             leaf3 = (leaf3 == IP4_FIB_MTRIE_LEAF_EMPTY ? mtrie3->default_leaf : leaf3);
              lb_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
              lb_index1 = ip4_fib_mtrie_leaf_get_adj_index (leaf1);
+             lb_index2 = ip4_fib_mtrie_leaf_get_adj_index (leaf2);
+             lb_index3 = ip4_fib_mtrie_leaf_get_adj_index (leaf3);
            }
 
          lb0 = load_balance_get (lb_index0);
          lb1 = load_balance_get (lb_index1);
+         lb2 = load_balance_get (lb_index2);
+         lb3 = load_balance_get (lb_index3);
 
          /* Use flow hash to compute multipath adjacency. */
           hash_c0 = vnet_buffer (p0)->ip.flow_hash = 0;
           hash_c1 = vnet_buffer (p1)->ip.flow_hash = 0;
+          hash_c2 = vnet_buffer (p2)->ip.flow_hash = 0;
+          hash_c3 = vnet_buffer (p3)->ip.flow_hash = 0;
           if (PREDICT_FALSE (lb0->lb_n_buckets > 1))
             {
               flow_hash_config0 = lb0->lb_hash_config;
               hash_c0 = vnet_buffer (p0)->ip.flow_hash =
                 ip4_compute_flow_hash (ip0, flow_hash_config0);
             }
-          if (PREDICT_FALSE(lb0->lb_n_buckets > 1))
+          if (PREDICT_FALSE(lb1->lb_n_buckets > 1))
             {
              flow_hash_config1 = lb1->lb_hash_config;
               hash_c1 = vnet_buffer (p1)->ip.flow_hash =
                 ip4_compute_flow_hash (ip1, flow_hash_config1);
             }
+          if (PREDICT_FALSE (lb2->lb_n_buckets > 1))
+            {
+              flow_hash_config2 = lb2->lb_hash_config;
+              hash_c2 = vnet_buffer (p2)->ip.flow_hash =
+                ip4_compute_flow_hash (ip2, flow_hash_config2);
+            }
+          if (PREDICT_FALSE(lb3->lb_n_buckets > 1))
+            {
+             flow_hash_config3 = lb3->lb_hash_config;
+              hash_c3 = vnet_buffer (p3)->ip.flow_hash =
+                ip4_compute_flow_hash (ip3, flow_hash_config3);
+            }
 
          ASSERT (lb0->lb_n_buckets > 0);
          ASSERT (is_pow2 (lb0->lb_n_buckets));
          ASSERT (lb1->lb_n_buckets > 0);
          ASSERT (is_pow2 (lb1->lb_n_buckets));
+         ASSERT (lb2->lb_n_buckets > 0);
+         ASSERT (is_pow2 (lb2->lb_n_buckets));
+         ASSERT (lb3->lb_n_buckets > 0);
+         ASSERT (is_pow2 (lb3->lb_n_buckets));
 
          dpo0 = load_balance_get_bucket_i(lb0,
                                            (hash_c0 &
                                             (lb0->lb_n_buckets_minus_1)));
          dpo1 = load_balance_get_bucket_i(lb1,
                                            (hash_c1 &
-                                            (lb0->lb_n_buckets_minus_1)));
+                                            (lb1->lb_n_buckets_minus_1)));
+         dpo2 = load_balance_get_bucket_i(lb2,
+                                           (hash_c2 &
+                                            (lb2->lb_n_buckets_minus_1)));
+         dpo3 = load_balance_get_bucket_i(lb3,
+                                           (hash_c3 &
+                                            (lb3->lb_n_buckets_minus_1)));
 
          next0 = dpo0->dpoi_next_node;
          vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
          next1 = dpo1->dpoi_next_node;
          vnet_buffer (p1)->ip.adj_index[VLIB_TX] = dpo1->dpoi_index;
+         next2 = dpo2->dpoi_next_node;
+         vnet_buffer (p2)->ip.adj_index[VLIB_TX] = dpo2->dpoi_index;
+         next3 = dpo3->dpoi_next_node;
+         vnet_buffer (p3)->ip.adj_index[VLIB_TX] = dpo3->dpoi_index;
 
           vlib_increment_combined_counter
               (cm, cpu_index, lb_index0, 1,
@@ -224,49 +310,21 @@ ip4_lookup_inline (vlib_main_t * vm,
               (cm, cpu_index, lb_index1, 1,
                vlib_buffer_length_in_chain (vm, p1)
                + sizeof(ethernet_header_t));
+          vlib_increment_combined_counter
+              (cm, cpu_index, lb_index2, 1,
+               vlib_buffer_length_in_chain (vm, p2)
+               + sizeof(ethernet_header_t));
+          vlib_increment_combined_counter
+              (cm, cpu_index, lb_index3, 1,
+               vlib_buffer_length_in_chain (vm, p3)
+               + sizeof(ethernet_header_t));
 
-         from += 2;
-         to_next += 2;
-         n_left_to_next -= 2;
-         n_left_from -= 2;
+         vlib_validate_buffer_enqueue_x4 (vm, node, next,
+                                          to_next, n_left_to_next,
+                                          pi0, pi1, pi2, pi3,
+                                           next0, next1, next2, next3);
+        }
 
-         wrong_next = (next0 != next) + 2*(next1 != next);
-         if (PREDICT_FALSE (wrong_next != 0))
-           {
-             switch (wrong_next)
-               {
-               case 1:
-                 /* A B A */
-                 to_next[-2] = pi1;
-                 to_next -= 1;
-                 n_left_to_next += 1;
-                 vlib_set_next_frame_buffer (vm, node, next0, pi0);
-                 break;
-
-               case 2:
-                 /* A A B */
-                 to_next -= 1;
-                 n_left_to_next += 1;
-                 vlib_set_next_frame_buffer (vm, node, next1, pi1);
-                 break;
-
-               case 3:
-                 /* A B C */
-                 to_next -= 2;
-                 n_left_to_next += 2;
-                 vlib_set_next_frame_buffer (vm, node, next0, pi0);
-                 vlib_set_next_frame_buffer (vm, node, next1, pi1);
-                 if (next0 == next1)
-                   {
-                     /* A B B */
-                     vlib_put_next_frame (vm, node, next, n_left_to_next);
-                     next = next1;
-                     vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
-                   }
-               }
-           }
-       }
-    
       while (n_left_from > 0 && n_left_to_next > 0)
        {
          vlib_buffer_t * p0;
@@ -335,7 +393,7 @@ ip4_lookup_inline (vlib_main_t * vm,
             {
              flow_hash_config0 = lb0->lb_hash_config;
 
-              hash_c0 = vnet_buffer (p0)->ip.flow_hash = 
+              hash_c0 = vnet_buffer (p0)->ip.flow_hash =
                 ip4_compute_flow_hash (ip0, flow_hash_config0);
             }
 
@@ -349,7 +407,7 @@ ip4_lookup_inline (vlib_main_t * vm,
          next0 = dpo0->dpoi_next_node;
          vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
 
-         vlib_increment_combined_counter 
+         vlib_increment_combined_counter
               (cm, cpu_index, lbi0, 1,
                vlib_buffer_length_in_chain (vm, p0));
 
@@ -458,7 +516,7 @@ ip4_load_balance (vlib_main_t * vm,
       vlib_get_next_frame (vm, node, next,
                           to_next, n_left_to_next);
 
-    
+
       while (n_left_from > 0 && n_left_to_next > 0)
        {
          ip_lookup_next_t next0;
@@ -480,14 +538,14 @@ ip4_load_balance (vlib_main_t * vm,
          hc0 = lb0->lb_hash_config;
          vnet_buffer(p0)->ip.flow_hash = ip4_compute_flow_hash(ip0, hc0);
 
-         dpo0 = load_balance_get_bucket_i(lb0, 
+         dpo0 = load_balance_get_bucket_i(lb0,
                                           vnet_buffer(p0)->ip.flow_hash &
                                           (lb0->lb_n_buckets_minus_1));
 
          next0 = dpo0->dpoi_next_node;
          vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
 
-         vlib_increment_combined_counter 
+         vlib_increment_combined_counter
               (cm, cpu_index, lbi0, 1,
                vlib_buffer_length_in_chain (vm, p0));
 
@@ -537,7 +595,7 @@ ip4_interface_first_address (ip4_main_t * im, u32 sw_if_index,
   ip_interface_address_t * ia = 0;
   ip4_address_t * result = 0;
 
-  foreach_ip_interface_address (lm, ia, sw_if_index, 
+  foreach_ip_interface_address (lm, ia, sw_if_index,
                                 1 /* honor unnumbered */,
   ({
     ip4_address_t * a = ip_interface_address_get_address (lm, ia);
@@ -591,12 +649,12 @@ ip4_add_interface_routes (u32 sw_if_index,
          lm->classify_table_index_by_sw_if_index [sw_if_index];
       if (classify_table_index != (u32) ~0)
       {
-          dpo_id_t dpo = DPO_NULL;
+          dpo_id_t dpo = DPO_INVALID;
 
           dpo_set(&dpo,
                   DPO_CLASSIFY,
                   DPO_PROTO_IP4,
-                  classify_dpo_create(FIB_PROTOCOL_IP4,
+                  classify_dpo_create(DPO_PROTO_IP4,
                                       classify_table_index));
 
          fib_table_entry_special_dpo_add(fib_index,
@@ -732,7 +790,7 @@ ip4_add_del_interface_address_internal (vlib_main_t * vm,
       /* When adding an address check that it does not conflict
         with an existing address. */
       ip_interface_address_t * ia;
-      foreach_ip_interface_address (&im->lookup_main, ia, sw_if_index, 
+      foreach_ip_interface_address (&im->lookup_main, ia, sw_if_index,
                                     0 /* honor unnumbered */,
       ({
        ip4_address_t * x = ip_interface_address_get_address (&im->lookup_main, ia);
@@ -757,7 +815,7 @@ ip4_add_del_interface_address_internal (vlib_main_t * vm,
      &if_address_index);
   if (error)
     goto done;
-  
+
   ip4_sw_interface_enable_disable(sw_if_index, !is_del);
 
   if (is_del)
@@ -766,7 +824,7 @@ ip4_add_del_interface_address_internal (vlib_main_t * vm,
   else
       ip4_add_interface_routes (sw_if_index,
                                im, ip4_af.fib_index,
-                               pool_elt_at_index 
+                               pool_elt_at_index
                                (lm->if_address_pool, if_address_index));
 
   /* If pool did not grow/shrink: add duplicate address. */
@@ -796,8 +854,14 @@ ip4_add_del_interface_address (vlib_main_t * vm, u32 sw_if_index,
 }
 
 /* Built-in ip4 unicast rx feature path definition */
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_flow_classify, static) = {
+  .node_name = "ip4-flow-classify",
+  .runs_before = ORDER_CONSTRAINTS {"ip4-inacl", 0},
+  .feature_index = &ip4_main.ip4_unicast_rx_feature_flow_classify,
+};
+
 VNET_IP4_UNICAST_FEATURE_INIT (ip4_inacl, static) = {
-  .node_name = "ip4-inacl", 
+  .node_name = "ip4-inacl",
   .runs_before = ORDER_CONSTRAINTS {"ip4-source-check-via-rx", 0},
   .feature_index = &ip4_main.ip4_unicast_rx_feature_check_access,
 };
@@ -805,14 +869,14 @@ VNET_IP4_UNICAST_FEATURE_INIT (ip4_inacl, static) = {
 VNET_IP4_UNICAST_FEATURE_INIT (ip4_source_check_1, static) = {
   .node_name = "ip4-source-check-via-rx",
   .runs_before = ORDER_CONSTRAINTS {"ip4-source-check-via-any", 0},
-  .feature_index = 
+  .feature_index =
   &ip4_main.ip4_unicast_rx_feature_source_reachable_via_rx,
 };
 
 VNET_IP4_UNICAST_FEATURE_INIT (ip4_source_check_2, static) = {
   .node_name = "ip4-source-check-via-any",
   .runs_before = ORDER_CONSTRAINTS {"ip4-policer-classify", 0},
-  .feature_index = 
+  .feature_index =
   &ip4_main.ip4_unicast_rx_feature_source_reachable_via_any,
 };
 
@@ -874,11 +938,14 @@ VNET_IP4_MULTICAST_FEATURE_INIT (ip4_mc_drop, static) = {
   .feature_index = &ip4_main.ip4_multicast_rx_feature_drop,
 };
 
-static char * rx_feature_start_nodes[] = 
+static char * rx_feature_start_nodes[] =
   { "ip4-input", "ip4-input-no-checksum"};
 
-static char * tx_feature_start_nodes[] = 
-{ "ip4-rewrite-transit"};
+static char * tx_feature_start_nodes[] =
+{
+  "ip4-rewrite-transit",
+  "ip4-midchain",
+};
 
 /* Source and port-range check ip4 tx feature path definition */
 VNET_IP4_TX_FEATURE_INIT (ip4_source_and_port_range_check_tx, static) = {
@@ -922,12 +989,12 @@ ip4_feature_init (vlib_main_t * vm, ip4_main_t * im)
           feature_start_nodes = tx_feature_start_nodes;
           feature_start_len = ARRAY_LEN(tx_feature_start_nodes);
         }
-      
-      if ((error = ip_feature_init_cast (vm, cm, vcm, 
+
+      if ((error = vnet_feature_arc_init (vm, vcm,
                                          feature_start_nodes,
                                          feature_start_len,
-                                         cast,
-                                         VNET_L3_PACKET_TYPE_IP4)))
+                                        im->next_feature[cast],
+                                        &im->feature_nodes[cast])))
         return error;
     }
 
@@ -964,7 +1031,7 @@ ip4_sw_interface_add_del (vnet_main_t * vnm,
         feature_index = im->ip4_tx_feature_interface_output;
 
       if (is_add)
-        ci = vnet_config_add_feature (vm, vcm, 
+        ci = vnet_config_add_feature (vm, vcm,
                                      ci,
                                       feature_index,
                                      /* config data */ 0,
@@ -1005,7 +1072,7 @@ ip4_lookup_init (vlib_main_t * vm)
 
       if (i < 32)
        m = pow2_mask (i) << (32 - i);
-      else 
+      else
        m = ~0;
       im->fib_masks[i] = clib_host_to_net_u32 (m);
     }
@@ -1056,7 +1123,7 @@ VLIB_INIT_FUNCTION (ip4_lookup_init);
 
 typedef struct {
   /* Adjacency taken. */
-  u32 adj_index;
+  u32 dpo_index;
   u32 flow_hash;
   u32 fib_index;
 
@@ -1071,8 +1138,8 @@ static u8 * format_ip4_forward_next_trace (u8 * s, va_list * args)
   ip4_forward_next_trace_t * t = va_arg (*args, ip4_forward_next_trace_t *);
   uword indent = format_get_indent (s);
   s = format (s, "%U%U",
-                format_white_space, indent,
-                format_ip4_header, t->packet_data);
+             format_white_space, indent,
+             format_ip4_header, t->packet_data, sizeof (t->packet_data));
   return s;
 }
 
@@ -1081,16 +1148,13 @@ static u8 * format_ip4_lookup_trace (u8 * s, va_list * args)
   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   ip4_forward_next_trace_t * t = va_arg (*args, ip4_forward_next_trace_t *);
-  vnet_main_t * vnm = vnet_get_main();
   uword indent = format_get_indent (s);
 
-  s = format (s, "fib %d adj-idx %d : %U flow hash: 0x%08x",
-              t->fib_index, t->adj_index, format_ip_adjacency,
-              vnm, t->adj_index, FORMAT_IP_ADJACENCY_NONE, 
-             t->flow_hash);
+  s = format (s, "fib %d dpo-idx %d flow hash: 0x%08x",
+              t->fib_index, t->dpo_index, t->flow_hash);
   s = format (s, "\n%U%U",
               format_white_space, indent,
-              format_ip4_header, t->packet_data);
+              format_ip4_header, t->packet_data, sizeof (t->packet_data));
   return s;
 }
 
@@ -1102,14 +1166,14 @@ static u8 * format_ip4_rewrite_trace (u8 * s, va_list * args)
   vnet_main_t * vnm = vnet_get_main();
   uword indent = format_get_indent (s);
 
-  s = format (s, "tx_sw_if_index %d adj-idx %d : %U flow hash: 0x%08x",
-              t->fib_index, t->adj_index, format_ip_adjacency,
-              vnm, t->adj_index, FORMAT_IP_ADJACENCY_NONE,
+  s = format (s, "tx_sw_if_index %d dpo-idx %d : %U flow hash: 0x%08x",
+              t->fib_index, t->dpo_index, format_ip_adjacency,
+              t->dpo_index, FORMAT_IP_ADJACENCY_NONE,
              t->flow_hash);
   s = format (s, "\n%U%U",
               format_white_space, indent,
               format_ip_adjacency_packet_data,
-              vnm, t->adj_index,
+              vnm, t->dpo_index,
               t->packet_data, sizeof (t->packet_data));
   return s;
 }
@@ -1126,7 +1190,7 @@ ip4_forward_next_trace (vlib_main_t * vm,
 
   n_left = frame->n_vectors;
   from = vlib_frame_vector_args (frame);
-  
+
   while (n_left >= 4)
     {
       u32 bi0, bi1;
@@ -1146,7 +1210,7 @@ ip4_forward_next_trace (vlib_main_t * vm,
       if (b0->flags & VLIB_BUFFER_IS_TRACED)
        {
          t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
-         t0->adj_index = vnet_buffer (b0)->ip.adj_index[which_adj_index];
+         t0->dpo_index = vnet_buffer (b0)->ip.adj_index[which_adj_index];
          t0->flow_hash = vnet_buffer (b0)->ip.flow_hash;
          t0->fib_index = (vnet_buffer(b0)->sw_if_index[VLIB_TX] != (u32)~0) ?
              vnet_buffer(b0)->sw_if_index[VLIB_TX] :
@@ -1160,7 +1224,7 @@ ip4_forward_next_trace (vlib_main_t * vm,
       if (b1->flags & VLIB_BUFFER_IS_TRACED)
        {
          t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
-         t1->adj_index = vnet_buffer (b1)->ip.adj_index[which_adj_index];
+         t1->dpo_index = vnet_buffer (b1)->ip.adj_index[which_adj_index];
          t1->flow_hash = vnet_buffer (b1)->ip.flow_hash;
          t1->fib_index = (vnet_buffer(b1)->sw_if_index[VLIB_TX] != (u32)~0) ?
              vnet_buffer(b1)->sw_if_index[VLIB_TX] :
@@ -1187,7 +1251,7 @@ ip4_forward_next_trace (vlib_main_t * vm,
       if (b0->flags & VLIB_BUFFER_IS_TRACED)
        {
          t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
-         t0->adj_index = vnet_buffer (b0)->ip.adj_index[which_adj_index];
+         t0->dpo_index = vnet_buffer (b0)->ip.adj_index[which_adj_index];
          t0->flow_hash = vnet_buffer (b0)->ip.flow_hash;
          t0->fib_index = (vnet_buffer(b0)->sw_if_index[VLIB_TX] != (u32)~0) ?
              vnet_buffer(b0)->sw_if_index[VLIB_TX] :
@@ -1277,7 +1341,7 @@ ip4_tcp_udp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0,
   u32 n_this_buffer, n_bytes_left;
   u16 sum16;
   void * data_this_buffer;
-  
+
   /* Initialize checksum with ip header. */
   ip_header_length = ip4_header_bytes (ip0);
   payload_length_host_byte_order = clib_net_to_host_u16 (ip0->length) - ip_header_length;
@@ -1353,7 +1417,7 @@ ip4_local (vlib_main_t * vm,
   from = vlib_frame_vector_args (frame);
   n_left_from = frame->n_vectors;
   next_index = node->cached_next_index;
-  
+
   if (node->flags & VLIB_NODE_FLAG_TRACE)
     ip4_forward_next_trace (vm, node, frame, VLIB_TX);
 
@@ -1376,23 +1440,23 @@ ip4_local (vlib_main_t * vm,
          u8 error0, is_udp0, is_tcp_udp0, good_tcp_udp0, proto0;
          u8 error1, is_udp1, is_tcp_udp1, good_tcp_udp1, proto1;
          u8 enqueue_code;
-      
+
          pi0 = to_next[0] = from[0];
          pi1 = to_next[1] = from[1];
          from += 2;
          n_left_from -= 2;
          to_next += 2;
          n_left_to_next -= 2;
-      
+
          p0 = vlib_get_buffer (vm, pi0);
          p1 = vlib_get_buffer (vm, pi1);
 
          ip0 = vlib_buffer_get_current (p0);
          ip1 = vlib_buffer_get_current (p1);
 
-         fib_index0 = vec_elt (im->fib_index_by_sw_if_index, 
+         fib_index0 = vec_elt (im->fib_index_by_sw_if_index,
                                 vnet_buffer(p0)->sw_if_index[VLIB_RX]);
-         fib_index1 = vec_elt (im->fib_index_by_sw_if_index, 
+         fib_index1 = vec_elt (im->fib_index_by_sw_if_index,
                                 vnet_buffer(p1)->sw_if_index[VLIB_RX]);
 
          mtrie0 = &ip4_fib_get (fib_index0)->mtrie;
@@ -1498,28 +1562,34 @@ ip4_local (vlib_main_t * vm,
          dpo0 = load_balance_get_bucket_i(lb0, 0);
          dpo1 = load_balance_get_bucket_i(lb1, 0);
 
-         /* 
+         /*
            * Must have a route to source otherwise we drop the packet.
            * ip4 broadcasts are accepted, e.g. to make dhcp client work
+          *
+          * The checks are:
+          *  - the source is a recieve => it's from us => bogus, do this
+          *    first since it sets a different error code.
+          *  - uRPF check for any route to source - accept if passes.
+          *  - allow packets destined to the broadcast address from unknown sources
            */
-         error0 = (error0 == IP4_ERROR_UNKNOWN_PROTOCOL
-                   && dpo0->dpoi_type != DPO_ADJACENCY
-                   && dpo0->dpoi_type != DPO_ADJACENCY_INCOMPLETE
-                   && ip0->dst_address.as_u32 != 0xFFFFFFFF
+          error0 = ((error0 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    dpo0->dpoi_type == DPO_RECEIVE) ?
+                    IP4_ERROR_SPOOFED_LOCAL_PACKETS :
+                    error0);
+         error0 = ((error0 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    !fib_urpf_check_size(lb0->lb_urpf) &&
+                    ip0->dst_address.as_u32 != 0xFFFFFFFF)
                    ? IP4_ERROR_SRC_LOOKUP_MISS
                    : error0);
-          error0 = (dpo0->dpoi_type == DPO_RECEIVE ?
-                    IP4_ERROR_SPOOFED_LOCAL_PACKETS : 
-                    error0);
-         error1 = (error1 == IP4_ERROR_UNKNOWN_PROTOCOL
-                   && dpo1->dpoi_type != DPO_ADJACENCY
-                   && dpo1->dpoi_type != DPO_ADJACENCY_INCOMPLETE
-                   && ip1->dst_address.as_u32 != 0xFFFFFFFF
+          error1 = ((error1 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    dpo1->dpoi_type == DPO_RECEIVE) ?
+                    IP4_ERROR_SPOOFED_LOCAL_PACKETS :
+                    error1);
+         error1 = ((error1 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    !fib_urpf_check_size(lb1->lb_urpf) &&
+                    ip1->dst_address.as_u32 != 0xFFFFFFFF)
                    ? IP4_ERROR_SRC_LOOKUP_MISS
                    : error1);
-          error1 = (dpo0->dpoi_type == DPO_RECEIVE ?
-                    IP4_ERROR_SPOOFED_LOCAL_PACKETS : 
-                    error1);
 
          next0 = lm->local_next_by_ip_protocol[proto0];
          next1 = lm->local_next_by_ip_protocol[proto1];
@@ -1586,12 +1656,12 @@ ip4_local (vlib_main_t * vm,
          n_left_from -= 1;
          to_next += 1;
          n_left_to_next -= 1;
-      
+
          p0 = vlib_get_buffer (vm, pi0);
 
          ip0 = vlib_buffer_get_current (p0);
 
-         fib_index0 = vec_elt (im->fib_index_by_sw_if_index, 
+         fib_index0 = vec_elt (im->fib_index_by_sw_if_index,
                                 vnet_buffer(p0)->sw_if_index[VLIB_RX]);
 
          mtrie0 = &ip4_fib_get (fib_index0)->mtrie;
@@ -1664,18 +1734,15 @@ ip4_local (vlib_main_t * vm,
              vnet_buffer (p0)->ip.adj_index[VLIB_RX] =
                  dpo0->dpoi_index;
 
-         /* Must have a route to source otherwise we drop the packet. */
-         error0 = (error0 == IP4_ERROR_UNKNOWN_PROTOCOL
-                   && dpo0->dpoi_type != DPO_ADJACENCY
-                   && dpo0->dpoi_type != DPO_ADJACENCY_INCOMPLETE
-                   && dpo0->dpoi_type != DPO_RECEIVE
-                   && ip0->dst_address.as_u32 != 0xFFFFFFFF
+          error0 = ((error0 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    dpo0->dpoi_type == DPO_RECEIVE) ?
+                    IP4_ERROR_SPOOFED_LOCAL_PACKETS :
+                    error0);
+         error0 = ((error0 == IP4_ERROR_UNKNOWN_PROTOCOL &&
+                    !fib_urpf_check_size(lb0->lb_urpf) &&
+                    ip0->dst_address.as_u32 != 0xFFFFFFFF)
                    ? IP4_ERROR_SRC_LOOKUP_MISS
                    : error0);
-          /* Packet originated from a local address => spoofing */
-          error0 = (dpo0->dpoi_type == DPO_RECEIVE ?
-                    IP4_ERROR_SPOOFED_LOCAL_PACKETS : 
-                    error0);
 
          next0 = lm->local_next_by_ip_protocol[proto0];
 
@@ -1695,7 +1762,7 @@ ip4_local (vlib_main_t * vm,
              n_left_to_next -= 1;
            }
        }
-  
+
       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     }
 
@@ -1750,11 +1817,25 @@ show_ip_local_command_fn (vlib_main_t * vm,
 
 
 
+/*?
+ * Display the set of protocols handled by the local IPv4 stack.
+ *
+ * @cliexpar
+ * Example of how to display local protocol table:
+ * @cliexstart{show ip local}
+ * Protocols handled by ip4_local
+ * 1
+ * 17
+ * 47
+ * @cliexend
+?*/
+/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (show_ip_local, static) = {
   .path = "show ip local",
   .function = show_ip_local_command_fn,
-  .short_help = "Show ip local protocol table",
+  .short_help = "show ip local",
 };
+/* *INDENT-ON* */
 
 always_inline uword
 ip4_arp_inline (vlib_main_t * vm,
@@ -1769,7 +1850,7 @@ ip4_arp_inline (vlib_main_t * vm,
   uword n_left_from, n_left_to_next_drop, next_index;
   static f64 time_last_seed_change = -1e100;
   static u32 hash_seeds[3];
-  static uword hash_bitmap[256 / BITS (uword)]; 
+  static uword hash_bitmap[256 / BITS (uword)];
   f64 time_now;
 
   if (node->flags & VLIB_NODE_FLAG_TRACE)
@@ -1818,10 +1899,6 @@ ip4_arp_inline (vlib_main_t * vm,
          adj0 = ip_get_adjacency (lm, adj_index0);
          ip0 = vlib_buffer_get_current (p0);
 
-         /*
-          * this is the Glean case, so we are ARPing for the
-          * packet's destination 
-          */
          a0 = hash_seeds[0];
          b0 = hash_seeds[1];
          c0 = hash_seeds[2];
@@ -1831,6 +1908,10 @@ ip4_arp_inline (vlib_main_t * vm,
 
           if (is_glean)
           {
+             /*
+              * this is the Glean case, so we are ARPing for the
+              * packet's destination
+              */
               a0 ^= ip0->dst_address.data_u32;
           }
           else
@@ -1859,10 +1940,17 @@ ip4_arp_inline (vlib_main_t * vm,
 
          p0->error = node->errors[drop0 ? IP4_ARP_ERROR_DROP : IP4_ARP_ERROR_REQUEST_SENT];
 
+         /*
+          * the adj has been updated to a rewrite but the node the DPO that got
+          * us here hasn't - yet. no big deal. we'll drop while we wait.
+          */
+         if (IP_LOOKUP_NEXT_REWRITE == adj0->lookup_next_index)
+           continue;
+
          if (drop0)
            continue;
 
-          /* 
+          /*
            * Can happen if the control-plane is programming tables
            * with traffic flowing; at least that's today's lame excuse.
            */
@@ -1998,7 +2086,7 @@ _(REPLICATE_FAIL)
 
 clib_error_t * arp_notrace_init (vlib_main_t * vm)
 {
-  vlib_node_runtime_t *rt = 
+  vlib_node_runtime_t *rt =
     vlib_node_get_runtime (vm, ip4_arp_node.index);
 
   /* don't trace ARP request packets */
@@ -2034,8 +2122,8 @@ ip4_probe_neighbor (vlib_main_t * vm, ip4_address_t * dst, u32 sw_if_index)
   if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
     {
       return clib_error_return (0, "%U: interface %U down",
-                                format_ip4_address, dst, 
-                                format_vnet_sw_if_index_name, vnm, 
+                                format_ip4_address, dst,
+                                format_vnet_sw_if_index_name, vnm,
                                 sw_if_index);
     }
 
@@ -2043,7 +2131,7 @@ ip4_probe_neighbor (vlib_main_t * vm, ip4_address_t * dst, u32 sw_if_index)
   if (! src)
     {
       vnm->api_errno = VNET_API_ERROR_NO_MATCHING_INTERFACE;
-      return clib_error_return 
+      return clib_error_return
         (0, "no matching interface address for destination %U (interface %U)",
          format_ip4_address, dst,
          format_vnet_sw_if_index_name, vnm, sw_if_index);
@@ -2088,7 +2176,8 @@ always_inline uword
 ip4_rewrite_inline (vlib_main_t * vm,
                    vlib_node_runtime_t * node,
                    vlib_frame_t * frame,
-                   int rewrite_for_locally_received_packets)
+                   int rewrite_for_locally_received_packets,
+                   int is_midchain)
 {
   ip_lookup_main_t * lm = &ip4_main.lookup_main;
   u32 * from = vlib_frame_vector_args (frame);
@@ -2100,7 +2189,7 @@ ip4_rewrite_inline (vlib_main_t * vm,
   n_left_from = frame->n_vectors;
   next_index = node->cached_next_index;
   u32 cpu_index = os_get_cpu_number();
-  
+
   while (n_left_from > 0)
     {
       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
@@ -2139,7 +2228,7 @@ ip4_rewrite_inline (vlib_main_t * vm,
          n_left_from -= 2;
          to_next += 2;
          n_left_to_next -= 2;
-      
+
          p0 = vlib_get_buffer (vm, pi0);
          p1 = vlib_get_buffer (vm, pi1);
 
@@ -2209,7 +2298,7 @@ ip4_rewrite_inline (vlib_main_t * vm,
          /* Rewrite packet header and updates lengths. */
          adj0 = ip_get_adjacency (lm, adj_index0);
          adj1 = ip_get_adjacency (lm, adj_index1);
-      
+
           if (rewrite_for_locally_received_packets)
             {
               if (PREDICT_FALSE(adj0->lookup_next_index
@@ -2246,20 +2335,20 @@ ip4_rewrite_inline (vlib_main_t * vm,
           if (rewrite_for_locally_received_packets)
               next1 = next1 && next1_override ? next1_override : next1;
 
-          /* 
+          /*
            * We've already accounted for an ethernet_header_t elsewhere
            */
           if (PREDICT_FALSE (rw_len0 > sizeof(ethernet_header_t)))
-              vlib_increment_combined_counter 
+              vlib_increment_combined_counter
                   (&adjacency_counters,
-                   cpu_index, adj_index0, 
+                   cpu_index, adj_index0,
                    /* packet increment */ 0,
                    /* byte increment */ rw_len0-sizeof(ethernet_header_t));
 
           if (PREDICT_FALSE (rw_len1 > sizeof(ethernet_header_t)))
-              vlib_increment_combined_counter 
+              vlib_increment_combined_counter
                   (&adjacency_counters,
-                   cpu_index, adj_index1, 
+                   cpu_index, adj_index1,
                    /* packet increment */ 0,
                    /* byte increment */ rw_len1-sizeof(ethernet_header_t));
 
@@ -2273,12 +2362,12 @@ ip4_rewrite_inline (vlib_main_t * vm,
               vnet_buffer (p0)->sw_if_index[VLIB_TX] =
                   tx_sw_if_index0;
 
-              if (PREDICT_FALSE 
-                  (clib_bitmap_get (lm->tx_sw_if_has_ip_output_features, 
+              if (PREDICT_FALSE
+                  (clib_bitmap_get (lm->tx_sw_if_has_ip_output_features,
                                     tx_sw_if_index0)))
                 {
-                  p0->current_config_index = 
-                    vec_elt (cm->config_index_by_sw_if_index, 
+                  p0->current_config_index =
+                    vec_elt (cm->config_index_by_sw_if_index,
                              tx_sw_if_index0);
                   vnet_get_config_data (&cm->config_main,
                                         &p0->current_config_index,
@@ -2295,12 +2384,12 @@ ip4_rewrite_inline (vlib_main_t * vm,
               vnet_buffer (p1)->sw_if_index[VLIB_TX] =
                   tx_sw_if_index1;
 
-              if (PREDICT_FALSE 
-                  (clib_bitmap_get (lm->tx_sw_if_has_ip_output_features, 
+              if (PREDICT_FALSE
+                  (clib_bitmap_get (lm->tx_sw_if_has_ip_output_features,
                                     tx_sw_if_index1)))
                 {
-                  p1->current_config_index = 
-                    vec_elt (cm->config_index_by_sw_if_index, 
+                  p1->current_config_index =
+                    vec_elt (cm->config_index_by_sw_if_index,
                              tx_sw_if_index1);
                   vnet_get_config_data (&cm->config_main,
                                         &p1->current_config_index,
@@ -2313,7 +2402,13 @@ ip4_rewrite_inline (vlib_main_t * vm,
          vnet_rewrite_two_headers (adj0[0], adj1[0],
                                    ip0, ip1,
                                    sizeof (ethernet_header_t));
-      
+
+         if (is_midchain)
+         {
+             adj0->sub_type.midchain.fixup_func(vm, adj0, p0);
+             adj1->sub_type.midchain.fixup_func(vm, adj1, p1);
+         }
+
          vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
                                           to_next, n_left_to_next,
                                           pi0, pi1, next0, next1);
@@ -2341,7 +2436,7 @@ ip4_rewrite_inline (vlib_main_t * vm,
           ASSERT(adj_index0);
 
          adj0 = ip_get_adjacency (lm, adj_index0);
-      
+
          ip0 = vlib_buffer_get_current (p0);
 
          error0 = IP4_ERROR_NONE;
@@ -2382,7 +2477,7 @@ ip4_rewrite_inline (vlib_main_t * vm,
 
           if (rewrite_for_locally_received_packets)
             {
-              /* 
+              /*
                * We have to override the next_index in ARP adjacencies,
                * because they're set up for ip4-arp, not this node...
                */
@@ -2392,22 +2487,22 @@ ip4_rewrite_inline (vlib_main_t * vm,
             }
 
          /* Guess we are only writing on simple Ethernet header. */
-          vnet_rewrite_one_header (adj0[0], ip0, 
+          vnet_rewrite_one_header (adj0[0], ip0,
                                    sizeof (ethernet_header_t));
-          
+
           /* Update packet buffer attributes/set output interface. */
           rw_len0 = adj0[0].rewrite_header.data_bytes;
           vnet_buffer(p0)->ip.save_rewrite_length = rw_len0;
-          
+
           if (PREDICT_FALSE (rw_len0 > sizeof(ethernet_header_t)))
-              vlib_increment_combined_counter 
+              vlib_increment_combined_counter
                   (&adjacency_counters,
-                   cpu_index, adj_index0, 
+                   cpu_index, adj_index0,
                    /* packet increment */ 0,
                    /* byte increment */ rw_len0-sizeof(ethernet_header_t));
-          
+
           /* Check MTU of outgoing interface. */
-          error0 = (vlib_buffer_length_in_chain (vm, p0) 
+          error0 = (vlib_buffer_length_in_chain (vm, p0)
                     > adj0[0].rewrite_header.max_l3_packet_bytes
                     ? IP4_ERROR_MTU_EXCEEDED
                     : error0);
@@ -2425,12 +2520,17 @@ ip4_rewrite_inline (vlib_main_t * vm,
               vnet_buffer (p0)->sw_if_index[VLIB_TX] = tx_sw_if_index0;
               next0 = adj0[0].rewrite_header.next_index;
 
-              if (PREDICT_FALSE 
-                  (clib_bitmap_get (lm->tx_sw_if_has_ip_output_features, 
+             if (is_midchain)
+               {
+                 adj0->sub_type.midchain.fixup_func(vm, adj0, p0);
+               }
+
+              if (PREDICT_FALSE
+                  (clib_bitmap_get (lm->tx_sw_if_has_ip_output_features,
                                     tx_sw_if_index0)))
                   {
-                    p0->current_config_index = 
-                      vec_elt (cm->config_index_by_sw_if_index, 
+                    p0->current_config_index =
+                      vec_elt (cm->config_index_by_sw_if_index,
                                tx_sw_if_index0);
                     vnet_get_config_data (&cm->config_main,
                                           &p0->current_config_index,
@@ -2446,12 +2546,12 @@ ip4_rewrite_inline (vlib_main_t * vm,
          n_left_from -= 1;
          to_next += 1;
          n_left_to_next -= 1;
-      
+
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                           to_next, n_left_to_next,
                                           pi0, next0);
        }
-  
+
       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     }
 
@@ -2482,7 +2582,7 @@ ip4_rewrite_inline (vlib_main_t * vm,
         - the rewrite adjacency index
     - <code>adj->lookup_next_index</code>
         - Must be IP_LOOKUP_NEXT_REWRITE or IP_LOOKUP_NEXT_ARP, otherwise
-          the packet will be dropped. 
+          the packet will be dropped.
     - <code>adj->rewrite_header</code>
         - Rewrite string length, rewrite string, next_index
 
@@ -2492,7 +2592,7 @@ ip4_rewrite_inline (vlib_main_t * vm,
 
     <em>Next Indices:</em>
     - <code> adj->rewrite_header.next_index </code>
-      or @c error-drop 
+      or @c error-drop
 */
 static uword
 ip4_rewrite_transit (vlib_main_t * vm,
@@ -2500,7 +2600,7 @@ ip4_rewrite_transit (vlib_main_t * vm,
                     vlib_frame_t * frame)
 {
   return ip4_rewrite_inline (vm, node, frame,
-                            /* rewrite_for_locally_received_packets */ 0);
+                            /* rewrite_for_locally_received_packets */ 0, 0);
 }
 
 /** @brief IPv4 local rewrite node.
@@ -2523,7 +2623,7 @@ ip4_rewrite_transit (vlib_main_t * vm,
         - the rewrite adjacency index
     - <code>adj->lookup_next_index</code>
         - Must be IP_LOOKUP_NEXT_REWRITE or IP_LOOKUP_NEXT_ARP, otherwise
-          the packet will be dropped. 
+          the packet will be dropped.
     - <code>adj->rewrite_header</code>
         - Rewrite string length, rewrite string, next_index
 
@@ -2533,7 +2633,7 @@ ip4_rewrite_transit (vlib_main_t * vm,
 
     <em>Next Indices:</em>
     - <code> adj->rewrite_header.next_index </code>
-      or @c error-drop 
+      or @c error-drop
 */
 
 static uword
@@ -2542,7 +2642,7 @@ ip4_rewrite_local (vlib_main_t * vm,
                   vlib_frame_t * frame)
 {
   return ip4_rewrite_inline (vm, node, frame,
-                            /* rewrite_for_locally_received_packets */ 1);
+                            /* rewrite_for_locally_received_packets */ 1, 0);
 }
 
 static uword
@@ -2551,7 +2651,7 @@ ip4_midchain (vlib_main_t * vm,
              vlib_frame_t * frame)
 {
   return ip4_rewrite_inline (vm, node, frame,
-                            /* rewrite_for_locally_received_packets */ 0);
+                            /* rewrite_for_locally_received_packets */ 0, 1);
 }
 
 VLIB_REGISTER_NODE (ip4_rewrite_node) = {
@@ -2578,11 +2678,7 @@ VLIB_REGISTER_NODE (ip4_midchain_node) = {
 
   .format_trace = format_ip4_forward_next_trace,
 
-  .n_next_nodes = 2,
-  .next_nodes = {
-    [IP4_REWRITE_NEXT_DROP] = "error-drop",
-    [IP4_REWRITE_NEXT_ARP] = "ip4-arp",
-  },
+  .sibling_of = "ip4-rewrite-transit",
 };
 
 VLIB_NODE_FUNCTION_MULTIARCH (ip4_midchain_node, ip4_midchain)
@@ -2649,24 +2745,32 @@ add_del_interface_table (vlib_main_t * vm,
 }
 
 /*?
- * Place the indicated interface into the supplied VRF
- *
- * @cliexpar
- * @cliexstart{set interface ip table}
+ * Place the indicated interface into the supplied IPv4 FIB table (also known
+ * as a VRF). If the FIB table does not exist, this command creates it. To
+ * display the current IPv4 FIB table, use the command '<em>show ip fib</em>'.
+ * FIB table will only be displayed if a route has been added to the table, or
+ * an IP Address is assigned to an interface in the table (which adds a route
+ * automatically).
  *
- *  vpp# set interface ip table GigabitEthernet2/0/0 2
+ * @note IP addresses added after setting the interface IP table end up in
+ * the indicated FIB table. If the IP address is added prior to adding the
+ * interface to the FIB table, it will NOT be part of the FIB table. Predictable
+ * but potentially counter-intuitive results occur if you provision interface
+ * addresses in multiple FIBs. Upon RX, packets will be processed in the last
+ * IP table ID provisioned. It might be marginally useful to evade source RPF
+ * drops to put an interface address into multiple FIBs.
  *
- * Interface addresses added after setting the interface IP table end up in the indicated VRF table.
- * Predictable but potentially counter-intuitive results occur if you provision interface addresses in multiple FIBs.
- * Upon RX, packets will be processed in the last IP table ID provisioned.
- * It might be marginally useful to evade source RPF drops to put an interface address into multiple FIBs.
- * @cliexend
+ * @cliexpar
+ * Example of how to add an interface to an IPv4 FIB table (where 2 is the table-id):
+ * @cliexcmd{set interface ip table GigabitEthernet2/0/0 2}
  ?*/
+/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (set_interface_ip_table_command, static) = {
   .path = "set interface ip table",
   .function = add_del_interface_table,
-  .short_help = "Add/delete FIB table id for interface",
+  .short_help = "set interface ip table <interface> <table-id>",
 };
+/* *INDENT-ON* */
 
 
 static uword
@@ -2742,10 +2846,10 @@ ip4_lookup_multicast (vlib_main_t * vm,
          ASSERT (lb1->lb_n_buckets > 0);
          ASSERT (is_pow2 (lb1->lb_n_buckets));
 
-         vnet_buffer (p0)->ip.flow_hash = ip4_compute_flow_hash 
+         vnet_buffer (p0)->ip.flow_hash = ip4_compute_flow_hash
               (ip0, lb0->lb_hash_config);
-                                                                  
-         vnet_buffer (p1)->ip.flow_hash = ip4_compute_flow_hash 
+
+         vnet_buffer (p1)->ip.flow_hash = ip4_compute_flow_hash
               (ip1, lb1->lb_hash_config);
 
          dpo0 = load_balance_get_bucket_i(lb0,
@@ -2753,7 +2857,7 @@ ip4_lookup_multicast (vlib_main_t * vm,
                                             (lb0->lb_n_buckets_minus_1)));
          dpo1 = load_balance_get_bucket_i(lb1,
                                            (vnet_buffer (p1)->ip.flow_hash &
-                                            (lb0->lb_n_buckets_minus_1)));
+                                            (lb1->lb_n_buckets_minus_1)));
 
          next0 = dpo0->dpoi_next_node;
          vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
@@ -2761,11 +2865,11 @@ ip4_lookup_multicast (vlib_main_t * vm,
          vnet_buffer (p1)->ip.adj_index[VLIB_TX] = dpo1->dpoi_index;
 
           if (1) /* $$$$$$ HACK FIXME */
-         vlib_increment_combined_counter 
+         vlib_increment_combined_counter
               (cm, cpu_index, lb_index0, 1,
                vlib_buffer_length_in_chain (vm, p0));
           if (1) /* $$$$$$ HACK FIXME */
-         vlib_increment_combined_counter 
+         vlib_increment_combined_counter
               (cm, cpu_index, lb_index1, 1,
                vlib_buffer_length_in_chain (vm, p1));
 
@@ -2810,7 +2914,7 @@ ip4_lookup_multicast (vlib_main_t * vm,
                }
            }
        }
-    
+
       while (n_left_from > 0 && n_left_to_next > 0)
        {
          vlib_buffer_t * p0;
@@ -2828,11 +2932,11 @@ ip4_lookup_multicast (vlib_main_t * vm,
 
          ip0 = vlib_buffer_get_current (p0);
 
-         fib_index0 = vec_elt (im->fib_index_by_sw_if_index, 
+         fib_index0 = vec_elt (im->fib_index_by_sw_if_index,
                                 vnet_buffer (p0)->sw_if_index[VLIB_RX]);
           fib_index0 = (vnet_buffer(p0)->sw_if_index[VLIB_TX] == (u32)~0) ?
               fib_index0 : vnet_buffer(p0)->sw_if_index[VLIB_TX];
-          
+
          lb_index0 = ip4_fib_table_lookup_lb (ip4_fib_get(fib_index0),
                                                &ip0->dst_address);
 
@@ -2841,7 +2945,7 @@ ip4_lookup_multicast (vlib_main_t * vm,
          ASSERT (lb0->lb_n_buckets > 0);
          ASSERT (is_pow2 (lb0->lb_n_buckets));
 
-         vnet_buffer (p0)->ip.flow_hash = ip4_compute_flow_hash 
+         vnet_buffer (p0)->ip.flow_hash = ip4_compute_flow_hash
               (ip0, lb0->lb_hash_config);
 
          dpo0 = load_balance_get_bucket_i(lb0,
@@ -2852,7 +2956,7 @@ ip4_lookup_multicast (vlib_main_t * vm,
          vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
 
           if (1) /* $$$$$$ HACK FIXME */
-              vlib_increment_combined_counter 
+              vlib_increment_combined_counter
                   (cm, cpu_index, lb_index0, 1,
                    vlib_buffer_length_in_chain (vm, p0));
 
@@ -2913,7 +3017,7 @@ int ip4_lookup_validate (ip4_address_t *a, u32 fib_index0)
   ip4_fib_mtrie_t * mtrie0;
   ip4_fib_mtrie_leaf_t leaf0;
   u32 lbi0;
-    
+
   mtrie0 = &ip4_fib_get (fib_index0)->mtrie;
 
   leaf0 = IP4_FIB_MTRIE_LEAF_ROOT;
@@ -2921,20 +3025,21 @@ int ip4_lookup_validate (ip4_address_t *a, u32 fib_index0)
   leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, a, 1);
   leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, a, 2);
   leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0, a, 3);
-  
+
   /* Handle default route. */
   leaf0 = (leaf0 == IP4_FIB_MTRIE_LEAF_EMPTY ? mtrie0->default_leaf : leaf0);
-  
+
   lbi0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
-  
+
   return lbi0 == ip4_fib_table_lookup_lb (ip4_fib_get(fib_index0), a);
 }
+
 static clib_error_t *
 test_lookup_command_fn (vlib_main_t * vm,
                         unformat_input_t * input,
                         vlib_cli_command_t * cmd)
 {
+  ip4_fib_t *fib;
   u32 table_id = 0;
   f64 count = 1;
   u32 n;
@@ -2944,7 +3049,13 @@ test_lookup_command_fn (vlib_main_t * vm,
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
       if (unformat (input, "table %d", &table_id))
-       ;
+      {
+          /* Make sure the entry exists. */
+          fib = ip4_fib_get(table_id);
+          if ((fib) && (fib->index != table_id))
+              return clib_error_return (0, "<fib-index> %d does not exist",
+                                        table_id);
+      }
       else if (unformat (input, "count %f", &count))
        ;
 
@@ -2963,12 +3074,12 @@ test_lookup_command_fn (vlib_main_t * vm,
       if (!ip4_lookup_validate (&ip4_base_address, table_id))
         errors++;
 
-      ip4_base_address.as_u32 = 
-        clib_host_to_net_u32 (1 + 
+      ip4_base_address.as_u32 =
+        clib_host_to_net_u32 (1 +
                               clib_net_to_host_u32 (ip4_base_address.as_u32));
     }
 
-  if (errors) 
+  if (errors)
     vlib_cli_output (vm, "%llu errors out of %d lookups\n", errors, n);
   else
     vlib_cli_output (vm, "No errors in %d lookups\n", n);
@@ -2976,11 +3087,30 @@ test_lookup_command_fn (vlib_main_t * vm,
   return 0;
 }
 
+/*?
+ * Perform a lookup of an IPv4 Address (or range of addresses) in the
+ * given FIB table to determine if there is a conflict with the
+ * adjacency table. The fib-id can be determined by using the
+ * '<em>show ip fib</em>' command. If fib-id is not entered, default value
+ * of 0 is used.
+ *
+ * @todo This command uses fib-id, other commands use table-id (not
+ * just a name, they are different indexes). Would like to change this
+ * to table-id for consistency.
+ *
+ * @cliexpar
+ * Example of how to run the test lookup command:
+ * @cliexstart{test lookup 172.16.1.1 table 1 count 2}
+ * No errors in 2 lookups
+ * @cliexend
+?*/
+/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (lookup_test_command, static) = {
     .path = "test lookup",
-    .short_help = "test lookup",
+    .short_help = "test lookup <ipv4-addr> [table <fib-id>] [count <nn>]",
     .function = test_lookup_command_fn,
 };
+/* *INDENT-ON* */
 
 int vnet_set_ip4_flow_hash (u32 table_id, u32 flow_hash_config)
 {
@@ -2996,7 +3126,7 @@ int vnet_set_ip4_flow_hash (u32 table_id, u32 flow_hash_config)
   fib->flow_hash_config = flow_hash_config;
   return 0;
 }
+
 static clib_error_t *
 set_ip_flow_hash_command_fn (vlib_main_t * vm,
                              unformat_input_t * input,
@@ -3016,36 +3146,121 @@ set_ip_flow_hash_command_fn (vlib_main_t * vm,
 #undef _
     else break;
   }
-  
+
   if (matched == 0)
     return clib_error_return (0, "unknown input `%U'",
                               format_unformat_error, input);
-  
+
   rv = vnet_set_ip4_flow_hash (table_id, flow_hash_config);
   switch (rv)
     {
     case 0:
       break;
-      
+
     case VNET_API_ERROR_NO_SUCH_FIB:
       return clib_error_return (0, "no such FIB table %d", table_id);
-      
+
     default:
       clib_warning ("BUG: illegal flow hash config 0x%x", flow_hash_config);
       break;
     }
-  
+
   return 0;
 }
+
+/*?
+ * Configure the set of IPv4 fields used by the flow hash.
+ *
+ * @cliexpar
+ * Example of how to set the flow hash on a given table:
+ * @cliexcmd{set ip flow-hash table 7 dst sport dport proto}
+ * Example of display the configured flow hash:
+ * @cliexstart{show ip fib}
+ * ipv4-VRF:0, fib_index 0, flow hash: src dst sport dport proto
+ * 0.0.0.0/0
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:0 buckets:1 uRPF:0 to:[0:0]]
+ *     [0] [@0]: dpo-drop ip6
+ * 0.0.0.0/32
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:1 buckets:1 uRPF:1 to:[0:0]]
+ *     [0] [@0]: dpo-drop ip6
+ * 224.0.0.0/8
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:3 buckets:1 uRPF:3 to:[0:0]]
+ *     [0] [@0]: dpo-drop ip6
+ * 6.0.1.2/32
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:30 buckets:1 uRPF:29 to:[0:0]]
+ *     [0] [@3]: arp-ipv4: via 6.0.0.1 af_packet0
+ * 7.0.0.1/32
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:31 buckets:4 uRPF:30 to:[0:0]]
+ *     [0] [@3]: arp-ipv4: via 6.0.0.2 af_packet0
+ *     [1] [@3]: arp-ipv4: via 6.0.0.2 af_packet0
+ *     [2] [@3]: arp-ipv4: via 6.0.0.2 af_packet0
+ *     [3] [@3]: arp-ipv4: via 6.0.0.1 af_packet0
+ * 240.0.0.0/8
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:2 buckets:1 uRPF:2 to:[0:0]]
+ *     [0] [@0]: dpo-drop ip6
+ * 255.255.255.255/32
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:4 buckets:1 uRPF:4 to:[0:0]]
+ *     [0] [@0]: dpo-drop ip6
+ * ipv4-VRF:7, fib_index 1, flow hash: dst sport dport proto
+ * 0.0.0.0/0
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:12 buckets:1 uRPF:11 to:[0:0]]
+ *     [0] [@0]: dpo-drop ip6
+ * 0.0.0.0/32
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:13 buckets:1 uRPF:12 to:[0:0]]
+ *     [0] [@0]: dpo-drop ip6
+ * 172.16.1.0/24
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:17 buckets:1 uRPF:16 to:[0:0]]
+ *     [0] [@4]: ipv4-glean: af_packet0
+ * 172.16.1.1/32
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:18 buckets:1 uRPF:17 to:[1:84]]
+ *     [0] [@2]: dpo-receive: 172.16.1.1 on af_packet0
+ * 172.16.1.2/32
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:21 buckets:1 uRPF:20 to:[0:0]]
+ *     [0] [@5]: ipv4 via 172.16.1.2 af_packet0: IP4: 02:fe:9e:70:7a:2b -> 26:a5:f6:9c:3a:36
+ * 172.16.2.0/24
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:19 buckets:1 uRPF:18 to:[0:0]]
+ *     [0] [@4]: ipv4-glean: af_packet1
+ * 172.16.2.1/32
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:20 buckets:1 uRPF:19 to:[0:0]]
+ *     [0] [@2]: dpo-receive: 172.16.2.1 on af_packet1
+ * 224.0.0.0/8
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:15 buckets:1 uRPF:14 to:[0:0]]
+ *     [0] [@0]: dpo-drop ip6
+ * 240.0.0.0/8
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:14 buckets:1 uRPF:13 to:[0:0]]
+ *     [0] [@0]: dpo-drop ip6
+ * 255.255.255.255/32
+ *   unicast-ip4-chain
+ *   [@0]: dpo-load-balance: [index:16 buckets:1 uRPF:15 to:[0:0]]
+ *     [0] [@0]: dpo-drop ip6
+ * @cliexend
+?*/
+/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (set_ip_flow_hash_command, static) = {
   .path = "set ip flow-hash",
-  .short_help = 
-  "set ip table flow-hash table <fib-id> src dst sport dport proto reverse",
+  .short_help =
+  "set ip flow-hash table <table-id> [src] [dst] [sport] [dport] [proto] [reverse]",
   .function = set_ip_flow_hash_command_fn,
 };
-int vnet_set_ip4_classify_intfc (vlib_main_t * vm, u32 sw_if_index, 
+/* *INDENT-ON* */
+
+int vnet_set_ip4_classify_intfc (vlib_main_t * vm, u32 sw_if_index,
                                  u32 table_index)
 {
   vnet_main_t * vnm = vnet_get_main();
@@ -3053,6 +3268,7 @@ int vnet_set_ip4_classify_intfc (vlib_main_t * vm, u32 sw_if_index,
   ip4_main_t * ipm = &ip4_main;
   ip_lookup_main_t * lm = &ipm->lookup_main;
   vnet_classify_main_t * cm = &vnet_classify_main;
+  ip4_address_t *if_addr;
 
   if (pool_is_free_index (im->sw_interfaces, sw_if_index))
     return VNET_API_ERROR_NO_MATCHING_INTERFACE;
@@ -3063,6 +3279,45 @@ int vnet_set_ip4_classify_intfc (vlib_main_t * vm, u32 sw_if_index,
   vec_validate (lm->classify_table_index_by_sw_if_index, sw_if_index);
   lm->classify_table_index_by_sw_if_index [sw_if_index] = table_index;
 
+  if_addr = ip4_interface_first_address (ipm, sw_if_index, NULL);
+
+  if (NULL != if_addr)
+  {
+      fib_prefix_t pfx = {
+         .fp_len = 32,
+         .fp_proto = FIB_PROTOCOL_IP4,
+         .fp_addr.ip4 = *if_addr,
+      };
+      u32 fib_index;
+
+      fib_index = fib_table_get_index_for_sw_if_index(FIB_PROTOCOL_IP4,
+                                                     sw_if_index);
+
+
+      if (table_index != (u32) ~0)
+      {
+          dpo_id_t dpo = DPO_INVALID;
+
+          dpo_set(&dpo,
+                  DPO_CLASSIFY,
+                  DPO_PROTO_IP4,
+                  classify_dpo_create(DPO_PROTO_IP4, table_index));
+
+         fib_table_entry_special_dpo_add(fib_index,
+                                         &pfx,
+                                         FIB_SOURCE_CLASSIFY,
+                                         FIB_ENTRY_FLAG_NONE,
+                                         &dpo);
+          dpo_reset(&dpo);
+      }
+      else
+      {
+         fib_table_entry_special_remove(fib_index,
+                                        &pfx,
+                                        FIB_SOURCE_CLASSIFY);
+      }
+  }
+
   return 0;
 }
 
@@ -3075,17 +3330,17 @@ set_ip_classify_command_fn (vlib_main_t * vm,
   int table_index_set = 0;
   u32 sw_if_index = ~0;
   int rv;
-  
+
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
     if (unformat (input, "table-index %d", &table_index))
       table_index_set = 1;
-    else if (unformat (input, "intfc %U", unformat_vnet_sw_interface, 
+    else if (unformat (input, "intfc %U", unformat_vnet_sw_interface,
                        vnet_get_main(), &sw_if_index))
       ;
     else
       break;
   }
-      
+
   if (table_index_set == 0)
     return clib_error_return (0, "classify table-index must be specified");
 
@@ -3108,10 +3363,22 @@ set_ip_classify_command_fn (vlib_main_t * vm,
   return 0;
 }
 
+/*?
+ * Assign a classification table to an interface. The classification
+ * table is created using the '<em>classify table</em>' and '<em>classify session</em>'
+ * commands. Once the table is create, use this command to filter packets
+ * on an interface.
+ *
+ * @cliexpar
+ * Example of how to assign a classification table to an interface:
+ * @cliexcmd{set ip classify intfc GigabitEthernet2/0/0 table-index 1}
+?*/
+/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (set_ip_classify_command, static) = {
     .path = "set ip classify",
-    .short_help = 
-    "set ip classify intfc <int> table-index <index>",
+    .short_help =
+    "set ip classify intfc <interface> table-index <classify-idx>",
     .function = set_ip_classify_command_fn,
 };
+/* *INDENT-ON* */