Fix endian issue in ARP Event Reply
[vpp.git] / src / plugins / snat / out2in.c
index 3bfc0aa..9b4c73d 100644 (file)
@@ -24,6 +24,7 @@
 #include <vnet/fib/ip4_fib.h>
 #include <snat/snat.h>
 #include <snat/snat_ipfix_logging.h>
+#include <snat/snat_det.h>
 
 #include <vppinfra/hash.h>
 #include <vppinfra/error.h>
@@ -80,6 +81,7 @@ static u8 * format_snat_out2in_worker_handoff_trace (u8 * s, va_list * args)
 vlib_node_registration_t snat_out2in_node;
 vlib_node_registration_t snat_out2in_fast_node;
 vlib_node_registration_t snat_out2in_worker_handoff_node;
+vlib_node_registration_t snat_det_out2in_node;
 
 #define foreach_snat_out2in_error                       \
 _(UNSUPPORTED_PROTOCOL, "Unsupported protocol")         \
@@ -147,6 +149,7 @@ create_session_for_static_mapping (snat_main_t *sm,
       pool_get (sm->per_thread_data[cpu_index].users, u);
       memset (u, 0, sizeof (*u));
       u->addr = in2out.addr;
+      u->fib_index = in2out.fib_index;
 
       pool_get (sm->per_thread_data[cpu_index].list_pool,
                 per_user_list_head_elt);
@@ -995,6 +998,418 @@ VLIB_REGISTER_NODE (snat_out2in_node) = {
 };
 VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_node, snat_out2in_node_fn);
 
+/**************************/
+/*** deterministic mode ***/
+/**************************/
+static uword
+snat_det_out2in_node_fn (vlib_main_t * vm,
+                         vlib_node_runtime_t * node,
+                         vlib_frame_t * frame)
+{
+  u32 n_left_from, * from, * to_next;
+  snat_out2in_next_t next_index;
+  u32 pkts_processed = 0;
+  snat_main_t * sm = &snat_main;
+
+  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 bi0, bi1;
+         vlib_buffer_t * b0, * b1;
+          u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP;
+          u32 next1 = SNAT_OUT2IN_NEXT_LOOKUP;
+          u32 sw_if_index0, sw_if_index1;
+          ip4_header_t * ip0, * ip1;
+          ip_csum_t sum0, sum1;
+          ip4_address_t new_addr0, old_addr0, new_addr1, old_addr1;
+          u16 new_port0, old_port0, old_port1, new_port1;
+          udp_header_t * udp0, * udp1;
+          tcp_header_t * tcp0, * tcp1;
+          u32 proto0, proto1;
+          snat_det_out_key_t key0, key1;
+          snat_det_map_t * dm0, * dm1;
+          snat_det_session_t * ses0 = 0, * ses1 = 0;
+
+         /* 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);
+
+          ip0 = vlib_buffer_get_current (b0);
+          udp0 = ip4_next_header (ip0);
+          tcp0 = (tcp_header_t *) udp0;
+
+          sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+
+          key0.ext_host_addr = ip0->src_address;
+          key0.ext_host_port = tcp0->src;
+          key0.out_port = tcp0->dst;
+
+          dm0 = snat_det_map_by_out(sm, &ip0->dst_address);
+          if (PREDICT_FALSE(!dm0))
+            {
+              clib_warning("unknown dst address:  %U",
+                           format_ip4_address, &ip0->dst_address);
+              next0 = SNAT_OUT2IN_NEXT_DROP;
+              goto trace0;
+            }
+
+          snat_det_reverse(dm0, &ip0->dst_address,
+                           clib_net_to_host_u16(tcp0->dst), &new_addr0);
+
+          ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64);
+          if (PREDICT_FALSE(!ses0))
+            {
+              clib_warning("no match src %U:%d dst %d for user %U",
+                           format_ip4_address, &ip0->dst_address,
+                           clib_net_to_host_u16 (tcp0->src),
+                           clib_net_to_host_u16 (tcp0->dst),
+                           format_ip4_address, &new_addr0);
+              next0 = SNAT_OUT2IN_NEXT_DROP;
+              goto trace0;
+            }
+          new_port0 = ses0->in_port;
+
+          proto0 = ip_proto_to_snat_proto (ip0->protocol);
+
+          old_addr0 = ip0->dst_address;
+          ip0->dst_address = new_addr0;
+          vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->inside_fib_index;
+
+          sum0 = ip0->checksum;
+          sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
+                                 ip4_header_t,
+                                 dst_address /* changed member */);
+          ip0->checksum = ip_csum_fold (sum0);
+
+          if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+            {
+              if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED)
+                ses0->state = SNAT_SESSION_TCP_CLOSE_WAIT;
+              else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_LAST_ACK)
+                snat_det_ses_close(dm0, ses0);
+
+              old_port0 = tcp0->dst;
+              tcp0->dst = new_port0;
+
+              sum0 = tcp0->checksum;
+              sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
+                                     ip4_header_t,
+                                     dst_address /* changed member */);
+
+              sum0 = ip_csum_update (sum0, old_port0, new_port0,
+                                     ip4_header_t /* cheat */,
+                                     length /* changed member */);
+              tcp0->checksum = ip_csum_fold(sum0);
+            }
+          else
+            {
+              old_port0 = udp0->dst_port;
+              udp0->dst_port = new_port0;
+              udp0->checksum = 0;
+            }
+
+        trace0:
+
+          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+            {
+              snat_out2in_trace_t *t =
+                 vlib_add_trace (vm, node, b0, sizeof (*t));
+              t->sw_if_index = sw_if_index0;
+              t->next_index = next0;
+              t->session_index = ~0;
+              if (ses0)
+                t->session_index = ses0 - dm0->sessions;
+            }
+
+          pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
+
+         b1 = vlib_get_buffer (vm, bi1);
+
+          ip1 = vlib_buffer_get_current (b1);
+          udp1 = ip4_next_header (ip1);
+          tcp1 = (tcp_header_t *) udp1;
+
+          sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
+
+          key1.ext_host_addr = ip1->src_address;
+          key1.ext_host_port = tcp1->src;
+          key1.out_port = tcp1->dst;
+
+          dm1 = snat_det_map_by_out(sm, &ip1->dst_address);
+          if (PREDICT_FALSE(!dm1))
+            {
+              clib_warning("unknown dst address:  %U",
+                           format_ip4_address, &ip1->dst_address);
+              next1 = SNAT_OUT2IN_NEXT_DROP;
+              goto trace1;
+            }
+
+          snat_det_reverse(dm1, &ip1->dst_address,
+                           clib_net_to_host_u16(tcp1->dst), &new_addr1);
+
+          ses1 = snat_det_get_ses_by_out (dm1, &new_addr1, key1.as_u64);
+          if (PREDICT_FALSE(!ses1))
+            {
+              clib_warning("no match src %U:%d dst %d for user %U",
+                           format_ip4_address, &ip1->dst_address,
+                           clib_net_to_host_u16 (tcp1->src),
+                           clib_net_to_host_u16 (tcp1->dst),
+                           format_ip4_address, &new_addr1);
+              next1 = SNAT_OUT2IN_NEXT_DROP;
+              goto trace1;
+            }
+          new_port1 = ses1->in_port;
+
+          proto1 = ip_proto_to_snat_proto (ip1->protocol);
+
+          old_addr1 = ip1->dst_address;
+          ip1->dst_address = new_addr1;
+          vnet_buffer(b1)->sw_if_index[VLIB_TX] = sm->inside_fib_index;
+
+          sum1 = ip1->checksum;
+          sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
+                                 ip4_header_t,
+                                 dst_address /* changed member */);
+          ip1->checksum = ip_csum_fold (sum1);
+
+          if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP))
+            {
+              if (tcp1->flags & TCP_FLAG_FIN && ses1->state == SNAT_SESSION_TCP_ESTABLISHED)
+                ses1->state = SNAT_SESSION_TCP_CLOSE_WAIT;
+              else if (tcp1->flags & TCP_FLAG_ACK && ses1->state == SNAT_SESSION_TCP_LAST_ACK)
+                snat_det_ses_close(dm1, ses1);
+
+              old_port1 = tcp1->dst;
+              tcp1->dst = new_port1;
+
+              sum1 = tcp1->checksum;
+              sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
+                                     ip4_header_t,
+                                     dst_address /* changed member */);
+
+              sum1 = ip_csum_update (sum1, old_port1, new_port1,
+                                     ip4_header_t /* cheat */,
+                                     length /* changed member */);
+              tcp1->checksum = ip_csum_fold(sum1);
+            }
+          else
+            {
+              old_port1 = udp1->dst_port;
+              udp1->dst_port = new_port1;
+              udp1->checksum = 0;
+            }
+
+        trace1:
+
+          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b1->flags & VLIB_BUFFER_IS_TRACED)))
+            {
+              snat_out2in_trace_t *t =
+                 vlib_add_trace (vm, node, b1, sizeof (*t));
+              t->sw_if_index = sw_if_index1;
+              t->next_index = next1;
+              t->session_index = ~0;
+              if (ses1)
+                t->session_index = ses1 - dm1->sessions;
+            }
+
+          pkts_processed += next1 != SNAT_OUT2IN_NEXT_DROP;
+
+          /* 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 bi0;
+         vlib_buffer_t * b0;
+          u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP;
+          u32 sw_if_index0;
+          ip4_header_t * ip0;
+          ip_csum_t sum0;
+          ip4_address_t new_addr0, old_addr0;
+          u16 new_port0, old_port0;
+          udp_header_t * udp0;
+          tcp_header_t * tcp0;
+          u32 proto0;
+          snat_det_out_key_t key0;
+          snat_det_map_t * dm0;
+          snat_det_session_t * ses0 = 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);
+          tcp0 = (tcp_header_t *) udp0;
+
+          sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+
+          key0.ext_host_addr = ip0->src_address;
+          key0.ext_host_port = tcp0->src;
+          key0.out_port = tcp0->dst;
+
+          dm0 = snat_det_map_by_out(sm, &ip0->dst_address);
+          if (PREDICT_FALSE(!dm0))
+            {
+              clib_warning("unknown dst address:  %U",
+                           format_ip4_address, &ip0->dst_address);
+              next0 = SNAT_OUT2IN_NEXT_DROP;
+              goto trace00;
+            }
+
+          snat_det_reverse(dm0, &ip0->dst_address,
+                           clib_net_to_host_u16(tcp0->dst), &new_addr0);
+
+          ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64);
+          if (PREDICT_FALSE(!ses0))
+            {
+              clib_warning("no match src %U:%d dst %d for user %U",
+                           format_ip4_address, &ip0->dst_address,
+                           clib_net_to_host_u16 (tcp0->src),
+                           clib_net_to_host_u16 (tcp0->dst),
+                           format_ip4_address, &new_addr0);
+              next0 = SNAT_OUT2IN_NEXT_DROP;
+              goto trace00;
+            }
+          new_port0 = ses0->in_port;
+
+          proto0 = ip_proto_to_snat_proto (ip0->protocol);
+
+          old_addr0 = ip0->dst_address;
+          ip0->dst_address = new_addr0;
+          vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->inside_fib_index;
+
+          sum0 = ip0->checksum;
+          sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
+                                 ip4_header_t,
+                                 dst_address /* changed member */);
+          ip0->checksum = ip_csum_fold (sum0);
+
+          if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+            {
+              if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED)
+                ses0->state = SNAT_SESSION_TCP_CLOSE_WAIT;
+              else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_LAST_ACK)
+                snat_det_ses_close(dm0, ses0);
+
+              old_port0 = tcp0->dst;
+              tcp0->dst = new_port0;
+
+              sum0 = tcp0->checksum;
+              sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
+                                     ip4_header_t,
+                                     dst_address /* changed member */);
+
+              sum0 = ip_csum_update (sum0, old_port0, new_port0,
+                                     ip4_header_t /* cheat */,
+                                     length /* changed member */);
+              tcp0->checksum = ip_csum_fold(sum0);
+            }
+          else
+            {
+              old_port0 = udp0->dst_port;
+              udp0->dst_port = new_port0;
+              udp0->checksum = 0;
+            }
+
+        trace00:
+
+          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+            {
+              snat_out2in_trace_t *t =
+                 vlib_add_trace (vm, node, b0, sizeof (*t));
+              t->sw_if_index = sw_if_index0;
+              t->next_index = next0;
+              t->session_index = ~0;
+              if (ses0)
+                t->session_index = ses0 - dm0->sessions;
+            }
+
+          pkts_processed += next0 != SNAT_OUT2IN_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, snat_det_out2in_node.index,
+                               SNAT_OUT2IN_ERROR_OUT2IN_PACKETS,
+                               pkts_processed);
+  return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (snat_det_out2in_node) = {
+  .function = snat_det_out2in_node_fn,
+  .name = "snat-det-out2in",
+  .vector_size = sizeof (u32),
+  .format_trace = format_snat_out2in_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(snat_out2in_error_strings),
+  .error_strings = snat_out2in_error_strings,
+
+  .runtime_data_bytes = sizeof (snat_runtime_t),
+
+  .n_next_nodes = 2,
+
+  /* edit / add dispositions here */
+  .next_nodes = {
+    [SNAT_OUT2IN_NEXT_DROP] = "error-drop",
+    [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
+  },
+};
+VLIB_NODE_FUNCTION_MULTIARCH (snat_det_out2in_node, snat_det_out2in_node_fn);
+
+/**********************/
+/*** worker handoff ***/
+/**********************/
 static uword
 snat_out2in_worker_handoff_fn (vlib_main_t * vm,
                                vlib_node_runtime_t * node,
@@ -1035,9 +1450,6 @@ snat_out2in_worker_handoff_fn (vlib_main_t * vm,
       u32 sw_if_index0;
       u32 rx_fib_index0;
       ip4_header_t * ip0;
-      udp_header_t * udp0;
-      snat_worker_key_t key0;
-      clib_bihash_kv_8_8_t kv0, value0;
       u8 do_handoff;
 
       bi0 = from[0];
@@ -1050,49 +1462,8 @@ snat_out2in_worker_handoff_fn (vlib_main_t * vm,
       rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0);
 
       ip0 = vlib_buffer_get_current (b0);
-      udp0 = ip4_next_header (ip0);
-
-      key0.addr = ip0->dst_address;
-      key0.port = udp0->dst_port;
-      key0.fib_index = rx_fib_index0;
-
-      if (PREDICT_FALSE(ip0->protocol == IP_PROTOCOL_ICMP))
-        {
-          icmp46_header_t * icmp0 = (icmp46_header_t *) udp0;
-          icmp_echo_header_t *echo0 = (icmp_echo_header_t *)(icmp0+1);
-          key0.port = echo0->identifier;
-        }
 
-      kv0.key = key0.as_u64;
-
-      /* Ever heard of of the "user" before? */
-      if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0))
-        {
-          key0.port = 0;
-          kv0.key = key0.as_u64;
-
-          if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0))
-            {
-              /* No, assign next available worker (RR) */
-              next_worker_index = sm->first_worker_index;
-              if (vec_len (sm->workers))
-                {
-                  next_worker_index += 
-                    sm->workers[sm->next_worker++ % _vec_len (sm->workers)];
-                }
-            }
-          else
-            {
-              /* Static mapping without port */
-              next_worker_index = value0.value;
-            }
-
-          /* Add to translated packets worker lookup */
-          kv0.value = next_worker_index;
-          clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv0, 1);
-        }
-      else
-        next_worker_index = value0.value;
+      next_worker_index = sm->worker_out2in_cb(ip0, rx_fib_index0);
 
       if (PREDICT_FALSE (next_worker_index != cpu_index))
         {
@@ -1132,7 +1503,7 @@ snat_out2in_worker_handoff_fn (vlib_main_t * vm,
           /* if this is 1st frame */
           if (!f)
             {
-              f = vlib_get_frame_to_node (vm, snat_out2in_node.index);
+              f = vlib_get_frame_to_node (vm, sm->out2in_node_index);
               to_next = vlib_frame_vector_args (f);
             }
 
@@ -1152,7 +1523,7 @@ snat_out2in_worker_handoff_fn (vlib_main_t * vm,
     }
 
   if (f)
-    vlib_put_frame_to_node (vm, snat_out2in_node.index, f);
+    vlib_put_frame_to_node (vm, sm->out2in_node_index, f);
 
   if (hf)
     hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker;
@@ -1199,6 +1570,9 @@ VLIB_REGISTER_NODE (snat_out2in_worker_handoff_node) = {
 
 VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_worker_handoff_node, snat_out2in_worker_handoff_fn);
 
+/********************************/
+/*** static mapping only mode ***/
+/********************************/
 static inline u32 icmp_out2in_fast (snat_main_t *sm,
                                     vlib_buffer_t * b0,
                                     ip4_header_t * ip0,