NAT: DS-Lite (VPP-1040)
[vpp.git] / src / plugins / nat / in2out.c
index dbbc67f..b059390 100755 (executable)
@@ -93,6 +93,7 @@ vlib_node_registration_t snat_in2out_output_slowpath_node;
 vlib_node_registration_t snat_in2out_output_worker_handoff_node;
 vlib_node_registration_t snat_hairpin_dst_node;
 vlib_node_registration_t snat_hairpin_src_node;
+vlib_node_registration_t nat44_hairpinning_node;
 
 
 #define foreach_snat_in2out_error                       \
@@ -181,7 +182,7 @@ snat_not_translate_fast (snat_main_t * sm, vlib_node_runtime_t *node,
       pool_foreach (i, sm->interfaces,
       ({
         /* NAT packet aimed at outside interface */
-        if ((i->is_inside == 0) && (sw_if_index == i->sw_if_index))
+        if ((nat_interface_is_outside(i)) && (sw_if_index == i->sw_if_index))
           return 0;
       }));
     }
@@ -365,12 +366,15 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
                                               s->in2out.fib_index);
 
           snat_free_outside_address_and_port
-            (sm, thread_index, &s->out2in, s->outside_address_index);
+            (sm->addresses, thread_index, &s->out2in, s->outside_address_index);
         }
       s->outside_address_index = ~0;
 
-      if (snat_alloc_outside_address_and_port (sm, rx_fib_index0, thread_index,
-                                               &key1, &address_index))
+      if (snat_alloc_outside_address_and_port (sm->addresses, rx_fib_index0,
+                                               thread_index, &key1,
+                                               &address_index, sm->vrf_mode,
+                                               sm->port_per_thread,
+                                               sm->per_thread_data[thread_index].snat_thread_index))
         {
           ASSERT(0);
 
@@ -388,9 +392,11 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
         {
           static_mapping = 0;
           /* Try to create dynamic translation */
-          if (snat_alloc_outside_address_and_port (sm, rx_fib_index0,
+          if (snat_alloc_outside_address_and_port (sm->addresses, rx_fib_index0,
                                                    thread_index, &key1,
-                                                   &address_index))
+                                                   &address_index, sm->vrf_mode,
+                                                   sm->port_per_thread,
+                                                   sm->per_thread_data[thread_index].snat_thread_index))
             {
               b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
               return SNAT_IN2OUT_NEXT_DROP;
@@ -523,11 +529,11 @@ snat_in2out_error_t icmp_get_key(ip4_header_t *ip0,
  * @param e                      optional parameter
  */
 u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node,
-                           u32 thread_index, vlib_buffer_t *b0, u8 *p_proto,
+                           u32 thread_index, vlib_buffer_t *b0,
+                           ip4_header_t *ip0, u8 *p_proto,
                            snat_session_key_t *p_value,
                            u8 *p_dont_translate, void *d, void *e)
 {
-  ip4_header_t *ip0;
   icmp46_header_t *icmp0;
   u32 sw_if_index0;
   u32 rx_fib_index0;
@@ -537,13 +543,7 @@ u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node,
   clib_bihash_kv_8_8_t kv0, value0;
   u32 next0 = ~0;
   int err;
-  u32 iph_offset0 = 0;
 
-  if (PREDICT_FALSE(vnet_buffer(b0)->sw_if_index[VLIB_TX] != ~0))
-    {
-      iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length;
-    }
-  ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + iph_offset0);
   icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
   sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
   rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
@@ -622,11 +622,11 @@ out:
  * @param e                      optional parameter
  */
 u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node,
-                           u32 thread_index, vlib_buffer_t *b0, u8 *p_proto,
+                           u32 thread_index, vlib_buffer_t *b0,
+                           ip4_header_t *ip0, u8 *p_proto,
                            snat_session_key_t *p_value,
                            u8 *p_dont_translate, void *d, void *e)
 {
-  ip4_header_t *ip0;
   icmp46_header_t *icmp0;
   u32 sw_if_index0;
   u32 rx_fib_index0;
@@ -637,7 +637,6 @@ u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node,
   u32 next0 = ~0;
   int err;
 
-  ip0 = vlib_buffer_get_current (b0);
   icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
   sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
   rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
@@ -715,7 +714,7 @@ static inline u32 icmp_in2out (snat_main_t *sm,
 
   echo0 = (icmp_echo_header_t *)(icmp0+1);
 
-  next0_tmp = sm->icmp_match_in2out_cb(sm, node, thread_index, b0,
+  next0_tmp = sm->icmp_match_in2out_cb(sm, node, thread_index, b0, ip0,
                                        &protocol, &sm0, &dont_translate, d, e);
   if (next0_tmp != ~0)
     next0 = next0_tmp;
@@ -825,7 +824,7 @@ out:
  * @param tcp0   TCP header.
  * @param proto0 NAT protocol.
  */
-static inline void
+static inline int
 snat_hairpinning (snat_main_t *sm,
                   vlib_buffer_t * b0,
                   ip4_header_t * ip0,
@@ -911,7 +910,9 @@ snat_hairpinning (snat_main_t *sm,
               tcp0->checksum = ip_csum_fold(sum0);
             }
         }
+      return 1;
     }
+  return 0;
 }
 
 static inline void
@@ -1269,7 +1270,8 @@ create_ses:
                                                   s->out2in.port,
                                                   s->in2out.fib_index);
 
-              snat_free_outside_address_and_port (sm, thread_index, &s->out2in,
+              snat_free_outside_address_and_port (sm->addresses, thread_index,
+                                                  &s->out2in,
                                                   s->outside_address_index);
 
               /* Remove in2out, out2in keys */
@@ -1698,6 +1700,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
                 }
             }
 
+          b0->flags |= VNET_BUFFER_F_IS_NATED;
+
           old_addr0 = ip0->src_address.as_u32;
           ip0->src_address = s0->out2in.addr;
           new_addr0 = ip0->src_address.as_u32;
@@ -1732,10 +1736,6 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
               udp0->checksum = 0;
             }
 
-          /* Hairpinning */
-          if (!is_output_feature)
-            snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0);
-
           /* Accounting */
           s0->last_heard = now;
           s0->total_pkts++;
@@ -1821,6 +1821,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
                 }
             }
 
+          b1->flags |= VNET_BUFFER_F_IS_NATED;
+
           key1.addr = ip1->src_address;
           key1.port = udp1->src_port;
           key1.protocol = proto1;
@@ -1908,10 +1910,6 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
               udp1->checksum = 0;
             }
 
-          /* Hairpinning */
-          if (!is_output_feature)
-            snat_hairpinning (sm, b1, ip1, udp1, tcp1, proto1);
-
           /* Accounting */
           s1->last_heard = now;
           s1->total_pkts++;
@@ -2087,6 +2085,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
                 }
             }
 
+          b0->flags |= VNET_BUFFER_F_IS_NATED;
+
           old_addr0 = ip0->src_address.as_u32;
           ip0->src_address = s0->out2in.addr;
           new_addr0 = ip0->src_address.as_u32;
@@ -2121,10 +2121,6 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
               udp0->checksum = 0;
             }
 
-          /* Hairpinning */
-          if (!is_output_feature)
-            snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0);
-
           /* Accounting */
           s0->last_heard = now;
           s0->total_pkts++;
@@ -2305,6 +2301,97 @@ VLIB_REGISTER_NODE (snat_in2out_output_slowpath_node) = {
 VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_output_slowpath_node,
                               snat_in2out_output_slow_path_fn);
 
+extern vnet_feature_arc_registration_t vnet_feat_arc_ip4_local;
+
+static uword
+nat44_hairpinning_fn (vlib_main_t * vm,
+                      vlib_node_runtime_t * node,
+                      vlib_frame_t * frame)
+{
+  u32 n_left_from, * from, * to_next;
+  snat_in2out_next_t next_index;
+  u32 pkts_processed = 0;
+  snat_main_t * sm = &snat_main;
+  vnet_feature_main_t *fm = &feature_main;
+  u8 arc_index = vnet_feat_arc_ip4_local.feature_arc_index;
+  vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_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 > 0 && n_left_to_next > 0)
+       {
+          u32 bi0;
+         vlib_buffer_t * b0;
+          u32 next0;
+          ip4_header_t * ip0;
+          u32 proto0;
+          udp_header_t * udp0;
+          tcp_header_t * tcp0;
+
+          /* 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);
+          tcp0 = (tcp_header_t *) udp0;
+
+          proto0 = ip_proto_to_snat_proto (ip0->protocol);
+
+          vnet_get_config_data (&cm->config_main, &b0->current_config_index,
+                                &next0, 0);
+
+          if (snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0))
+            next0 = SNAT_IN2OUT_NEXT_LOOKUP;
+
+          pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP;
+
+          /* 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);
+    }
+
+  vlib_node_increment_counter (vm, nat44_hairpinning_node.index,
+                               SNAT_IN2OUT_ERROR_IN2OUT_PACKETS,
+                               pkts_processed);
+  return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (nat44_hairpinning_node) = {
+  .function = nat44_hairpinning_fn,
+  .name = "nat44-hairpinning",
+  .vector_size = sizeof (u32),
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN(snat_in2out_error_strings),
+  .error_strings = snat_in2out_error_strings,
+  .n_next_nodes = 2,
+  .next_nodes = {
+    [SNAT_IN2OUT_NEXT_DROP] = "error-drop",
+    [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (nat44_hairpinning_node,
+                              nat44_hairpinning_fn);
+
 /**************************/
 /*** deterministic mode ***/
 /**************************/
@@ -2919,11 +3006,11 @@ VLIB_NODE_FUNCTION_MULTIARCH (snat_det_in2out_node, snat_det_in2out_node_fn);
  * @param e                      optional parameter
  */
 u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node,
-                          u32 thread_index, vlib_buffer_t *b0, u8 *p_proto,
+                          u32 thread_index, vlib_buffer_t *b0,
+                          ip4_header_t *ip0, u8 *p_proto,
                           snat_session_key_t *p_value,
                           u8 *p_dont_translate, void *d, void *e)
 {
-  ip4_header_t *ip0;
   icmp46_header_t *icmp0;
   u32 sw_if_index0;
   u32 rx_fib_index0;
@@ -2942,7 +3029,6 @@ u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node,
   ip4_address_t in_addr;
   u16 in_port;
 
-  ip0 = vlib_buffer_get_current (b0);
   icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
   echo0 = (icmp_echo_header_t *)(icmp0+1);
   sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
@@ -3443,7 +3529,7 @@ snat_hairpin_src_fn (vlib_main_t * vm,
           pool_foreach (i, sm->output_feature_interfaces,
           ({
             /* Only packets from NAT inside interface */
-            if ((i->is_inside == 1) && (sw_if_index0 == i->sw_if_index))
+            if ((nat_interface_is_inside(i)) && (sw_if_index0 == i->sw_if_index))
               {
                 if (PREDICT_FALSE ((vnet_buffer (b0)->snat.flags) &
                                     SNAT_FLAG_HAIRPINNING))