nat: ED: global session LRU list
[vpp.git] / src / plugins / nat / out2in_ed.c
index 91b8d5a..5b70b0c 100644 (file)
@@ -131,6 +131,16 @@ nat44_o2i_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void *arg)
       if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0))
        nat_elog_warn ("in2out_ed key del failed");
 
+      ed_bihash_kv_t bihash_key;
+      clib_memset (&bihash_key, 0, sizeof (bihash_key));
+      bihash_key.k.dst_address = s->ext_host_addr.as_u32;
+      bihash_key.k.dst_port = s->ext_host_port;
+      bihash_key.k.src_address = s->out2in.addr.as_u32;
+      bihash_key.k.src_port = s->out2in.port;
+      bihash_key.k.protocol = s->out2in.protocol;
+      clib_bihash_add_del_16_8 (&sm->ed_ext_ports, &bihash_key.kv,
+                               0 /* is_add */ );
+
       if (snat_is_unk_proto_session (s))
        goto delete;
 
@@ -203,8 +213,6 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
   snat_session_key_t eh_key;
   nat44_is_idle_session_ctx_t ctx;
 
-  nat44_session_try_cleanup (&l_key.addr, l_key.fib_index, thread_index, now);
-
   if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index)))
     {
       b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED];
@@ -215,6 +223,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
   u = nat_user_get_or_create (sm, &l_key.addr, l_key.fib_index, thread_index);
   if (!u)
     {
+      b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED];
       nat_elog_warn ("create NAT user failed");
       return 0;
     }
@@ -222,6 +231,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
   s = nat_ed_session_alloc (sm, u, thread_index, now);
   if (!s)
     {
+      b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_USER_SESS_EXCEEDED];
       nat44_delete_user_with_no_session (sm, u, thread_index);
       nat_elog_warn ("create NAT session failed");
       return 0;
@@ -414,7 +424,8 @@ create_bypass_for_fwd (snat_main_t * sm, vlib_buffer_t * b, ip4_header_t * ip,
     {
       tcp_header_t *tcp = ip4_next_header (ip);
       if (nat44_set_tcp_session_state_o2i
-         (sm, s, tcp->flags, tcp->ack_number, tcp->seq_number, thread_index))
+         (sm, now, s, tcp->flags, tcp->ack_number, tcp->seq_number,
+          thread_index))
        return;
     }
 
@@ -615,6 +626,7 @@ nat44_ed_out2in_unknown_proto (snat_main_t * sm,
                                  thread_index);
       if (!u)
        {
+         b->error = node->errors[NAT_OUT2IN_ED_ERROR_CANNOT_CREATE_USER];
          nat_elog_warn ("create NAT user failed");
          return 0;
        }
@@ -623,6 +635,7 @@ nat44_ed_out2in_unknown_proto (snat_main_t * sm,
       s = nat_ed_session_alloc (sm, u, thread_index, now);
       if (!s)
        {
+         b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_USER_SESS_EXCEEDED];
          nat44_delete_user_with_no_session (sm, u, thread_index);
          nat_elog_warn ("create NAT session failed");
          return 0;
@@ -764,6 +777,36 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
            }
          s0 = pool_elt_at_index (tsm->sessions, value0.value);
 
+         if (s0->tcp_close_timestamp)
+           {
+             if (now >= s0->tcp_close_timestamp)
+               {
+                 // session is closed, go slow path
+                 next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
+               }
+             else
+               {
+                 // session in transitory timeout, drop
+                 b0->error = node->errors[NAT_OUT2IN_ED_ERROR_TCP_CLOSED];
+                 next0 = NAT_NEXT_DROP;
+               }
+             goto trace0;
+           }
+
+         // drop if session expired
+         u64 sess_timeout_time;
+         sess_timeout_time = s0->last_heard +
+           (f64) nat44_session_get_timeout (sm, s0);
+         if (now >= sess_timeout_time)
+           {
+             // session is closed, go slow path
+             nat_free_session_data (sm, s0, thread_index, 0);
+             nat44_delete_session (sm, s0, thread_index);
+             next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
+             goto trace0;
+           }
+         //
+
          old_addr0 = ip0->dst_address.as_u32;
          new_addr0 = ip0->dst_address.as_u32 = s0->in2out.addr.as_u32;
          vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
@@ -809,7 +852,8 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
                }
              tcp_packets++;
              if (nat44_set_tcp_session_state_o2i
-                 (sm, s0, vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags,
+                 (sm, now, s0,
+                  vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags,
                   vnet_buffer (b0)->ip.reass.tcp_ack_number,
                   vnet_buffer (b0)->ip.reass.tcp_seq_number, thread_index))
                goto trace0;
@@ -988,13 +1032,13 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
              s0 =
                nat44_ed_out2in_unknown_proto (sm, b0, ip0, rx_fib_index0,
                                               thread_index, now, vm, node);
-             other_packets++;
              if (!sm->forwarding_enabled)
                {
                  if (!s0)
                    next0 = NAT_NEXT_DROP;
-                 goto trace0;
                }
+             other_packets++;
+             goto trace0;
            }
 
          if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
@@ -1011,7 +1055,20 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
                      vnet_buffer (b0)->ip.reass.l4_dst_port,
                      vnet_buffer (b0)->ip.reass.l4_src_port);
 
-         if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv0, &value0))
+         s0 = NULL;
+         if (!clib_bihash_search_16_8 (&tsm->out2in_ed, &kv0, &value0))
+           {
+             s0 = pool_elt_at_index (tsm->sessions, value0.value);
+
+             if (s0->tcp_close_timestamp && now >= s0->tcp_close_timestamp)
+               {
+                 nat_free_session_data (sm, s0, thread_index, 0);
+                 nat44_delete_session (sm, s0, thread_index);
+                 s0 = NULL;
+               }
+           }
+
+         if (!s0)
            {
              /* Try to match static mapping by external address and port,
                 destination address and port in packet */
@@ -1090,10 +1147,6 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
                  goto trace0;
                }
            }
-         else
-           {
-             s0 = pool_elt_at_index (tsm->sessions, value0.value);
-           }
 
          old_addr0 = ip0->dst_address.as_u32;
          new_addr0 = ip0->dst_address.as_u32 = s0->in2out.addr.as_u32;
@@ -1140,7 +1193,8 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
                }
              tcp_packets++;
              if (nat44_set_tcp_session_state_o2i
-                 (sm, s0, vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags,
+                 (sm, now, s0,
+                  vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags,
                   vnet_buffer (b0)->ip.reass.tcp_ack_number,
                   vnet_buffer (b0)->ip.reass.tcp_seq_number, thread_index))
                goto trace0;