nat: respect arc features (multi worker) 04/23204/5
authorFilip Varga <fivarga@cisco.com>
Mon, 9 Sep 2019 14:55:19 +0000 (16:55 +0200)
committerOle Trøan <otroan@employees.org>
Tue, 19 Nov 2019 03:42:50 +0000 (03:42 +0000)
Type: fix
Ticket: VPP-1747

Change-Id: If282aae3e584d7017c200f897b99c8a37eb1b2e5
Signed-off-by: Filip Varga <fivarga@cisco.com>
src/plugins/nat/in2out_ed.c
src/plugins/nat/nat.c
src/plugins/nat/nat.h
src/plugins/nat/nat44_classify.c
src/plugins/nat/nat44_handoff.c
src/plugins/nat/nat_inlines.h
src/plugins/nat/out2in_ed.c

index 77d620f..733e4f0 100644 (file)
@@ -37,16 +37,6 @@ static char *nat_in2out_ed_error_strings[] = {
 #undef _
 };
 
-typedef enum
-{
-  NAT_IN2OUT_ED_NEXT_LOOKUP,
-  NAT_IN2OUT_ED_NEXT_DROP,
-  NAT_IN2OUT_ED_NEXT_ICMP_ERROR,
-  NAT_IN2OUT_ED_NEXT_SLOW_PATH,
-  NAT_IN2OUT_ED_NEXT_REASS,
-  NAT_IN2OUT_ED_N_NEXT,
-} nat_in2out_ed_next_t;
-
 typedef struct
 {
   u32 sw_if_index;
@@ -179,7 +169,7 @@ icmp_in2out_ed_slow_path (snat_main_t * sm, vlib_buffer_t * b0,
   next0 = icmp_in2out (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
                       next0, thread_index, p_s0, 0);
   snat_session_t *s0 = *p_s0;
-  if (PREDICT_TRUE (next0 != NAT_IN2OUT_ED_NEXT_DROP && s0))
+  if (PREDICT_TRUE (next0 != NAT_NEXT_DROP && s0))
     {
       /* Accounting */
       nat44_session_update_counters (s0, now,
@@ -224,7 +214,7 @@ slow_path_ed (snat_main_t * sm,
       b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED];
       nat_ipfix_logging_max_sessions (thread_index, sm->max_translations);
       nat_elog_notice ("maximum sessions exceeded");
-      return NAT_IN2OUT_ED_NEXT_DROP;
+      return NAT_NEXT_DROP;
     }
 
   key0.addr = key->l_addr;
@@ -244,7 +234,7 @@ slow_path_ed (snat_main_t * sm,
        {
          nat_elog_notice ("addresses exhausted");
          b->error = node->errors[NAT_IN2OUT_ED_ERROR_OUT_OF_PORTS];
-         return NAT_IN2OUT_ED_NEXT_DROP;
+         return NAT_NEXT_DROP;
        }
     }
   else
@@ -263,7 +253,7 @@ slow_path_ed (snat_main_t * sm,
       if (!tcp_is_init (tcp))
        {
          b->error = node->errors[NAT_IN2OUT_ED_ERROR_NON_SYN];
-         return NAT_IN2OUT_ED_NEXT_DROP;
+         return NAT_NEXT_DROP;
        }
     }
 
@@ -274,7 +264,7 @@ slow_path_ed (snat_main_t * sm,
       if (!is_sm)
        snat_free_outside_address_and_port (sm->addresses,
                                            thread_index, &key1);
-      return NAT_IN2OUT_ED_NEXT_DROP;
+      return NAT_NEXT_DROP;
     }
 
   s = nat_ed_session_alloc (sm, u, thread_index, now);
@@ -285,7 +275,7 @@ slow_path_ed (snat_main_t * sm,
       if (!is_sm)
        snat_free_outside_address_and_port (sm->addresses,
                                            thread_index, &key1);
-      return NAT_IN2OUT_ED_NEXT_DROP;
+      return NAT_NEXT_DROP;
     }
 
   user_session_increment (sm, u, is_sm);
@@ -544,7 +534,7 @@ icmp_match_in2out_ed (snat_main_t * sm, vlib_node_runtime_t * node,
   if (err != 0)
     {
       b->error = node->errors[err];
-      next = NAT_IN2OUT_ED_NEXT_DROP;
+      next = NAT_NEXT_DROP;
       goto out;
     }
   key.fib_index = rx_fib_index;
@@ -588,14 +578,14 @@ icmp_match_in2out_ed (snat_main_t * sm, vlib_node_runtime_t * node,
       if (PREDICT_FALSE (icmp_is_error_message (icmp)))
        {
          b->error = node->errors[NAT_IN2OUT_ED_ERROR_BAD_ICMP_TYPE];
-         next = NAT_IN2OUT_ED_NEXT_DROP;
+         next = NAT_NEXT_DROP;
          goto out;
        }
 
       next = slow_path_ed (sm, b, rx_fib_index, &kv, &s, node, next,
                           thread_index, vlib_time_now (sm->vlib_main), 0);
 
-      if (PREDICT_FALSE (next == NAT_IN2OUT_ED_NEXT_DROP))
+      if (PREDICT_FALSE (next == NAT_NEXT_DROP))
        goto out;
 
       if (!s)
@@ -611,7 +601,7 @@ icmp_match_in2out_ed (snat_main_t * sm, vlib_node_runtime_t * node,
                         !icmp_is_error_message (icmp)))
        {
          b->error = node->errors[NAT_IN2OUT_ED_ERROR_BAD_ICMP_TYPE];
-         next = NAT_IN2OUT_ED_NEXT_DROP;
+         next = NAT_NEXT_DROP;
          goto out;
        }
 
@@ -837,13 +827,19 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                                int is_output_feature)
 {
   u32 n_left_from, *from, *to_next, pkts_processed = 0, stats_node_index;
-  nat_in2out_ed_next_t next_index;
+  nat_next_t next_index;
   snat_main_t *sm = &snat_main;
   f64 now = vlib_time_now (vm);
   u32 thread_index = vm->thread_index;
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
   u32 tcp_packets = 0, udp_packets = 0, icmp_packets = 0, other_packets =
-    0, fragments = 0;
+    0, fragments = 0, def_slow, def_reass;
+
+  def_slow = is_output_feature ? NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH :
+    NAT_NEXT_IN2OUT_ED_SLOW_PATH;
+
+  def_reass = is_output_feature ? NAT_NEXT_IN2OUT_ED_OUTPUT_REASS :
+    NAT_NEXT_IN2OUT_ED_REASS;
 
   stats_node_index = is_slow_path ? sm->ed_in2out_slowpath_node_index :
     sm->ed_in2out_node_index;
@@ -900,10 +896,22 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
          b0 = vlib_get_buffer (vm, bi0);
          b1 = vlib_get_buffer (vm, bi1);
 
-         next0 = NAT_IN2OUT_ED_NEXT_LOOKUP;
-
          if (is_output_feature)
-           iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length;
+           {
+             // output feature fast path is enabled on the arc
+             // we need new arc_next feature
+             if (PREDICT_TRUE (!is_slow_path))
+               {
+                 vnet_feature_next (&nat_buffer_opaque (b0)->arc_next, b0);
+                 vnet_feature_next (&nat_buffer_opaque (b1)->arc_next, b1);
+               }
+
+             iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length;
+             iph_offset1 = vnet_buffer (b1)->ip.save_rewrite_length;
+           }
+
+         next0 = nat_buffer_opaque (b0)->arc_next;
+         next1 = nat_buffer_opaque (b1)->arc_next;
 
          ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) +
                                  iph_offset0);
@@ -919,7 +927,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
              icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
                                           ICMP4_time_exceeded_ttl_exceeded_in_transit,
                                           0);
-             next0 = NAT_IN2OUT_ED_NEXT_ICMP_ERROR;
+             next0 = NAT_NEXT_ICMP_ERROR;
              goto trace00;
            }
 
@@ -937,7 +945,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                                                      thread_index, now, vm,
                                                      node);
                  if (!s0)
-                   next0 = NAT_IN2OUT_ED_NEXT_DROP;
+                   next0 = NAT_NEXT_DROP;
                  other_packets++;
                  goto trace00;
                }
@@ -955,13 +963,13 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
            {
              if (PREDICT_FALSE (proto0 == ~0))
                {
-                 next0 = NAT_IN2OUT_ED_NEXT_SLOW_PATH;
+                 next0 = def_slow;
                  goto trace00;
                }
 
              if (ip4_is_fragment (ip0))
                {
-                 next0 = NAT_IN2OUT_ED_NEXT_REASS;
+                 next0 = def_reass;
                  fragments++;
                  goto trace00;
                }
@@ -976,7 +984,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
 
              if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
                {
-                 next0 = NAT_IN2OUT_ED_NEXT_SLOW_PATH;
+                 next0 = def_slow;
                  goto trace00;
                }
            }
@@ -1012,7 +1020,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                    slow_path_ed (sm, b0, rx_fib_index0, &kv0, &s0, node,
                                  next0, thread_index, now, tcp0);
 
-                 if (PREDICT_FALSE (next0 == NAT_IN2OUT_ED_NEXT_DROP))
+                 if (PREDICT_FALSE (next0 == NAT_NEXT_DROP))
                    goto trace00;
 
                  if (PREDICT_FALSE (!s0))
@@ -1020,7 +1028,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                }
              else
                {
-                 next0 = NAT_IN2OUT_ED_NEXT_SLOW_PATH;
+                 next0 = def_slow;
                  goto trace00;
                }
            }
@@ -1126,13 +1134,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                t->session_index = s0 - tsm->sessions;
            }
 
-         pkts_processed += next0 == NAT_IN2OUT_ED_NEXT_LOOKUP;
-
-
-         next1 = NAT_IN2OUT_ED_NEXT_LOOKUP;
-
-         if (is_output_feature)
-           iph_offset1 = vnet_buffer (b1)->ip.save_rewrite_length;
+         pkts_processed += next0 == nat_buffer_opaque (b0)->arc_next;
 
          ip1 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b1) +
                                  iph_offset1);
@@ -1148,7 +1150,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
              icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded,
                                           ICMP4_time_exceeded_ttl_exceeded_in_transit,
                                           0);
-             next1 = NAT_IN2OUT_ED_NEXT_ICMP_ERROR;
+             next1 = NAT_NEXT_ICMP_ERROR;
              goto trace01;
            }
 
@@ -1166,7 +1168,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                                                      thread_index, now, vm,
                                                      node);
                  if (!s1)
-                   next1 = NAT_IN2OUT_ED_NEXT_DROP;
+                   next1 = NAT_NEXT_DROP;
                  other_packets++;
                  goto trace01;
                }
@@ -1184,13 +1186,13 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
            {
              if (PREDICT_FALSE (proto1 == ~0))
                {
-                 next1 = NAT_IN2OUT_ED_NEXT_SLOW_PATH;
+                 next1 = def_slow;
                  goto trace01;
                }
 
              if (ip4_is_fragment (ip1))
                {
-                 next1 = NAT_IN2OUT_ED_NEXT_REASS;
+                 next1 = def_reass;
                  fragments++;
                  goto trace01;
                }
@@ -1205,7 +1207,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
 
              if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP))
                {
-                 next1 = NAT_IN2OUT_ED_NEXT_SLOW_PATH;
+                 next1 = def_slow;
                  goto trace01;
                }
            }
@@ -1241,7 +1243,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                    slow_path_ed (sm, b1, rx_fib_index1, &kv1, &s1, node,
                                  next1, thread_index, now, tcp1);
 
-                 if (PREDICT_FALSE (next1 == NAT_IN2OUT_ED_NEXT_DROP))
+                 if (PREDICT_FALSE (next1 == NAT_NEXT_DROP))
                    goto trace01;
 
                  if (PREDICT_FALSE (!s1))
@@ -1249,7 +1251,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                }
              else
                {
-                 next1 = NAT_IN2OUT_ED_NEXT_SLOW_PATH;
+                 next1 = def_slow;
                  goto trace01;
                }
            }
@@ -1355,7 +1357,8 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                t->session_index = s1 - tsm->sessions;
            }
 
-         pkts_processed += next1 == NAT_IN2OUT_ED_NEXT_LOOKUP;
+         pkts_processed += next1 == nat_buffer_opaque (b1)->arc_next;
+
 
          /* verify speculative enqueues, maybe switch current next frame */
          vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
@@ -1387,10 +1390,18 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
          n_left_to_next -= 1;
 
          b0 = vlib_get_buffer (vm, bi0);
-         next0 = NAT_IN2OUT_ED_NEXT_LOOKUP;
 
          if (is_output_feature)
-           iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length;
+           {
+             // output feature fast path is enabled on the arc
+             // we need new arc_next feature
+             if (PREDICT_TRUE (!is_slow_path))
+               vnet_feature_next (&nat_buffer_opaque (b0)->arc_next, b0);
+
+             iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length;
+           }
+
+         next0 = nat_buffer_opaque (b0)->arc_next;
 
          ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) +
                                  iph_offset0);
@@ -1406,7 +1417,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
              icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
                                           ICMP4_time_exceeded_ttl_exceeded_in_transit,
                                           0);
-             next0 = NAT_IN2OUT_ED_NEXT_ICMP_ERROR;
+             next0 = NAT_NEXT_ICMP_ERROR;
              goto trace0;
            }
 
@@ -1424,7 +1435,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                                                      thread_index, now, vm,
                                                      node);
                  if (!s0)
-                   next0 = NAT_IN2OUT_ED_NEXT_DROP;
+                   next0 = NAT_NEXT_DROP;
                  other_packets++;
                  goto trace0;
                }
@@ -1442,13 +1453,13 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
            {
              if (PREDICT_FALSE (proto0 == ~0))
                {
-                 next0 = NAT_IN2OUT_ED_NEXT_SLOW_PATH;
+                 next0 = def_slow;
                  goto trace0;
                }
 
              if (ip4_is_fragment (ip0))
                {
-                 next0 = NAT_IN2OUT_ED_NEXT_REASS;
+                 next0 = def_reass;
                  fragments++;
                  goto trace0;
                }
@@ -1463,7 +1474,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
 
              if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
                {
-                 next0 = NAT_IN2OUT_ED_NEXT_SLOW_PATH;
+                 next0 = def_slow;
                  goto trace0;
                }
            }
@@ -1499,7 +1510,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                    slow_path_ed (sm, b0, rx_fib_index0, &kv0, &s0, node,
                                  next0, thread_index, now, tcp0);
 
-                 if (PREDICT_FALSE (next0 == NAT_IN2OUT_ED_NEXT_DROP))
+                 if (PREDICT_FALSE (next0 == NAT_NEXT_DROP))
                    goto trace0;
 
                  if (PREDICT_FALSE (!s0))
@@ -1507,7 +1518,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                }
              else
                {
-                 next0 = NAT_IN2OUT_ED_NEXT_SLOW_PATH;
+                 next0 = def_slow;
                  goto trace0;
                }
            }
@@ -1612,7 +1623,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
                t->session_index = s0 - tsm->sessions;
            }
 
-         pkts_processed += next0 == NAT_IN2OUT_ED_NEXT_LOOKUP;
+         pkts_processed += next0 == nat_buffer_opaque (b0)->arc_next;
 
          /* verify speculative enqueue, maybe switch current next frame */
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
@@ -1642,115 +1653,6 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm,
   return frame->n_vectors;
 }
 
-VLIB_NODE_FN (nat44_ed_in2out_node) (vlib_main_t * vm,
-                                    vlib_node_runtime_t * node,
-                                    vlib_frame_t * frame)
-{
-  return nat44_ed_in2out_node_fn_inline (vm, node, frame, 0, 0);
-}
-
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (nat44_ed_in2out_node) = {
-  .name = "nat44-ed-in2out",
-  .vector_size = sizeof (u32),
-  .format_trace = format_nat_in2out_ed_trace,
-  .type = VLIB_NODE_TYPE_INTERNAL,
-  .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings),
-  .error_strings = nat_in2out_ed_error_strings,
-  .runtime_data_bytes = sizeof (snat_runtime_t),
-  .n_next_nodes = NAT_IN2OUT_ED_N_NEXT,
-  .next_nodes = {
-    [NAT_IN2OUT_ED_NEXT_DROP] = "error-drop",
-    [NAT_IN2OUT_ED_NEXT_LOOKUP] = "ip4-lookup",
-    [NAT_IN2OUT_ED_NEXT_SLOW_PATH] = "nat44-ed-in2out-slowpath",
-    [NAT_IN2OUT_ED_NEXT_ICMP_ERROR] = "ip4-icmp-error",
-    [NAT_IN2OUT_ED_NEXT_REASS] = "nat44-ed-in2out-reass",
-  },
-};
-/* *INDENT-ON* */
-
-VLIB_NODE_FN (nat44_ed_in2out_output_node) (vlib_main_t * vm,
-                                           vlib_node_runtime_t * node,
-                                           vlib_frame_t * frame)
-{
-  return nat44_ed_in2out_node_fn_inline (vm, node, frame, 0, 1);
-}
-
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (nat44_ed_in2out_output_node) = {
-  .name = "nat44-ed-in2out-output",
-  .vector_size = sizeof (u32),
-  .format_trace = format_nat_in2out_ed_trace,
-  .type = VLIB_NODE_TYPE_INTERNAL,
-  .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings),
-  .error_strings = nat_in2out_ed_error_strings,
-  .runtime_data_bytes = sizeof (snat_runtime_t),
-  .n_next_nodes = NAT_IN2OUT_ED_N_NEXT,
-  .next_nodes = {
-    [NAT_IN2OUT_ED_NEXT_DROP] = "error-drop",
-    [NAT_IN2OUT_ED_NEXT_LOOKUP] = "interface-output",
-    [NAT_IN2OUT_ED_NEXT_SLOW_PATH] = "nat44-ed-in2out-output-slowpath",
-    [NAT_IN2OUT_ED_NEXT_ICMP_ERROR] = "ip4-icmp-error",
-    [NAT_IN2OUT_ED_NEXT_REASS] = "nat44-ed-in2out-reass-output",
-  },
-};
-/* *INDENT-ON* */
-
-VLIB_NODE_FN (nat44_ed_in2out_slowpath_node) (vlib_main_t * vm,
-                                             vlib_node_runtime_t * node,
-                                             vlib_frame_t * frame)
-{
-  return nat44_ed_in2out_node_fn_inline (vm, node, frame, 1, 0);
-}
-
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (nat44_ed_in2out_slowpath_node) = {
-  .name = "nat44-ed-in2out-slowpath",
-  .vector_size = sizeof (u32),
-  .format_trace = format_nat_in2out_ed_trace,
-  .type = VLIB_NODE_TYPE_INTERNAL,
-  .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings),
-  .error_strings = nat_in2out_ed_error_strings,
-  .runtime_data_bytes = sizeof (snat_runtime_t),
-  .n_next_nodes = NAT_IN2OUT_ED_N_NEXT,
-  .next_nodes = {
-    [NAT_IN2OUT_ED_NEXT_DROP] = "error-drop",
-    [NAT_IN2OUT_ED_NEXT_LOOKUP] = "ip4-lookup",
-    [NAT_IN2OUT_ED_NEXT_SLOW_PATH] = "nat44-ed-in2out-slowpath",
-    [NAT_IN2OUT_ED_NEXT_ICMP_ERROR] = "ip4-icmp-error",
-    [NAT_IN2OUT_ED_NEXT_REASS] = "nat44-ed-in2out-reass",
-  },
-};
-/* *INDENT-ON* */
-
-VLIB_NODE_FN (nat44_ed_in2out_output_slowpath_node) (vlib_main_t * vm,
-                                                    vlib_node_runtime_t *
-                                                    node,
-                                                    vlib_frame_t * frame)
-{
-  return nat44_ed_in2out_node_fn_inline (vm, node, frame, 1, 1);
-}
-
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (nat44_ed_in2out_output_slowpath_node) = {
-  .name = "nat44-ed-in2out-output-slowpath",
-  .vector_size = sizeof (u32),
-  .format_trace = format_nat_in2out_ed_trace,
-  .type = VLIB_NODE_TYPE_INTERNAL,
-  .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings),
-  .error_strings = nat_in2out_ed_error_strings,
-  .runtime_data_bytes = sizeof (snat_runtime_t),
-  .n_next_nodes = NAT_IN2OUT_ED_N_NEXT,
-  .next_nodes = {
-    [NAT_IN2OUT_ED_NEXT_DROP] = "error-drop",
-    [NAT_IN2OUT_ED_NEXT_LOOKUP] = "interface-output",
-    [NAT_IN2OUT_ED_NEXT_SLOW_PATH] = "nat44-ed-in2out-output-slowpath",
-    [NAT_IN2OUT_ED_NEXT_ICMP_ERROR] = "ip4-icmp-error",
-    [NAT_IN2OUT_ED_NEXT_REASS] = "nat44-ed-in2out-reass",
-  },
-};
-/* *INDENT-ON* */
-
 static inline uword
 nat44_ed_in2out_reass_node_fn_inline (vlib_main_t * vm,
                                      vlib_node_runtime_t * node,
@@ -1758,7 +1660,7 @@ nat44_ed_in2out_reass_node_fn_inline (vlib_main_t * vm,
                                      int is_output_feature)
 {
   u32 n_left_from, *from, *to_next;
-  nat_in2out_ed_next_t next_index;
+  nat_next_t next_index;
   u32 pkts_processed = 0, cached_fragments = 0;
   snat_main_t *sm = &snat_main;
   f64 now = vlib_time_now (vm);
@@ -1805,7 +1707,7 @@ nat44_ed_in2out_reass_node_fn_inline (vlib_main_t * vm,
 
          b0 = vlib_get_buffer (vm, bi0);
 
-         next0 = NAT_IN2OUT_ED_NEXT_LOOKUP;
+         next0 = nat_buffer_opaque (b0)->arc_next;
 
          sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
          rx_fib_index0 =
@@ -1814,7 +1716,7 @@ nat44_ed_in2out_reass_node_fn_inline (vlib_main_t * vm,
 
          if (PREDICT_FALSE (nat_reass_is_drop_frag (0)))
            {
-             next0 = NAT_IN2OUT_ED_NEXT_DROP;
+             next0 = NAT_NEXT_DROP;
              b0->error = node->errors[NAT_IN2OUT_ED_ERROR_DROP_FRAGMENT];
              goto trace0;
            }
@@ -1838,7 +1740,7 @@ nat44_ed_in2out_reass_node_fn_inline (vlib_main_t * vm,
 
          if (PREDICT_FALSE (!reass0))
            {
-             next0 = NAT_IN2OUT_ED_NEXT_DROP;
+             next0 = NAT_NEXT_DROP;
              b0->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_REASS];
              nat_elog_notice ("maximum reassemblies exceeded");
              goto trace0;
@@ -1861,7 +1763,7 @@ nat44_ed_in2out_reass_node_fn_inline (vlib_main_t * vm,
                    (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
                     next0, now, thread_index, &s0);
 
-                 if (PREDICT_TRUE (next0 != NAT_IN2OUT_ED_NEXT_DROP))
+                 if (PREDICT_TRUE (next0 != NAT_NEXT_DROP))
                    {
                      if (s0)
                        reass0->sess_index = s0 - per_thread_data->sessions;
@@ -1914,7 +1816,7 @@ nat44_ed_in2out_reass_node_fn_inline (vlib_main_t * vm,
                                        &s0, node, next0, thread_index, now,
                                        tcp0);
 
-                 if (PREDICT_FALSE (next0 == NAT_IN2OUT_ED_NEXT_DROP))
+                 if (PREDICT_FALSE (next0 == NAT_NEXT_DROP))
                    goto trace0;
 
                  if (PREDICT_FALSE (!s0))
@@ -1945,7 +1847,7 @@ nat44_ed_in2out_reass_node_fn_inline (vlib_main_t * vm,
                      b0->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_FRAG];
                      nat_elog_notice
                        ("maximum fragments per reassembly exceeded");
-                     next0 = NAT_IN2OUT_ED_NEXT_DROP;
+                     next0 = NAT_NEXT_DROP;
                      goto trace0;
                    }
                  cached0 = 1;
@@ -2060,7 +1962,7 @@ nat44_ed_in2out_reass_node_fn_inline (vlib_main_t * vm,
            }
          else
            {
-             pkts_processed += next0 != NAT_IN2OUT_ED_NEXT_DROP;
+             pkts_processed += next0 == nat_buffer_opaque (b0)->arc_next;
 
              /* verify speculative enqueue, maybe switch current next frame */
              vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
@@ -2102,13 +2004,95 @@ nat44_ed_in2out_reass_node_fn_inline (vlib_main_t * vm,
 
   nat_send_all_to_node (vm, fragments_to_drop, node,
                        &node->errors[NAT_IN2OUT_ED_ERROR_DROP_FRAGMENT],
-                       NAT_IN2OUT_ED_NEXT_DROP);
+                       NAT_NEXT_DROP);
 
   vec_free (fragments_to_drop);
   vec_free (fragments_to_loopback);
   return frame->n_vectors;
 }
 
+VLIB_NODE_FN (nat44_ed_in2out_node) (vlib_main_t * vm,
+                                    vlib_node_runtime_t * node,
+                                    vlib_frame_t * frame)
+{
+  return nat44_ed_in2out_node_fn_inline (vm, node, frame, 0, 0);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat44_ed_in2out_node) = {
+  .name = "nat44-ed-in2out",
+  .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
+  .format_trace = format_nat_in2out_ed_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings),
+  .error_strings = nat_in2out_ed_error_strings,
+  .runtime_data_bytes = sizeof (snat_runtime_t),
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FN (nat44_ed_in2out_output_node) (vlib_main_t * vm,
+                                           vlib_node_runtime_t * node,
+                                           vlib_frame_t * frame)
+{
+  return nat44_ed_in2out_node_fn_inline (vm, node, frame, 0, 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat44_ed_in2out_output_node) = {
+  .name = "nat44-ed-in2out-output",
+  .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
+  .format_trace = format_nat_in2out_ed_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings),
+  .error_strings = nat_in2out_ed_error_strings,
+  .runtime_data_bytes = sizeof (snat_runtime_t),
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FN (nat44_ed_in2out_slowpath_node) (vlib_main_t * vm,
+                                             vlib_node_runtime_t * node,
+                                             vlib_frame_t * frame)
+{
+  return nat44_ed_in2out_node_fn_inline (vm, node, frame, 1, 0);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat44_ed_in2out_slowpath_node) = {
+  .name = "nat44-ed-in2out-slowpath",
+  .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
+  .format_trace = format_nat_in2out_ed_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings),
+  .error_strings = nat_in2out_ed_error_strings,
+  .runtime_data_bytes = sizeof (snat_runtime_t),
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FN (nat44_ed_in2out_output_slowpath_node) (vlib_main_t * vm,
+                                                    vlib_node_runtime_t *
+                                                    node,
+                                                    vlib_frame_t * frame)
+{
+  return nat44_ed_in2out_node_fn_inline (vm, node, frame, 1, 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat44_ed_in2out_output_slowpath_node) = {
+  .name = "nat44-ed-in2out-output-slowpath",
+  .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
+  .format_trace = format_nat_in2out_ed_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings),
+  .error_strings = nat_in2out_ed_error_strings,
+  .runtime_data_bytes = sizeof (snat_runtime_t),
+};
+/* *INDENT-ON* */
+
+
 VLIB_NODE_FN (nat44_ed_in2out_reass_node) (vlib_main_t * vm,
                                           vlib_node_runtime_t * node,
                                           vlib_frame_t * frame)
@@ -2120,18 +2104,11 @@ VLIB_NODE_FN (nat44_ed_in2out_reass_node) (vlib_main_t * vm,
 VLIB_REGISTER_NODE (nat44_ed_in2out_reass_node) = {
   .name = "nat44-ed-in2out-reass",
   .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
   .format_trace = format_nat44_reass_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
   .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings),
   .error_strings = nat_in2out_ed_error_strings,
-  .n_next_nodes = NAT_IN2OUT_ED_N_NEXT,
-  .next_nodes = {
-    [NAT_IN2OUT_ED_NEXT_DROP] = "error-drop",
-    [NAT_IN2OUT_ED_NEXT_LOOKUP] = "ip4-lookup",
-    [NAT_IN2OUT_ED_NEXT_SLOW_PATH] = "nat44-in2out-slowpath",
-    [NAT_IN2OUT_ED_NEXT_ICMP_ERROR] = "ip4-icmp-error",
-    [NAT_IN2OUT_ED_NEXT_REASS] = "nat44-ed-in2out-reass",
-  },
 };
 /* *INDENT-ON* */
 
@@ -2146,18 +2123,39 @@ VLIB_NODE_FN (nat44_ed_in2out_reass_output_node) (vlib_main_t * vm,
 VLIB_REGISTER_NODE (nat44_ed_in2out_reass_output_node) = {
   .name = "nat44-ed-in2out-reass-output",
   .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
   .format_trace = format_nat44_reass_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
   .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings),
   .error_strings = nat_in2out_ed_error_strings,
-  .n_next_nodes = NAT_IN2OUT_ED_N_NEXT,
-  .next_nodes = {
-    [NAT_IN2OUT_ED_NEXT_DROP] = "error-drop",
-    [NAT_IN2OUT_ED_NEXT_LOOKUP] = "interface-output",
-    [NAT_IN2OUT_ED_NEXT_SLOW_PATH] = "nat44-in2out-slowpath",
-    [NAT_IN2OUT_ED_NEXT_ICMP_ERROR] = "ip4-icmp-error",
-    [NAT_IN2OUT_ED_NEXT_REASS] = "nat44-ed-in2out-reass",
-  },
+};
+/* *INDENT-ON* */
+
+static u8 *
+format_nat_pre_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 *);
+  nat_pre_trace_t *t = va_arg (*args, nat_pre_trace_t *);
+  return format (s, "in2out next_index %d", t->next_index);
+}
+
+VLIB_NODE_FN (nat_pre_in2out_node) (vlib_main_t * vm,
+                                   vlib_node_runtime_t * node,
+                                   vlib_frame_t * frame)
+{
+  return nat_pre_node_fn_inline (vm, node, frame,
+                                NAT_NEXT_IN2OUT_ED_FAST_PATH);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat_pre_in2out_node) = {
+  .name = "nat-pre-in2out",
+  .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
+  .format_trace = format_nat_pre_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = 0,
 };
 /* *INDENT-ON* */
 
index 85072bc..0a30caf 100755 (executable)
 snat_main_t snat_main;
 
 /* *INDENT-OFF* */
-
 /* Hook up input features */
+VNET_FEATURE_INIT (nat_pre_in2out, static) = {
+  .arc_name = "ip4-unicast",
+  .node_name = "nat-pre-in2out",
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
+};
+VNET_FEATURE_INIT (nat_pre_out2in, static) = {
+  .arc_name = "ip4-unicast",
+  .node_name = "nat-pre-out2in",
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
+                               "ip4-dhcp-client-detect"),
+};
+VNET_FEATURE_INIT (snat_in2out_worker_handoff, static) = {
+  .arc_name = "ip4-unicast",
+  .node_name = "nat44-in2out-worker-handoff",
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
+};
+VNET_FEATURE_INIT (snat_out2in_worker_handoff, static) = {
+  .arc_name = "ip4-unicast",
+  .node_name = "nat44-out2in-worker-handoff",
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
+                               "ip4-dhcp-client-detect"),
+};
 VNET_FEATURE_INIT (ip4_snat_in2out, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-in2out",
@@ -89,17 +110,6 @@ VNET_FEATURE_INIT (ip4_nat44_ed_classify, static) = {
   .node_name = "nat44-ed-classify",
   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
-VNET_FEATURE_INIT (ip4_snat_in2out_worker_handoff, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "nat44-in2out-worker-handoff",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
-};
-VNET_FEATURE_INIT (ip4_snat_out2in_worker_handoff, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "nat44-out2in-worker-handoff",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
-                               "ip4-dhcp-client-detect"),
-};
 VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-handoff-classify",
@@ -1775,18 +1785,20 @@ snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
       else if (sm->deterministic)
        feature_name = is_inside ? "nat44-det-in2out" : "nat44-det-out2in";
       else if (sm->endpoint_dependent)
-       feature_name = is_inside ? "nat44-ed-in2out" : "nat44-ed-out2in";
+       {
+         feature_name = is_inside ? "nat-pre-in2out" : "nat-pre-out2in";
+       }
       else
        feature_name = is_inside ? "nat44-in2out" : "nat44-out2in";
     }
 
   if (sm->fq_in2out_index == ~0 && !sm->deterministic && sm->num_workers > 1)
-    sm->fq_in2out_index = vlib_frame_queue_main_init (sm->in2out_node_index,
-                                                     NAT_FQ_NELTS);
+    sm->fq_in2out_index =
+      vlib_frame_queue_main_init (sm->handoff_in2out_index, NAT_FQ_NELTS);
 
   if (sm->fq_out2in_index == ~0 && !sm->deterministic && sm->num_workers > 1)
-    sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index,
-                                                     NAT_FQ_NELTS);
+    sm->fq_out2in_index =
+      vlib_frame_queue_main_init (sm->handoff_out2in_index, NAT_FQ_NELTS);
 
   if (!is_inside)
     {
@@ -1844,8 +1856,8 @@ feature_set:
                 else if (sm->endpoint_dependent)
                   {
                     del_feature_name = "nat44-ed-classify";
-                    feature_name = !is_inside ?  "nat44-ed-in2out" :
-                                                 "nat44-ed-out2in";
+                    feature_name = !is_inside ?  "nat-pre-in2out" :
+                                                 "nat-pre-out2in";
                   }
                 else
                   {
@@ -1907,8 +1919,9 @@ feature_set:
               }
             else if (sm->endpoint_dependent)
               {
-                del_feature_name = !is_inside ?  "nat44-ed-in2out" :
-                                                 "nat44-ed-out2in";
+                del_feature_name = !is_inside ?  "nat-pre-in2out" :
+                                                 "nat-pre-out2in";
+
                 feature_name = "nat44-ed-classify";
               }
             else
@@ -2074,7 +2087,7 @@ feature_set:
     {
       if (sm->endpoint_dependent)
        {
-         vnet_feature_enable_disable ("ip4-unicast", "nat44-ed-out2in",
+         vnet_feature_enable_disable ("ip4-unicast", "nat-pre-out2in",
                                       sw_if_index, !is_del, 0, 0);
          vnet_feature_enable_disable ("ip4-output", "nat44-ed-in2out-output",
                                       sw_if_index, !is_del, 0, 0);
@@ -2091,11 +2104,11 @@ feature_set:
 fq:
   if (sm->fq_in2out_output_index == ~0 && sm->num_workers > 1)
     sm->fq_in2out_output_index =
-      vlib_frame_queue_main_init (sm->in2out_output_node_index, 0);
+      vlib_frame_queue_main_init (sm->handoff_in2out_output_index, 0);
 
   if (sm->fq_out2in_index == ~0 && sm->num_workers > 1)
     sm->fq_out2in_index =
-      vlib_frame_queue_main_init (sm->out2in_node_index, 0);
+      vlib_frame_queue_main_init (sm->handoff_out2in_index, 0);
 
   /* *INDENT-OFF* */
   pool_foreach (i, sm->output_feature_interfaces,
@@ -2316,6 +2329,19 @@ snat_init (vlib_main_t * vm)
   node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
   sm->error_node_index = node->index;
 
+  node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-in2out");
+  sm->pre_in2out_node_index = node->index;
+  node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-out2in");
+  sm->pre_out2in_node_index = node->index;
+
+  node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-in2out");
+  sm->pre_in2out_node_index = node->index;
+
+  node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-out2in");
+  sm->pre_out2in_node_index = node->index;
+
+  // TODO: output ?? (special node)
+
   node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out");
   sm->in2out_node_index = node->index;
   node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-output");
@@ -3839,9 +3865,16 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
        {
          sm->worker_in2out_cb = nat44_ed_get_worker_in2out_cb;
          sm->worker_out2in_cb = nat44_ed_get_worker_out2in_cb;
+
+         sm->handoff_out2in_index = nat_pre_out2in_node.index;
+         sm->handoff_in2out_index = nat_pre_in2out_node.index;
+         // TODO: test
+         sm->handoff_in2out_output_index = nat44_ed_in2out_output_node.index;
+
          sm->in2out_node_index = nat44_ed_in2out_node.index;
          sm->in2out_output_node_index = nat44_ed_in2out_output_node.index;
          sm->out2in_node_index = nat44_ed_out2in_node.index;
+
          sm->icmp_match_in2out_cb = icmp_match_in2out_ed;
          sm->icmp_match_out2in_cb = icmp_match_out2in_ed;
          nat_affinity_init (vm);
@@ -3852,6 +3885,12 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
        {
          sm->worker_in2out_cb = snat_get_worker_in2out_cb;
          sm->worker_out2in_cb = snat_get_worker_out2in_cb;
+
+         sm->handoff_out2in_index = snat_in2out_node.index;
+         sm->handoff_in2out_index = snat_out2in_node.index;
+         // TODO: test
+         sm->handoff_in2out_output_index = snat_in2out_output_node.index;
+
          sm->in2out_node_index = snat_in2out_node.index;
          sm->in2out_output_node_index = snat_in2out_output_node.index;
          sm->out2in_node_index = snat_out2in_node.index;
@@ -4263,6 +4302,38 @@ nat_set_alloc_addr_and_port_default (void)
   sm->alloc_addr_and_port = nat_alloc_addr_and_port_default;
 }
 
+VLIB_NODE_FN (nat_default_node) (vlib_main_t * vm,
+                                vlib_node_runtime_t * node,
+                                vlib_frame_t * frame)
+{
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat_default_node) = {
+  .name = "nat-default",
+  .vector_size = sizeof (u32),
+  .format_trace = 0,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = 0,
+  .n_next_nodes = NAT_N_NEXT,
+  .next_nodes = {
+    [NAT_NEXT_DROP] = "error-drop",
+    [NAT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
+    [NAT_NEXT_IN2OUT_PRE] = "nat-pre-in2out",
+    [NAT_NEXT_OUT2IN_PRE] = "nat-pre-out2in",
+    [NAT_NEXT_IN2OUT_ED_FAST_PATH] = "nat44-ed-in2out",
+    [NAT_NEXT_IN2OUT_ED_SLOW_PATH] = "nat44-ed-in2out-slowpath",
+    [NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH] = "nat44-ed-in2out-output-slowpath",
+    [NAT_NEXT_IN2OUT_ED_REASS] = "nat44-ed-in2out-reass",
+    [NAT_NEXT_IN2OUT_ED_OUTPUT_REASS] = "nat44-ed-in2out-reass-output",
+    [NAT_NEXT_OUT2IN_ED_FAST_PATH] = "nat44-ed-out2in",
+    [NAT_NEXT_OUT2IN_ED_SLOW_PATH] = "nat44-ed-out2in-slowpath",
+    [NAT_NEXT_OUT2IN_ED_REASS] = "nat44-ed-out2in-reass",
+  },
+};
+/* *INDENT-ON* */
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index f7ce5f9..b65ad1b 100644 (file)
 /* NAT buffer flags */
 #define SNAT_FLAG_HAIRPINNING (1 << 0)
 
+typedef struct
+{
+  u32 arc_next;
+} nat_buffer_opaque_t;
+
+typedef enum
+{
+  NAT_NEXT_DROP,
+  NAT_NEXT_ICMP_ERROR,
+  NAT_NEXT_IN2OUT_PRE,
+  NAT_NEXT_OUT2IN_PRE,
+  NAT_NEXT_IN2OUT_ED_FAST_PATH,
+  NAT_NEXT_IN2OUT_ED_SLOW_PATH,
+  NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH,
+  NAT_NEXT_IN2OUT_ED_REASS,
+  NAT_NEXT_IN2OUT_ED_OUTPUT_REASS,
+  NAT_NEXT_OUT2IN_ED_FAST_PATH,
+  NAT_NEXT_OUT2IN_ED_SLOW_PATH,
+  NAT_NEXT_OUT2IN_ED_REASS,
+  NAT_N_NEXT,
+} nat_next_t;
+
+typedef struct
+{
+  u32 next_index;
+} nat_pre_trace_t;
+
+#define nat_buffer_opaque(b) \
+  ((nat_buffer_opaque_t *)((vnet_buffer_opaque2_t *)b->opaque2)->__unused2)
+
+/*
+STATIC_ASSERT (sizeof (nat_buffer_opaque_t) <=
+               STRUCT_SIZE_OF (vnet_buffer_opaque_t, unused),
+               "Custom meta-data too large for vnet_buffer_opaque_t");
+
+#define nat_buffer_opaque(b) \
+  ((nat_buffer_opaque_t *)((u8 *)((b)->opaque) + \
+    STRUCT_OFFSET_OF (vnet_buffer_opaque_t, unused)))*/
+
 /* session key (4-tuple) */
 typedef struct
 {
@@ -575,6 +614,15 @@ typedef struct snat_main_s
   /* node indexes */
   u32 error_node_index;
 
+  /* handoff fq nodes  */
+  u32 handoff_out2in_index;
+  u32 handoff_in2out_index;
+  u32 handoff_in2out_output_index;
+
+  /* respect feature arc nodes */
+  u32 pre_out2in_node_index;
+  u32 pre_in2out_node_index;
+
   u32 in2out_node_index;
   u32 in2out_output_node_index;
   u32 in2out_fast_node_index;
@@ -667,6 +715,12 @@ typedef struct
 } snat_runtime_t;
 
 extern snat_main_t snat_main;
+
+// nat pre ed next_node feature classification
+extern vlib_node_registration_t nat_default_node;
+extern vlib_node_registration_t nat_pre_in2out_node;
+extern vlib_node_registration_t nat_pre_out2in_node;
+
 extern vlib_node_registration_t snat_in2out_node;
 extern vlib_node_registration_t snat_in2out_output_node;
 extern vlib_node_registration_t snat_out2in_node;
index aa2bf73..b6ce4d7 100644 (file)
@@ -81,14 +81,13 @@ format_nat44_classify_trace (u8 * s, va_list * args)
 static inline uword
 nat44_classify_node_fn_inline (vlib_main_t * vm,
                               vlib_node_runtime_t * node,
-                              vlib_frame_t * frame, int is_ed)
+                              vlib_frame_t * frame)
 {
   u32 n_left_from, *from, *to_next;
   nat44_classify_next_t next_index;
   snat_main_t *sm = &snat_main;
   snat_static_mapping_t *m;
   u32 thread_index = vm->thread_index;
-  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
   u32 *fragments_to_drop = 0;
   u32 *fragments_to_loopback = 0;
   u32 next_in2out = 0, next_out2in = 0, frag_cached = 0;
@@ -107,12 +106,11 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
        {
          u32 bi0;
          vlib_buffer_t *b0;
-         u32 next0 = NAT44_CLASSIFY_NEXT_IN2OUT, sw_if_index0, rx_fib_index0;
+         u32 next0 = NAT44_CLASSIFY_NEXT_IN2OUT;
          ip4_header_t *ip0;
          snat_address_t *ap;
          snat_session_key_t m_key0;
          clib_bihash_kv_8_8_t kv0, value0;
-         clib_bihash_kv_16_8_t ed_kv0, ed_value0;
          udp_header_t *udp0;
          nat_reass_ip4_t *reass0;
          u8 cached0 = 0;
@@ -129,18 +127,47 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
          ip0 = vlib_buffer_get_current (b0);
          udp0 = ip4_next_header (ip0);
 
-         if (is_ed && ip0->protocol != IP_PROTOCOL_ICMP)
+          /* *INDENT-OFF* */
+          vec_foreach (ap, sm->addresses)
+            {
+              if (ip0->dst_address.as_u32 == ap->addr.as_u32)
+                {
+                  next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                  goto enqueue0;
+                }
+            }
+          /* *INDENT-ON* */
+
+         if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
            {
+             m_key0.addr = ip0->dst_address;
+             m_key0.port = 0;
+             m_key0.protocol = 0;
+             m_key0.fib_index = 0;
+             kv0.key = m_key0.as_u64;
+             /* try to classify the fragment based on IP header alone */
+             if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external,
+                                          &kv0, &value0))
+               {
+                 m = pool_elt_at_index (sm->static_mappings, value0.value);
+                 if (m->local_addr.as_u32 != m->external_addr.as_u32)
+                   next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                 goto enqueue0;
+               }
              if (!ip4_is_fragment (ip0) || ip4_is_first_fragment (ip0))
                {
                  /* process leading fragment/whole packet (with L4 header) */
-                 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
-                 rx_fib_index0 =
-                   fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
-                                                        sw_if_index0);
-                 make_ed_kv (&ed_kv0, &ip0->src_address, &ip0->dst_address,
-                             ip0->protocol, rx_fib_index0, udp0->src_port,
-                             udp0->dst_port);
+                 m_key0.port = clib_net_to_host_u16 (udp0->dst_port);
+                 m_key0.protocol = ip_proto_to_snat_proto (ip0->protocol);
+                 kv0.key = m_key0.as_u64;
+                 if (!clib_bihash_search_8_8
+                     (&sm->static_mapping_by_external, &kv0, &value0))
+                   {
+                     m =
+                       pool_elt_at_index (sm->static_mappings, value0.value);
+                     if (m->local_addr.as_u32 != m->external_addr.as_u32)
+                       next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                   }
                  if (ip4_is_fragment (ip0))
                    {
                      reass0 = nat_ip4_reass_find_or_create (ip0->src_address,
@@ -157,8 +184,219 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                          nat_elog_notice ("maximum reassemblies exceeded");
                          goto enqueue0;
                        }
-                     if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &ed_kv0,
-                                                   &ed_value0))
+                     /* save classification for future fragments and set past
+                      * fragments to be looped over and reprocessed */
+                     if (next0 == NAT44_CLASSIFY_NEXT_OUT2IN)
+                       reass0->classify_next =
+                         NAT_REASS_IP4_CLASSIFY_NEXT_OUT2IN;
+                     else
+                       reass0->classify_next =
+                         NAT_REASS_IP4_CLASSIFY_NEXT_IN2OUT;
+                     nat_ip4_reass_get_frags (reass0,
+                                              &fragments_to_loopback);
+                   }
+               }
+             else
+               {
+                 /* process non-first fragment */
+                 reass0 = nat_ip4_reass_find_or_create (ip0->src_address,
+                                                        ip0->dst_address,
+                                                        ip0->fragment_id,
+                                                        ip0->protocol,
+                                                        1,
+                                                        &fragments_to_drop);
+                 if (PREDICT_FALSE (!reass0))
+                   {
+                     next0 = NAT44_CLASSIFY_NEXT_DROP;
+                     b0->error =
+                       node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
+                     nat_elog_notice ("maximum reassemblies exceeded");
+                     goto enqueue0;
+                   }
+                 if (reass0->classify_next == NAT_REASS_IP4_CLASSIFY_NONE)
+                   /* first fragment still hasn't arrived */
+                   {
+                     if (nat_ip4_reass_add_fragment
+                         (thread_index, reass0, bi0, &fragments_to_drop))
+                       {
+                         b0->error =
+                           node->errors[NAT44_CLASSIFY_ERROR_MAX_FRAG];
+                         nat_elog_notice
+                           ("maximum fragments per reassembly exceeded");
+                         next0 = NAT44_CLASSIFY_NEXT_DROP;
+                         goto enqueue0;
+                       }
+                     cached0 = 1;
+                     goto enqueue0;
+                   }
+                 else if (reass0->classify_next ==
+                          NAT_REASS_IP4_CLASSIFY_NEXT_OUT2IN)
+                   next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                 else if (reass0->classify_next ==
+                          NAT_REASS_IP4_CLASSIFY_NEXT_IN2OUT)
+                   next0 = NAT44_CLASSIFY_NEXT_IN2OUT;
+               }
+           }
+
+       enqueue0:
+         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             nat44_classify_trace_t *t =
+               vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->cached = cached0;
+             if (!cached0)
+               t->next_in2out = next0 == NAT44_CLASSIFY_NEXT_IN2OUT ? 1 : 0;
+           }
+
+         if (cached0)
+           {
+             n_left_to_next++;
+             to_next--;
+             frag_cached++;
+           }
+         else
+           {
+             next_in2out += next0 == NAT44_CLASSIFY_NEXT_IN2OUT;
+             next_out2in += next0 == NAT44_CLASSIFY_NEXT_OUT2IN;
+
+             /* verify speculative enqueue, maybe switch current next frame */
+             vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                              to_next, n_left_to_next,
+                                              bi0, next0);
+           }
+
+         if (n_left_from == 0 && vec_len (fragments_to_loopback))
+           {
+             from = vlib_frame_vector_args (frame);
+             u32 len = vec_len (fragments_to_loopback);
+             if (len <= VLIB_FRAME_SIZE)
+               {
+                 clib_memcpy_fast (from, fragments_to_loopback,
+                                   sizeof (u32) * len);
+                 n_left_from = len;
+                 vec_reset_length (fragments_to_loopback);
+               }
+             else
+               {
+                 clib_memcpy_fast (from, fragments_to_loopback +
+                                   (len - VLIB_FRAME_SIZE),
+                                   sizeof (u32) * VLIB_FRAME_SIZE);
+                 n_left_from = VLIB_FRAME_SIZE;
+                 _vec_len (fragments_to_loopback) = len - VLIB_FRAME_SIZE;
+               }
+           }
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  nat_send_all_to_node (vm, fragments_to_drop, node, 0,
+                       NAT44_CLASSIFY_NEXT_DROP);
+
+  vec_free (fragments_to_drop);
+
+  vlib_node_increment_counter (vm, node->node_index,
+                              NAT44_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out);
+  vlib_node_increment_counter (vm, node->node_index,
+                              NAT44_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in);
+  vlib_node_increment_counter (vm, node->node_index,
+                              NAT44_CLASSIFY_ERROR_FRAG_CACHED, frag_cached);
+
+  return frame->n_vectors;
+}
+
+static inline uword
+nat44_ed_classify_node_fn_inline (vlib_main_t * vm,
+                                 vlib_node_runtime_t * node,
+                                 vlib_frame_t * frame)
+{
+  u32 n_left_from, *from, *to_next;
+  nat44_classify_next_t next_index;
+  snat_main_t *sm = &snat_main;
+  snat_static_mapping_t *m;
+  u32 thread_index = vm->thread_index;
+  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
+  u32 *fragments_to_drop = 0;
+  u32 *fragments_to_loopback = 0;
+  u32 next_in2out = 0, next_out2in = 0, frag_cached = 0;
+  u8 in_loopback = 0;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0;
+         vlib_buffer_t *b0;
+         u32 next0 =
+           NAT_NEXT_IN2OUT_ED_FAST_PATH, sw_if_index0, rx_fib_index0;
+         ip4_header_t *ip0;
+         snat_address_t *ap;
+         snat_session_key_t m_key0;
+         clib_bihash_kv_8_8_t kv0, value0;
+         clib_bihash_kv_16_8_t ed_kv0, ed_value0;
+         udp_header_t *udp0;
+         nat_reass_ip4_t *reass0;
+         u8 cached0 = 0;
+
+         /* speculatively enqueue b0 to the current next frame */
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         ip0 = vlib_buffer_get_current (b0);
+         udp0 = ip4_next_header (ip0);
+
+         if (!in_loopback)
+           {
+             u32 arc_next = 0;
+
+             vnet_feature_next (&arc_next, b0);
+             nat_buffer_opaque (b0)->arc_next = arc_next;
+           }
+
+         if (ip0->protocol != IP_PROTOCOL_ICMP)
+           {
+             if (!ip4_is_fragment (ip0) || ip4_is_first_fragment (ip0))
+               {
+                 /* process leading fragment/whole packet (with L4 header) */
+                 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+                 rx_fib_index0 =
+                   fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
+                                                        sw_if_index0);
+                 make_ed_kv (&ed_kv0, &ip0->src_address,
+                             &ip0->dst_address, ip0->protocol,
+                             rx_fib_index0, udp0->src_port, udp0->dst_port);
+                 if (ip4_is_fragment (ip0))
+                   {
+                     reass0 =
+                       nat_ip4_reass_find_or_create (ip0->src_address,
+                                                     ip0->dst_address,
+                                                     ip0->fragment_id,
+                                                     ip0->protocol, 1,
+                                                     &fragments_to_drop);
+                     if (PREDICT_FALSE (!reass0))
+                       {
+                         next0 = NAT_NEXT_DROP;
+                         b0->error =
+                           node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
+                         nat_elog_notice ("maximum reassemblies exceeded");
+                         goto enqueue0;
+                       }
+                     if (!clib_bihash_search_16_8
+                         (&tsm->in2out_ed, &ed_kv0, &ed_value0))
                        {
                          /* session exists so classify as IN2OUT,
                           * save this information for future fragments and set
@@ -184,8 +422,8 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                  else
                    {
                      /* process whole packet */
-                     if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &ed_kv0,
-                                                   &ed_value0))
+                     if (!clib_bihash_search_16_8
+                         (&tsm->in2out_ed, &ed_kv0, &ed_value0))
                        goto enqueue0;
                      /* session doesn't exist so continue in code */
                    }
@@ -201,15 +439,16 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                                                         &fragments_to_drop);
                  if (PREDICT_FALSE (!reass0))
                    {
-                     next0 = NAT44_CLASSIFY_NEXT_DROP;
+                     next0 = NAT_NEXT_DROP;
                      b0->error =
                        node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
                      nat_elog_notice ("maximum reassemblies exceeded");
                      goto enqueue0;
                    }
                  /* check if first fragment has arrived */
-                 if (reass0->classify_next == NAT_REASS_IP4_CLASSIFY_NONE &&
-                     !(reass0->flags & NAT_REASS_FLAG_CLASSIFY_ED_CONTINUE))
+                 if (reass0->classify_next == NAT_REASS_IP4_CLASSIFY_NONE
+                     && !(reass0->flags &
+                          NAT_REASS_FLAG_CLASSIFY_ED_CONTINUE))
                    {
                      /* first fragment still hasn't arrived, cache this fragment */
                      if (nat_ip4_reass_add_fragment
@@ -219,7 +458,7 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                            node->errors[NAT44_CLASSIFY_ERROR_MAX_FRAG];
                          nat_elog_notice
                            ("maximum fragments per reassembly exceeded");
-                         next0 = NAT44_CLASSIFY_NEXT_DROP;
+                         next0 = NAT_NEXT_DROP;
                          goto enqueue0;
                        }
                      cached0 = 1;
@@ -239,7 +478,7 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
             {
               if (ip0->dst_address.as_u32 == ap->addr.as_u32)
                 {
-                  next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                  next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
                   goto enqueue0;
                 }
             }
@@ -258,7 +497,7 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                {
                  m = pool_elt_at_index (sm->static_mappings, value0.value);
                  if (m->local_addr.as_u32 != m->external_addr.as_u32)
-                   next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                   next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
                  goto enqueue0;
                }
              if (!ip4_is_fragment (ip0) || ip4_is_first_fragment (ip0))
@@ -273,7 +512,7 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                      m =
                        pool_elt_at_index (sm->static_mappings, value0.value);
                      if (m->local_addr.as_u32 != m->external_addr.as_u32)
-                       next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                       next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
                    }
                  if (ip4_is_fragment (ip0))
                    {
@@ -285,7 +524,7 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                                                             &fragments_to_drop);
                      if (PREDICT_FALSE (!reass0))
                        {
-                         next0 = NAT44_CLASSIFY_NEXT_DROP;
+                         next0 = NAT_NEXT_DROP;
                          b0->error =
                            node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
                          nat_elog_notice ("maximum reassemblies exceeded");
@@ -293,12 +532,10 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                        }
                      /* save classification for future fragments and set past
                       * fragments to be looped over and reprocessed */
-                     if (next0 == NAT44_CLASSIFY_NEXT_OUT2IN)
-                       reass0->classify_next =
-                         NAT_REASS_IP4_CLASSIFY_NEXT_OUT2IN;
+                     if (next0 == NAT_NEXT_OUT2IN_ED_FAST_PATH)
+                       reass0->classify_next = NAT_NEXT_OUT2IN_ED_REASS;
                      else
-                       reass0->classify_next =
-                         NAT_REASS_IP4_CLASSIFY_NEXT_IN2OUT;
+                       reass0->classify_next = NAT_NEXT_IN2OUT_ED_REASS;
                      nat_ip4_reass_get_frags (reass0,
                                               &fragments_to_loopback);
                    }
@@ -314,7 +551,7 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                                                         &fragments_to_drop);
                  if (PREDICT_FALSE (!reass0))
                    {
-                     next0 = NAT44_CLASSIFY_NEXT_DROP;
+                     next0 = NAT_NEXT_DROP;
                      b0->error =
                        node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
                      nat_elog_notice ("maximum reassemblies exceeded");
@@ -330,7 +567,7 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                            node->errors[NAT44_CLASSIFY_ERROR_MAX_FRAG];
                          nat_elog_notice
                            ("maximum fragments per reassembly exceeded");
-                         next0 = NAT44_CLASSIFY_NEXT_DROP;
+                         next0 = NAT_NEXT_DROP;
                          goto enqueue0;
                        }
                      cached0 = 1;
@@ -338,10 +575,10 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                    }
                  else if (reass0->classify_next ==
                           NAT_REASS_IP4_CLASSIFY_NEXT_OUT2IN)
-                   next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                   next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
                  else if (reass0->classify_next ==
                           NAT_REASS_IP4_CLASSIFY_NEXT_IN2OUT)
-                   next0 = NAT44_CLASSIFY_NEXT_IN2OUT;
+                   next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH;
                }
            }
 
@@ -353,7 +590,8 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
                vlib_add_trace (vm, node, b0, sizeof (*t));
              t->cached = cached0;
              if (!cached0)
-               t->next_in2out = next0 == NAT44_CLASSIFY_NEXT_IN2OUT ? 1 : 0;
+               t->next_in2out =
+                 next0 == NAT_NEXT_IN2OUT_ED_FAST_PATH ? 1 : 0;
            }
 
          if (cached0)
@@ -364,8 +602,8 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
            }
          else
            {
-             next_in2out += next0 == NAT44_CLASSIFY_NEXT_IN2OUT;
-             next_out2in += next0 == NAT44_CLASSIFY_NEXT_OUT2IN;
+             next_in2out += next0 == NAT_NEXT_IN2OUT_ED_FAST_PATH;
+             next_out2in += next0 == NAT_NEXT_OUT2IN_ED_FAST_PATH;
 
              /* verify speculative enqueue, maybe switch current next frame */
              vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
@@ -375,6 +613,7 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
 
          if (n_left_from == 0 && vec_len (fragments_to_loopback))
            {
+             in_loopback = 1;
              from = vlib_frame_vector_args (frame);
              u32 len = vec_len (fragments_to_loopback);
              if (len <= VLIB_FRAME_SIZE)
@@ -417,7 +656,7 @@ VLIB_NODE_FN (nat44_classify_node) (vlib_main_t * vm,
                                    vlib_node_runtime_t * node,
                                    vlib_frame_t * frame)
 {
-  return nat44_classify_node_fn_inline (vm, node, frame, 0);
+  return nat44_classify_node_fn_inline (vm, node, frame);
 }
 
 /* *INDENT-OFF* */
@@ -441,21 +680,16 @@ VLIB_NODE_FN (nat44_ed_classify_node) (vlib_main_t * vm,
                                       vlib_node_runtime_t * node,
                                       vlib_frame_t * frame)
 {
-  return nat44_classify_node_fn_inline (vm, node, frame, 1);
+  return nat44_ed_classify_node_fn_inline (vm, node, frame);
 }
 
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (nat44_ed_classify_node) = {
   .name = "nat44-ed-classify",
   .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
   .format_trace = format_nat44_classify_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
-  .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
-  .next_nodes = {
-    [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-ed-in2out",
-    [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-ed-out2in",
-    [NAT44_CLASSIFY_NEXT_DROP] = "error-drop",
-  },
 };
 /* *INDENT-ON* */
 
@@ -463,7 +697,7 @@ VLIB_NODE_FN (nat44_det_classify_node) (vlib_main_t * vm,
                                        vlib_node_runtime_t * node,
                                        vlib_frame_t * frame)
 {
-  return nat44_classify_node_fn_inline (vm, node, frame, 0);
+  return nat44_classify_node_fn_inline (vm, node, frame);
 }
 
 /* *INDENT-OFF* */
@@ -485,7 +719,7 @@ VLIB_NODE_FN (nat44_handoff_classify_node) (vlib_main_t * vm,
                                            vlib_node_runtime_t * node,
                                            vlib_frame_t * frame)
 {
-  return nat44_classify_node_fn_inline (vm, node, frame, 0);
+  return nat44_classify_node_fn_inline (vm, node, frame);
 }
 
 /* *INDENT-OFF* */
index c97c958..277f2de 100644 (file)
@@ -23,6 +23,7 @@
 #include <vnet/fib/ip4_fib.h>
 #include <vppinfra/error.h>
 #include <nat/nat.h>
+#include <nat/nat_inlines.h>
 
 typedef struct
 {
@@ -260,14 +261,11 @@ VLIB_NODE_FN (snat_in2out_worker_handoff_node) (vlib_main_t * vm,
 VLIB_REGISTER_NODE (snat_in2out_worker_handoff_node) = {
   .name = "nat44-in2out-worker-handoff",
   .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
   .format_trace = format_nat44_handoff_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
   .n_errors = ARRAY_LEN(nat44_handoff_error_strings),
   .error_strings = nat44_handoff_error_strings,
-  .n_next_nodes = 1,
-  .next_nodes = {
-    [0] = "error-drop",
-  },
 };
 /* *INDENT-ON* */
 
@@ -283,14 +281,11 @@ VLIB_NODE_FN (snat_in2out_output_worker_handoff_node) (vlib_main_t * vm,
 VLIB_REGISTER_NODE (snat_in2out_output_worker_handoff_node) = {
   .name = "nat44-in2out-output-worker-handoff",
   .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
   .format_trace = format_nat44_handoff_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
   .n_errors = ARRAY_LEN(nat44_handoff_error_strings),
   .error_strings = nat44_handoff_error_strings,
-  .n_next_nodes = 1,
-  .next_nodes = {
-    [0] = "error-drop",
-  },
 };
 /* *INDENT-ON* */
 
@@ -305,14 +300,11 @@ VLIB_NODE_FN (snat_out2in_worker_handoff_node) (vlib_main_t * vm,
 VLIB_REGISTER_NODE (snat_out2in_worker_handoff_node) = {
   .name = "nat44-out2in-worker-handoff",
   .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
   .format_trace = format_nat44_handoff_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
   .n_errors = ARRAY_LEN(nat44_handoff_error_strings),
   .error_strings = nat44_handoff_error_strings,
-  .n_next_nodes = 1,
-  .next_nodes = {
-    [0] = "error-drop",
-  },
 };
 /* *INDENT-ON* */
 
index a7ab235..2f68ed4 100644 (file)
 #include <nat/nat.h>
 #include <nat/nat_ha.h>
 
+static inline uword
+nat_pre_node_fn_inline (vlib_main_t * vm,
+                       vlib_node_runtime_t * node,
+                       vlib_frame_t * frame, u32 def_next)
+{
+  u32 n_left_from, *from, *to_next;
+  u16 next_index;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from >= 4 && n_left_to_next >= 2)
+       {
+         u32 next0, next1;
+         u32 arc_next0, arc_next1;
+         u32 bi0, bi1;
+         vlib_buffer_t *b0, *b1;
+
+         /* 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, CLIB_CACHE_LINE_BYTES, STORE);
+           CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
+         }
+
+         /* speculatively enqueue b0 and b1 to the current next frame */
+         to_next[0] = bi0 = from[0];
+         to_next[1] = bi1 = from[1];
+         from += 2;
+         to_next += 2;
+         n_left_from -= 2;
+         n_left_to_next -= 2;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         b1 = vlib_get_buffer (vm, bi1);
+
+         next0 = def_next;
+         next1 = def_next;
+
+         vnet_feature_next (&arc_next0, b0);
+         vnet_feature_next (&arc_next1, b1);
+
+         nat_buffer_opaque (b0)->arc_next = arc_next0;
+         nat_buffer_opaque (b1)->arc_next = arc_next1;
+
+         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
+           {
+             if (b0->flags & VLIB_BUFFER_IS_TRACED)
+               {
+                 nat_pre_trace_t *t =
+                   vlib_add_trace (vm, node, b0, sizeof (*t));
+                 t->next_index = next0;
+               }
+             if (b1->flags & VLIB_BUFFER_IS_TRACED)
+               {
+                 nat_pre_trace_t *t =
+                   vlib_add_trace (vm, node, b0, sizeof (*t));
+                 t->next_index = next0;
+               }
+           }
+
+         /* verify speculative enqueues, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, bi1, next0, next1);
+       }
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 next0;
+         u32 arc_next0;
+         u32 bi0;
+         vlib_buffer_t *b0;
+
+         /* speculatively enqueue b0 to the current next frame */
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         next0 = def_next;
+         vnet_feature_next (&arc_next0, b0);
+         nat_buffer_opaque (b0)->arc_next = arc_next0;
+
+         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             nat_pre_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->next_index = next0;
+           }
+
+         /* verify speculative enqueue, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return frame->n_vectors;
+}
+
 always_inline u32
 ip_proto_to_snat_proto (u8 ip_proto)
 {
@@ -552,13 +672,13 @@ snat_not_translate_fast (snat_main_t * sm, vlib_node_runtime_t * node,
        return 1;
 
       snat_interface_t *i;
-      pool_foreach (i, sm->interfaces, (
-                                        {
-                                        /* NAT packet aimed at outside interface */
-                                        if ((nat_interface_is_outside (i))
-                                            && (sw_if_index ==
-                                                i->sw_if_index)) return 0;}
-                   ));
+      /* *INDENT-OFF* */
+      pool_foreach (i, sm->interfaces, ({
+        /* NAT packet aimed at outside interface */
+       if ((nat_interface_is_outside (i)) && (sw_if_index == i->sw_if_index))
+          return 0;
+      }));
+      /* *INDENT-ON* */
     }
 
   return 1;
index 2067c51..a81d58e 100644 (file)
@@ -38,17 +38,6 @@ static char *nat_out2in_ed_error_strings[] = {
 #undef _
 };
 
-typedef enum
-{
-  NAT44_ED_OUT2IN_NEXT_DROP,
-  NAT44_ED_OUT2IN_NEXT_LOOKUP,
-  NAT44_ED_OUT2IN_NEXT_ICMP_ERROR,
-  NAT44_ED_OUT2IN_NEXT_IN2OUT,
-  NAT44_ED_OUT2IN_NEXT_SLOW_PATH,
-  NAT44_ED_OUT2IN_NEXT_REASS,
-  NAT44_ED_OUT2IN_N_NEXT,
-} nat44_ed_out2in_next_t;
-
 typedef struct
 {
   u32 sw_if_index;
@@ -85,7 +74,7 @@ icmp_out2in_ed_slow_path (snat_main_t * sm, vlib_buffer_t * b0,
   next0 = icmp_out2in (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
                       next0, thread_index, p_s0, 0);
   snat_session_t *s0 = *p_s0;
-  if (PREDICT_TRUE (next0 != NAT44_ED_OUT2IN_NEXT_DROP && s0))
+  if (PREDICT_TRUE (next0 != NAT_NEXT_DROP && s0))
     {
       /* Accounting */
       nat44_session_update_counters (s0, now,
@@ -467,7 +456,7 @@ icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node,
   if (get_icmp_o2i_ed_key (ip, &key))
     {
       b->error = node->errors[NAT_OUT2IN_ED_ERROR_UNSUPPORTED_PROTOCOL];
-      next = NAT44_ED_OUT2IN_NEXT_DROP;
+      next = NAT_NEXT_DROP;
       goto out;
     }
   key.fib_index = rx_fib_index;
@@ -494,7 +483,7 @@ icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node,
                  goto out;
                }
              b->error = node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION];
-             next = NAT44_ED_OUT2IN_NEXT_DROP;
+             next = NAT_NEXT_DROP;
              goto out;
            }
          else
@@ -503,7 +492,7 @@ icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node,
              if (next_src_nat (sm, ip, key.proto, key.l_port, key.r_port,
                                thread_index, rx_fib_index))
                {
-                 next = NAT44_ED_OUT2IN_NEXT_IN2OUT;
+                 next = NAT_NEXT_IN2OUT_ED_FAST_PATH;
                  goto out;
                }
              if (sm->num_workers > 1)
@@ -518,7 +507,7 @@ icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node,
                         (icmp->type != ICMP4_echo_request || !is_addr_only)))
        {
          b->error = node->errors[NAT_OUT2IN_ED_ERROR_BAD_ICMP_TYPE];
-         next = NAT44_ED_OUT2IN_NEXT_DROP;
+         next = NAT_NEXT_DROP;
          goto out;
        }
 
@@ -536,7 +525,7 @@ icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node,
 
       if (!s)
        {
-         next = NAT44_ED_OUT2IN_NEXT_DROP;
+         next = NAT_NEXT_DROP;
          goto out;
        }
     }
@@ -547,7 +536,7 @@ icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node,
                         !icmp_is_error_message (icmp)))
        {
          b->error = node->errors[NAT_OUT2IN_ED_ERROR_BAD_ICMP_TYPE];
-         next = NAT44_ED_OUT2IN_NEXT_DROP;
+         next = NAT_NEXT_DROP;
          goto out;
        }
 
@@ -676,7 +665,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                                vlib_frame_t * frame, int is_slow_path)
 {
   u32 n_left_from, *from, *to_next, pkts_processed = 0, stats_node_index;
-  nat44_ed_out2in_next_t next_index;
+  nat_next_t next_index;
   snat_main_t *sm = &snat_main;
   f64 now = vlib_time_now (vm);
   u32 thread_index = vm->thread_index;
@@ -743,7 +732,9 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
          b0 = vlib_get_buffer (vm, bi0);
          b1 = vlib_get_buffer (vm, bi1);
 
-         next0 = NAT44_ED_OUT2IN_NEXT_LOOKUP;
+         next0 = nat_buffer_opaque (b0)->arc_next;
+         next1 = nat_buffer_opaque (b1)->arc_next;
+
          vnet_buffer (b0)->snat.flags = 0;
          ip0 = vlib_buffer_get_current (b0);
 
@@ -758,7 +749,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
              icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
                                           ICMP4_time_exceeded_ttl_exceeded_in_transit,
                                           0);
-             next0 = NAT44_ED_OUT2IN_NEXT_ICMP_ERROR;
+             next0 = NAT_NEXT_ICMP_ERROR;
              goto trace00;
            }
 
@@ -779,7 +770,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                  if (!sm->forwarding_enabled)
                    {
                      if (!s0)
-                       next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                       next0 = NAT_NEXT_DROP;
                      goto trace00;
                    }
                }
@@ -797,20 +788,20 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
            {
              if (PREDICT_FALSE (proto0 == ~0))
                {
-                 next0 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH;
+                 next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
                  goto trace00;
                }
 
              if (ip4_is_fragment (ip0))
                {
-                 next0 = NAT44_ED_OUT2IN_NEXT_REASS;
+                 next0 = NAT_NEXT_OUT2IN_ED_REASS;
                  fragments++;
                  goto trace00;
                }
 
              if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
                {
-                 next0 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH;
+                 next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
                  goto trace00;
                }
            }
@@ -843,7 +834,6 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                                             clib_host_to_net_u16
                                             (UDP_DST_PORT_dhcp_to_client))))
                        {
-                         vnet_feature_next (&next0, b0);
                          goto trace00;
                        }
 
@@ -851,7 +841,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                        {
                          b0->error =
                            node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION];
-                         next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                         next0 = NAT_NEXT_DROP;
                        }
                      else
                        {
@@ -859,7 +849,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                                            udp0->src_port, udp0->dst_port,
                                            thread_index, rx_fib_index0))
                            {
-                             next0 = NAT44_ED_OUT2IN_NEXT_IN2OUT;
+                             next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH;
                              goto trace00;
                            }
                          if (sm->num_workers > 1)
@@ -878,7 +868,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                  if ((proto0 == SNAT_PROTOCOL_TCP) && !tcp_is_init (tcp0))
                    {
                      b0->error = node->errors[NAT_OUT2IN_ED_ERROR_NON_SYN];
-                     next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                     next0 = NAT_NEXT_DROP;
                      goto trace00;
                    }
 
@@ -891,13 +881,13 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
 
                  if (!s0)
                    {
-                     next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                     next0 = NAT_NEXT_DROP;
                      goto trace00;
                    }
                }
              else
                {
-                 next0 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH;
+                 next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
                  goto trace00;
                }
            }
@@ -998,9 +988,8 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                t->session_index = s0 - tsm->sessions;
            }
 
-         pkts_processed += next0 == NAT44_ED_OUT2IN_NEXT_LOOKUP;
+         pkts_processed += next0 == nat_buffer_opaque (b0)->arc_next;
 
-         next1 = NAT44_ED_OUT2IN_NEXT_LOOKUP;
          vnet_buffer (b1)->snat.flags = 0;
          ip1 = vlib_buffer_get_current (b1);
 
@@ -1015,7 +1004,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
              icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded,
                                           ICMP4_time_exceeded_ttl_exceeded_in_transit,
                                           0);
-             next1 = NAT44_ED_OUT2IN_NEXT_ICMP_ERROR;
+             next1 = NAT_NEXT_ICMP_ERROR;
              goto trace01;
            }
 
@@ -1036,7 +1025,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                  if (!sm->forwarding_enabled)
                    {
                      if (!s1)
-                       next1 = NAT44_ED_OUT2IN_NEXT_DROP;
+                       next1 = NAT_NEXT_DROP;
                      goto trace01;
                    }
                }
@@ -1054,20 +1043,20 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
            {
              if (PREDICT_FALSE (proto1 == ~0))
                {
-                 next1 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH;
+                 next1 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
                  goto trace01;
                }
 
              if (ip4_is_fragment (ip1))
                {
-                 next1 = NAT44_ED_OUT2IN_NEXT_REASS;
+                 next1 = NAT_NEXT_OUT2IN_ED_REASS;
                  fragments++;
                  goto trace01;
                }
 
              if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP))
                {
-                 next1 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH;
+                 next1 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
                  goto trace01;
                }
            }
@@ -1100,7 +1089,6 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                                             clib_host_to_net_u16
                                             (UDP_DST_PORT_dhcp_to_client))))
                        {
-                         vnet_feature_next (&next1, b1);
                          goto trace01;
                        }
 
@@ -1108,7 +1096,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                        {
                          b1->error =
                            node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION];
-                         next1 = NAT44_ED_OUT2IN_NEXT_DROP;
+                         next1 = NAT_NEXT_DROP;
                        }
                      else
                        {
@@ -1116,7 +1104,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                                            udp1->src_port, udp1->dst_port,
                                            thread_index, rx_fib_index1))
                            {
-                             next1 = NAT44_ED_OUT2IN_NEXT_IN2OUT;
+                             next1 = NAT_NEXT_IN2OUT_ED_FAST_PATH;
                              goto trace01;
                            }
                          if (sm->num_workers > 1)
@@ -1135,7 +1123,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                  if ((proto1 == SNAT_PROTOCOL_TCP) && !tcp_is_init (tcp1))
                    {
                      b1->error = node->errors[NAT_OUT2IN_ED_ERROR_NON_SYN];
-                     next1 = NAT44_ED_OUT2IN_NEXT_DROP;
+                     next1 = NAT_NEXT_DROP;
                      goto trace01;
                    }
 
@@ -1148,13 +1136,13 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
 
                  if (!s1)
                    {
-                     next1 = NAT44_ED_OUT2IN_NEXT_DROP;
+                     next1 = NAT_NEXT_DROP;
                      goto trace01;
                    }
                }
              else
                {
-                 next1 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH;
+                 next1 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
                  goto trace01;
                }
            }
@@ -1255,7 +1243,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                t->session_index = s1 - tsm->sessions;
            }
 
-         pkts_processed += next1 == NAT44_ED_OUT2IN_NEXT_LOOKUP;
+         pkts_processed += next1 == nat_buffer_opaque (b1)->arc_next;
 
          /* verify speculative enqueues, maybe switch current next frame */
          vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
@@ -1291,7 +1279,8 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
          n_left_to_next -= 1;
 
          b0 = vlib_get_buffer (vm, bi0);
-         next0 = NAT44_ED_OUT2IN_NEXT_LOOKUP;
+         next0 = nat_buffer_opaque (b0)->arc_next;
+
          vnet_buffer (b0)->snat.flags = 0;
          ip0 = vlib_buffer_get_current (b0);
 
@@ -1306,7 +1295,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
              icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
                                           ICMP4_time_exceeded_ttl_exceeded_in_transit,
                                           0);
-             next0 = NAT44_ED_OUT2IN_NEXT_ICMP_ERROR;
+             next0 = NAT_NEXT_ICMP_ERROR;
              goto trace0;
            }
 
@@ -1327,7 +1316,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                  if (!sm->forwarding_enabled)
                    {
                      if (!s0)
-                       next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                       next0 = NAT_NEXT_DROP;
                      goto trace0;
                    }
                }
@@ -1345,20 +1334,20 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
            {
              if (PREDICT_FALSE (proto0 == ~0))
                {
-                 next0 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH;
+                 next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
                  goto trace0;
                }
 
              if (ip4_is_fragment (ip0))
                {
-                 next0 = NAT44_ED_OUT2IN_NEXT_REASS;
+                 next0 = NAT_NEXT_OUT2IN_ED_REASS;
                  fragments++;
                  goto trace0;
                }
 
              if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
                {
-                 next0 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH;
+                 next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
                  goto trace0;
                }
            }
@@ -1391,7 +1380,6 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                                             clib_host_to_net_u16
                                             (UDP_DST_PORT_dhcp_to_client))))
                        {
-                         vnet_feature_next (&next0, b0);
                          goto trace0;
                        }
 
@@ -1399,7 +1387,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                        {
                          b0->error =
                            node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION];
-                         next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                         next0 = NAT_NEXT_DROP;
                        }
                      else
                        {
@@ -1407,7 +1395,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                                            udp0->src_port, udp0->dst_port,
                                            thread_index, rx_fib_index0))
                            {
-                             next0 = NAT44_ED_OUT2IN_NEXT_IN2OUT;
+                             next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH;
                              goto trace0;
                            }
                          if (sm->num_workers > 1)
@@ -1426,7 +1414,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                  if ((proto0 == SNAT_PROTOCOL_TCP) && !tcp_is_init (tcp0))
                    {
                      b0->error = node->errors[NAT_OUT2IN_ED_ERROR_NON_SYN];
-                     next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                     next0 = NAT_NEXT_DROP;
                      goto trace0;
                    }
 
@@ -1439,13 +1427,13 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
 
                  if (!s0)
                    {
-                     next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                     next0 = NAT_NEXT_DROP;
                      goto trace0;
                    }
                }
              else
                {
-                 next0 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH;
+                 next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
                  goto trace0;
                }
            }
@@ -1546,7 +1534,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                t->session_index = s0 - tsm->sessions;
            }
 
-         pkts_processed += next0 == NAT44_ED_OUT2IN_NEXT_LOOKUP;
+         pkts_processed += next0 == nat_buffer_opaque (b0)->arc_next;
          /* verify speculative enqueue, maybe switch current next frame */
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                           to_next, n_left_to_next,
@@ -1574,68 +1562,13 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
   return frame->n_vectors;
 }
 
-VLIB_NODE_FN (nat44_ed_out2in_node) (vlib_main_t * vm,
-                                    vlib_node_runtime_t * node,
-                                    vlib_frame_t * frame)
-{
-  return nat44_ed_out2in_node_fn_inline (vm, node, frame, 0);
-}
-
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (nat44_ed_out2in_node) = {
-  .name = "nat44-ed-out2in",
-  .vector_size = sizeof (u32),
-  .format_trace = format_nat44_ed_out2in_trace,
-  .type = VLIB_NODE_TYPE_INTERNAL,
-  .n_errors = ARRAY_LEN(nat_out2in_ed_error_strings),
-  .error_strings = nat_out2in_ed_error_strings,
-  .runtime_data_bytes = sizeof (snat_runtime_t),
-  .n_next_nodes = NAT44_ED_OUT2IN_N_NEXT,
-  .next_nodes = {
-    [NAT44_ED_OUT2IN_NEXT_DROP] = "error-drop",
-    [NAT44_ED_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
-    [NAT44_ED_OUT2IN_NEXT_SLOW_PATH] = "nat44-ed-out2in-slowpath",
-    [NAT44_ED_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error",
-    [NAT44_ED_OUT2IN_NEXT_IN2OUT] = "nat44-ed-in2out",
-    [NAT44_ED_OUT2IN_NEXT_REASS] = "nat44-ed-out2in-reass",
-  },
-};
-/* *INDENT-ON* */
-
-VLIB_NODE_FN (nat44_ed_out2in_slowpath_node) (vlib_main_t * vm,
-                                             vlib_node_runtime_t * node,
-                                             vlib_frame_t * frame)
-{
-  return nat44_ed_out2in_node_fn_inline (vm, node, frame, 1);
-}
-
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (nat44_ed_out2in_slowpath_node) = {
-  .name = "nat44-ed-out2in-slowpath",
-  .vector_size = sizeof (u32),
-  .format_trace = format_nat44_ed_out2in_trace,
-  .type = VLIB_NODE_TYPE_INTERNAL,
-  .n_errors = ARRAY_LEN(nat_out2in_ed_error_strings),
-  .error_strings = nat_out2in_ed_error_strings,
-  .runtime_data_bytes = sizeof (snat_runtime_t),
-  .n_next_nodes = NAT44_ED_OUT2IN_N_NEXT,
-  .next_nodes = {
-    [NAT44_ED_OUT2IN_NEXT_DROP] = "error-drop",
-    [NAT44_ED_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
-    [NAT44_ED_OUT2IN_NEXT_SLOW_PATH] = "nat44-ed-out2in-slowpath",
-    [NAT44_ED_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error",
-    [NAT44_ED_OUT2IN_NEXT_IN2OUT] = "nat44-ed-in2out",
-    [NAT44_ED_OUT2IN_NEXT_REASS] = "nat44-ed-out2in-reass",
-  },
-};
-/* *INDENT-ON* */
-
-VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
-                                          vlib_node_runtime_t * node,
-                                          vlib_frame_t * frame)
+static inline uword
+nat44_ed_out2in_reass_node_fn_inline (vlib_main_t * vm,
+                                     vlib_node_runtime_t * node,
+                                     vlib_frame_t * frame)
 {
   u32 n_left_from, *from, *to_next;
-  nat44_ed_out2in_next_t next_index;
+  nat_next_t next_index;
   u32 pkts_processed = 0;
   snat_main_t *sm = &snat_main;
   f64 now = vlib_time_now (vm);
@@ -1684,7 +1617,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
          n_left_to_next -= 1;
 
          b0 = vlib_get_buffer (vm, bi0);
-         next0 = NAT44_ED_OUT2IN_NEXT_LOOKUP;
+         next0 = nat_buffer_opaque (b0)->arc_next;
 
          sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
          rx_fib_index0 =
@@ -1693,7 +1626,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
 
          if (PREDICT_FALSE (nat_reass_is_drop_frag (0)))
            {
-             next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+             next0 = NAT_NEXT_DROP;
              b0->error = node->errors[NAT_OUT2IN_ED_ERROR_DROP_FRAGMENT];
              goto trace0;
            }
@@ -1712,7 +1645,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
 
          if (PREDICT_FALSE (!reass0))
            {
-             next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+             next0 = NAT_NEXT_DROP;
              b0->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_REASS];
              nat_elog_notice ("maximum reassemblies exceeded");
              goto trace0;
@@ -1726,7 +1659,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
                    (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
                     next0, now, thread_index, &s0);
 
-                 if (PREDICT_TRUE (next0 != NAT44_ED_OUT2IN_NEXT_DROP))
+                 if (PREDICT_TRUE (next0 != NAT_NEXT_DROP))
                    {
                      if (s0)
                        reass0->sess_index = s0 - per_thread_data->sessions;
@@ -1767,7 +1700,6 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
                                             clib_host_to_net_u16
                                             (UDP_DST_PORT_dhcp_to_client))))
                        {
-                         vnet_feature_next (&next0, b0);
                          goto trace0;
                        }
 
@@ -1775,7 +1707,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
                        {
                          b0->error =
                            node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION];
-                         next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                         next0 = NAT_NEXT_DROP;
                        }
                      else
                        {
@@ -1783,7 +1715,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
                                            udp0->src_port, udp0->dst_port,
                                            thread_index, rx_fib_index0))
                            {
-                             next0 = NAT44_ED_OUT2IN_NEXT_IN2OUT;
+                             next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH;
                              goto trace0;
                            }
                          if (sm->num_workers > 1)
@@ -1808,7 +1740,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
                  if ((proto0 == SNAT_PROTOCOL_TCP) && !tcp_is_init (tcp0))
                    {
                      b0->error = node->errors[NAT_OUT2IN_ED_ERROR_NON_SYN];
-                     next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                     next0 = NAT_NEXT_DROP;
                      goto trace0;
                    }
 
@@ -1822,7 +1754,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
                    {
                      b0->error =
                        node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION];
-                     next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                     next0 = NAT_NEXT_DROP;
                      goto trace0;
                    }
                  reass0->sess_index = s0 - per_thread_data->sessions;
@@ -1848,7 +1780,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
                      b0->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_FRAG];
                      nat_elog_notice
                        ("maximum fragments per reassembly exceeded");
-                     next0 = NAT44_ED_OUT2IN_NEXT_DROP;
+                     next0 = NAT_NEXT_DROP;
                      goto trace0;
                    }
                  cached0 = 1;
@@ -1958,7 +1890,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
            }
          else
            {
-             pkts_processed += next0 != NAT44_ED_OUT2IN_NEXT_DROP;
+             pkts_processed += next0 != NAT_NEXT_DROP;
 
              /* verify speculative enqueue, maybe switch current next frame */
              vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
@@ -1997,33 +1929,100 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
 
   nat_send_all_to_node (vm, fragments_to_drop, node,
                        &node->errors[NAT_OUT2IN_ED_ERROR_DROP_FRAGMENT],
-                       NAT44_ED_OUT2IN_NEXT_DROP);
+                       NAT_NEXT_DROP);
 
   vec_free (fragments_to_drop);
   vec_free (fragments_to_loopback);
   return frame->n_vectors;
 }
 
+VLIB_NODE_FN (nat44_ed_out2in_node) (vlib_main_t * vm,
+                                    vlib_node_runtime_t * node,
+                                    vlib_frame_t * frame)
+{
+  return nat44_ed_out2in_node_fn_inline (vm, node, frame, 0);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat44_ed_out2in_node) = {
+  .name = "nat44-ed-out2in",
+  .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
+  .format_trace = format_nat44_ed_out2in_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN(nat_out2in_ed_error_strings),
+  .error_strings = nat_out2in_ed_error_strings,
+  .runtime_data_bytes = sizeof (snat_runtime_t),
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FN (nat44_ed_out2in_slowpath_node) (vlib_main_t * vm,
+                                             vlib_node_runtime_t * node,
+                                             vlib_frame_t * frame)
+{
+  return nat44_ed_out2in_node_fn_inline (vm, node, frame, 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat44_ed_out2in_slowpath_node) = {
+  .name = "nat44-ed-out2in-slowpath",
+  .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
+  .format_trace = format_nat44_ed_out2in_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN(nat_out2in_ed_error_strings),
+  .error_strings = nat_out2in_ed_error_strings,
+  .runtime_data_bytes = sizeof (snat_runtime_t),
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
+                                          vlib_node_runtime_t * node,
+                                          vlib_frame_t * frame)
+{
+  return nat44_ed_out2in_reass_node_fn_inline (vm, node, frame);
+}
+
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (nat44_ed_out2in_reass_node) = {
   .name = "nat44-ed-out2in-reass",
   .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
   .format_trace = format_nat44_reass_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
   .n_errors = ARRAY_LEN(nat_out2in_ed_error_strings),
   .error_strings = nat_out2in_ed_error_strings,
-  .n_next_nodes = NAT44_ED_OUT2IN_N_NEXT,
-  .next_nodes = {
-    [NAT44_ED_OUT2IN_NEXT_DROP] = "error-drop",
-    [NAT44_ED_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
-    [NAT44_ED_OUT2IN_NEXT_SLOW_PATH] = "nat44-ed-out2in-slowpath",
-    [NAT44_ED_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error",
-    [NAT44_ED_OUT2IN_NEXT_IN2OUT] = "nat44-ed-in2out",
-    [NAT44_ED_OUT2IN_NEXT_REASS] = "nat44-ed-out2in-reass",
-  },
 };
 /* *INDENT-ON* */
 
+static u8 *
+format_nat_pre_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 *);
+  nat_pre_trace_t *t = va_arg (*args, nat_pre_trace_t *);
+  return format (s, "out2in next_index %d", t->next_index);
+}
+
+VLIB_NODE_FN (nat_pre_out2in_node) (vlib_main_t * vm,
+                                   vlib_node_runtime_t * node,
+                                   vlib_frame_t * frame)
+{
+  return nat_pre_node_fn_inline (vm, node, frame,
+                                NAT_NEXT_OUT2IN_ED_FAST_PATH);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat_pre_out2in_node) = {
+  .name = "nat-pre-out2in",
+  .vector_size = sizeof (u32),
+  .sibling_of = "nat-default",
+  .format_trace = format_nat_pre_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = 0,
+ };
+/* *INDENT-ON* */
+
 /*
  * fd.io coding-style-patch-verification: ON
  *