NAT44: nat44_del_session and nat44_user_session_details API update (VPP-1271)
[vpp.git] / src / plugins / nat / in2out.c
index f009ce4..1659ed0 100755 (executable)
@@ -239,7 +239,7 @@ snat_not_translate (snat_main_t * sm, vlib_node_runtime_t *node,
                               &value0))
     {
       /* or is static mappings */
-      if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
+      if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0, 0))
         return 0;
     }
   else
@@ -254,21 +254,41 @@ snat_not_translate (snat_main_t * sm, vlib_node_runtime_t *node,
 
 static inline int
 nat_not_translate_output_feature (snat_main_t * sm, ip4_header_t * ip0,
-                                  u32 proto0, u32 thread_index)
+                                  u32 proto0, u16 src_port, u16 dst_port,
+                                  u32 thread_index, u32 sw_if_index)
 {
-  udp_header_t * udp0 = ip4_next_header (ip0);
   snat_session_key_t key0;
   clib_bihash_kv_8_8_t kv0, value0;
+  snat_interface_t *i;
 
+  /* src NAT check */
   key0.addr = ip0->src_address;
-  key0.port = udp0->src_port;
+  key0.port = src_port;
   key0.protocol = proto0;
   key0.fib_index = sm->outside_fib_index;
   kv0.key = key0.as_u64;
 
   if (!clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0,
-                              &value0))
+                               &value0))
+    return 1;
+
+  /* dst NAT check */
+  key0.addr = ip0->dst_address;
+  key0.port = dst_port;
+  key0.protocol = proto0;
+  key0.fib_index = sm->inside_fib_index;
+  kv0.key = key0.as_u64;
+  if (!clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].in2out, &kv0,
+                               &value0))
+  {
+    /* hairpinning */
+    pool_foreach (i, sm->output_feature_interfaces,
+    ({
+      if ((nat_interface_is_inside(i)) && (sw_if_index == i->sw_if_index))
+        return 0;
+    }));
     return 1;
+  }
 
   return 0;
 }
@@ -290,6 +310,7 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
   u32 outside_fib_index;
   uword * p;
   udp_header_t * udp0 = ip4_next_header (ip0);
+  u8 is_sm = 0;
 
   if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index)))
     {
@@ -317,7 +338,7 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
     }
 
   /* First try to match static mapping by local address and port */
-  if (snat_static_mapping_match (sm, *key0, &key1, 0, 0, 0))
+  if (snat_static_mapping_match (sm, *key0, &key1, 0, 0, 0, 0))
     {
       /* Try to create dynamic translation */
       if (snat_alloc_outside_address_and_port (sm->addresses, rx_fib_index0,
@@ -329,12 +350,9 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
           b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
           return SNAT_IN2OUT_NEXT_DROP;
         }
-      u->nsessions++;
     }
   else
-    {
-      u->nstaticsessions++;
-    }
+    is_sm = 1;
 
   s = nat_session_alloc_or_recycle (sm, u, thread_index);
   if (!s)
@@ -343,8 +361,9 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
       return SNAT_IN2OUT_NEXT_DROP;
     }
 
-  if (address_index == ~0)
+  if (is_sm)
     s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
+  user_session_increment (sm, u, is_sm);
   s->outside_address_index = address_index;
   s->in2out = *key0;
   s->out2in = key1;
@@ -471,11 +490,15 @@ icmp_get_ed_key(ip4_header_t *ip0, nat_ed_ses_key_t *p_key0)
 }
 
 static inline int
-nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip)
+nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip,
+                                      u32 thread_index)
 {
   nat_ed_ses_key_t key;
   clib_bihash_kv_16_8_t kv, value;
   udp_header_t *udp;
+  snat_session_t *s = 0;
+  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
+  f64 now = vlib_time_now (sm->vlib_main);
 
   if (!sm->forwarding_enabled)
     return 0;
@@ -506,7 +529,28 @@ nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip)
   kv.key[1] = key.as_u64[1];
 
   if (!clib_bihash_search_16_8 (&sm->in2out_ed, &kv, &value))
-    return value.value == ~0ULL;
+    {
+      s = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, value.value);
+      if (is_fwd_bypass_session (s))
+        {
+          if (ip->protocol == IP_PROTOCOL_TCP)
+            {
+              tcp_header_t *tcp = ip4_next_header(ip);
+              if (nat44_set_tcp_session_state_i2o (sm, s, tcp, thread_index))
+                return 1;
+            }
+          /* Per-user LRU list maintenance */
+          clib_dlist_remove (tsm->list_pool, s->per_user_index);
+          clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index,
+                              s->per_user_index);
+          /* Accounting */
+          s->last_heard = now;
+          s->total_pkts++;
+          return 1;
+        }
+      else
+        return 0;
+    }
 
   return 0;
 }
@@ -562,7 +606,7 @@ u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node,
       if (vnet_buffer(b0)->sw_if_index[VLIB_TX] != ~0)
         {
           if (PREDICT_FALSE(nat_not_translate_output_feature(sm,
-              ip0, IP_PROTOCOL_ICMP, thread_index)))
+              ip0, SNAT_PROTOCOL_ICMP, key0.port, key0.port, thread_index, sw_if_index0)))
             {
               dont_translate = 1;
               goto out;
@@ -571,7 +615,7 @@ u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node,
       else
         {
           if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index0,
-              ip0, IP_PROTOCOL_ICMP, rx_fib_index0, thread_index)))
+              ip0, SNAT_PROTOCOL_ICMP, rx_fib_index0, thread_index)))
             {
               dont_translate = 1;
               goto out;
@@ -684,7 +728,7 @@ u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node,
     }
   key0.fib_index = rx_fib_index0;
 
-  if (snat_static_mapping_match(sm, key0, &sm0, 0, &is_addr_only, 0))
+  if (snat_static_mapping_match(sm, key0, &sm0, 0, &is_addr_only, 0, 0))
     {
       if (PREDICT_FALSE(snat_not_translate_fast(sm, node, sw_if_index0, ip0,
           IP_PROTOCOL_ICMP, rx_fib_index0)))
@@ -774,6 +818,9 @@ static inline u32 icmp_in2out (snat_main_t *sm,
                          src_address /* changed member */);
   ip0->checksum = ip_csum_fold (sum0);
 
+  if (icmp0->checksum == 0)
+    icmp0->checksum = 0xffff;
+
   if (!icmp_is_error_message (icmp0))
     {
       new_id0 = sm0.port;
@@ -880,7 +927,7 @@ snat_hairpinning (snat_main_t *sm,
   kv0.key = key0.as_u64;
 
   /* Check if destination is static mappings */
-  if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
+  if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0, 0))
     {
       new_dst_addr0 = sm0.addr.as_u32;
       new_dst_port0 = sm0.port;
@@ -981,7 +1028,7 @@ snat_icmp_hairpinning (snat_main_t *sm,
                                   &value0))
         {
           /* or static mappings */
-          if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
+          if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0, 0))
             {
               new_dst_addr0 = sm0.addr.as_u32;
               vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
@@ -1136,7 +1183,7 @@ snat_in2out_unknown_proto (snat_main_t *sm,
   key.fib_index = rx_fib_index;
   key.proto = ip->protocol;
   key.l_port = 0;
-  key.l_port = 0;
+  key.r_port = 0;
   s_kv.key[0] = key.as_u64[0];
   s_kv.key[1] = key.as_u64[1];
 
@@ -1180,34 +1227,37 @@ snat_in2out_unknown_proto (snat_main_t *sm,
       else
         {
           /* Choose same out address as for TCP/UDP session to same destination */
-          if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
+          head_index = u->sessions_per_user_list_head_index;
+          head = pool_elt_at_index (tsm->list_pool, head_index);
+          elt_index = head->next;
+         if (PREDICT_FALSE (elt_index == ~0))
+           ses_index = ~0;
+         else
+           {
+             elt = pool_elt_at_index (tsm->list_pool, elt_index);
+             ses_index = elt->value;
+           }
+
+          while (ses_index != ~0)
             {
-              head_index = u->sessions_per_user_list_head_index;
-              head = pool_elt_at_index (tsm->list_pool, head_index);
-              elt_index = head->next;
+              s =  pool_elt_at_index (tsm->sessions, ses_index);
+              elt_index = elt->next;
               elt = pool_elt_at_index (tsm->list_pool, elt_index);
               ses_index = elt->value;
-              while (ses_index != ~0)
-                {
-                  s =  pool_elt_at_index (tsm->sessions, ses_index);
-                  elt_index = elt->next;
-                  elt = pool_elt_at_index (tsm->list_pool, elt_index);
-                  ses_index = elt->value;
 
-                  if (s->ext_host_addr.as_u32 == ip->dst_address.as_u32)
-                    {
-                      new_addr = ip->src_address.as_u32 = s->out2in.addr.as_u32;
-                      address_index = s->outside_address_index;
+              if (s->ext_host_addr.as_u32 == ip->dst_address.as_u32)
+                {
+                  new_addr = ip->src_address.as_u32 = s->out2in.addr.as_u32;
+                  address_index = s->outside_address_index;
 
-                      key.fib_index = sm->outside_fib_index;
-                      key.l_addr.as_u32 = new_addr;
-                      s_kv.key[0] = key.as_u64[0];
-                      s_kv.key[1] = key.as_u64[1];
-                      if (clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value))
-                        break;
+                  key.fib_index = sm->outside_fib_index;
+                  key.l_addr.as_u32 = new_addr;
+                  s_kv.key[0] = key.as_u64[0];
+                  s_kv.key[1] = key.as_u64[1];
+                  if (clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value))
+                    break;
 
-                      goto create_ses;
-                    }
+                  goto create_ses;
                 }
             }
           key.fib_index = sm->outside_fib_index;
@@ -1236,6 +1286,7 @@ create_ses:
 
       s->ext_host_addr.as_u32 = ip->dst_address.as_u32;
       s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO;
+      s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT;
       s->outside_address_index = address_index;
       s->out2in.addr.as_u32 = new_addr;
       s->out2in.fib_index = sm->outside_fib_index;
@@ -1243,14 +1294,8 @@ create_ses:
       s->in2out.fib_index = rx_fib_index;
       s->in2out.port = s->out2in.port = ip->protocol;
       if (is_sm)
-        {
-          u->nstaticsessions++;
-          s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
-        }
-      else
-        {
-          u->nsessions++;
-        }
+       s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
+      user_session_increment (sm, u, is_sm);
 
       /* Add to lookup tables */
       key.l_addr.as_u32 = old_addr;
@@ -1317,6 +1362,7 @@ snat_in2out_lb (snat_main_t *sm,
   u32 proto = ip_proto_to_snat_proto (ip->protocol);
   snat_session_key_t e_key, l_key;
   snat_user_t *u;
+  u8 lb;
 
   old_addr = ip->src_address.as_u32;
 
@@ -1331,9 +1377,20 @@ snat_in2out_lb (snat_main_t *sm,
 
   if (!clib_bihash_search_16_8 (&sm->in2out_ed, &s_kv, &s_value))
     {
-      if (s_value.value == ~0ULL)
-        return 0;
       s = pool_elt_at_index (tsm->sessions, s_value.value);
+      if (is_fwd_bypass_session (s))
+        {
+          if (ip->protocol == IP_PROTOCOL_TCP)
+            {
+              if (nat44_set_tcp_session_state_i2o (sm, s, tcp, thread_index))
+                return 0;
+            }
+          /* Per-user LRU list maintenance */
+          clib_dlist_remove (tsm->list_pool, s->per_user_index);
+          clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index,
+                              s->per_user_index);
+          return 0;
+        }
     }
   else
     {
@@ -1348,7 +1405,7 @@ snat_in2out_lb (snat_main_t *sm,
       l_key.port = udp->src_port;
       l_key.protocol = proto;
       l_key.fib_index = rx_fib_index;
-      if (snat_static_mapping_match(sm, l_key, &e_key, 0, 0, 0))
+      if (snat_static_mapping_match(sm, l_key, &e_key, 0, 0, 0, &lb))
         return 0;
 
       u = nat_user_get_or_create (sm, &ip->src_address, rx_fib_index,
@@ -1368,12 +1425,14 @@ snat_in2out_lb (snat_main_t *sm,
 
       s->ext_host_addr.as_u32 = ip->dst_address.as_u32;
       s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
-      s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING;
+      if (lb)
+        s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING;
+      s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT;
       s->outside_address_index = ~0;
       s->in2out = l_key;
       s->out2in = e_key;
       s->out2in.protocol = l_key.protocol;
-      u->nstaticsessions++;
+      user_session_increment (sm, u, 1 /* static */);
 
       /* Add to lookup tables */
       s_kv.value = s - tsm->sessions;
@@ -1399,6 +1458,9 @@ snat_in2out_lb (snat_main_t *sm,
                           s->ext_host_addr.as_u32, ip4_header_t, dst_address);
   ip->checksum = ip_csum_fold (sum);
 
+  if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0)
+    vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index;
+
   if (PREDICT_TRUE(proto == SNAT_PROTOCOL_TCP))
     {
       old_port = tcp->src_port;
@@ -1419,6 +1481,8 @@ snat_in2out_lb (snat_main_t *sm,
           ip->dst_address.as_u32 = s->ext_host_addr.as_u32;
         }
       tcp->checksum = ip_csum_fold(sum);
+      if (nat44_set_tcp_session_state_i2o (sm, s, tcp, thread_index))
+        return s;
     }
   else
     {
@@ -1431,9 +1495,6 @@ snat_in2out_lb (snat_main_t *sm,
       udp->checksum = 0;
     }
 
-  if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0)
-    vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index;
-
   /* Accounting */
   s->last_heard = now;
   s->total_pkts++;
@@ -1570,7 +1631,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
             {
               if (is_output_feature)
                 {
-                  if (PREDICT_FALSE(nat_not_translate_output_feature_fwd(sm, ip0)))
+                  if (PREDICT_FALSE(nat_not_translate_output_feature_fwd(sm, ip0, thread_index)))
                     goto trace00;
                 }
 
@@ -1602,7 +1663,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
                   if (is_output_feature)
                     {
                       if (PREDICT_FALSE(nat_not_translate_output_feature(sm,
-                          ip0, proto0, thread_index)))
+                          ip0, proto0, udp0->src_port, udp0->dst_port, thread_index, sw_if_index0)))
                         goto trace00;
                     }
                   else
@@ -1762,7 +1823,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
             {
               if (is_output_feature)
                 {
-                  if (PREDICT_FALSE(nat_not_translate_output_feature_fwd(sm, ip1)))
+                  if (PREDICT_FALSE(nat_not_translate_output_feature_fwd(sm, ip1, thread_index)))
                     goto trace01;
                 }
 
@@ -1794,8 +1855,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
                   if (is_output_feature)
                     {
                       if (PREDICT_FALSE(nat_not_translate_output_feature(sm,
-                          ip1, proto1, thread_index)))
-                        goto trace00;
+                          ip1, proto1, udp1->src_port, udp1->dst_port, thread_index, sw_if_index1)))
+                        goto trace01;
                     }
                   else
                     {
@@ -1990,7 +2051,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
             {
                if (is_output_feature)
                 {
-                  if (PREDICT_FALSE(nat_not_translate_output_feature_fwd(sm, ip0)))
+                  if (PREDICT_FALSE(nat_not_translate_output_feature_fwd(sm, ip0, thread_index)))
                     goto trace0;
                 }
 
@@ -2022,7 +2083,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
                   if (is_output_feature)
                     {
                       if (PREDICT_FALSE(nat_not_translate_output_feature(sm,
-                          ip0, proto0, thread_index)))
+                          ip0, proto0, udp0->src_port, udp0->dst_port, thread_index, sw_if_index0)))
                         goto trace0;
                     }
                   else
@@ -2404,7 +2465,7 @@ nat44_reass_hairpinning (snat_main_t *sm,
   udp0 = ip4_next_header (ip0);
 
   /* Check if destination is static mappings */
-  if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
+  if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0, 0))
     {
       new_dst_addr0 = sm0.addr.as_u32;
       new_dst_port0 = sm0.port;
@@ -4013,7 +4074,7 @@ snat_in2out_fast_static_map_fn (vlib_main_t * vm,
           key0.port = udp0->src_port;
           key0.fib_index = rx_fib_index0;
 
-          if (snat_static_mapping_match(sm, key0, &sm0, 0, 0, 0))
+          if (snat_static_mapping_match(sm, key0, &sm0, 0, 0, 0, 0))
             {
               b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION];
               next0= SNAT_IN2OUT_NEXT_DROP;