aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/nat/lib/log.h15
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed.c47
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed.h88
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_api.c23
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_cli.c166
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_format.c26
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_in2out.c57
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_inlines.h380
-rw-r--r--src/plugins/nat/nat44-ed/nat44_ed_out2in.c54
-rw-r--r--src/plugins/nat/nat44-ed/tcp_conn_track.rst65
-rw-r--r--test/framework.py4
-rw-r--r--test/test_nat44_ed.py620
12 files changed, 1053 insertions, 492 deletions
diff --git a/src/plugins/nat/lib/log.h b/src/plugins/nat/lib/log.h
index 26bd93f2589..a82028ed8bf 100644
--- a/src/plugins/nat/lib/log.h
+++ b/src/plugins/nat/lib/log.h
@@ -21,20 +21,7 @@
#include <vppinfra/elog.h>
-#define foreach_nat_log_level \
- _ (0x00, LOG_NONE) \
- _ (0x01, LOG_ERROR) \
- _ (0x02, LOG_WARNING) \
- _ (0x03, LOG_NOTICE) \
- _ (0x04, LOG_INFO) \
- _ (0x05, LOG_DEBUG)
-
-typedef enum nat_log_level_t_
-{
-#define _(n, f) NAT_##f = n,
- foreach_nat_log_level
-#undef _
-} nat_log_level_t;
+#include <nat/lib/nat_types.api_types.h>
#define nat_elog(_pm, _level, _str) \
do \
diff --git a/src/plugins/nat/nat44-ed/nat44_ed.c b/src/plugins/nat/nat44-ed/nat44_ed.c
index d3ef3d54f89..e389a81aab1 100644
--- a/src/plugins/nat/nat44-ed/nat44_ed.c
+++ b/src/plugins/nat/nat44-ed/nat44_ed.c
@@ -2390,6 +2390,8 @@ nat44_plugin_enable (nat44_config_t c)
nat44_ed_db_init (sm->max_translations_per_thread, sm->translation_buckets);
+ nat44_ed_init_tcp_state_stable (sm);
+
nat_affinity_enable ();
nat_reset_timeouts (&sm->timeouts);
@@ -4066,6 +4068,51 @@ nat_syslog_nat44_sdel (u32 ssubix, u32 sfibix, ip4_address_t *isaddr,
is_twicenat);
}
+u8 *
+format_nat44_ed_tcp_state (u8 *s, va_list *args)
+{
+ nat44_ed_tcp_state_e e = va_arg (*args, nat44_ed_tcp_state_e);
+ switch (e)
+ {
+ case NAT44_ED_TCP_STATE_CLOSED:
+ s = format (s, "closed");
+ break;
+ case NAT44_ED_TCP_STATE_SYN_I2O:
+ s = format (s, "SYN seen in in2out direction");
+ break;
+ case NAT44_ED_TCP_STATE_SYN_O2I:
+ s = format (s, "SYN seen in out2in direction");
+ break;
+ case NAT44_ED_TCP_STATE_ESTABLISHED:
+ s = format (s, "SYN seen in both directions/established");
+ break;
+ case NAT44_ED_TCP_STATE_FIN_I2O:
+ s = format (s, "FIN seen in in2out direction");
+ break;
+ case NAT44_ED_TCP_STATE_FIN_O2I:
+ s = format (s, "FIN seen in out2in direction");
+ break;
+ case NAT44_ED_TCP_STATE_RST_TRANS:
+ s = format (s, "RST seen/transitory timeout");
+ break;
+ case NAT44_ED_TCP_STATE_FIN_TRANS:
+ s = format (s, "FIN seen in both directions/transitory timeout");
+ break;
+ case NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I:
+ s = format (s, "FIN seen in both directions/transitory timeout/session "
+ "reopening in out2in direction");
+ break;
+ case NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O:
+ s = format (s, "FIN seen in both directions/transitory timeout/session "
+ "reopening in in2out direction");
+ break;
+ case NAT44_ED_TCP_N_STATE:
+ s = format (s, "BUG! unexpected N_STATE! BUG!");
+ break;
+ }
+ return s;
+}
+
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/plugins/nat/nat44-ed/nat44_ed.h b/src/plugins/nat/nat44-ed/nat44_ed.h
index 9772f1ec79a..0706785514b 100644
--- a/src/plugins/nat/nat44-ed/nat44_ed.h
+++ b/src/plugins/nat/nat44-ed/nat44_ed.h
@@ -105,33 +105,12 @@ typedef enum
#undef _
} nat_addr_and_port_alloc_alg_t;
-/* Session state */
-#define foreach_snat_session_state \
- _(0, UNKNOWN, "unknown") \
- _(1, UDP_ACTIVE, "udp-active") \
- _(2, TCP_SYN_SENT, "tcp-syn-sent") \
- _(3, TCP_ESTABLISHED, "tcp-established") \
- _(4, TCP_FIN_WAIT, "tcp-fin-wait") \
- _(5, TCP_CLOSE_WAIT, "tcp-close-wait") \
- _(6, TCP_CLOSING, "tcp-closing") \
- _(7, TCP_LAST_ACK, "tcp-last-ack") \
- _(8, TCP_CLOSED, "tcp-closed") \
- _(9, ICMP_ACTIVE, "icmp-active")
-
-typedef enum
-{
-#define _(v, N, s) SNAT_SESSION_##N = v,
- foreach_snat_session_state
-#undef _
-} snat_session_state_t;
-
#define foreach_nat_in2out_ed_error \
_ (UNSUPPORTED_PROTOCOL, "unsupported protocol") \
_ (OUT_OF_PORTS, "out of ports") \
_ (BAD_ICMP_TYPE, "unsupported ICMP type") \
_ (MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded") \
_ (NON_SYN, "non-SYN packet try to create session") \
- _ (TCP_CLOSED, "drops due to TCP in transitory timeout") \
_ (TRNSL_FAILED, "couldn't translate packet")
typedef enum
@@ -161,15 +140,43 @@ typedef enum
NAT_OUT2IN_ED_N_ERROR,
} nat_out2in_ed_error_t;
+typedef enum
+{
+ NAT44_ED_TCP_FLAG_NONE = 0,
+ NAT44_ED_TCP_FLAG_FIN,
+ NAT44_ED_TCP_FLAG_SYN,
+ NAT44_ED_TCP_FLAG_SYNFIN,
+ NAT44_ED_TCP_FLAG_RST,
+ NAT44_ED_TCP_FLAG_FINRST,
+ NAT44_ED_TCP_FLAG_SYNRST,
+ NAT44_ED_TCP_FLAG_SYNFINRST,
+ NAT44_ED_TCP_N_FLAG,
+} nat44_ed_tcp_flag_e;
+
+typedef enum
+{
+ NAT44_ED_DIR_I2O = 0,
+ NAT44_ED_DIR_O2I,
+ NAT44_ED_N_DIR,
+} nat44_ed_dir_e;
/* Endpoint dependent TCP session state */
-#define NAT44_SES_I2O_FIN 1
-#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_SES_RST 64
+typedef enum
+{
+ NAT44_ED_TCP_STATE_CLOSED = 0,
+ NAT44_ED_TCP_STATE_SYN_I2O,
+ NAT44_ED_TCP_STATE_SYN_O2I,
+ NAT44_ED_TCP_STATE_ESTABLISHED,
+ NAT44_ED_TCP_STATE_FIN_I2O,
+ NAT44_ED_TCP_STATE_FIN_O2I,
+ NAT44_ED_TCP_STATE_RST_TRANS,
+ NAT44_ED_TCP_STATE_FIN_TRANS,
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O,
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I,
+ NAT44_ED_TCP_N_STATE,
+} nat44_ed_tcp_state_e;
+
+format_function_t format_nat44_ed_tcp_state;
/* Session flags */
#define SNAT_SESSION_FLAG_STATIC_MAPPING (1 << 0)
@@ -341,10 +348,7 @@ typedef CLIB_PACKED(struct
u16 ext_host_nat_port;
/* TCP session state */
- u8 state;
- u32 i2o_fin_seq;
- u32 o2i_fin_seq;
- u64 tcp_closed_timestamp;
+ nat44_ed_tcp_state_e tcp_state;
/* per vrf sessions index */
u32 per_vrf_sessions_index;
@@ -668,6 +672,16 @@ typedef struct snat_main_s
vnet_main_t *vnet_main;
+ /* TCP session state machine table:
+ * first dimension is possible states
+ * second dimension is direction (in2out/out2in)
+ * third dimension is TCP flag (SYN, RST, FIN)
+ *
+ * value is next state to change to
+ */
+ nat44_ed_tcp_state_e tcp_state_change_table[NAT44_ED_TCP_N_STATE]
+ [NAT44_ED_N_DIR]
+ [NAT44_ED_TCP_N_FLAG];
} snat_main_t;
typedef struct
@@ -789,16 +803,6 @@ nat44_ed_is_interface_outside (snat_interface_t *i)
return i->flags & NAT_INTERFACE_FLAG_IS_OUTSIDE;
}
-/** \brief Check if NAT44 endpoint-dependent TCP session is closed.
- @param s NAT session
- @return true if session is closed
-*/
-always_inline bool
-nat44_is_ses_closed (snat_session_t *s)
-{
- return s->state == 0xf;
-}
-
/** \brief Check if client initiating TCP connection (received SYN from client)
@param t TCP header
@return true if client initiating TCP connection
diff --git a/src/plugins/nat/nat44-ed/nat44_ed_api.c b/src/plugins/nat/nat44-ed/nat44_ed_api.c
index 4664fabfec5..6ab3aaa35d9 100644
--- a/src/plugins/nat/nat44-ed/nat44_ed_api.c
+++ b/src/plugins/nat/nat44-ed/nat44_ed_api.c
@@ -31,6 +31,8 @@
#include <nat/nat44-ed/nat44_ed.api_enum.h>
#include <nat/nat44-ed/nat44_ed.api_types.h>
+#include <nat/nat44-ed/nat44_ed_inlines.h>
+
#define REPLY_MSG_ID_BASE sm->msg_id_base
#include <vlibapi/api_helper_macros.h>
@@ -1806,26 +1808,7 @@ send_nat44_user_session_v2_details (snat_session_t *s,
rmp->ext_host_nat_port = s->ext_host_nat_port;
}
- sess_timeout_time = s->last_heard;
- switch (s->proto)
- {
- case IP_PROTOCOL_TCP:
- if (s->state)
- sess_timeout_time += sm->timeouts.tcp.established;
- else
- sess_timeout_time += sm->timeouts.tcp.transitory;
- break;
- case IP_PROTOCOL_UDP:
- sess_timeout_time += sm->timeouts.udp;
- break;
- case IP_PROTOCOL_ICMP:
- sess_timeout_time += sm->timeouts.icmp;
- break;
- default:
- sess_timeout_time += sm->timeouts.udp;
- break;
- }
-
+ sess_timeout_time = s->last_heard + nat44_session_get_timeout (sm, s);
rmp->is_timed_out = (now >= sess_timeout_time);
vl_api_send_msg (reg, (u8 *) rmp);
diff --git a/src/plugins/nat/nat44-ed/nat44_ed_cli.c b/src/plugins/nat/nat44-ed/nat44_ed_cli.c
index cfd36278674..7693063b8d8 100644
--- a/src/plugins/nat/nat44-ed/nat44_ed_cli.c
+++ b/src/plugins/nat/nat44-ed/nat44_ed_cli.c
@@ -478,16 +478,12 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input,
u64 now = vlib_time_now (vm);
u64 sess_timeout_time = 0;
- u32 udp_sessions = 0;
- u32 tcp_sessions = 0;
- u32 icmp_sessions = 0;
- u32 other_sessions = 0;
-
- u32 timed_out = 0;
- u32 transitory = 0;
- u32 transitory_wait_closed = 0;
- u32 transitory_closed = 0;
- u32 established = 0;
+ struct
+ {
+ u32 total;
+ u32 timed_out;
+ } udp = { 0 }, tcp = { 0 }, tcp_established = { 0 }, tcp_transitory = { 0 },
+ icmp = { 0 }, other = { 0 };
u32 fib;
@@ -501,43 +497,44 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input,
{
pool_foreach (s, tsm->sessions)
{
- sess_timeout_time = s->last_heard +
- (f64) nat44_session_get_timeout (sm, s);
- if (now >= sess_timeout_time)
- timed_out++;
-
- switch (s->proto)
- {
- case IP_PROTOCOL_ICMP:
- icmp_sessions++;
- break;
- case IP_PROTOCOL_TCP:
- tcp_sessions++;
- if (s->state)
- {
- if (s->tcp_closed_timestamp)
- {
- if (now >= s->tcp_closed_timestamp)
- {
- ++transitory_closed;
- }
- else
- {
- ++transitory_wait_closed;
- }
- }
- transitory++;
- }
- else
- established++;
- break;
- case IP_PROTOCOL_UDP:
- udp_sessions++;
- break;
- default:
- ++other_sessions;
- break;
- }
+ sess_timeout_time =
+ s->last_heard + (f64) nat44_session_get_timeout (sm, s);
+
+ switch (s->proto)
+ {
+ case IP_PROTOCOL_ICMP:
+ ++icmp.total;
+ if (now >= sess_timeout_time)
+ ++icmp.timed_out;
+ break;
+ case IP_PROTOCOL_TCP:
+ ++tcp.total;
+ if (now >= sess_timeout_time)
+ ++tcp.timed_out;
+ if (nat44_ed_tcp_is_established (s->tcp_state))
+ {
+ ++tcp_established.total;
+ if (now >= sess_timeout_time)
+ ++tcp_established.timed_out;
+ }
+ else
+ {
+ ++tcp_transitory.total;
+ if (now >= sess_timeout_time)
+ ++tcp_transitory.timed_out;
+ }
+ break;
+ case IP_PROTOCOL_UDP:
+ ++udp.total;
+ if (now >= sess_timeout_time)
+ ++udp.timed_out;
+ break;
+ default:
+ ++other.total;
+ if (now >= sess_timeout_time)
+ ++other.timed_out;
+ break;
+ }
}
nat44_show_lru_summary (vm, tsm, now, sess_timeout_time);
count += pool_elts (tsm->sessions);
@@ -550,39 +547,40 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input,
{
sess_timeout_time = s->last_heard +
(f64) nat44_session_get_timeout (sm, s);
- if (now >= sess_timeout_time)
- timed_out++;
switch (s->proto)
{
case IP_PROTOCOL_ICMP:
- icmp_sessions++;
+ ++icmp.total;
+ if (now >= sess_timeout_time)
+ ++icmp.timed_out;
break;
case IP_PROTOCOL_TCP:
- tcp_sessions++;
- if (s->state)
+ ++tcp.total;
+ if (now >= sess_timeout_time)
+ ++tcp.timed_out;
+ if (nat44_ed_tcp_is_established (s->tcp_state))
{
- if (s->tcp_closed_timestamp)
- {
- if (now >= s->tcp_closed_timestamp)
- {
- ++transitory_closed;
- }
- else
- {
- ++transitory_wait_closed;
- }
- }
- transitory++;
+ ++tcp_established.total;
+ if (now >= sess_timeout_time)
+ ++tcp_established.timed_out;
}
else
- established++;
+ {
+ ++tcp_transitory.total;
+ if (now >= sess_timeout_time)
+ ++tcp_transitory.timed_out;
+ }
break;
case IP_PROTOCOL_UDP:
- udp_sessions++;
+ ++udp.total;
+ if (now >= sess_timeout_time)
+ ++udp.timed_out;
break;
default:
- ++other_sessions;
+ ++other.total;
+ if (now >= sess_timeout_time)
+ ++other.timed_out;
break;
}
}
@@ -590,18 +588,25 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input,
count = pool_elts (tsm->sessions);
}
- vlib_cli_output (vm, "total timed out sessions: %u", timed_out);
- vlib_cli_output (vm, "total sessions: %u", count);
- vlib_cli_output (vm, "total tcp sessions: %u", tcp_sessions);
- vlib_cli_output (vm, "total tcp established sessions: %u", established);
- vlib_cli_output (vm, "total tcp transitory sessions: %u", transitory);
- vlib_cli_output (vm, "total tcp transitory (WAIT-CLOSED) sessions: %u",
- transitory_wait_closed);
- vlib_cli_output (vm, "total tcp transitory (CLOSED) sessions: %u",
- transitory_closed);
- vlib_cli_output (vm, "total udp sessions: %u", udp_sessions);
- vlib_cli_output (vm, "total icmp sessions: %u", icmp_sessions);
- vlib_cli_output (vm, "total other sessions: %u", other_sessions);
+ u32 timed_out =
+ tcp.timed_out + icmp.timed_out + udp.timed_out + other.timed_out;
+ vlib_cli_output (vm, "total sessions: %u (timed out: %u)", count, timed_out);
+ vlib_cli_output (vm, "tcp sessions:");
+ vlib_cli_output (vm, " total: %u (timed out: %u)", tcp.total,
+ tcp.timed_out);
+ vlib_cli_output (vm, " established: %u (timed out: %u)",
+ tcp_established.total, tcp_established.timed_out);
+ vlib_cli_output (vm, " transitory: %u (timed out: %u)",
+ tcp_transitory.total, tcp_transitory.timed_out);
+ vlib_cli_output (vm, "udp sessions:");
+ vlib_cli_output (vm, " total: %u (timed out: %u)", udp.total,
+ udp.timed_out);
+ vlib_cli_output (vm, "icmp sessions:");
+ vlib_cli_output (vm, " total: %u (timed out: %u)", icmp.total,
+ icmp.timed_out);
+ vlib_cli_output (vm, "other sessions:");
+ vlib_cli_output (vm, " total: %u (timed out: %u)", other.total,
+ other.timed_out);
return 0;
}
@@ -1456,7 +1461,8 @@ print:
continue;
showed_sessions++;
}
- vlib_cli_output (vm, " %U\n", format_snat_session, tsm, s);
+ vlib_cli_output (vm, " %U\n", format_snat_session, sm, tsm, s,
+ vlib_time_now (vm));
}
if (filtering)
{
diff --git a/src/plugins/nat/nat44-ed/nat44_ed_format.c b/src/plugins/nat/nat44-ed/nat44_ed_format.c
index a6f63026897..4598c02208f 100644
--- a/src/plugins/nat/nat44-ed/nat44_ed_format.c
+++ b/src/plugins/nat/nat44-ed/nat44_ed_format.c
@@ -40,29 +40,13 @@ format_nat_addr_and_port_alloc_alg (u8 * s, va_list * args)
}
u8 *
-format_snat_session_state (u8 * s, va_list * args)
-{
- u32 i = va_arg (*args, u32);
- u8 *t = 0;
-
- switch (i)
- {
-#define _(v, N, str) case SNAT_SESSION_##N: t = (u8 *) str; break;
- foreach_snat_session_state
-#undef _
- default:
- t = format (t, "unknown");
- }
- s = format (s, "%s", t);
- return s;
-}
-
-u8 *
format_snat_session (u8 * s, va_list * args)
{
+ snat_main_t *sm = va_arg (*args, snat_main_t *);
snat_main_per_thread_data_t *tsm =
va_arg (*args, snat_main_per_thread_data_t *);
snat_session_t *sess = va_arg (*args, snat_session_t *);
+ f64 now = va_arg (*args, f64);
if (nat44_ed_is_unk_proto (sess->proto))
{
@@ -103,8 +87,10 @@ format_snat_session (u8 * s, va_list * args)
s = format (s, " o2i flow: %U\n", format_nat_6t_flow, &sess->o2i);
s = format (s, " index %llu\n", sess - tsm->sessions);
s = format (s, " last heard %.2f\n", sess->last_heard);
- s = format (s, " total pkts %d, total bytes %lld\n",
- sess->total_pkts, sess->total_bytes);
+ s = format (s, " timeout in %.2f\n",
+ nat44_session_get_timeout (sm, sess) - (now - sess->last_heard));
+ s = format (s, " total pkts %d, total bytes %lld\n", sess->total_pkts,
+ sess->total_bytes);
if (nat44_ed_is_session_static (sess))
s = format (s, " static translation\n");
else
diff --git a/src/plugins/nat/nat44-ed/nat44_ed_in2out.c b/src/plugins/nat/nat44-ed/nat44_ed_in2out.c
index 23e0957dabe..99db6010783 100644
--- a/src/plugins/nat/nat44-ed/nat44_ed_in2out.c
+++ b/src/plugins/nat/nat44-ed/nat44_ed_in2out.c
@@ -49,6 +49,7 @@ typedef struct
u8 is_slow_path;
u8 translation_via_i2of;
u8 lookup_skipped;
+ u8 tcp_state;
} nat_in2out_ed_trace_t;
static u8 *
@@ -78,7 +79,7 @@ format_nat_in2out_ed_trace (u8 * s, va_list * args)
{
if (t->lookup_skipped)
{
- s = format (s, "\n lookup skipped - cached session index used");
+ s = format (s, "\n lookup skipped - cached session index used");
}
else
{
@@ -86,6 +87,11 @@ format_nat_in2out_ed_trace (u8 * s, va_list * args)
&t->search_key);
}
}
+ if (IP_PROTOCOL_TCP == t->i2of.match.proto)
+ {
+ s = format (s, "\n TCP state: %U", format_nat44_ed_tcp_state,
+ t->tcp_state);
+ }
return s;
}
@@ -675,7 +681,9 @@ nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip,
{
if (ip->protocol == IP_PROTOCOL_TCP)
{
- nat44_set_tcp_session_state_i2o (sm, now, s, b, thread_index);
+ nat44_set_tcp_session_state_i2o (
+ sm, now, s, vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags,
+ thread_index);
}
/* Accounting */
nat44_session_update_counters (s, now,
@@ -697,7 +705,7 @@ nat44_ed_not_translate_output_feature (snat_main_t *sm, vlib_buffer_t *b,
ip4_header_t *ip, u16 src_port,
u16 dst_port, u32 thread_index,
u32 rx_sw_if_index, u32 tx_sw_if_index,
- f64 now, int is_multi_worker)
+ int is_multi_worker)
{
clib_bihash_kv_16_8_t kv, value;
snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
@@ -715,12 +723,6 @@ nat44_ed_not_translate_output_feature (snat_main_t *sm, vlib_buffer_t *b,
s =
pool_elt_at_index (tsm->sessions,
ed_value_get_session_index (&value));
- if (nat44_is_ses_closed (s)
- && (!s->tcp_closed_timestamp || now >= s->tcp_closed_timestamp))
- {
- nat44_ed_free_session_data (sm, s, thread_index, 0);
- nat_ed_session_delete (sm, s, thread_index, 1);
- }
return 1;
}
@@ -800,7 +802,7 @@ icmp_in2out_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip,
{
if (PREDICT_FALSE (nat44_ed_not_translate_output_feature (
sm, b, ip, lookup_sport, lookup_dport, thread_index, sw_if_index,
- tx_sw_if_index, now, is_multi_worker)))
+ tx_sw_if_index, is_multi_worker)))
{
return next;
}
@@ -1173,22 +1175,6 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm,
goto trace0;
}
- if (s0->tcp_closed_timestamp)
- {
- if (now >= s0->tcp_closed_timestamp)
- {
- // session is closed, go slow path, freed in slow path
- next[0] = def_slow;
- }
- else
- {
- // session in transitory timeout, drop
- b0->error = node->errors[NAT_IN2OUT_ED_ERROR_TCP_CLOSED];
- next[0] = NAT_NEXT_DROP;
- }
- goto trace0;
- }
-
// drop if session expired
u64 sess_timeout_time;
sess_timeout_time =
@@ -1239,7 +1225,9 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm,
case IP_PROTOCOL_TCP:
vlib_increment_simple_counter (&sm->counters.fastpath.in2out.tcp,
thread_index, cntr_sw_if_index0, 1);
- nat44_set_tcp_session_state_i2o (sm, now, s0, b0, thread_index);
+ nat44_set_tcp_session_state_i2o (
+ sm, now, s0, vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags,
+ thread_index);
break;
case IP_PROTOCOL_UDP:
vlib_increment_simple_counter (&sm->counters.fastpath.in2out.udp,
@@ -1282,6 +1270,7 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm,
clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of));
clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if));
t->translation_via_i2of = (&s0->i2o == f);
+ t->tcp_state = s0->tcp_state;
}
else
{
@@ -1426,13 +1415,6 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm,
s0 =
pool_elt_at_index (tsm->sessions,
ed_value_get_session_index (&value0));
-
- if (s0->tcp_closed_timestamp && now >= s0->tcp_closed_timestamp)
- {
- nat44_ed_free_session_data (sm, s0, thread_index, 0);
- nat_ed_session_delete (sm, s0, thread_index, 1);
- s0 = NULL;
- }
}
if (!s0)
@@ -1442,7 +1424,7 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm,
if (PREDICT_FALSE (nat44_ed_not_translate_output_feature (
sm, b0, ip0, vnet_buffer (b0)->ip.reass.l4_src_port,
vnet_buffer (b0)->ip.reass.l4_dst_port, thread_index,
- rx_sw_if_index0, tx_sw_if_index0, now, is_multi_worker)))
+ rx_sw_if_index0, tx_sw_if_index0, is_multi_worker)))
goto trace0;
/*
@@ -1496,7 +1478,9 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm,
{
vlib_increment_simple_counter (&sm->counters.slowpath.in2out.tcp,
thread_index, cntr_sw_if_index0, 1);
- nat44_set_tcp_session_state_i2o (sm, now, s0, b0, thread_index);
+ nat44_set_tcp_session_state_i2o (
+ sm, now, s0, vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags,
+ thread_index);
}
else
{
@@ -1529,6 +1513,7 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm,
clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of));
clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if));
t->translation_via_i2of = 1;
+ t->tcp_state = s0->tcp_state;
}
else
diff --git a/src/plugins/nat/nat44-ed/nat44_ed_inlines.h b/src/plugins/nat/nat44-ed/nat44_ed_inlines.h
index cb418960249..a13f250cd3b 100644
--- a/src/plugins/nat/nat44-ed/nat44_ed_inlines.h
+++ b/src/plugins/nat/nat44-ed/nat44_ed_inlines.h
@@ -24,6 +24,7 @@
#include <vnet/fib/ip4_fib.h>
#include <nat/lib/log.h>
+#include <nat/lib/ipfix_logging.h>
#include <nat/nat44-ed/nat44_ed.h>
always_inline void
@@ -171,6 +172,26 @@ nat_get_icmp_session_lookup_values (vlib_buffer_t *b, ip4_header_t *ip0,
return 0;
}
+always_inline int
+nat44_ed_tcp_is_established (nat44_ed_tcp_state_e state)
+{
+ static int lookup[] = {
+ [NAT44_ED_TCP_STATE_CLOSED] = 0,
+ [NAT44_ED_TCP_STATE_SYN_I2O] = 0,
+ [NAT44_ED_TCP_STATE_SYN_O2I] = 0,
+ [NAT44_ED_TCP_STATE_ESTABLISHED] = 1,
+ [NAT44_ED_TCP_STATE_FIN_I2O] = 1,
+ [NAT44_ED_TCP_STATE_FIN_O2I] = 1,
+ [NAT44_ED_TCP_STATE_RST_TRANS] = 0,
+ [NAT44_ED_TCP_STATE_FIN_TRANS] = 0,
+ [NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O] = 0,
+ [NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I] = 0,
+ [NAT44_ED_TCP_N_STATE] = 0,
+ };
+ ASSERT (state <= ARRAY_LEN (lookup));
+ return lookup[state];
+}
+
always_inline u32
nat44_session_get_timeout (snat_main_t *sm, snat_session_t *s)
{
@@ -184,10 +205,10 @@ nat44_session_get_timeout (snat_main_t *sm, snat_session_t *s)
return sm->timeouts.udp;
case IP_PROTOCOL_TCP:
{
- if (s->state)
- return sm->timeouts.tcp.transitory;
- else
+ if (nat44_ed_tcp_is_established (s->tcp_state))
return sm->timeouts.tcp.established;
+ else
+ return sm->timeouts.tcp.transitory;
}
default:
return sm->timeouts.udp;
@@ -340,8 +361,7 @@ nat_lru_free_one_with_head (snat_main_t *sm, int thread_index, f64 now,
sess_timeout_time =
s->last_heard + (f64) nat44_session_get_timeout (sm, s);
- if (now >= sess_timeout_time ||
- (s->tcp_closed_timestamp && now >= s->tcp_closed_timestamp))
+ if (now >= sess_timeout_time)
{
nat44_ed_free_session_data (sm, s, thread_index, 0);
nat_ed_session_delete (sm, s, thread_index, 0);
@@ -701,101 +721,303 @@ is_interface_addr (snat_main_t *sm, vlib_node_runtime_t *node,
}
always_inline void
-nat44_set_tcp_session_state_i2o (snat_main_t *sm, f64 now, snat_session_t *ses,
- vlib_buffer_t *b, u32 thread_index)
+nat44_ed_session_reopen (u32 thread_index, snat_session_t *s)
{
- snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
- u8 tcp_flags = vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags;
- u32 tcp_ack_number = vnet_buffer (b)->ip.reass.tcp_ack_number;
- u32 tcp_seq_number = vnet_buffer (b)->ip.reass.tcp_seq_number;
- if ((ses->state == 0) && (tcp_flags & TCP_FLAG_RST))
- ses->state = NAT44_SES_RST;
- if ((ses->state == NAT44_SES_RST) && !(tcp_flags & TCP_FLAG_RST))
- ses->state = 0;
- 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);
- ses->state |= NAT44_SES_I2O_FIN;
- }
- if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_O2I_FIN))
+ nat_syslog_nat44_sdel (0, s->in2out.fib_index, &s->in2out.addr,
+ s->in2out.port, &s->ext_host_nat_addr,
+ s->ext_host_nat_port, &s->out2in.addr, s->out2in.port,
+ &s->ext_host_addr, s->ext_host_port, s->proto,
+ nat44_ed_is_twice_nat_session (s));
+
+ nat_ipfix_logging_nat44_ses_delete (
+ thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32, s->proto,
+ s->in2out.port, s->out2in.port, s->in2out.fib_index);
+ nat_ipfix_logging_nat44_ses_create (
+ thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32, s->proto,
+ s->in2out.port, s->out2in.port, s->in2out.fib_index);
+
+ nat_syslog_nat44_sadd (0, s->in2out.fib_index, &s->in2out.addr,
+ s->in2out.port, &s->ext_host_nat_addr,
+ s->ext_host_nat_port, &s->out2in.addr, s->out2in.port,
+ &s->ext_host_addr, s->ext_host_port, s->proto, 0);
+ s->total_pkts = 0;
+ s->total_bytes = 0;
+}
+
+always_inline void
+nat44_ed_init_tcp_state_stable (snat_main_t *sm)
+{
+ /* first make sure whole table is initialised in a way where state
+ * is not changed, then define special cases */
+ nat44_ed_tcp_state_e s;
+ for (s = 0; s < NAT44_ED_TCP_N_STATE; ++s)
{
- if (clib_net_to_host_u32 (tcp_ack_number) > ses->o2i_fin_seq)
+ int i;
+ for (i = 0; i < NAT44_ED_N_DIR; ++i)
{
- ses->state |= NAT44_SES_O2I_FIN_ACK;
- if (nat44_is_ses_closed (ses))
- { // if session is now closed, save the timestamp
- ses->tcp_closed_timestamp = now + sm->timeouts.tcp.transitory;
- ses->last_lru_update = now;
+ int j = 0;
+ for (j = 0; j < NAT44_ED_TCP_N_FLAG; ++j)
+ {
+ sm->tcp_state_change_table[s][i][j] = s;
}
}
}
- // move the session to proper LRU
- if (ses->state)
- {
- ses->lru_head_index = tsm->tcp_trans_lru_head_index;
- }
- else
- {
- ses->lru_head_index = tsm->tcp_estab_lru_head_index;
- }
- clib_dlist_remove (tsm->lru_pool, ses->lru_index);
- clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index);
+ /* CLOSED and any kind of SYN -> HALF-OPEN */
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_CLOSED][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYN] =
+ NAT44_ED_TCP_STATE_SYN_I2O;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_CLOSED][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYN] =
+ NAT44_ED_TCP_STATE_SYN_O2I;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_CLOSED][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_SYN_I2O;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_CLOSED][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_SYN_O2I;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_CLOSED][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_SYN_I2O;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_CLOSED][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_SYN_O2I;
+
+ /* HALF-OPEN and any kind of SYN in right direction -> ESTABLISHED */
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_SYN_I2O][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_SYN_O2I][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_SYN_I2O][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_SYN_O2I][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_SYN_I2O][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_SYN_O2I][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+
+ /* ESTABLISHED and any kind of RST -> RST_TRANS */
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_RST] =
+ NAT44_ED_TCP_STATE_RST_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_RST] =
+ NAT44_ED_TCP_STATE_RST_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNRST] =
+ NAT44_ED_TCP_STATE_RST_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNRST] =
+ NAT44_ED_TCP_STATE_RST_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_FINRST] =
+ NAT44_ED_TCP_STATE_RST_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_FINRST] =
+ NAT44_ED_TCP_STATE_RST_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_RST_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_RST_TRANS;
+
+ /* ESTABLISHED and any kind of FIN without RST -> HALF-CLOSED */
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_FIN] =
+ NAT44_ED_TCP_STATE_FIN_I2O;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_FIN] =
+ NAT44_ED_TCP_STATE_FIN_O2I;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_FIN_I2O;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_ESTABLISHED][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_FIN_O2I;
+
+ /* HALF-CLOSED and any kind of FIN -> FIN_TRANS */
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_I2O][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_FIN] =
+ NAT44_ED_TCP_STATE_FIN_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_O2I][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_FIN] =
+ NAT44_ED_TCP_STATE_FIN_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_I2O][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_FIN_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_O2I][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_FIN_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_I2O][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_FINRST] =
+ NAT44_ED_TCP_STATE_FIN_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_O2I][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_FINRST] =
+ NAT44_ED_TCP_STATE_FIN_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_I2O][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_FIN_TRANS;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_O2I][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_FIN_TRANS;
+
+ /* RST_TRANS and anything non-RST -> ESTABLISHED */
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_RST_TRANS][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_NONE] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_RST_TRANS][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_NONE] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_RST_TRANS][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_RST_TRANS][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_RST_TRANS][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_FIN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_RST_TRANS][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_FIN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_RST_TRANS][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_RST_TRANS][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+
+ /* FIN_TRANS and any kind of SYN -> HALF-REOPEN */
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_TRANS][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYN] =
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_TRANS][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYN] =
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_TRANS][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNRST] =
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_TRANS][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNRST] =
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_TRANS][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_TRANS][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_TRANS][NAT44_ED_DIR_I2O]
+ [NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_TRANS][NAT44_ED_DIR_O2I]
+ [NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I;
+
+ /* HALF-REOPEN and any kind of SYN in right direction -> ESTABLISHED */
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O]
+ [NAT44_ED_DIR_O2I][NAT44_ED_TCP_FLAG_SYN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I]
+ [NAT44_ED_DIR_I2O][NAT44_ED_TCP_FLAG_SYN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O]
+ [NAT44_ED_DIR_O2I][NAT44_ED_TCP_FLAG_SYNRST] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I]
+ [NAT44_ED_DIR_I2O][NAT44_ED_TCP_FLAG_SYNRST] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O]
+ [NAT44_ED_DIR_O2I][NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I]
+ [NAT44_ED_DIR_I2O][NAT44_ED_TCP_FLAG_SYNFIN] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O]
+ [NAT44_ED_DIR_O2I][NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
+ sm->tcp_state_change_table[NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I]
+ [NAT44_ED_DIR_I2O][NAT44_ED_TCP_FLAG_SYNFINRST] =
+ NAT44_ED_TCP_STATE_ESTABLISHED;
}
+/* TCP state tracking according to RFC 7857 (and RFC 6146, which is referenced
+ * by RFC 7857). Our implementation also goes beyond by supporting creation of
+ * a new session while old session is in transitory timeout after seeing FIN
+ * packets from both sides. */
always_inline void
-nat44_set_tcp_session_state_o2i (snat_main_t *sm, f64 now, snat_session_t *ses,
- u8 tcp_flags, u32 tcp_ack_number,
- u32 tcp_seq_number, u32 thread_index)
+nat44_set_tcp_session_state (snat_main_t *sm, f64 now, snat_session_t *ses,
+ u8 tcp_flags, u32 thread_index,
+ nat44_ed_dir_e dir)
{
snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
- if ((ses->state == 0) && (tcp_flags & TCP_FLAG_RST))
- ses->state = NAT44_SES_RST;
- if ((ses->state == NAT44_SES_RST) && !(tcp_flags & TCP_FLAG_RST))
- ses->state = 0;
- 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);
- ses->state |= NAT44_SES_O2I_FIN;
- }
- if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_FIN))
+ nat44_ed_tcp_flag_e flags =
+ tcp_flags & (TCP_FLAG_SYN | TCP_FLAG_FIN | TCP_FLAG_RST);
+
+ u8 old_state = ses->tcp_state;
+ ses->tcp_state = sm->tcp_state_change_table[ses->tcp_state][dir][flags];
+
+ if (old_state != ses->tcp_state)
{
- if (clib_net_to_host_u32 (tcp_ack_number) > ses->i2o_fin_seq)
- ses->state |= NAT44_SES_I2O_FIN_ACK;
- if (nat44_is_ses_closed (ses))
- { // if session is now closed, save the timestamp
- ses->tcp_closed_timestamp = now + sm->timeouts.tcp.transitory;
- ses->last_lru_update = now;
+ if (nat44_ed_tcp_is_established (ses->tcp_state))
+ {
+ if (NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O == old_state ||
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I == old_state)
+ {
+ nat44_ed_session_reopen (thread_index, ses);
+ }
+ ses->lru_head_index = tsm->tcp_estab_lru_head_index;
}
+ else
+ {
+ if (NAT44_ED_TCP_STATE_ESTABLISHED == old_state)
+ { // need to update last heard otherwise session might get
+ // immediately timed out if it has been idle longer than
+ // transitory timeout
+ ses->last_heard = now;
+ }
+ ses->lru_head_index = tsm->tcp_trans_lru_head_index;
+ }
+ ses->last_lru_update = now;
+ clib_dlist_remove (tsm->lru_pool, ses->lru_index);
+ clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index);
}
- // move the session to proper LRU
- if (ses->state)
- {
- ses->lru_head_index = tsm->tcp_trans_lru_head_index;
- }
- else
- {
- ses->lru_head_index = tsm->tcp_estab_lru_head_index;
- }
- clib_dlist_remove (tsm->lru_pool, ses->lru_index);
- clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index);
+}
+
+always_inline void
+nat44_set_tcp_session_state_i2o (snat_main_t *sm, f64 now, snat_session_t *ses,
+ u8 tcp_flags, u32 thread_index)
+{
+ return nat44_set_tcp_session_state (sm, now, ses, tcp_flags, thread_index,
+ NAT44_ED_DIR_I2O);
+}
+
+always_inline void
+nat44_set_tcp_session_state_o2i (snat_main_t *sm, f64 now, snat_session_t *ses,
+ u8 tcp_flags, u32 thread_index)
+{
+ return nat44_set_tcp_session_state (sm, now, ses, tcp_flags, thread_index,
+ NAT44_ED_DIR_O2I);
}
always_inline void
nat44_session_update_counters (snat_session_t *s, f64 now, uword bytes,
u32 thread_index)
{
- s->last_heard = now;
+ if (NAT44_ED_TCP_STATE_RST_TRANS != s->tcp_state &&
+ NAT44_ED_TCP_STATE_FIN_TRANS != s->tcp_state &&
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O != s->tcp_state &&
+ NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I != s->tcp_state)
+ {
+ s->last_heard = now;
+ }
s->total_pkts++;
s->total_bytes += bytes;
}
diff --git a/src/plugins/nat/nat44-ed/nat44_ed_out2in.c b/src/plugins/nat/nat44-ed/nat44_ed_out2in.c
index 5ad57a17098..40a72122140 100644
--- a/src/plugins/nat/nat44-ed/nat44_ed_out2in.c
+++ b/src/plugins/nat/nat44-ed/nat44_ed_out2in.c
@@ -41,7 +41,6 @@ typedef enum
NAT_ED_SP_REASON_NO_REASON,
NAT_ED_SP_REASON_LOOKUP_FAILED,
NAT_ED_SP_REASON_VRF_EXPIRED,
- NAT_ED_SP_TCP_CLOSED,
NAT_ED_SP_SESS_EXPIRED,
} nat_slow_path_reason_e;
@@ -57,6 +56,7 @@ typedef struct
u8 is_slow_path;
u8 translation_via_i2of;
u8 lookup_skipped;
+ u8 tcp_state;
nat_slow_path_reason_e slow_path_reason;
} nat44_ed_out2in_trace_t;
@@ -72,8 +72,6 @@ format_slow_path_reason (u8 *s, va_list *args)
return format (s, "slow path because lookup failed");
case NAT_ED_SP_REASON_VRF_EXPIRED:
return format (s, "slow path because vrf expired");
- case NAT_ED_SP_TCP_CLOSED:
- return format (s, "slow path because tcp closed");
case NAT_ED_SP_SESS_EXPIRED:
return format (s, "slow path because session expired");
}
@@ -107,14 +105,19 @@ format_nat44_ed_out2in_trace (u8 * s, va_list * args)
{
if (t->lookup_skipped)
{
- s = format (s, "\n lookup skipped - cached session index used");
+ s = format (s, "\n lookup skipped - cached session index used");
}
else
{
s = format (s, "\n search key %U", format_ed_session_kvp,
&t->search_key);
}
- s = format (s, "\n %U", format_slow_path_reason, t->slow_path_reason);
+ s = format (s, "\n %U", format_slow_path_reason, t->slow_path_reason);
+ }
+ if (IP_PROTOCOL_TCP == t->i2of.match.proto)
+ {
+ s = format (s, "\n TCP state: %U", format_nat44_ed_tcp_state,
+ t->tcp_state);
}
return s;
@@ -645,10 +648,9 @@ create_bypass_for_fwd (snat_main_t *sm, vlib_buffer_t *b, snat_session_t *s,
if (ip->protocol == IP_PROTOCOL_TCP)
{
- tcp_header_t *tcp = ip4_next_header (ip);
- nat44_set_tcp_session_state_o2i (sm, now, s, tcp->flags,
- tcp->ack_number, tcp->seq_number,
- thread_index);
+ nat44_set_tcp_session_state_o2i (
+ sm, now, s, vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags,
+ thread_index);
}
/* Accounting */
@@ -883,23 +885,6 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
goto trace0;
}
- if (s0->tcp_closed_timestamp)
- {
- if (now >= s0->tcp_closed_timestamp)
- {
- // session is closed, go slow path, freed in slow path
- slow_path_reason = NAT_ED_SP_TCP_CLOSED;
- next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
- }
- else
- {
- // session in transitory timeout, drop
- b0->error = node->errors[NAT_OUT2IN_ED_ERROR_TCP_CLOSED];
- next[0] = NAT_NEXT_DROP;
- }
- goto trace0;
- }
-
// drop if session expired
u64 sess_timeout_time;
sess_timeout_time =
@@ -981,10 +966,6 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
nat44_set_tcp_session_state_o2i (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);
break;
case IP_PROTOCOL_UDP:
@@ -1028,6 +1009,7 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of));
clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if));
t->translation_via_i2of = (&s0->i2o == f);
+ t->tcp_state = s0->tcp_state;
}
else
{
@@ -1170,13 +1152,6 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
s0 =
pool_elt_at_index (tsm->sessions,
ed_value_get_session_index (&value0));
-
- if (s0->tcp_closed_timestamp && now >= s0->tcp_closed_timestamp)
- {
- nat44_ed_free_session_data (sm, s0, thread_index, 0);
- nat_ed_session_delete (sm, s0, thread_index, 1);
- s0 = NULL;
- }
}
if (!s0)
@@ -1264,10 +1239,6 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
nat44_set_tcp_session_state_o2i (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);
}
else
@@ -1300,6 +1271,7 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
t->session_index = s0 - tsm->sessions;
clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of));
clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if));
+ t->tcp_state = s0->tcp_state;
}
else
{
diff --git a/src/plugins/nat/nat44-ed/tcp_conn_track.rst b/src/plugins/nat/nat44-ed/tcp_conn_track.rst
new file mode 100644
index 00000000000..faf0dec8b06
--- /dev/null
+++ b/src/plugins/nat/nat44-ed/tcp_conn_track.rst
@@ -0,0 +1,65 @@
+NAT44ED TCP connection tracking
+===============================
+
+TCP connection tracking in endpoint-dependent NAT is based on RFC 7857
+and RFC 6146, which RFC 7857 references.
+
+See RFC 7857 for the original graph - our graph is slightly different,
+allowing creation of new session, while an old session is in transitory
+timeout after seeing FIN packets from both sides:
+
+After discussion on vpp-dev and with Andrew Yourtschenko we agreed that
+it's friendly behaviour to allow creating a new session while the old
+one is closed and in transitory timeout. The alternative means VPP is
+insisting that a 5-tuple connection cannot be created while an old one
+is finished and timing out. There is no apparent reason why our change
+would break anything and we agreed that it could only help users.
+
+::
+
+
+ +------------transitory timeout----------------+
+ | |
+ | +-------------+ |
+ | session created---->+ CLOSED | |
+ | +-------------+ |
+ | | | |
++-----+ | SYN SYN |
+| v v IN2OUT OUT2IN |
+| +->session removed | | |
+| | ^ ^ ^ ^ ^ v v |
+| | | | | | | +-------+ +-------+ |
+| | | | | | +----transitory timeout---+SYN_I2O| |SYN_O2I+--+
+| | | | | | +---------+ |-------| |-------|
+| | | | | +-transitory---+RST_TRANS| | |
+| | | | | timeout +---------+ SYN SYN
+| | | | | | ^ OUT2IN IN2OUT
+| | | | | | | | |
+| | | | | | | v v
+| | | | | | | +-----------+
+| | | | | | +--RST----+ESTABLISHED+<-SYN IN2OUT-+
+| | | | | | +-----------+ |
+| | | | | +---data pkt-----^ | | | ^ |
+| | | | | | | | | |
+| | | | +----established timeout---------------+ | | | |
+| | | | | | | |
+| | | | +-----FIN IN2OUT---------+ | | |
+| | | | v | | |
+| | | | +-------+ +--FIN OUT2IN----+ | |
+| | | +--established---+FIN_I2O| | | |
+| | | timeout +-------+ v +-SYN OUT2IN-+ |
+| | | | +-------+ | |
+| | +----established-------------+FIN_O2I| +--------------+ |
+| | timeout | +-------+ |REOPEN_SYN_I2O| +--------------+
+| | | | +--------------+ |REOPEN_SYN_O2I|
+| | FIN FIN ^ | +--------------+
+| | OUT2IN IN2OUT | | ^ |
+| | | | | | | |
+| | v v | | | |
+| | +-------------+ | | | |
+| +--transitory timeout---+ FIN_TRANS +-SYN IN2OUT-+ | | |
+| +-------------+ | | |
+| | | | |
+| +--------SYN OUT2IN----|-----------+ |
+| v |
++------------------transitory timeout-------------------+<-------------+
diff --git a/test/framework.py b/test/framework.py
index f4b168b3c75..8c0df282153 100644
--- a/test/framework.py
+++ b/test/framework.py
@@ -26,7 +26,7 @@ from abc import ABC, abstractmethod
from struct import pack, unpack
import scapy.compat
-from scapy.packet import Raw
+from scapy.packet import Raw, Packet
import hook as hookmodule
from vpp_pg_interface import VppPGInterface
from vpp_sub_interface import VppSubInterface
@@ -1308,7 +1308,7 @@ class VppTestCase(CPUInterface, unittest.TestCase):
def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
trace=True):
if not n_rx:
- n_rx = len(pkts)
+ n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
self.pg_send(intf, pkts, worker=worker, trace=trace)
rx = output.get_capture(n_rx)
if trace:
diff --git a/test/test_nat44_ed.py b/test/test_nat44_ed.py
index 5684f5921ba..9bb803e4435 100644
--- a/test/test_nat44_ed.py
+++ b/test/test_nat44_ed.py
@@ -881,7 +881,7 @@ class TestNAT44ED(VppTestCase):
sessions = self.vapi.nat44_user_session_dump(server.ip4, 0)
self.assertEqual(len(sessions), 0)
- def verify_syslog_sess(self, data, is_add=True, is_ip6=False):
+ def verify_syslog_sess(self, data, msgid, is_ip6=False):
message = data.decode('utf-8')
try:
message = SyslogMessage.parse(message)
@@ -891,7 +891,7 @@ class TestNAT44ED(VppTestCase):
else:
self.assertEqual(message.severity, SyslogSeverity.info)
self.assertEqual(message.appname, 'NAT')
- self.assertEqual(message.msgid, 'SADD' if is_add else 'SDEL')
+ self.assertEqual(message.msgid, msgid)
sd_params = message.sd.get('nsess')
self.assertTrue(sd_params is not None)
if is_ip6:
@@ -2550,21 +2550,70 @@ class TestNAT44EDMW(TestNAT44ED):
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
flags="R"))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.get_capture(1)
+ self.send_and_expect(self.pg0, p, self.pg1)
self.virtual_sleep(6)
+ # The session is already closed
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
- TCP(sport=self.tcp_port_in + 1, dport=self.tcp_external_port + 1,
+ TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+ flags="P"))
+ self.send_and_assert_no_replies(self.pg0, p, self.pg1)
+
+ # The session can be re-opened
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
flags="S"))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.get_capture(1)
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ def test_session_rst_established_timeout(self):
+ """ NAT44ED session RST timeouts """
+
+ self.nat_add_address(self.nat_addr)
+ self.nat_add_inside_interface(self.pg0)
+ self.nat_add_outside_interface(self.pg1)
+
+ self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
+ tcp_transitory=5, icmp=60)
+
+ self.init_tcp_session(self.pg0, self.pg1, self.tcp_port_in,
+ self.tcp_external_port)
+
+ # Wait at least the transitory time, the session is in established
+ # state anyway. RST followed by a data packet should keep it
+ # established.
+ self.virtual_sleep(6)
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+ flags="R"))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+ flags="P"))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # State is established, session should be still open after 6 seconds
+ self.virtual_sleep(6)
+
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+ flags="R"))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # State is transitory, session should be closed after 6 seconds
+ self.virtual_sleep(6)
+
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+ flags="P"))
+ self.send_and_assert_no_replies(self.pg0, p, self.pg1)
def test_dynamic_out_of_ports(self):
""" NAT44ED dynamic translation test: out of ports """
@@ -3000,7 +3049,10 @@ class TestNAT44EDMW(TestNAT44ED):
self.assertEqual(server2_n, 0)
self.assertGreater(server3_n, 0)
- def test_syslog_sess(self):
+ # put zzz in front of syslog test name so that it runs as a last test
+ # setting syslog sender cannot be undone and if it is set, it messes
+ # with self.send_and_assert_no_replies functionality
+ def test_zzz_syslog_sess(self):
""" NAT44ED Test syslog session creation and deletion """
self.vapi.syslog_set_filter(
self.syslog_severity.SYSLOG_API_SEVERITY_INFO)
@@ -3019,13 +3071,72 @@ class TestNAT44EDMW(TestNAT44ED):
capture = self.pg1.get_capture(1)
self.tcp_port_out = capture[0][TCP].sport
capture = self.pg3.get_capture(1)
- self.verify_syslog_sess(capture[0][Raw].load)
+ self.verify_syslog_sess(capture[0][Raw].load, 'SADD')
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
self.nat_add_address(self.nat_addr, is_add=0)
capture = self.pg3.get_capture(1)
- self.verify_syslog_sess(capture[0][Raw].load, False)
+ self.verify_syslog_sess(capture[0][Raw].load, 'SDEL')
+
+ # put zzz in front of syslog test name so that it runs as a last test
+ # setting syslog sender cannot be undone and if it is set, it messes
+ # with self.send_and_assert_no_replies functionality
+ def test_zzz_syslog_sess_reopen(self):
+ """ Syslog events for session reopen """
+ self.vapi.syslog_set_filter(
+ self.syslog_severity.SYSLOG_API_SEVERITY_INFO)
+ self.vapi.syslog_set_sender(self.pg3.local_ip4, self.pg3.remote_ip4)
+
+ self.nat_add_address(self.nat_addr)
+ self.nat_add_inside_interface(self.pg0)
+ self.nat_add_outside_interface(self.pg1)
+
+ # SYN in2out
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=self.tcp_port_in, dport=self.tcp_external_port))
+ capture = self.send_and_expect(self.pg0, p, self.pg1)[0]
+ self.tcp_port_out = capture[0][TCP].sport
+ capture = self.pg3.get_capture(1)
+ self.verify_syslog_sess(capture[0][Raw].load, 'SADD')
+
+ # SYN out2in
+ p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=self.tcp_external_port, dport=self.tcp_port_out))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ # FIN in2out
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+ flags="F"))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # FIN out2in
+ p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
+ flags="F"))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ # SYN in2out
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=self.tcp_port_in, dport=self.tcp_external_port))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # SYN out2in
+ p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=self.tcp_external_port, dport=self.tcp_port_out))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ # 2 records should be produced - first one del & add
+ capture = self.pg3.get_capture(2)
+ self.verify_syslog_sess(capture[0][Raw].load, 'SDEL')
+ self.verify_syslog_sess(capture[1][Raw].load, 'SADD')
def test_twice_nat_interface_addr(self):
""" NAT44ED Acquire twice NAT addresses from interface """
@@ -3150,10 +3261,7 @@ class TestNAT44EDMW(TestNAT44ED):
p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
IP(src=self.pg1.remote_ip4, dst=service_ip) /
TCP(sport=33898, dport=80, flags="S"))
- self.pg1.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- capture = self.pg0.get_capture(1)
+ capture = self.send_and_expect(self.pg1, p, self.pg0, n_rx=1)
p = capture[0]
tcp_port = p[TCP].sport
@@ -3161,58 +3269,43 @@ class TestNAT44EDMW(TestNAT44ED):
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) /
TCP(sport=80, dport=tcp_port, flags="SA"))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.get_capture(1)
+ self.send_and_expect(self.pg0, p, self.pg1, n_rx=1)
# ACK packet out->in
p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
IP(src=self.pg1.remote_ip4, dst=service_ip) /
TCP(sport=33898, dport=80, flags="A"))
- self.pg1.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg0.get_capture(1)
+ self.send_and_expect(self.pg1, p, self.pg0, n_rx=1)
# FIN packet in -> out
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) /
TCP(sport=80, dport=tcp_port, flags="FA", seq=100, ack=300))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.get_capture(1)
+ self.send_and_expect(self.pg0, p, self.pg1, n_rx=1)
# FIN+ACK packet out -> in
p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
IP(src=self.pg1.remote_ip4, dst=service_ip) /
TCP(sport=33898, dport=80, flags="FA", seq=300, ack=101))
- self.pg1.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg0.get_capture(1)
+ self.send_and_expect(self.pg1, p, self.pg0, n_rx=1)
# ACK packet in -> out
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) /
TCP(sport=80, dport=tcp_port, flags="A", seq=101, ack=301))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.get_capture(1)
+ self.send_and_expect(self.pg0, p, self.pg1, n_rx=1)
- # session now in transitory timeout
- # try SYN packet out->in - should be dropped
+ # session now in transitory timeout, but traffic still flows
+ # try FIN packet out->in
p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
IP(src=self.pg1.remote_ip4, dst=service_ip) /
- TCP(sport=33898, dport=80, flags="S"))
+ TCP(sport=33898, dport=80, flags="F"))
self.pg1.add_stream(p)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
self.virtual_sleep(new_transitory, "wait for transitory timeout")
- self.pg0.assert_nothing_captured(0)
+ self.pg0.get_capture(1)
# session should still exist
sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
@@ -3223,13 +3316,9 @@ class TestNAT44EDMW(TestNAT44ED):
p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
IP(src=self.pg1.remote_ip4, dst=service_ip) /
TCP(sport=33898, dport=80, flags="FA", seq=300, ack=101))
- self.pg1.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
-
+ self.send_and_assert_no_replies(self.pg1, p)
sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
self.assertEqual(len(sessions) - start_sessnum, 0)
- self.pg0.assert_nothing_captured(0)
def test_tcp_session_close_in(self):
""" NAT44ED Close TCP session from inside network """
@@ -3258,11 +3347,7 @@ class TestNAT44EDMW(TestNAT44ED):
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
TCP(sport=in_port, dport=ext_port,
flags="FA", seq=100, ack=300))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.get_capture(1)
-
+ self.send_and_expect(self.pg0, p, self.pg1)
pkts = []
# ACK packet out -> in
@@ -3279,67 +3364,40 @@ class TestNAT44EDMW(TestNAT44ED):
flags="FA", seq=300, ack=101))
pkts.append(p)
- self.pg1.add_stream(pkts)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg0.get_capture(2)
+ self.send_and_expect(self.pg1, pkts, self.pg0)
# ACK packet in -> out
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
TCP(sport=in_port, dport=ext_port,
flags="A", seq=101, ack=301))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.get_capture(1)
+ self.send_and_expect(self.pg0, p, self.pg1)
sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
self.assertEqual(len(sessions) - session_n, 1)
- out2in_drops = self.get_err_counter(
- '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
- in2out_drops = self.get_err_counter(
- '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
-
- # extra FIN packet out -> in - this should be dropped
+ # retransmit FIN packet out -> in
p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
TCP(sport=ext_port, dport=out_port,
flags="FA", seq=300, ack=101))
- self.pg1.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg0.assert_nothing_captured()
+ self.send_and_expect(self.pg1, p, self.pg0)
- # extra ACK packet in -> out - this should be dropped
+ # retransmit ACK packet in -> out
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
TCP(sport=in_port, dport=ext_port,
flags="A", seq=101, ack=301))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.assert_nothing_captured()
-
- stats = self.get_err_counter(
- '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
- self.assertEqual(stats - out2in_drops, 1)
- stats = self.get_err_counter(
- '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
- self.assertEqual(stats - in2out_drops, 1)
+ self.send_and_expect(self.pg0, p, self.pg1)
self.virtual_sleep(3)
- # extra ACK packet in -> out - this will cause session to be wiped
+ # retransmit ACK packet in -> out - this will cause session to be wiped
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
TCP(sport=in_port, dport=ext_port,
flags="A", seq=101, ack=301))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.assert_nothing_captured()
+ self.send_and_assert_no_replies(self.pg0, p)
sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
self.assertEqual(len(sessions) - session_n, 0)
@@ -3399,54 +3457,32 @@ class TestNAT44EDMW(TestNAT44ED):
sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
self.assertEqual(len(sessions) - session_n, 1)
- out2in_drops = self.get_err_counter(
- '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
- in2out_drops = self.get_err_counter(
- '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
-
- # extra FIN packet out -> in - this should be dropped
+ # retransmit FIN packet out -> in
p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
TCP(sport=ext_port, dport=out_port,
flags="FA", seq=300, ack=101))
+ self.send_and_expect(self.pg1, p, self.pg0)
- self.pg1.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg0.assert_nothing_captured()
-
- # extra ACK packet in -> out - this should be dropped
+ # retransmit ACK packet in -> out
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
TCP(sport=in_port, dport=ext_port,
flags="A", seq=101, ack=301))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.assert_nothing_captured()
-
- stats = self.get_err_counter(
- '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
- self.assertEqual(stats - out2in_drops, 1)
- stats = self.get_err_counter(
- '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
- self.assertEqual(stats - in2out_drops, 1)
+ self.send_and_expect(self.pg0, p, self.pg1)
self.virtual_sleep(3)
- # extra ACK packet in -> out - this will cause session to be wiped
+ # retransmit ACK packet in -> out - this will cause session to be wiped
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
TCP(sport=in_port, dport=ext_port,
flags="A", seq=101, ack=301))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.assert_nothing_captured()
+ self.send_and_assert_no_replies(self.pg0, p)
sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
self.assertEqual(len(sessions) - session_n, 0)
def test_tcp_session_close_simultaneous(self):
- """ NAT44ED Close TCP session from inside network """
+ """ Simultaneous TCP close from both sides """
in_port = self.tcp_port_in
ext_port = 10505
@@ -3471,90 +3507,226 @@ class TestNAT44EDMW(TestNAT44ED):
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
TCP(sport=in_port, dport=ext_port,
flags="FA", seq=100, ack=300))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.get_capture(1)
+ self.send_and_expect(self.pg0, p, self.pg1)
# FIN packet out -> in
p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
TCP(sport=ext_port, dport=out_port,
flags="FA", seq=300, ack=100))
- self.pg1.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg0.get_capture(1)
+ self.send_and_expect(self.pg1, p, self.pg0)
# ACK packet in -> out
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
TCP(sport=in_port, dport=ext_port,
flags="A", seq=101, ack=301))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.get_capture(1)
+ self.send_and_expect(self.pg0, p, self.pg1)
# ACK packet out -> in
p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
TCP(sport=ext_port, dport=out_port,
flags="A", seq=301, ack=101))
- self.pg1.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg0.get_capture(1)
+ self.send_and_expect(self.pg1, p, self.pg0)
sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
self.assertEqual(len(sessions) - session_n, 1)
- out2in_drops = self.get_err_counter(
- '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
- in2out_drops = self.get_err_counter(
- '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
-
- # extra FIN packet out -> in - this should be dropped
+ # retransmit FIN packet out -> in
p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
TCP(sport=ext_port, dport=out_port,
flags="FA", seq=300, ack=101))
+ self.send_and_expect(self.pg1, p, self.pg0)
- self.pg1.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg0.assert_nothing_captured()
+ # retransmit ACK packet in -> out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port,
+ flags="A", seq=101, ack=301))
+ self.send_and_expect(self.pg0, p, self.pg1)
- # extra ACK packet in -> out - this should be dropped
+ self.virtual_sleep(3)
+ # retransmit ACK packet in -> out - this will cause session to be wiped
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
TCP(sport=in_port, dport=ext_port,
flags="A", seq=101, ack=301))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.assert_nothing_captured()
+ self.pg_send(self.pg0, p)
+ self.send_and_assert_no_replies(self.pg0, p)
+ sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+ self.assertEqual(len(sessions) - session_n, 0)
+
+ def test_tcp_session_half_reopen_inside(self):
+ """ TCP session in FIN/FIN state not reopened by in2out SYN only """
+ in_port = self.tcp_port_in
+ ext_port = 10505
- stats = self.get_err_counter(
- '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
- self.assertEqual(stats - out2in_drops, 1)
- stats = self.get_err_counter(
- '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
- self.assertEqual(stats - in2out_drops, 1)
+ self.nat_add_address(self.nat_addr)
+ self.nat_add_inside_interface(self.pg0)
+ self.nat_add_outside_interface(self.pg1)
+ self.nat_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
+ in_port, ext_port, proto=IP_PROTOS.tcp,
+ flags=self.config_flags.NAT_IS_TWICE_NAT)
+
+ sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+ session_n = len(sessions)
+
+ self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
+ tcp_transitory=2, icmp=5)
+
+ out_port = self.init_tcp_session(self.pg0, self.pg1, in_port, ext_port)
+
+ # FIN packet in -> out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port,
+ flags="FA", seq=100, ack=300))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # FIN packet out -> in
+ p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=ext_port, dport=out_port,
+ flags="FA", seq=300, ack=100))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+ self.assertEqual(len(sessions) - session_n, 1)
+
+ # send SYN packet in -> out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port,
+ flags="S", seq=101, ack=301))
+ self.send_and_expect(self.pg0, p, self.pg1)
self.virtual_sleep(3)
- # extra ACK packet in -> out - this will cause session to be wiped
+ # send ACK packet in -> out - session should be wiped
p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
TCP(sport=in_port, dport=ext_port,
flags="A", seq=101, ack=301))
- self.pg0.add_stream(p)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- self.pg1.assert_nothing_captured()
+ self.send_and_assert_no_replies(self.pg0, p, self.pg1)
+ sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+ self.assertEqual(len(sessions) - session_n, 0)
+
+ def test_tcp_session_half_reopen_outside(self):
+ """ TCP session in FIN/FIN state not reopened by out2in SYN only """
+ in_port = self.tcp_port_in
+ ext_port = 10505
+
+ self.nat_add_address(self.nat_addr)
+ self.nat_add_inside_interface(self.pg0)
+ self.nat_add_outside_interface(self.pg1)
+ self.nat_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
+ in_port, ext_port, proto=IP_PROTOS.tcp,
+ flags=self.config_flags.NAT_IS_TWICE_NAT)
+
+ sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+ session_n = len(sessions)
+
+ self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
+ tcp_transitory=2, icmp=5)
+
+ out_port = self.init_tcp_session(self.pg0, self.pg1, in_port, ext_port)
+
+ # FIN packet in -> out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port,
+ flags="FA", seq=100, ack=300))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # FIN packet out -> in
+ p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=ext_port, dport=out_port,
+ flags="FA", seq=300, ack=100))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+ self.assertEqual(len(sessions) - session_n, 1)
+
+ # send SYN packet out -> in
+ p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=ext_port, dport=out_port,
+ flags="S", seq=300, ack=101))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ self.virtual_sleep(3)
+ # send ACK packet in -> out - session should be wiped
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port,
+ flags="A", seq=101, ack=301))
+ self.send_and_assert_no_replies(self.pg0, p, self.pg1)
sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
self.assertEqual(len(sessions) - session_n, 0)
+ def test_tcp_session_reopen(self):
+ """ TCP session in FIN/FIN state reopened by SYN from both sides """
+ in_port = self.tcp_port_in
+ ext_port = 10505
+
+ self.nat_add_address(self.nat_addr)
+ self.nat_add_inside_interface(self.pg0)
+ self.nat_add_outside_interface(self.pg1)
+ self.nat_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
+ in_port, ext_port, proto=IP_PROTOS.tcp,
+ flags=self.config_flags.NAT_IS_TWICE_NAT)
+
+ sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+ session_n = len(sessions)
+
+ self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
+ tcp_transitory=2, icmp=5)
+
+ out_port = self.init_tcp_session(self.pg0, self.pg1, in_port, ext_port)
+
+ # FIN packet in -> out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port,
+ flags="FA", seq=100, ack=300))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # FIN packet out -> in
+ p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=ext_port, dport=out_port,
+ flags="FA", seq=300, ack=100))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+ self.assertEqual(len(sessions) - session_n, 1)
+
+ # send SYN packet out -> in
+ p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=ext_port, dport=out_port,
+ flags="S", seq=300, ack=101))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ # send SYN packet in -> out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port,
+ flags="S", seq=101, ack=301))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ self.virtual_sleep(3)
+ # send ACK packet in -> out - should be forwarded and session alive
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port,
+ flags="A", seq=101, ack=301))
+ self.send_and_expect(self.pg0, p, self.pg1)
+ sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+ self.assertEqual(len(sessions) - session_n, 1)
+
def test_dynamic_vrf(self):
""" NAT44ED dynamic translation test: different VRF"""
@@ -3837,6 +4009,138 @@ class TestNAT44EDMW(TestNAT44ED):
self.logger.info(ppp("p2 packet:", p2))
self.logger.info(ppp("capture packet:", capture))
+ def test_tcp_session_open_retransmit1(self):
+ """ NAT44ED Open TCP session with SYN,ACK retransmit 1
+
+ The client does not receive the [SYN,ACK] or the
+ ACK from the client is lost. Therefore, the [SYN, ACK]
+ is retransmitted by the server.
+ """
+
+ in_port = self.tcp_port_in
+ ext_port = self.tcp_external_port
+ payload = "H" * 10
+
+ self.nat_add_address(self.nat_addr)
+ self.nat_add_inside_interface(self.pg0)
+ self.nat_add_outside_interface(self.pg1)
+
+ self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
+ tcp_transitory=5, icmp=60)
+ # SYN packet in->out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port, flags="S"))
+ p = self.send_and_expect(self.pg0, p, self.pg1)[0]
+ out_port = p[TCP].sport
+
+ # SYN + ACK packet out->in
+ p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=ext_port, dport=out_port, flags="SA"))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ # ACK in->out does not arrive
+
+ # resent SYN + ACK packet out->in
+ p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=ext_port, dport=out_port, flags="SA"))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ # ACK packet in->out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port, flags="A"))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # Verify that the data can be transmitted after the transitory time
+ self.virtual_sleep(6)
+
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port, flags="PA") /
+ Raw(payload))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ def test_tcp_session_open_retransmit2(self):
+ """ NAT44ED Open TCP session with SYN,ACK retransmit 2
+
+ The ACK is lost to the server after the TCP session is opened.
+ Data is sent by the client, then the [SYN,ACK] is
+ retransmitted by the server.
+ """
+
+ in_port = self.tcp_port_in
+ ext_port = self.tcp_external_port
+ payload = "H" * 10
+
+ self.nat_add_address(self.nat_addr)
+ self.nat_add_inside_interface(self.pg0)
+ self.nat_add_outside_interface(self.pg1)
+
+ self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
+ tcp_transitory=5, icmp=60)
+ # SYN packet in->out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port, flags="S"))
+ p = self.send_and_expect(self.pg0, p, self.pg1)[0]
+ out_port = p[TCP].sport
+
+ # SYN + ACK packet out->in
+ p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=ext_port, dport=out_port, flags="SA"))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ # ACK packet in->out -- not received by the server
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port, flags="A"))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # PUSH + ACK packet in->out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port, flags="PA") /
+ Raw(payload))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # resent SYN + ACK packet out->in
+ p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=ext_port, dport=out_port, flags="SA"))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ # resent ACK packet in->out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port, flags="A"))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # resent PUSH + ACK packet in->out
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port, flags="PA") /
+ Raw(payload))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
+ # ACK packet out->in
+ p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ TCP(sport=ext_port, dport=out_port, flags="A"))
+ self.send_and_expect(self.pg1, p, self.pg0)
+
+ # Verify that the data can be transmitted after the transitory time
+ self.virtual_sleep(6)
+
+ p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+ IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+ TCP(sport=in_port, dport=ext_port, flags="PA") /
+ Raw(payload))
+ self.send_and_expect(self.pg0, p, self.pg1)
+
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)