From 878c646aea9b9ccf68011ffd964694c43bbe5fdd Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Thu, 23 Aug 2018 00:33:35 -0700 Subject: [PATCH] NAT44: add support for session timeout (VPP-1272) NAT44 (vanilla/simple and endpoint-dependent mode) now lazily delete expired sessions. When inserting to session lookup hash and bucket is full, expired session is overwritten. Change-Id: Ib1b34959f60f0ca4f5b13525b1d41dd2f992288d Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 217 ++++++++++++++++++++++++++++------ src/plugins/nat/nat.api | 135 +++++++--------------- src/plugins/nat/nat.c | 52 ++++++++- src/plugins/nat/nat.h | 17 ++- src/plugins/nat/nat44_cli.c | 128 ++++++++++++-------- src/plugins/nat/nat64.c | 18 +-- src/plugins/nat/nat64.h | 11 +- src/plugins/nat/nat64_cli.c | 137 ---------------------- src/plugins/nat/nat_api.c | 212 +++++++++++---------------------- src/plugins/nat/nat_inlines.h | 33 ++++++ src/plugins/nat/nat_ipfix_logging.c | 13 +++ src/plugins/nat/nat_test.c | 28 ++--- src/plugins/nat/out2in.c | 217 ++++++++++++++++++++++++++++------ test/test_nat.py | 225 +++++++++++++++++++++++++++++------- test/vpp_papi_provider.py | 14 +-- 15 files changed, 876 insertions(+), 581 deletions(-) diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index ac0490f36e9..9c8b2c6c5e5 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -311,6 +311,43 @@ nat_not_translate_output_feature (snat_main_t * sm, ip4_header_t * ip0, return 0; } +int +nat44_i2o_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void * arg) +{ + snat_main_t *sm = &snat_main; + nat44_is_idle_session_ctx_t *ctx = arg; + snat_session_t *s; + u64 sess_timeout_time; + snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, + ctx->thread_index); + clib_bihash_kv_8_8_t s_kv; + + s = pool_elt_at_index (tsm->sessions, kv->value); + sess_timeout_time = s->last_heard + (f64)nat44_session_get_timeout(sm, s); + if (ctx->now >= sess_timeout_time) + { + s_kv.key = s->out2in.as_u64; + if (clib_bihash_add_del_8_8 (&tsm->out2in, &s_kv, 0)) + nat_log_warn ("out2in key del failed"); + + snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + + if (!snat_is_session_static (s)) + snat_free_outside_address_and_port (sm->addresses, ctx->thread_index, + &s->out2in); + + nat44_delete_session (sm, s, ctx->thread_index); + return 1; + } + + return 0; +} + static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t * ip0, u32 rx_fib_index0, @@ -318,7 +355,8 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, snat_session_t ** sessionp, vlib_node_runtime_t * node, u32 next0, - u32 thread_index) + u32 thread_index, + f64 now) { snat_user_t *u; snat_session_t *s; @@ -336,6 +374,7 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, .ip4.as_u32 = ip0->dst_address.as_u32, }, }; + nat44_is_idle_session_ctx_t ctx0; if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) { @@ -416,17 +455,21 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, *sessionp = s; /* Add to translation hashes */ + ctx0.now = now; + ctx0.thread_index = thread_index; kv0.key = s->in2out.as_u64; kv0.value = s - sm->per_thread_data[thread_index].sessions; - if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].in2out, &kv0, - 1 /* is_add */)) + if (clib_bihash_add_or_overwrite_stale_8_8 ( + &sm->per_thread_data[thread_index].in2out, &kv0, + nat44_i2o_is_idle_session_cb, &ctx0)) nat_log_notice ("in2out key add failed"); kv0.key = s->out2in.as_u64; kv0.value = s - sm->per_thread_data[thread_index].sessions; - if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0, - 1 /* is_add */)) + if (clib_bihash_add_or_overwrite_stale_8_8 ( + &sm->per_thread_data[thread_index].out2in, &kv0, + nat44_o2i_is_idle_session_cb, &ctx0)) nat_log_notice ("out2in key add failed"); /* log NAT event */ @@ -558,8 +601,8 @@ u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, goto out; } - next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, - &s0, node, next0, thread_index); + next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, &s0, node, next0, + thread_index, vlib_time_now (sm->vlib_main)); if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) goto out; @@ -1252,7 +1295,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, - &s0, node, next0, thread_index); + &s0, node, next0, thread_index, now); if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) goto trace00; } @@ -1412,7 +1455,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } next1 = slow_path (sm, b1, ip1, rx_fib_index1, &key1, - &s1, node, next1, thread_index); + &s1, node, next1, thread_index, now); if (PREDICT_FALSE (next1 == SNAT_IN2OUT_NEXT_DROP)) goto trace01; } @@ -1608,7 +1651,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, - &s0, node, next0, thread_index); + &s0, node, next0, thread_index, now); if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) goto trace0; @@ -2156,7 +2199,7 @@ nat44_in2out_reass_node_fn (vlib_main_t * vm, goto trace0; next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, - &s0, node, next0, thread_index); + &s0, node, next0, thread_index, now); if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) goto trace0; @@ -2374,6 +2417,110 @@ icmp_get_ed_key(ip4_header_t *ip0, nat_ed_ses_key_t *p_key0) return 0; } +int +nat44_i2o_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void * arg) +{ + snat_main_t *sm = &snat_main; + nat44_is_idle_session_ctx_t *ctx = arg; + snat_session_t *s; + u64 sess_timeout_time; + nat_ed_ses_key_t ed_key; + clib_bihash_kv_16_8_t ed_kv; + int i; + snat_address_t *a; + snat_session_key_t key; + snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, + ctx->thread_index); + + s = pool_elt_at_index (tsm->sessions, kv->value); + sess_timeout_time = s->last_heard + (f64)nat44_session_get_timeout(sm, s); + if (ctx->now >= sess_timeout_time) + { + if (is_fwd_bypass_session (s)) + goto delete; + + ed_key.l_addr = s->out2in.addr; + ed_key.r_addr = s->ext_host_addr; + ed_key.fib_index = s->out2in.fib_index; + if (snat_is_unk_proto_session (s)) + { + ed_key.proto = s->in2out.port; + ed_key.r_port = 0; + ed_key.l_port = 0; + } + else + { + ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol); + ed_key.l_port = s->out2in.port; + ed_key.r_port = s->ext_host_port; + } + ed_kv.key[0] = ed_key.as_u64[0]; + ed_kv.key[1] = ed_key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &ed_kv, 0)) + nat_log_warn ("out2in_ed key del failed"); + + if (snat_is_unk_proto_session (s)) + goto delete; + + snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + + if (is_twice_nat_session (s)) + { + for (i = 0; i < vec_len (sm->twice_nat_addresses); i++) + { + key.protocol = s->in2out.protocol; + key.port = s->ext_host_nat_port; + a = sm->twice_nat_addresses + i; + if (a->addr.as_u32 == s->ext_host_nat_addr.as_u32) + { + snat_free_outside_address_and_port (sm->twice_nat_addresses, + ctx->thread_index, &key); + break; + } + } + } + + if (snat_is_session_static (s)) + goto delete; + + if (s->outside_address_index != ~0) + snat_free_outside_address_and_port (sm->addresses, ctx->thread_index, + &s->out2in); + delete: + nat44_delete_session (sm, s, ctx->thread_index); + return 1; + } + + return 0; +} + +static inline u32 +icmp_in2out_ed_slow_path (snat_main_t * sm, vlib_buffer_t * b0, + ip4_header_t * ip0, icmp46_header_t * icmp0, + u32 sw_if_index0, u32 rx_fib_index0, + vlib_node_runtime_t * node, u32 next0, f64 now, + u32 thread_index, snat_session_t ** p_s0) +{ + 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 != SNAT_IN2OUT_NEXT_DROP && s0)) + { + /* Hairpinning */ + if (vnet_buffer(b0)->sw_if_index[VLIB_TX] == ~0) + snat_icmp_hairpinning(sm, b0, ip0, icmp0, sm->endpoint_dependent); + /* Accounting */ + nat44_session_update_counters (s0, now, + vlib_buffer_length_in_chain (sm->vlib_main, b0)); + } + return next0; +} + static u32 slow_path_ed (snat_main_t *sm, vlib_buffer_t *b, @@ -2382,7 +2529,8 @@ slow_path_ed (snat_main_t *sm, snat_session_t ** sessionp, vlib_node_runtime_t * node, u32 next, - u32 thread_index) + u32 thread_index, + f64 now) { snat_session_t *s; snat_user_t *u; @@ -2401,6 +2549,7 @@ slow_path_ed (snat_main_t *sm, .ip4.as_u32 = key->r_addr.as_u32, }, }; + nat44_is_idle_session_ctx_t ctx; if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index))) { @@ -2437,14 +2586,20 @@ slow_path_ed (snat_main_t *sm, if (!u) { nat_log_warn ("create NAT user failed"); + if (!is_sm) + snat_free_outside_address_and_port (sm->addresses, + thread_index, &key1); return SNAT_IN2OUT_NEXT_DROP; } - s = nat_session_alloc_or_recycle (sm, u, thread_index); + s = nat_ed_session_alloc (sm, u, thread_index); if (!s) { nat44_delete_user_with_no_session (sm, u, thread_index); nat_log_warn ("create NAT session failed"); + if (!is_sm) + snat_free_outside_address_and_port (sm->addresses, + thread_index, &key1); return SNAT_IN2OUT_NEXT_DROP; } @@ -2487,13 +2642,19 @@ slow_path_ed (snat_main_t *sm, /* Add to lookup tables */ kv->value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, kv, 1)) + ctx.now = now; + ctx.thread_index = thread_index; + if (clib_bihash_add_or_overwrite_stale_16_8 (&tsm->in2out_ed, kv, + nat44_i2o_ed_is_idle_session_cb, + &ctx)) nat_log_notice ("in2out-ed key add failed"); make_ed_kv (kv, &key1.addr, &key->r_addr, key->proto, s->out2in.fib_index, key1.port, key->r_port); kv->value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, kv, 1)) + if (clib_bihash_add_or_overwrite_stale_16_8 (&tsm->out2in_ed, kv, + nat44_o2i_ed_is_idle_session_cb, + &ctx)) nat_log_notice ("out2in-ed key add failed"); *sessionp = s; @@ -2588,8 +2749,6 @@ nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip, if (nat44_set_tcp_session_state_i2o (sm, s, tcp, thread_index)) return 1; } - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); /* Accounting */ nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b)); @@ -2703,7 +2862,7 @@ icmp_match_in2out_ed(snat_main_t *sm, vlib_node_runtime_t *node, } next = slow_path_ed (sm, b, rx_fib_index, &kv, &s, node, next, - thread_index); + thread_index, vlib_time_now (sm->vlib_main)); if (PREDICT_FALSE (next == SNAT_IN2OUT_NEXT_DROP)) goto out; @@ -2923,7 +3082,7 @@ nat44_ed_in2out_unknown_proto (snat_main_t *sm, } create_ses: - s = nat_session_alloc_or_recycle (sm, u, thread_index); + s = nat_ed_session_alloc (sm, u, thread_index); if (!s) { nat44_delete_user_with_no_session (sm, u, thread_index); @@ -2965,8 +3124,6 @@ create_ses: /* Accounting */ nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); /* Hairpinning */ if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) @@ -3088,7 +3245,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) { - next0 = icmp_in2out_slow_path + next0 = icmp_in2out_ed_slow_path (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, next0, now, thread_index, &s0); goto trace00; @@ -3140,7 +3297,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm, } next0 = slow_path_ed (sm, b0, rx_fib_index0, &kv0, &s0, node, - next0, thread_index); + next0, thread_index, now); if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) goto trace00; @@ -3211,8 +3368,6 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm, /* Accounting */ nat44_session_update_counters (s0, now, vlib_buffer_length_in_chain (vm, b0)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); trace00: if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) @@ -3273,7 +3428,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm, if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP)) { - next1 = icmp_in2out_slow_path + next1 = icmp_in2out_ed_slow_path (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, next1, now, thread_index, &s1); goto trace01; @@ -3325,7 +3480,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm, } next1 = slow_path_ed (sm, b1, rx_fib_index1, &kv1, &s1, node, - next1, thread_index); + next1, thread_index, now); if (PREDICT_FALSE (next1 == SNAT_IN2OUT_NEXT_DROP)) goto trace01; @@ -3396,8 +3551,6 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm, /* Accounting */ nat44_session_update_counters (s1, now, vlib_buffer_length_in_chain (vm, b1)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s1, thread_index); trace01: if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) @@ -3487,7 +3640,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) { - next0 = icmp_in2out_slow_path + next0 = icmp_in2out_ed_slow_path (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, next0, now, thread_index, &s0); goto trace0; @@ -3539,7 +3692,7 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm, } next0 = slow_path_ed (sm, b0, rx_fib_index0, &kv0, &s0, node, - next0, thread_index); + next0, thread_index, now); if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) goto trace0; @@ -3610,8 +3763,6 @@ nat44_ed_in2out_node_fn_inline (vlib_main_t * vm, /* Accounting */ nat44_session_update_counters (s0, now, vlib_buffer_length_in_chain (vm, b0)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); trace0: if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api index 45e504fa2ff..8e37567a9e8 100644 --- a/src/plugins/nat/nat.api +++ b/src/plugins/nat/nat.api @@ -13,7 +13,7 @@ * limitations under the License. */ -option version = "3.1.0"; +option version = "4.0.0"; /** * @file nat.api @@ -234,6 +234,49 @@ define nat_reass_details { u8 frag_n; }; +/** \brief Set values of timeouts for NAT sessions (seconds) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param udp - UDP timeout (default 300sec) + @param tcp_established - TCP established timeout (default 7440sec) + @param tcp_transitory - TCP transitory timeout (default 240sec) + @param icmp - ICMP timeout (default 60sec) +*/ +autoreply define nat_set_timeouts { + u32 client_index; + u32 context; + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; +}; + +/** \brief Get values of timeouts for NAT sessions (seconds) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_get_timeouts { + u32 client_index; + u32 context; +}; + +/** \brief Get values of timeouts for NAT sessions reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param udp - UDP timeout + @param tcp_established - TCP established timeout + @param tcp_transitory - TCP transitory timeout + @param icmp - ICMP timeout +*/ +define nat_get_timeouts_reply { + u32 context; + i32 retval; + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; +}; + /* * NAT44 APIs */ @@ -849,49 +892,6 @@ define nat_det_map_details { u32 ses_num; }; -/** \brief Set values of timeouts for deterministic NAT (seconds, 0 = default) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param udp - UDP timeout (default 300sec) - @param tcp_established - TCP established timeout (default 7440sec) - @param tcp_transitory - TCP transitory timeout (default 240sec) - @param icmp - ICMP timeout (default 60sec) -*/ -autoreply define nat_det_set_timeouts { - u32 client_index; - u32 context; - u32 udp; - u32 tcp_established; - u32 tcp_transitory; - u32 icmp; -}; - -/** \brief Get values of timeouts for deterministic NAT (seconds) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat_det_get_timeouts { - u32 client_index; - u32 context; -}; - -/** \brief Get values of timeouts for deterministic NAT reply - @param context - sender context, to match reply w/ request - @param retval - return code - @param udp - UDP timeout (default 300sec) - @param tcp_established - TCP established timeout (default 7440sec) - @param tcp_transitory - TCP transitory timeout (default 240sec) - @param icmp - ICMP timeout (default 60sec) -*/ -define nat_det_get_timeouts_reply { - u32 context; - i32 retval; - u32 udp; - u32 tcp_established; - u32 tcp_transitory; - u32 icmp; -}; - /** \brief Close deterministic NAT session by outside address and port @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -1097,53 +1097,6 @@ define nat64_bib_details { u32 ses_num; }; -/** \brief Set values of timeouts for NAT64 (seconds, 0 = default) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param udp - UDP timeout (default 300sec) - @param icmp - ICMP timeout (default 60sec) - @param tcp_trans - TCP transitory timeout (default 240sec) - @param tcp_est - TCP established timeout (default 7440sec) - @param tcp_incoming_syn - TCP incoming SYN timeout (default 6sec) -*/ -autoreply define nat64_set_timeouts { - u32 client_index; - u32 context; - u32 udp; - u32 icmp; - u32 tcp_trans; - u32 tcp_est; - u32 tcp_incoming_syn; -}; - -/** \brief Get values of timeouts for NAT64 (seconds) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat64_get_timeouts { - u32 client_index; - u32 context; -}; - -/** \brief Get values of timeouts for NAT64 reply - @param context - sender context, to match reply w/ request - @param retval - return code - @param udp - UDP timeout - @param icmp - ICMP timeout - @param tcp_trans - TCP transitory timeout - @param tcp_est - TCP established timeout - @param tcp_incoming_syn - TCP incoming SYN timeout -*/ -define nat64_get_timeouts_reply { - u32 context; - i32 retval; - u32 udp; - u32 icmp; - u32 tcp_trans; - u32 tcp_est; - u32 tcp_incoming_syn; -}; - /** \brief Dump NAT64 session table @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index bb82e924947..364d5f5ce94 100755 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -319,8 +319,6 @@ nat_user_get_or_create (snat_main_t *sm, ip4_address_t *addr, u32 fib_index, /* add user */ if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1)) nat_log_warn ("user_hash keay add failed"); - - clib_warning("%U %d", format_ip4_address, addr, fib_index); } else { @@ -400,6 +398,41 @@ nat_session_alloc_or_recycle (snat_main_t *sm, snat_user_t *u, u32 thread_index) return s; } +snat_session_t * +nat_ed_session_alloc (snat_main_t *sm, snat_user_t *u, u32 thread_index) +{ + snat_session_t *s; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + dlist_elt_t * per_user_translation_list_elt; + + if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user) + { + nat_log_warn ("max translations per user %U", format_ip4_address, &u->addr); + snat_ipfix_logging_max_entries_per_user (sm->max_translations_per_user, + u->addr.as_u32); + return 0; + } + + pool_get (tsm->sessions, s); + memset (s, 0, sizeof (*s)); + s->outside_address_index = ~0; + + /* Create list elts */ + pool_get (tsm->list_pool, per_user_translation_list_elt); + clib_dlist_init (tsm->list_pool, + per_user_translation_list_elt - tsm->list_pool); + + per_user_translation_list_elt->value = s - tsm->sessions; + s->per_user_index = per_user_translation_list_elt - tsm->list_pool; + s->per_user_list_head_index = u->sessions_per_user_list_head_index; + + clib_dlist_addtail (tsm->list_pool, + s->per_user_list_head_index, + per_user_translation_list_elt - tsm->list_pool); + + return s; +} + typedef struct { u8 next_in2out; } nat44_classify_trace_t; @@ -2456,6 +2489,7 @@ format_snat_protocol (u8 * s, va_list * args) } u8 * format_snat_key (u8 * s, va_list * args); +u8 * format_static_mapping_key (u8 * s, va_list * args); u8 * format_session_kvp (u8 * s, va_list * args) @@ -2478,7 +2512,8 @@ format_static_mapping_kvp (u8 * s, va_list * args) k.as_u64 = v->key; - s = format (s, "%U static-mapping-index %llu", format_snat_key, &k, v->value); + s = format (s, "%U static-mapping-index %llu", + format_static_mapping_key, &k, v->value); return s; } @@ -2972,6 +3007,17 @@ u8 * format_snat_key (u8 * s, va_list * args) return s; } +u8 * format_static_mapping_key (u8 * s, va_list * args) +{ + snat_session_key_t * key = va_arg (*args, snat_session_key_t *); + + s = format (s, "%U proto %U port %d fib %d", + format_ip4_address, &key->addr, + format_snat_protocol, key->protocol, + key->port, key->fib_index); + return s; +} + u8 * format_snat_session (u8 * s, va_list * args) { snat_main_per_thread_data_t * sm = va_arg (*args, snat_main_per_thread_data_t *); diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index 6ff6cd891ec..76f57542d16 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -32,10 +32,8 @@ #define SNAT_UDP_TIMEOUT 300 -#define SNAT_UDP_TIMEOUT_MIN 120 #define SNAT_TCP_TRANSITORY_TIMEOUT 240 #define SNAT_TCP_ESTABLISHED_TIMEOUT 7440 -#define SNAT_TCP_INCOMING_SYN 6 #define SNAT_ICMP_TIMEOUT 60 #define NAT_FQ_NELTS 64 @@ -133,6 +131,8 @@ typedef enum { #define NAT44_SES_O2I_FIN 2 #define NAT44_SES_I2O_FIN_ACK 4 #define NAT44_SES_O2I_FIN_ACK 8 +#define NAT44_SES_I2O_SYN 16 +#define NAT44_SES_O2I_SYN 32 #define nat44_is_ses_closed(s) s->state == 0xf @@ -172,7 +172,7 @@ typedef CLIB_PACKED(struct { ip4_address_t ext_host_addr; /* 68-71 */ u16 ext_host_port; /* 72-73 */ - /* External hos address and port after translation */ + /* External host address and port after translation */ ip4_address_t ext_host_nat_addr; /* 74-77 */ u16 ext_host_nat_port; /* 78-79 */ @@ -427,6 +427,11 @@ typedef struct snat_main_s { api_main_t * api_main; } snat_main_t; +typedef struct { + u32 thread_index; + f64 now; +} nat44_is_idle_session_ctx_t; + extern snat_main_t snat_main; extern vlib_node_registration_t snat_in2out_node; extern vlib_node_registration_t snat_in2out_output_node; @@ -626,8 +631,14 @@ snat_user_t * nat_user_get_or_create (snat_main_t *sm, ip4_address_t *addr, u32 fib_index, u32 thread_index); snat_session_t * nat_session_alloc_or_recycle (snat_main_t *sm, snat_user_t *u, u32 thread_index); +snat_session_t * nat_ed_session_alloc (snat_main_t *sm, snat_user_t *u, + u32 thread_index); void nat_set_alloc_addr_and_port_mape (u16 psid, u16 psid_offset, u16 psid_length); void nat_set_alloc_addr_and_port_default (void); +int nat44_i2o_ed_is_idle_session_cb (clib_bihash_kv_16_8_t *kv, void *arg); +int nat44_o2i_ed_is_idle_session_cb (clib_bihash_kv_16_8_t *kv, void *arg); +int nat44_i2o_is_idle_session_cb (clib_bihash_kv_8_8_t *kv, void *arg); +int nat44_o2i_is_idle_session_cb (clib_bihash_kv_8_8_t *kv, void *arg); #endif /* __included_snat_h__ */ diff --git a/src/plugins/nat/nat44_cli.c b/src/plugins/nat/nat44_cli.c index e4fd0caa302..e51f6d68511 100644 --- a/src/plugins/nat/nat44_cli.c +++ b/src/plugins/nat/nat44_cli.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -978,8 +979,9 @@ nat44_show_sessions_command_fn (vlib_main_t * vm, unformat_input_t * input, { tsm = vec_elt_at_index (sm->per_thread_data, i); - vlib_cli_output (vm, "-------- thread %d %s --------\n", - i, vlib_worker_threads[i].name); + vlib_cli_output (vm, "-------- thread %d %s: %d sessions --------\n", + i, vlib_worker_threads[i].name, + pool_elts (tsm->sessions)); pool_foreach (u, tsm->users, ({ vlib_cli_output (vm, " %U", format_snat_user, tsm, u, verbose); @@ -1307,9 +1309,6 @@ set_timeout_command_fn (vlib_main_t * vm, unformat_input_t _line_input, *line_input = &_line_input; clib_error_t *error = 0; - if (!sm->deterministic) - return clib_error_return (0, SUPPORTED_ONLY_IN_DET_MODE_STR); - /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return 0; @@ -1317,21 +1316,54 @@ set_timeout_command_fn (vlib_main_t * vm, while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { if (unformat (line_input, "udp %u", &sm->udp_timeout)) - ; + { + if (nat64_set_udp_timeout (sm->udp_timeout)) + { + error = clib_error_return (0, "Invalid UDP timeout value"); + goto done; + } + } else if (unformat (line_input, "tcp-established %u", &sm->tcp_established_timeout)) - ; + { + if (nat64_set_tcp_timeouts + (sm->tcp_transitory_timeout, sm->tcp_established_timeout)) + { + error = + clib_error_return (0, + "Invalid TCP established timeouts value"); + goto done; + } + } else if (unformat (line_input, "tcp-transitory %u", &sm->tcp_transitory_timeout)) - ; + { + if (nat64_set_tcp_timeouts + (sm->tcp_transitory_timeout, sm->tcp_established_timeout)) + { + error = + clib_error_return (0, + "Invalid TCP transitory timeouts value"); + goto done; + } + } else if (unformat (line_input, "icmp %u", &sm->icmp_timeout)) - ; + { + if (nat64_set_icmp_timeout (sm->icmp_timeout)) + { + error = clib_error_return (0, "Invalid ICMP timeout value"); + goto done; + } + } else if (unformat (line_input, "reset")) { sm->udp_timeout = SNAT_UDP_TIMEOUT; sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; sm->icmp_timeout = SNAT_ICMP_TIMEOUT; + nat64_set_udp_timeout (0); + nat64_set_icmp_timeout (0); + nat64_set_tcp_timeouts (0, 0); } else { @@ -1348,15 +1380,12 @@ done: } static clib_error_t * -nat44_det_show_timeouts_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) +nat_show_timeouts_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) { snat_main_t *sm = &snat_main; - if (!sm->deterministic) - return clib_error_return (0, SUPPORTED_ONLY_IN_DET_MODE_STR); - vlib_cli_output (vm, "udp timeout: %dsec", sm->udp_timeout); vlib_cli_output (vm, "tcp-established timeout: %dsec", sm->tcp_established_timeout); @@ -1542,6 +1571,40 @@ VLIB_CLI_COMMAND (nat_show_workers_command, static) = { .function = nat_show_workers_commnad_fn, }; +/*? + * @cliexpar + * @cliexstart{set nat timeout} + * Set values of timeouts for NAT sessions (in seconds), use: + * vpp# set nat timeout udp 120 tcp-established 7500 tcp-transitory 250 icmp 90 + * To reset default values use: + * vpp# set nat44 deterministic timeout reset + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_timeout_command, static) = { + .path = "set nat timeout", + .function = set_timeout_command_fn, + .short_help = + "set nat timeout [udp | tcp-established " + "tcp-transitory | icmp | reset]", +}; + +/*? + * @cliexpar + * @cliexstart{show nat timeouts} + * Show values of timeouts for NAT sessions. + * vpp# show nat timeouts + * udp timeout: 300sec + * tcp-established timeout: 7440sec + * tcp-transitory timeout: 240sec + * icmp timeout: 60sec + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_show_timeouts_command, static) = { + .path = "show nat timeouts", + .short_help = "show nat timeouts", + .function = nat_show_timeouts_command_fn, +}; + /*? * @cliexpar * @cliexstart{snat ipfix logging} @@ -1891,41 +1954,6 @@ VLIB_CLI_COMMAND (snat_det_reverse_command, static) = { .function = snat_det_reverse_command_fn, }; -/*? - * @cliexpar - * @cliexstart{set nat44 deterministic timeout} - * Set values of timeouts for deterministic NAT (in seconds), use: - * vpp# set nat44 deterministic timeout udp 120 tcp-established 7500 - * tcp-transitory 250 icmp 90 - * To reset default values use: - * vpp# set nat44 deterministic timeout reset - * @cliexend -?*/ -VLIB_CLI_COMMAND (set_timeout_command, static) = { - .path = "set nat44 deterministic timeout", - .function = set_timeout_command_fn, - .short_help = - "set nat44 deterministic timeout [udp | tcp-established " - "tcp-transitory | icmp | reset]", -}; - -/*? - * @cliexpar - * @cliexstart{show nat44 deterministic timeouts} - * Show values of timeouts for deterministic NAT. - * vpp# show nat44 deterministic timeouts - * udp timeout: 300sec - * tcp-established timeout: 7440sec - * tcp-transitory timeout: 240sec - * icmp timeout: 60sec - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_det_show_timeouts_command, static) = { - .path = "show nat44 deterministic timeouts", - .short_help = "show nat44 deterministic timeouts", - .function = nat44_det_show_timeouts_command_fn, -}; - /*? * @cliexpar * @cliexstart{show nat44 deterministic sessions} diff --git a/src/plugins/nat/nat64.c b/src/plugins/nat/nat64.c index 1ca5e369b98..04055af99bf 100644 --- a/src/plugins/nat/nat64.c +++ b/src/plugins/nat/nat64.c @@ -231,7 +231,6 @@ nat64_init (vlib_main_t * vm) nm->icmp_timeout = SNAT_ICMP_TIMEOUT; nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; - nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN; nm->total_enabled_count = 0; @@ -757,8 +756,6 @@ nat64_set_udp_timeout (u32 timeout) if (timeout == 0) nm->udp_timeout = SNAT_UDP_TIMEOUT; - else if (timeout < SNAT_UDP_TIMEOUT_MIN) - return VNET_API_ERROR_INVALID_VALUE; else nm->udp_timeout = timeout; @@ -795,7 +792,7 @@ nat64_get_icmp_timeout (void) } int -nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn) +nat64_set_tcp_timeouts (u32 trans, u32 est) { nat64_main_t *nm = &nat64_main; @@ -809,11 +806,6 @@ nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn) else nm->tcp_est_timeout = est; - if (incoming_syn == 0) - nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN; - else - nm->tcp_incoming_syn_timeout = incoming_syn; - return 0; } @@ -833,14 +825,6 @@ nat64_get_tcp_est_timeout (void) return nm->tcp_est_timeout; } -u32 -nat64_get_tcp_incoming_syn_timeout (void) -{ - nat64_main_t *nm = &nat64_main; - - return nm->tcp_incoming_syn_timeout; -} - void nat64_session_reset_timeout (nat64_db_st_entry_t * ste, vlib_main_t * vm) { diff --git a/src/plugins/nat/nat64.h b/src/plugins/nat/nat64.h index addba7a3704..d365e12b2f7 100644 --- a/src/plugins/nat/nat64.h +++ b/src/plugins/nat/nat64.h @@ -102,7 +102,6 @@ typedef struct u32 icmp_timeout; u32 tcp_trans_timeout; u32 tcp_est_timeout; - u32 tcp_incoming_syn_timeout; /* Total count of interfaces enabled */ u32 total_enabled_count; @@ -256,11 +255,10 @@ u32 nat64_get_icmp_timeout (void); * * @param trans Transitory timeout in seconds (if 0 reset to default value 240sec). * @param est Established timeout in seconds (if 0 reset to default value 7440sec). - * @param incoming_syn Incoming SYN timeout in seconds (if 0 reset to default value 6sec). * * @returns 0 on success, non-zero value otherwise. */ -int nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn); +int nat64_set_tcp_timeouts (u32 trans, u32 est); /** * @brief Get TCP transitory timeout. @@ -276,13 +274,6 @@ u32 nat64_get_tcp_trans_timeout (void); */ u32 nat64_get_tcp_est_timeout (void); -/** - * @brief Get TCP incoming SYN timeout. - * - * @returns TCP incoming SYN timeout in seconds. - */ -u32 nat64_get_tcp_incoming_syn_timeout (void); - /** * @brief Reset NAT64 session timeout. * diff --git a/src/plugins/nat/nat64_cli.c b/src/plugins/nat/nat64_cli.c index c2639f4fb16..efeaa0be0c2 100644 --- a/src/plugins/nat/nat64_cli.c +++ b/src/plugins/nat/nat64_cli.c @@ -451,107 +451,6 @@ done: return error; } -static clib_error_t * -nat64_set_timeouts_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - u32 timeout, tcp_trans, tcp_est, tcp_incoming_syn; - - tcp_trans = nat64_get_tcp_trans_timeout (); - tcp_est = nat64_get_tcp_est_timeout (); - tcp_incoming_syn = nat64_get_tcp_incoming_syn_timeout (); - - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "udp %u", &timeout)) - { - if (nat64_set_udp_timeout (timeout)) - { - error = clib_error_return (0, "Invalid UDP timeout value"); - goto done; - } - } - else if (unformat (line_input, "icmp %u", &timeout)) - { - if (nat64_set_icmp_timeout (timeout)) - { - error = clib_error_return (0, "Invalid ICMP timeout value"); - goto done; - } - } - else if (unformat (line_input, "tcp-trans %u", &tcp_trans)) - { - if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn)) - { - error = - clib_error_return (0, - "Invalid TCP transitory timeouts value"); - goto done; - } - } - else if (unformat (line_input, "tcp-est %u", &tcp_est)) - { - if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn)) - { - error = - clib_error_return (0, - "Invalid TCP established timeouts value"); - goto done; - } - } - else - if (unformat (line_input, "tcp-incoming-syn %u", &tcp_incoming_syn)) - { - if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn)) - { - error = - clib_error_return (0, - "Invalid TCP incoming SYN timeouts value"); - goto done; - } - } - else if (unformat (line_input, "reset")) - { - nat64_set_udp_timeout (0); - nat64_set_icmp_timeout (0); - nat64_set_tcp_timeouts (0, 0, 0); - } - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat64_show_timeouts_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - vlib_cli_output (vm, "NAT64 session timeouts:"); - vlib_cli_output (vm, " UDP %usec", nat64_get_udp_timeout ()); - vlib_cli_output (vm, " ICMP %usec", nat64_get_icmp_timeout ()); - vlib_cli_output (vm, " TCP transitory %usec", - nat64_get_tcp_trans_timeout ()); - vlib_cli_output (vm, " TCP established %usec", - nat64_get_tcp_est_timeout ()); - vlib_cli_output (vm, " TCP incoming SYN %usec", - nat64_get_tcp_incoming_syn_timeout ()); - - return 0; -} - typedef struct nat64_cli_st_walk_ctx_t_ { vlib_main_t *vm; @@ -944,42 +843,6 @@ VLIB_CLI_COMMAND (show_nat64_bib_command, static) = { .function = nat64_show_bib_command_fn, }; -/*? - * @cliexpar - * @cliexstart{set nat64 timeouts} - * Set NAT64 session timeouts (in seconds). - * To set NAT64 session timeoutes use use: - * vpp# set nat64 timeouts udp 200 icmp 30 tcp-trans 250 tcp-est 7450 - * To reset NAT64 session timeoutes to default values use: - * vpp# set nat64 timeouts reset - * @cliexend -?*/ -VLIB_CLI_COMMAND (set_nat64_timeouts_command, static) = { - .path = "set nat64 timeouts", - .short_help = "set nat64 timeouts udp icmp tcp-trans " - "tcp-est tcp-incoming-syn | reset", - .function = nat64_set_timeouts_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat64 timeoutss} - * Show NAT64 session timeouts: - * vpp# show nat64 timeouts - * NAT64 session timeouts: - * UDP 300sec - * ICMP 60sec - * TCP transitory 240sec - * TCP established 7440sec - * TCP incoming SYN 6sec - * @cliexend -?*/ -VLIB_CLI_COMMAND (show_nat64_timeouts_command, static) = { - .path = "show nat64 timeouts", - .short_help = "show nat64 timeouts", - .function = nat64_show_timeouts_command_fn, -}; - /*? * @cliexpar * @cliexstart{show nat64 session table} diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index 629045f8c31..8055259616f 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -416,6 +416,74 @@ vl_api_nat_reass_dump_t_print (vl_api_nat_reass_dump_t * mp, void *handle) FINISH; } +static void +vl_api_nat_set_timeouts_t_handler (vl_api_nat_set_timeouts_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_set_timeouts_reply_t *rmp; + int rv = 0; + + sm->udp_timeout = ntohl (mp->udp); + sm->tcp_established_timeout = ntohl (mp->tcp_established); + sm->tcp_transitory_timeout = ntohl (mp->tcp_transitory); + sm->icmp_timeout = ntohl (mp->icmp); + + rv = nat64_set_icmp_timeout (ntohl (mp->icmp)); + if (rv) + goto send_reply; + rv = nat64_set_udp_timeout (ntohl (mp->udp)); + if (rv) + goto send_reply; + rv = + nat64_set_tcp_timeouts (ntohl (mp->tcp_transitory), + ntohl (mp->tcp_established)); + +send_reply: + REPLY_MACRO (VL_API_NAT_SET_TIMEOUTS_REPLY); +} + +static void * +vl_api_nat_set_timeouts_t_print (vl_api_nat_set_timeouts_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_set_timeouts "); + s = format (s, "udp %d tcp_established %d tcp_transitory %d icmp %d\n", + ntohl (mp->udp), + ntohl (mp->tcp_established), + ntohl (mp->tcp_transitory), ntohl (mp->icmp)); + + FINISH; +} + +static void +vl_api_nat_get_timeouts_t_handler (vl_api_nat_get_timeouts_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_get_timeouts_reply_t *rmp; + int rv = 0; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_NAT_GET_TIMEOUTS_REPLY, + ({ + rmp->udp = htonl (sm->udp_timeout); + rmp->tcp_established = htonl (sm->tcp_established_timeout); + rmp->tcp_transitory = htonl (sm->tcp_transitory_timeout); + rmp->icmp = htonl (sm->icmp_timeout); + })) + /* *INDENT-ON* */ +} + +static void * +vl_api_nat_get_timeouts_t_print (vl_api_nat_get_timeouts_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_get_timeouts"); + + FINISH; +} + /*************/ /*** NAT44 ***/ /*************/ @@ -1838,79 +1906,6 @@ vl_api_nat_det_map_dump_t_print (vl_api_nat_det_map_dump_t * mp, void *handle) FINISH; } -static void -vl_api_nat_det_set_timeouts_t_handler (vl_api_nat_det_set_timeouts_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_det_set_timeouts_reply_t *rmp; - int rv = 0; - - if (!sm->deterministic) - { - rv = VNET_API_ERROR_UNSUPPORTED; - goto send_reply; - } - - sm->udp_timeout = ntohl (mp->udp); - sm->tcp_established_timeout = ntohl (mp->tcp_established); - sm->tcp_transitory_timeout = ntohl (mp->tcp_transitory); - sm->icmp_timeout = ntohl (mp->icmp); - -send_reply: - REPLY_MACRO (VL_API_NAT_DET_SET_TIMEOUTS_REPLY); -} - -static void * -vl_api_nat_det_set_timeouts_t_print (vl_api_nat_det_set_timeouts_t * mp, - void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat_det_set_timeouts "); - s = format (s, "udp %d tcp_established %d tcp_transitory %d icmp %d\n", - ntohl (mp->udp), - ntohl (mp->tcp_established), - ntohl (mp->tcp_transitory), ntohl (mp->icmp)); - - FINISH; -} - -static void -vl_api_nat_det_get_timeouts_t_handler (vl_api_nat_det_get_timeouts_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_det_get_timeouts_reply_t *rmp; - int rv = 0; - - if (!sm->deterministic) - { - rv = VNET_API_ERROR_UNSUPPORTED; - REPLY_MACRO (VL_API_NAT_DET_GET_TIMEOUTS_REPLY); - return; - } - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_NAT_DET_GET_TIMEOUTS_REPLY, - ({ - rmp->udp = htonl (sm->udp_timeout); - rmp->tcp_established = htonl (sm->tcp_established_timeout); - rmp->tcp_transitory = htonl (sm->tcp_transitory_timeout); - rmp->icmp = htonl (sm->icmp_timeout); - })) - /* *INDENT-ON* */ -} - -static void * -vl_api_nat_det_get_timeouts_t_print (vl_api_nat_det_get_timeouts_t * mp, - void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat_det_get_timeouts"); - - FINISH; -} - static void vl_api_nat_det_close_session_out_t_handler (vl_api_nat_det_close_session_out_t * mp) @@ -2396,71 +2391,6 @@ vl_api_nat64_bib_dump_t_print (vl_api_nat64_bib_dump_t * mp, void *handle) FINISH; } -static void -vl_api_nat64_set_timeouts_t_handler (vl_api_nat64_set_timeouts_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat64_set_timeouts_reply_t *rmp; - int rv = 0; - - rv = nat64_set_icmp_timeout (ntohl (mp->icmp)); - if (rv) - goto send_reply; - rv = nat64_set_udp_timeout (ntohl (mp->udp)); - if (rv) - goto send_reply; - rv = - nat64_set_tcp_timeouts (ntohl (mp->tcp_trans), ntohl (mp->tcp_est), - ntohl (mp->tcp_incoming_syn)); - -send_reply: - REPLY_MACRO (VL_API_NAT64_SET_TIMEOUTS_REPLY); -} - -static void *vl_api_nat64_set_timeouts_t_print - (vl_api_nat64_set_timeouts_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat64_set_timeouts "); - s = - format (s, - "udp %d icmp %d, tcp_trans %d, tcp_est %d, tcp_incoming_syn %d\n", - ntohl (mp->udp), ntohl (mp->icmp), ntohl (mp->tcp_trans), - ntohl (mp->tcp_est), ntohl (mp->tcp_incoming_syn)); - - FINISH; -} - -static void -vl_api_nat64_get_timeouts_t_handler (vl_api_nat64_get_timeouts_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat64_get_timeouts_reply_t *rmp; - int rv = 0; - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_NAT64_GET_TIMEOUTS_REPLY, - ({ - rmp->udp = htonl (nat64_get_udp_timeout()); - rmp->icmp = htonl (nat64_get_icmp_timeout()); - rmp->tcp_trans = htonl (nat64_get_tcp_trans_timeout()); - rmp->tcp_est = htonl (nat64_get_tcp_est_timeout()); - rmp->tcp_incoming_syn = htonl (nat64_get_tcp_incoming_syn_timeout()); - })) - /* *INDENT-ON* */ -} - -static void *vl_api_nat64_get_timeouts_t_print - (vl_api_nat64_get_timeouts_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat64_get_timeouts"); - - FINISH; -} - static int nat64_api_st_walk (nat64_db_st_entry_t * ste, void *arg) { @@ -3059,6 +2989,8 @@ _(NAT_IPFIX_ENABLE_DISABLE, nat_ipfix_enable_disable) \ _(NAT_SET_REASS, nat_set_reass) \ _(NAT_GET_REASS, nat_get_reass) \ _(NAT_REASS_DUMP, nat_reass_dump) \ +_(NAT_SET_TIMEOUTS, nat_set_timeouts) \ +_(NAT_GET_TIMEOUTS, nat_get_timeouts) \ _(NAT44_ADD_DEL_ADDRESS_RANGE, nat44_add_del_address_range) \ _(NAT44_INTERFACE_ADD_DEL_FEATURE, nat44_interface_add_del_feature) \ _(NAT44_ADD_DEL_STATIC_MAPPING, nat44_add_del_static_mapping) \ @@ -3084,8 +3016,6 @@ _(NAT_DET_ADD_DEL_MAP, nat_det_add_del_map) \ _(NAT_DET_FORWARD, nat_det_forward) \ _(NAT_DET_REVERSE, nat_det_reverse) \ _(NAT_DET_MAP_DUMP, nat_det_map_dump) \ -_(NAT_DET_SET_TIMEOUTS, nat_det_set_timeouts) \ -_(NAT_DET_GET_TIMEOUTS, nat_det_get_timeouts) \ _(NAT_DET_CLOSE_SESSION_OUT, nat_det_close_session_out) \ _(NAT_DET_CLOSE_SESSION_IN, nat_det_close_session_in) \ _(NAT_DET_SESSION_DUMP, nat_det_session_dump) \ @@ -3095,8 +3025,6 @@ _(NAT64_ADD_DEL_INTERFACE, nat64_add_del_interface) \ _(NAT64_INTERFACE_DUMP, nat64_interface_dump) \ _(NAT64_ADD_DEL_STATIC_BIB, nat64_add_del_static_bib) \ _(NAT64_BIB_DUMP, nat64_bib_dump) \ -_(NAT64_SET_TIMEOUTS, nat64_set_timeouts) \ -_(NAT64_GET_TIMEOUTS, nat64_get_timeouts) \ _(NAT64_ST_DUMP, nat64_st_dump) \ _(NAT64_ADD_DEL_PREFIX, nat64_add_del_prefix) \ _(NAT64_PREFIX_DUMP, nat64_prefix_dump) \ diff --git a/src/plugins/nat/nat_inlines.h b/src/plugins/nat/nat_inlines.h index a069d66bad9..adfb1d51954 100644 --- a/src/plugins/nat/nat_inlines.h +++ b/src/plugins/nat/nat_inlines.h @@ -198,6 +198,11 @@ always_inline int nat44_set_tcp_session_state_i2o (snat_main_t * sm, snat_session_t * ses, tcp_header_t * tcp, u32 thread_index) { + if ((tcp->flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_SYN) && + (ses->state & NAT44_SES_O2I_SYN)) + ses->state = 0; + if (tcp->flags & TCP_FLAG_SYN) + ses->state |= NAT44_SES_I2O_SYN; if (tcp->flags & TCP_FLAG_FIN) { ses->i2o_fin_seq = clib_net_to_host_u32 (tcp->seq_number); @@ -223,6 +228,11 @@ always_inline int nat44_set_tcp_session_state_o2i (snat_main_t * sm, snat_session_t * ses, tcp_header_t * tcp, u32 thread_index) { + if ((tcp->flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_SYN) && + (ses->state & NAT44_SES_O2I_SYN)) + ses->state = 0; + if (tcp->flags & TCP_FLAG_SYN) + ses->state |= NAT44_SES_O2I_SYN; if (tcp->flags & TCP_FLAG_FIN) { ses->o2i_fin_seq = clib_net_to_host_u32 (tcp->seq_number); @@ -244,6 +254,29 @@ nat44_set_tcp_session_state_o2i (snat_main_t * sm, snat_session_t * ses, return 0; } +always_inline u32 +nat44_session_get_timeout (snat_main_t * sm, snat_session_t * s) +{ + switch (s->in2out.protocol) + { + case SNAT_PROTOCOL_ICMP: + return sm->icmp_timeout; + case SNAT_PROTOCOL_UDP: + return sm->udp_timeout; + case SNAT_PROTOCOL_TCP: + { + if (s->state) + return sm->tcp_transitory_timeout; + else + return sm->tcp_established_timeout; + } + default: + return sm->udp_timeout; + } + + return 0; +} + always_inline void nat44_session_update_counters (snat_session_t * s, f64 now, uword bytes) { diff --git a/src/plugins/nat/nat_ipfix_logging.c b/src/plugins/nat/nat_ipfix_logging.c index 4a61fb03f55..d863d0cc674 100644 --- a/src/plugins/nat/nat_ipfix_logging.c +++ b/src/plugins/nat/nat_ipfix_logging.c @@ -2027,6 +2027,19 @@ snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port) clib_warning ("vnet_flow_report_add_del returned %d", rv); return -1; } + + if (sm->endpoint_dependent) + { + a.rewrite_callback = snat_template_rewrite_max_entries_per_usr; + a.flow_data_callback = snat_data_callback_max_entries_per_usr; + + rv = vnet_flow_report_add_del (frm, &a, NULL); + if (rv) + { + clib_warning ("vnet_flow_report_add_del returned %d", rv); + return -1; + } + } } return 0; diff --git a/src/plugins/nat/nat_test.c b/src/plugins/nat/nat_test.c index c5a96d3f7db..0a2896b9848 100644 --- a/src/plugins/nat/nat_test.c +++ b/src/plugins/nat/nat_test.c @@ -69,7 +69,7 @@ _(nat_set_workers_reply) \ _(nat44_add_del_interface_addr_reply) \ _(nat_ipfix_enable_disable_reply) \ _(nat_det_add_del_map_reply) \ -_(nat_det_set_timeouts_reply) \ +_(nat_set_timeouts_reply) \ _(nat_det_close_session_out_reply) \ _(nat_det_close_session_in_reply) @@ -118,8 +118,8 @@ _(NAT_DET_ADD_DEL_MAP_REPLY, nat_det_add_del_map_reply) \ _(NAT_DET_FORWARD_REPLY, nat_det_forward_reply) \ _(NAT_DET_REVERSE_REPLY, nat_det_reverse_reply) \ _(NAT_DET_MAP_DETAILS, nat_det_map_details) \ -_(NAT_DET_SET_TIMEOUTS_REPLY, nat_det_set_timeouts_reply) \ -_(NAT_DET_GET_TIMEOUTS_REPLY, nat_det_get_timeouts_reply) \ +_(NAT_SET_TIMEOUTS_REPLY, nat_set_timeouts_reply) \ +_(NAT_GET_TIMEOUTS_REPLY, nat_get_timeouts_reply) \ _(NAT_DET_CLOSE_SESSION_OUT_REPLY, \ nat_det_close_session_out_reply) \ _(NAT_DET_CLOSE_SESSION_IN_REPLY, \ @@ -904,10 +904,10 @@ static int api_nat_det_map_dump(vat_main_t * vam) return ret; } -static int api_nat_det_set_timeouts (vat_main_t * vam) +static int api_nat_set_timeouts (vat_main_t * vam) { unformat_input_t * i = vam->input; - vl_api_nat_det_set_timeouts_t * mp; + vl_api_nat_set_timeouts_t * mp; u32 udp = SNAT_UDP_TIMEOUT; u32 tcp_established = SNAT_TCP_ESTABLISHED_TIMEOUT; u32 tcp_transitory = SNAT_TCP_TRANSITORY_TIMEOUT; @@ -928,7 +928,7 @@ static int api_nat_det_set_timeouts (vat_main_t * vam) return -99; } - M(NAT_DET_SET_TIMEOUTS, mp); + M(NAT_SET_TIMEOUTS, mp); mp->udp = htonl(udp); mp->tcp_established = htonl(tcp_established); mp->tcp_transitory = htonl(tcp_transitory); @@ -939,8 +939,8 @@ static int api_nat_det_set_timeouts (vat_main_t * vam) return ret; } -static void vl_api_nat_det_get_timeouts_reply_t_handler - (vl_api_nat_det_get_timeouts_reply_t *mp) +static void vl_api_nat_get_timeouts_reply_t_handler + (vl_api_nat_get_timeouts_reply_t *mp) { snat_test_main_t * sm = &snat_test_main; vat_main_t *vam = sm->vat_main; @@ -959,18 +959,18 @@ static void vl_api_nat_det_get_timeouts_reply_t_handler vam->result_ready = 1; } -static int api_nat_det_get_timeouts(vat_main_t * vam) +static int api_nat_get_timeouts(vat_main_t * vam) { - vl_api_nat_det_get_timeouts_t * mp; + vl_api_nat_get_timeouts_t * mp; int ret; if (vam->json_output) { - clib_warning ("JSON output not supported for nat_show_config"); + clib_warning ("JSON output not supported for nat_get_timeouts"); return -99; } - M(NAT_DET_GET_TIMEOUTS, mp); + M(NAT_GET_TIMEOUTS, mp); S(mp); W (ret); return ret; @@ -1110,9 +1110,9 @@ _(nat_det_add_del_map, "in / out " \ _(nat_det_forward, "") \ _(nat_det_reverse, " ") \ _(nat_det_map_dump, "") \ -_(nat_det_set_timeouts, "[udp | tcp_established | " \ +_(nat_set_timeouts, "[udp | tcp_established | " \ "tcp_transitory | icmp ]") \ -_(nat_det_get_timeouts, "") \ +_(nat_get_timeouts, "") \ _(nat_det_close_session_out, ": " \ ":") \ _(nat_det_close_session_in, ": " \ diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c index d5fad807570..46a8a1ed078 100755 --- a/src/plugins/nat/out2in.c +++ b/src/plugins/nat/out2in.c @@ -140,6 +140,43 @@ typedef enum { SNAT_OUT2IN_N_NEXT, } snat_out2in_next_t; +int +nat44_o2i_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void * arg) +{ + snat_main_t *sm = &snat_main; + nat44_is_idle_session_ctx_t *ctx = arg; + snat_session_t *s; + u64 sess_timeout_time; + snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, + ctx->thread_index); + clib_bihash_kv_8_8_t s_kv; + + s = pool_elt_at_index (tsm->sessions, kv->value); + sess_timeout_time = s->last_heard + (f64)nat44_session_get_timeout(sm, s); + if (ctx->now >= sess_timeout_time) + { + s_kv.key = s->in2out.as_u64; + if (clib_bihash_add_del_8_8 (&tsm->in2out, &s_kv, 0)) + nat_log_warn ("out2in key del failed"); + + snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + + if (!snat_is_session_static (s)) + snat_free_outside_address_and_port (sm->addresses, ctx->thread_index, + &s->out2in); + + nat44_delete_session (sm, s, ctx->thread_index); + return 1; + } + + return 0; +} + /** * @brief Create session for static mapping. * @@ -160,13 +197,15 @@ create_session_for_static_mapping (snat_main_t *sm, snat_session_key_t in2out, snat_session_key_t out2in, vlib_node_runtime_t * node, - u32 thread_index) + u32 thread_index, + f64 now) { snat_user_t *u; snat_session_t *s; clib_bihash_kv_8_8_t kv0; ip4_header_t *ip0; udp_header_t *udp0; + nat44_is_idle_session_ctx_t ctx0; if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) { @@ -203,16 +242,20 @@ create_session_for_static_mapping (snat_main_t *sm, s->in2out.protocol = out2in.protocol; /* Add to translation hashes */ + ctx0.now = now; + ctx0.thread_index = thread_index; kv0.key = s->in2out.as_u64; kv0.value = s - sm->per_thread_data[thread_index].sessions; - if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].in2out, &kv0, - 1 /* is_add */)) + if (clib_bihash_add_or_overwrite_stale_8_8 ( + &sm->per_thread_data[thread_index].in2out, &kv0, + nat44_i2o_is_idle_session_cb, &ctx0)) nat_log_notice ("in2out key add failed"); kv0.key = s->out2in.as_u64; - if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0, - 1 /* is_add */)) + if (clib_bihash_add_or_overwrite_stale_8_8 ( + &sm->per_thread_data[thread_index].out2in, &kv0, + nat44_o2i_is_idle_session_cb, &ctx0)) nat_log_notice ("out2in key add failed"); /* log NAT event */ @@ -222,7 +265,7 @@ create_session_for_static_mapping (snat_main_t *sm, s->in2out.port, s->out2in.port, s->in2out.fib_index); - return s; + return s; } static_always_inline @@ -356,7 +399,8 @@ u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, /* Create session initiated by host from external network */ s0 = create_session_for_static_mapping(sm, b0, sm0, key0, - node, thread_index); + node, thread_index, + vlib_time_now (sm->vlib_main)); if (!s0) { @@ -801,7 +845,7 @@ snat_out2in_node_fn (vlib_main_t * vm, /* Create session initiated by host from external network */ s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node, - thread_index); + thread_index, now); if (!s0) { next0 = SNAT_OUT2IN_NEXT_DROP; @@ -952,7 +996,7 @@ snat_out2in_node_fn (vlib_main_t * vm, /* Create session initiated by host from external network */ s1 = create_session_for_static_mapping(sm, b1, sm1, key1, node, - thread_index); + thread_index, now); if (!s1) { next1 = SNAT_OUT2IN_NEXT_DROP; @@ -1139,7 +1183,7 @@ snat_out2in_node_fn (vlib_main_t * vm, /* Create session initiated by host from external network */ s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node, - thread_index); + thread_index, now); if (!s0) { next0 = SNAT_OUT2IN_NEXT_DROP; @@ -1364,7 +1408,7 @@ nat44_out2in_reass_node_fn (vlib_main_t * vm, /* Create session initiated by host from external network */ s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node, - thread_index); + thread_index, now); if (!s0) { b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; @@ -1564,6 +1608,109 @@ format_nat44_ed_out2in_trace (u8 * s, va_list * args) return s; } +static inline u32 +icmp_out2in_ed_slow_path (snat_main_t * sm, vlib_buffer_t * b0, + ip4_header_t * ip0, icmp46_header_t * icmp0, + u32 sw_if_index0, u32 rx_fib_index0, + vlib_node_runtime_t * node, u32 next0, f64 now, + u32 thread_index, snat_session_t ** p_s0) +{ + 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 != SNAT_OUT2IN_NEXT_DROP && s0)) + { + /* Accounting */ + nat44_session_update_counters (s0, now, + vlib_buffer_length_in_chain (sm->vlib_main, b0)); + } + return next0; +} + +int +nat44_o2i_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void * arg) +{ + snat_main_t *sm = &snat_main; + nat44_is_idle_session_ctx_t *ctx = arg; + snat_session_t *s; + u64 sess_timeout_time; + nat_ed_ses_key_t ed_key; + clib_bihash_kv_16_8_t ed_kv; + int i; + snat_address_t *a; + snat_session_key_t key; + snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, + ctx->thread_index); + + s = pool_elt_at_index (tsm->sessions, kv->value); + sess_timeout_time = s->last_heard + (f64)nat44_session_get_timeout(sm, s); + if (ctx->now >= sess_timeout_time) + { + ed_key.l_addr = s->in2out.addr; + ed_key.r_addr = s->ext_host_addr; + ed_key.fib_index = s->out2in.fib_index; + if (snat_is_unk_proto_session (s)) + { + ed_key.proto = s->in2out.port; + ed_key.r_port = 0; + ed_key.l_port = 0; + } + else + { + ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol); + ed_key.l_port = s->in2out.port; + ed_key.r_port = s->ext_host_port; + } + if (is_twice_nat_session (s)) + { + ed_key.r_addr = s->ext_host_nat_addr; + ed_key.r_port = s->ext_host_nat_port; + } + ed_kv.key[0] = ed_key.as_u64[0]; + ed_kv.key[1] = ed_key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0)) + nat_log_warn ("in2out_ed key del failed"); + + if (snat_is_unk_proto_session (s)) + goto delete; + + snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + + if (is_twice_nat_session (s)) + { + for (i = 0; i < vec_len (sm->twice_nat_addresses); i++) + { + key.protocol = s->in2out.protocol; + key.port = s->ext_host_nat_port; + a = sm->twice_nat_addresses + i; + if (a->addr.as_u32 == s->ext_host_nat_addr.as_u32) + { + snat_free_outside_address_and_port (sm->twice_nat_addresses, + ctx->thread_index, &key); + break; + } + } + } + + if (snat_is_session_static (s)) + goto delete; + + if (s->outside_address_index != ~0) + snat_free_outside_address_and_port (sm->addresses, ctx->thread_index, + &s->out2in); + delete: + nat44_delete_session (sm, s, ctx->thread_index); + return 1; + } + + return 0; +} + static snat_session_t * create_session_for_static_mapping_ed (snat_main_t * sm, vlib_buffer_t *b, @@ -1572,7 +1719,8 @@ create_session_for_static_mapping_ed (snat_main_t * sm, vlib_node_runtime_t * node, u32 thread_index, twice_nat_type_t twice_nat, - u8 is_lb) + u8 is_lb, + f64 now) { snat_session_t *s; snat_user_t *u; @@ -1582,6 +1730,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm, clib_bihash_kv_16_8_t kv; snat_session_key_t eh_key; u32 address_index; + nat44_is_idle_session_ctx_t ctx; if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) { @@ -1597,7 +1746,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm, return 0; } - s = nat_session_alloc_or_recycle (sm, u, thread_index); + s = nat_ed_session_alloc (sm, u, thread_index); if (!s) { nat44_delete_user_with_no_session (sm, u, thread_index); @@ -1624,7 +1773,11 @@ create_session_for_static_mapping_ed (snat_main_t * sm, make_ed_kv (&kv, &e_key.addr, &s->ext_host_addr, ip->protocol, e_key.fib_index, e_key.port, s->ext_host_port); kv.value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &kv, 1)) + ctx.now = now; + ctx.thread_index = thread_index; + if (clib_bihash_add_or_overwrite_stale_16_8 (&tsm->out2in_ed, &kv, + nat44_o2i_ed_is_idle_session_cb, + &ctx)) nat_log_notice ("out2in-ed key add failed"); if (twice_nat == TWICE_NAT || (twice_nat == TWICE_NAT_SELF && @@ -1655,7 +1808,9 @@ create_session_for_static_mapping_ed (snat_main_t * sm, l_key.fib_index, l_key.port, s->ext_host_port); } kv.value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &kv, 1)) + if (clib_bihash_add_or_overwrite_stale_16_8 (&tsm->in2out_ed, &kv, + nat44_i2o_ed_is_idle_session_cb, + &ctx)) nat_log_notice ("in2out-ed key add failed"); return s; @@ -1779,7 +1934,7 @@ create_bypass_for_fwd(snat_main_t * sm, ip4_header_t * ip, u32 rx_fib_index, return; } - s = nat_session_alloc_or_recycle (sm, u, thread_index); + s = nat_ed_session_alloc (sm, u, thread_index); if (!s) { nat44_delete_user_with_no_session (sm, u, thread_index); @@ -1810,8 +1965,6 @@ create_bypass_for_fwd(snat_main_t * sm, ip4_header_t * ip, u32 rx_fib_index, return; } - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); /* Accounting */ nat44_session_update_counters (s, now, 0); } @@ -1891,7 +2044,8 @@ icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node, /* Create session initiated by host from external network */ s = create_session_for_static_mapping_ed(sm, b, l_key, e_key, node, - thread_index, 0, 0); + thread_index, 0, 0, + vlib_time_now (sm->vlib_main)); if (!s) { @@ -1981,7 +2135,7 @@ nat44_ed_out2in_unknown_proto (snat_main_t *sm, } /* Create a new session */ - s = nat_session_alloc_or_recycle (sm, u, thread_index); + s = nat_ed_session_alloc (sm, u, thread_index); if (!s) { nat44_delete_user_with_no_session (sm, u, thread_index); @@ -2023,8 +2177,6 @@ nat44_ed_out2in_unknown_proto (snat_main_t *sm, /* Accounting */ nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); return s; } @@ -2136,7 +2288,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) { - next0 = icmp_out2in_slow_path + next0 = icmp_out2in_ed_slow_path (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, next0, now, thread_index, &s0); goto trace00; @@ -2210,7 +2362,8 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm, s0 = create_session_for_static_mapping_ed(sm, b0, l_key0, e_key0, node, thread_index, - twice_nat0, is_lb0); + twice_nat0, is_lb0, + now); if (!s0) { @@ -2281,8 +2434,6 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm, /* Accounting */ nat44_session_update_counters (s0, now, vlib_buffer_length_in_chain (vm, b0)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); trace00: if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) @@ -2339,7 +2490,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm, if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP)) { - next1 = icmp_out2in_slow_path + next1 = icmp_out2in_ed_slow_path (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, next1, now, thread_index, &s1); goto trace01; @@ -2413,7 +2564,8 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm, s1 = create_session_for_static_mapping_ed(sm, b1, l_key1, e_key1, node, thread_index, - twice_nat1, is_lb1); + twice_nat1, is_lb1, + now); if (!s1) { @@ -2484,8 +2636,6 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm, /* Accounting */ nat44_session_update_counters (s1, now, vlib_buffer_length_in_chain (vm, b1)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s1, thread_index); trace01: if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) @@ -2574,7 +2724,7 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) { - next0 = icmp_out2in_slow_path + next0 = icmp_out2in_ed_slow_path (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, next0, now, thread_index, &s0); goto trace0; @@ -2648,7 +2798,8 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm, s0 = create_session_for_static_mapping_ed(sm, b0, l_key0, e_key0, node, thread_index, - twice_nat0, is_lb0); + twice_nat0, is_lb0, + now); if (!s0) { @@ -2719,8 +2870,6 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm, /* Accounting */ nat44_session_update_counters (s0, now, vlib_buffer_length_in_chain (vm, b0)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); trace0: if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) diff --git a/test/test_nat.py b/test/test_nat.py index 286c74a8037..79d26224ad0 100644 --- a/test/test_nat.py +++ b/test/test_nat.py @@ -137,6 +137,7 @@ class MethodHolder(VppTestCase): self.vapi.nat_set_reass() self.vapi.nat_set_reass(is_ip6=1) self.verify_no_nat44_user() + self.vapi.nat_set_timeouts() def nat44_add_static_mapping(self, local_ip, external_ip='0.0.0.0', local_port=0, external_port=0, vrf_id=0, @@ -993,6 +994,25 @@ class MethodHolder(VppTestCase): users = self.vapi.nat44_user_dump() self.assertEqual(len(users), 0) + def verify_ipfix_max_entries_per_user(self, data, limit, src_addr): + """ + Verify IPFIX maximum entries per user exceeded event + + :param data: Decoded IPFIX data records + :param limit: Number of maximum entries per user + :param src_addr: IPv4 source address + """ + self.assertEqual(1, len(data)) + record = data[0] + # natEvent + self.assertEqual(ord(record[230]), 13) + # natQuotaExceededEvent + self.assertEqual(struct.pack("I", 3), record[466]) + # maxEntriesPerUser + self.assertEqual(struct.pack("I", limit), record[473]) + # sourceIPv4Address + self.assertEqual(src_addr, record[8]) + class TestNAT44(MethodHolder): """ NAT44 Test Cases """ @@ -3217,6 +3237,48 @@ class TestNAT44(MethodHolder): self.pg1.resolve_arp() self.pg2.resolve_arp() + @unittest.skipUnless(running_extended_tests(), "part of extended tests") + def test_session_timeout(self): + """ NAT44 session timeouts """ + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + self.vapi.nat_set_timeouts(udp=5) + + max_sessions = 1000 + pkts = [] + for i in range(0, max_sessions): + src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=src, dst=self.pg1.remote_ip4) / + UDP(sport=1025, dport=53)) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(max_sessions) + + sleep(6) + + pkts = [] + for i in range(0, max_sessions): + src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=src, dst=self.pg1.remote_ip4) / + UDP(sport=1026, dport=53)) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(max_sessions) + + nsessions = 0 + users = self.vapi.nat44_user_dump() + for user in users: + nsessions = nsessions + user.nsessions + self.assertLess(nsessions, 2 * max_sessions) + def tearDown(self): super(TestNAT44, self).tearDown() if not self.vpp_dead: @@ -3227,6 +3289,7 @@ class TestNAT44(MethodHolder): self.logger.info(self.vapi.cli("show nat44 sessions detail")) self.logger.info(self.vapi.cli("show nat virtual-reassembly")) self.logger.info(self.vapi.cli("show nat44 hash tables detail")) + self.logger.info(self.vapi.cli("show nat timeouts")) self.vapi.cli("nat addr-port-assignment-alg default") self.clear_nat44() self.vapi.cli("clear logging") @@ -4909,6 +4972,105 @@ class TestNAT44EndpointDependent(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise + @unittest.skipUnless(running_extended_tests(), "part of extended tests") + def test_session_timeout(self): + """ NAT44 session timeouts """ + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + self.vapi.nat_set_timeouts(icmp=5) + + max_sessions = 1000 + pkts = [] + for i in range(0, max_sessions): + src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=src, dst=self.pg1.remote_ip4) / + ICMP(id=1025, type='echo-request')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(max_sessions) + + sleep(10) + + pkts = [] + for i in range(0, max_sessions): + src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=src, dst=self.pg1.remote_ip4) / + ICMP(id=1026, type='echo-request')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(max_sessions) + + nsessions = 0 + users = self.vapi.nat44_user_dump() + for user in users: + nsessions = nsessions + user.nsessions + self.assertLess(nsessions, 2 * max_sessions) + + @unittest.skipUnless(running_extended_tests(), "part of extended tests") + def test_session_limit_per_user(self): + """ Maximum sessions per user limit """ + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + self.vapi.set_ipfix_exporter(collector_address=self.pg2.remote_ip4n, + src_address=self.pg2.local_ip4n, + path_mtu=512, + template_interval=10) + + # get maximum number of translations per user + nat44_config = self.vapi.nat_show_config() + + pkts = [] + for port in range(0, nat44_config.max_translations_per_user): + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + UDP(sport=1025 + port, dport=1025 + port)) + pkts.append(p) + + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + + self.vapi.nat_ipfix(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port) + + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + UDP(sport=3001, dport=3002)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.assert_nothing_captured() + + # verify IPFIX logging + self.vapi.cli("ipfix flush") # FIXME this should be an API call + sleep(1) + capture = self.pg2.get_capture(10) + ipfix = IPFIXDecoder() + # first load template + for p in capture: + self.assertTrue(p.haslayer(IPFIX)) + if p.haslayer(Template): + ipfix.add_template(p.getlayer(Template)) + # verify events in data set + for p in capture: + if p.haslayer(Data): + data = ipfix.decode_data_set(p.getlayer(Set)) + self.verify_ipfix_max_entries_per_user( + data, + nat44_config.max_translations_per_user, + self.pg0.remote_ip4n) + def tearDown(self): super(TestNAT44EndpointDependent, self).tearDown() if not self.vpp_dead: @@ -4918,6 +5080,7 @@ class TestNAT44EndpointDependent(MethodHolder): self.logger.info(self.vapi.cli("show nat44 interface address")) self.logger.info(self.vapi.cli("show nat44 sessions detail")) self.logger.info(self.vapi.cli("show nat44 hash tables detail")) + self.logger.info(self.vapi.cli("show nat timeouts")) self.clear_nat44() self.vapi.cli("clear logging") @@ -5165,23 +5328,6 @@ class TestDeterministicNAT(MethodHolder): "(outside network):", packet)) raise - def verify_ipfix_max_entries_per_user(self, data): - """ - Verify IPFIX maximum entries per user exceeded event - - :param data: Decoded IPFIX data records - """ - self.assertEqual(1, len(data)) - record = data[0] - # natEvent - self.assertEqual(ord(record[230]), 13) - # natQuotaExceededEvent - self.assertEqual('\x03\x00\x00\x00', record[466]) - # maxEntriesPerUser - self.assertEqual('\xe8\x03\x00\x00', record[473]) - # sourceIPv4Address - self.assertEqual(self.pg0.remote_ip4n, record[8]) - def test_deterministic_mode(self): """ NAT plugin run deterministic mode """ in_addr = '172.16.255.0' @@ -5217,14 +5363,14 @@ class TestDeterministicNAT(MethodHolder): def test_set_timeouts(self): """ Set deterministic NAT timeouts """ - timeouts_before = self.vapi.nat_det_get_timeouts() + timeouts_before = self.vapi.nat_get_timeouts() - self.vapi.nat_det_set_timeouts(timeouts_before.udp + 10, - timeouts_before.tcp_established + 10, - timeouts_before.tcp_transitory + 10, - timeouts_before.icmp + 10) + self.vapi.nat_set_timeouts(timeouts_before.udp + 10, + timeouts_before.tcp_established + 10, + timeouts_before.tcp_transitory + 10, + timeouts_before.icmp + 10) - timeouts_after = self.vapi.nat_det_get_timeouts() + timeouts_after = self.vapi.nat_get_timeouts() self.assertNotEqual(timeouts_before.udp, timeouts_after.udp) self.assertNotEqual(timeouts_before.icmp, timeouts_after.icmp) @@ -5539,7 +5685,7 @@ class TestDeterministicNAT(MethodHolder): is_inside=0) self.initiate_tcp_session(self.pg0, self.pg1) - self.vapi.nat_det_set_timeouts(5, 5, 5, 5) + self.vapi.nat_set_timeouts(5, 5, 5, 5) pkts = self.create_stream_in(self.pg0, self.pg1) self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) @@ -5616,14 +5762,16 @@ class TestDeterministicNAT(MethodHolder): for p in capture: if p.haslayer(Data): data = ipfix.decode_data_set(p.getlayer(Set)) - self.verify_ipfix_max_entries_per_user(data) + self.verify_ipfix_max_entries_per_user(data, + 1000, + self.pg0.remote_ip4n) def clear_nat_det(self): """ Clear deterministic NAT configuration. """ self.vapi.nat_ipfix(enable=0) - self.vapi.nat_det_set_timeouts() + self.vapi.nat_set_timeouts() deterministic_mappings = self.vapi.nat_det_map_dump() for dsm in deterministic_mappings: self.vapi.nat_det_add_del_map(dsm.in_addr, @@ -5642,10 +5790,9 @@ class TestDeterministicNAT(MethodHolder): super(TestDeterministicNAT, self).tearDown() if not self.vpp_dead: self.logger.info(self.vapi.cli("show nat44 interfaces")) + self.logger.info(self.vapi.cli("show nat timeouts")) self.logger.info( self.vapi.cli("show nat44 deterministic mappings")) - self.logger.info( - self.vapi.cli("show nat44 deterministic timeouts")) self.logger.info( self.vapi.cli("show nat44 deterministic sessions")) self.clear_nat_det() @@ -5854,22 +6001,20 @@ class TestNAT64(MethodHolder): def test_set_timeouts(self): """ Set NAT64 timeouts """ # verify default values - timeouts = self.vapi.nat64_get_timeouts() + timeouts = self.vapi.nat_get_timeouts() self.assertEqual(timeouts.udp, 300) self.assertEqual(timeouts.icmp, 60) - self.assertEqual(timeouts.tcp_trans, 240) - self.assertEqual(timeouts.tcp_est, 7440) - self.assertEqual(timeouts.tcp_incoming_syn, 6) + self.assertEqual(timeouts.tcp_transitory, 240) + self.assertEqual(timeouts.tcp_established, 7440) # set and verify custom values - self.vapi.nat64_set_timeouts(udp=200, icmp=30, tcp_trans=250, - tcp_est=7450, tcp_incoming_syn=10) - timeouts = self.vapi.nat64_get_timeouts() + self.vapi.nat_set_timeouts(udp=200, icmp=30, tcp_transitory=250, + tcp_established=7450) + timeouts = self.vapi.nat_get_timeouts() self.assertEqual(timeouts.udp, 200) self.assertEqual(timeouts.icmp, 30) - self.assertEqual(timeouts.tcp_trans, 250) - self.assertEqual(timeouts.tcp_est, 7450) - self.assertEqual(timeouts.tcp_incoming_syn, 10) + self.assertEqual(timeouts.tcp_transitory, 250) + self.assertEqual(timeouts.tcp_established, 7450) def test_dynamic(self): """ NAT64 dynamic translation test """ @@ -6006,7 +6151,7 @@ class TestNAT64(MethodHolder): self.nat_addr_n) self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) - self.vapi.nat64_set_timeouts(icmp=5, tcp_trans=5, tcp_est=5) + self.vapi.nat_set_timeouts(icmp=5, tcp_transitory=5, tcp_established=5) pkts = self.create_stream_in_ip6(self.pg0, self.pg1) self.pg0.add_stream(pkts) @@ -6892,7 +7037,7 @@ class TestNAT64(MethodHolder): self.ipfix_src_port = 4739 self.ipfix_domain_id = 1 - self.vapi.nat64_set_timeouts() + self.vapi.nat_set_timeouts() interfaces = self.vapi.nat64_interface_dump() for intf in interfaces: diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 17c7e9c4150..c8ca93c022e 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -1766,13 +1766,13 @@ class VppPapiProvider(object): """ return self.api(self.papi.nat_det_map_dump, {}) - def nat_det_set_timeouts( + def nat_set_timeouts( self, udp=300, tcp_established=7440, tcp_transitory=240, icmp=60): - """Set values of timeouts for deterministic NAT (in seconds) + """Set values of timeouts for NAT sessions (in seconds) :param udp - UDP timeout (Default value = 300) :param tcp_established - TCP established timeout (Default value = 7440) @@ -1780,18 +1780,18 @@ class VppPapiProvider(object): :param icmp - ICMP timeout (Default value = 60) """ return self.api( - self.papi.nat_det_set_timeouts, + self.papi.nat_set_timeouts, {'udp': udp, 'tcp_established': tcp_established, 'tcp_transitory': tcp_transitory, 'icmp': icmp}) - def nat_det_get_timeouts(self): - """Get values of timeouts for deterministic NAT + def nat_get_timeouts(self): + """Get values of timeouts for NAT sessions - :return: Timeouts for deterministic NAT (in seconds) + :return: Timeouts for NAT sessions (in seconds) """ - return self.api(self.papi.nat_det_get_timeouts, {}) + return self.api(self.papi.nat_get_timeouts, {}) def nat_det_close_session_out( self, -- 2.16.6