aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKlement Sekera <ksekera@cisco.com>2021-02-02 13:25:40 +0100
committerOle Tr�an <otroan@employees.org>2021-02-10 13:12:33 +0000
commit98d82ca04ba438cd2ba3c03de6e1e82e4786cd83 (patch)
treed63d0b002555b63730fab3cea261d824c21986b1
parent4f423bf6b4f3dedf6a3e8d5bbb38c31558d13534 (diff)
nat: fix EI hairpinning thread safety
Avoid doing inter-thread reads without locks by doing a handoff before destination address rewrite. Destination address is read from a session which is possibly owned by a different thread. By splitting the work in two parts with a handoff in the middle, we can do both in a thread safe way. Type: improvement Signed-off-by: Klement Sekera <ksekera@cisco.com> Change-Id: I1c50d188393a610f5564fa230c75771a8065f273
-rw-r--r--src/plugins/nat/nat.c18
-rw-r--r--src/plugins/nat/nat.h16
-rw-r--r--src/plugins/nat/nat44-ei/nat44_ei_in2out.c266
-rw-r--r--src/plugins/nat/nat44_hairpinning.c142
-rw-r--r--src/plugins/nat/nat44_hairpinning.h92
-rw-r--r--src/plugins/nat/test/test_nat44_ei.py340
-rw-r--r--src/vnet/buffer.h1
-rw-r--r--test/run_tests.py2
8 files changed, 813 insertions, 64 deletions
diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c
index 11e2d193240..85d4775c8fe 100644
--- a/src/plugins/nat/nat.c
+++ b/src/plugins/nat/nat.c
@@ -2514,6 +2514,13 @@ do \
vlib_zero_simple_counter (&c, 0); \
} while (0);
+extern vlib_node_registration_t nat44_hairpinning_node;
+extern vlib_node_registration_t snat_hairpin_dst_node;
+extern vlib_node_registration_t
+ nat44_in2out_hairpinning_finish_ip4_lookup_node;
+extern vlib_node_registration_t
+ nat44_in2out_hairpinning_finish_interface_output_node;
+
static clib_error_t *
nat_init (vlib_main_t * vm)
{
@@ -2632,6 +2639,17 @@ nat_init (vlib_main_t * vm)
nat_ha_init (vm, sm->num_workers, num_threads);
test_key_calc_split ();
+
+ sm->nat44_hairpinning_fq_index =
+ vlib_frame_queue_main_init (nat44_hairpinning_node.index, 0);
+ sm->snat_hairpin_dst_fq_index =
+ vlib_frame_queue_main_init (snat_hairpin_dst_node.index, 0);
+ sm->nat44_in2out_hairpinning_finish_ip4_lookup_node_fq_index =
+ vlib_frame_queue_main_init (
+ nat44_in2out_hairpinning_finish_ip4_lookup_node.index, 0);
+ sm->nat44_in2out_hairpinning_finish_interface_output_node_fq_index =
+ vlib_frame_queue_main_init (
+ nat44_in2out_hairpinning_finish_interface_output_node.index, 0);
return nat44_api_hookup (vm);
}
diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h
index e913484887a..c1dc31e8710 100644
--- a/src/plugins/nat/nat.h
+++ b/src/plugins/nat/nat.h
@@ -783,6 +783,11 @@ typedef struct snat_main_s
u8 enabled;
vnet_main_t *vnet_main;
+
+ u32 nat44_in2out_hairpinning_finish_ip4_lookup_node_fq_index;
+ u32 nat44_in2out_hairpinning_finish_interface_output_node_fq_index;
+ u32 nat44_hairpinning_fq_index;
+ u32 snat_hairpin_dst_fq_index;
} snat_main_t;
typedef struct
@@ -1149,14 +1154,17 @@ u32 icmp_match_out2in_slow (snat_main_t *sm, vlib_node_runtime_t *node,
/* hairpinning functions */
u32 snat_icmp_hairpinning (snat_main_t *sm, vlib_buffer_t *b0,
- ip4_header_t *ip0, icmp46_header_t *icmp0);
+ u32 thread_index, ip4_header_t *ip0,
+ icmp46_header_t *icmp0, u32 *required_thread_index);
void nat_hairpinning_sm_unknown_proto (snat_main_t * sm, vlib_buffer_t * b,
ip4_header_t * ip);
+
int snat_hairpinning (vlib_main_t *vm, vlib_node_runtime_t *node,
- snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0,
- udp_header_t *udp0, tcp_header_t *tcp0, u32 proto0,
- int do_trace);
+ snat_main_t *sm, u32 thread_index, vlib_buffer_t *b0,
+ ip4_header_t *ip0, udp_header_t *udp0,
+ tcp_header_t *tcp0, u32 proto0, int do_trace,
+ u32 *required_thread_index);
/* Call back functions for clib_bihash_add_or_overwrite_stale */
int nat44_i2o_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void *arg);
diff --git a/src/plugins/nat/nat44-ei/nat44_ei_in2out.c b/src/plugins/nat/nat44-ei/nat44_ei_in2out.c
index ad0007bb8df..54ed1a92e8b 100644
--- a/src/plugins/nat/nat44-ei/nat44_ei_in2out.c
+++ b/src/plugins/nat/nat44-ei/nat44_ei_in2out.c
@@ -35,6 +35,7 @@
#include <vppinfra/error.h>
#include <vppinfra/elog.h>
#include <nat/lib/nat_inlines.h>
+#include <nat/nat44_hairpinning.h>
typedef struct
{
@@ -108,9 +109,17 @@ typedef enum
SNAT_IN2OUT_NEXT_DROP,
SNAT_IN2OUT_NEXT_ICMP_ERROR,
SNAT_IN2OUT_NEXT_SLOW_PATH,
+ SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF,
SNAT_IN2OUT_N_NEXT,
} snat_in2out_next_t;
+typedef enum
+{
+ NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP,
+ NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP,
+ NAT44_IN2OUT_HAIRPINNING_FINISH_N_NEXT,
+} nat44_in2out_hairpinnig_finish_next_t;
+
static inline int
snat_not_translate (snat_main_t * sm, vlib_node_runtime_t * node,
u32 sw_if_index0, ip4_header_t * ip0, u32 proto0,
@@ -167,13 +176,11 @@ nat_not_translate_output_feature (snat_main_t * sm, ip4_header_t * ip0,
if (!clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0))
{
/* hairpinning */
- /* *INDENT-OFF* */
- pool_foreach (i, sm->output_feature_interfaces)
- {
- if ((nat_interface_is_inside(i)) && (sw_if_index == i->sw_if_index))
- return 0;
- }
- /* *INDENT-ON* */
+ pool_foreach (i, sm->output_feature_interfaces)
+ {
+ if ((nat_interface_is_inside (i)) && (sw_if_index == i->sw_if_index))
+ return 0;
+ }
return 1;
}
@@ -328,7 +335,6 @@ slow_path (snat_main_t * sm, vlib_buffer_t * b0,
s->out2in.fib_index = sm->outside_fibs[0].fib_index;
break;
default:
- /* *INDENT-OFF* */
vec_foreach (outside_fib, sm->outside_fibs)
{
fei = fib_table_lookup (outside_fib->fib_index, &pfx);
@@ -341,7 +347,6 @@ slow_path (snat_main_t * sm, vlib_buffer_t * b0,
}
}
}
- /* *INDENT-ON* */
break;
}
s->ext_host_addr.as_u32 = ip0->dst_address.as_u32;
@@ -660,6 +665,7 @@ icmp_in2out (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0,
ip_csum_t sum0;
u16 checksum0;
u32 next0_tmp;
+ u32 required_thread_index = thread_index;
echo0 = (icmp_echo_header_t *) (icmp0 + 1);
@@ -784,8 +790,14 @@ icmp_in2out (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0,
if (vnet_buffer (b0)->sw_if_index[VLIB_TX] == ~0)
{
- if (0 != snat_icmp_hairpinning (sm, b0, ip0, icmp0))
+ if (0 != snat_icmp_hairpinning (sm, b0, thread_index, ip0, icmp0,
+ &required_thread_index))
vnet_buffer (b0)->sw_if_index[VLIB_TX] = fib_index;
+ if (thread_index != required_thread_index)
+ {
+ vnet_buffer (b0)->snat.required_thread_index = required_thread_index;
+ next0 = SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF;
+ }
}
out:
@@ -1623,7 +1635,6 @@ VLIB_NODE_FN (snat_in2out_node) (vlib_main_t * vm,
0);
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (snat_in2out_node) = {
.name = "nat44-in2out",
.vector_size = sizeof (u32),
@@ -1643,9 +1654,9 @@ VLIB_REGISTER_NODE (snat_in2out_node) = {
[SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
[SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-slowpath",
[SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
+ [SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-in2out-hairpinning-handoff-ip4-lookup",
},
};
-/* *INDENT-ON* */
VLIB_NODE_FN (snat_in2out_output_node) (vlib_main_t * vm,
vlib_node_runtime_t * node,
@@ -1655,7 +1666,6 @@ VLIB_NODE_FN (snat_in2out_output_node) (vlib_main_t * vm,
1);
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (snat_in2out_output_node) = {
.name = "nat44-in2out-output",
.vector_size = sizeof (u32),
@@ -1675,9 +1685,9 @@ VLIB_REGISTER_NODE (snat_in2out_output_node) = {
[SNAT_IN2OUT_NEXT_LOOKUP] = "interface-output",
[SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-output-slowpath",
[SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
+ [SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-in2out-hairpinning-handoff-interface-output",
},
};
-/* *INDENT-ON* */
VLIB_NODE_FN (snat_in2out_slowpath_node) (vlib_main_t * vm,
vlib_node_runtime_t * node,
@@ -1687,7 +1697,6 @@ VLIB_NODE_FN (snat_in2out_slowpath_node) (vlib_main_t * vm,
0);
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (snat_in2out_slowpath_node) = {
.name = "nat44-in2out-slowpath",
.vector_size = sizeof (u32),
@@ -1707,9 +1716,9 @@ VLIB_REGISTER_NODE (snat_in2out_slowpath_node) = {
[SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
[SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-slowpath",
[SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
+ [SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-in2out-hairpinning-handoff-ip4-lookup",
},
};
-/* *INDENT-ON* */
VLIB_NODE_FN (snat_in2out_output_slowpath_node) (vlib_main_t * vm,
vlib_node_runtime_t * node,
@@ -1719,7 +1728,6 @@ VLIB_NODE_FN (snat_in2out_output_slowpath_node) (vlib_main_t * vm,
1);
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (snat_in2out_output_slowpath_node) = {
.name = "nat44-in2out-output-slowpath",
.vector_size = sizeof (u32),
@@ -1739,15 +1747,16 @@ VLIB_REGISTER_NODE (snat_in2out_output_slowpath_node) = {
[SNAT_IN2OUT_NEXT_LOOKUP] = "interface-output",
[SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-output-slowpath",
[SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
+ [SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-in2out-hairpinning-handoff-interface-output",
},
};
-/* *INDENT-ON* */
VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
u32 n_left_from, *from, *to_next;
+ u32 thread_index = vm->thread_index;
snat_in2out_next_t next_index;
snat_main_t *sm = &snat_main;
int is_hairpinning = 0;
@@ -1780,7 +1789,7 @@ VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm,
ip4_address_t sm0_addr;
u16 sm0_port;
u32 sm0_fib_index;
-
+ u32 required_thread_index = thread_index;
/* speculatively enqueue b0 to the current next frame */
bi0 = from[0];
@@ -1896,8 +1905,16 @@ VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm,
}
/* Hairpinning */
- is_hairpinning = snat_hairpinning (vm, node, sm, b0, ip0, udp0, tcp0,
- proto0, 0 /* do_trace */);
+ is_hairpinning = snat_hairpinning (
+ vm, node, sm, thread_index, b0, ip0, udp0, tcp0, proto0,
+ 0 /* do_trace */, &required_thread_index);
+
+ if (thread_index != required_thread_index)
+ {
+ vnet_buffer (b0)->snat.required_thread_index =
+ required_thread_index;
+ next0 = SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF;
+ }
trace0:
if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -1930,8 +1947,6 @@ VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm,
return frame->n_vectors;
}
-
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (snat_in2out_fast_node) = {
.name = "nat44-in2out-fast",
.vector_size = sizeof (u32),
@@ -1951,9 +1966,212 @@ VLIB_REGISTER_NODE (snat_in2out_fast_node) = {
[SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
[SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-slowpath",
[SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
+ [SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-in2out-hairpinning-handoff-ip4-lookup",
+ },
+};
+
+VLIB_NODE_FN (nat44_in2out_hairpinning_handoff_ip4_lookup_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return nat44_hairpinning_handoff_fn_inline (
+ vm, node, frame,
+ snat_main.nat44_in2out_hairpinning_finish_ip4_lookup_node_fq_index);
+}
+
+VLIB_REGISTER_NODE (nat44_in2out_hairpinning_handoff_ip4_lookup_node) = {
+ .name = "nat44-in2out-hairpinning-handoff-ip4-lookup",
+ .vector_size = sizeof (u32),
+ .n_errors = ARRAY_LEN(nat44_hairpinning_handoff_error_strings),
+ .error_strings = nat44_hairpinning_handoff_error_strings,
+ .format_trace = format_nat44_hairpinning_handoff_trace,
+
+ .n_next_nodes = 1,
+
+ .next_nodes = {
+ [0] = "error-drop",
+ },
+};
+
+VLIB_NODE_FN (nat44_in2out_hairpinning_handoff_interface_output_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return nat44_hairpinning_handoff_fn_inline (
+ vm, node, frame,
+ snat_main.nat44_in2out_hairpinning_finish_interface_output_node_fq_index);
+}
+
+VLIB_REGISTER_NODE (nat44_in2out_hairpinning_handoff_interface_output_node) = {
+ .name = "nat44-in2out-hairpinning-handoff-interface-output",
+ .vector_size = sizeof (u32),
+ .n_errors = ARRAY_LEN(nat44_hairpinning_handoff_error_strings),
+ .error_strings = nat44_hairpinning_handoff_error_strings,
+ .format_trace = format_nat44_hairpinning_handoff_trace,
+
+ .n_next_nodes = 1,
+
+ .next_nodes = {
+ [0] = "error-drop",
+ },
+};
+
+static_always_inline int
+nat44_in2out_hairpinning_finish_inline (vlib_main_t *vm,
+ vlib_node_runtime_t *node,
+ vlib_frame_t *frame)
+{
+ u32 n_left_from, *from, *to_next;
+ u32 thread_index = vm->thread_index;
+ snat_in2out_next_t next_index;
+ snat_main_t *sm = &snat_main;
+ int is_hairpinning = 0;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0;
+ vlib_buffer_t *b0;
+ u32 next0;
+ u32 sw_if_index0;
+ ip4_header_t *ip0;
+ udp_header_t *udp0;
+ tcp_header_t *tcp0;
+ icmp46_header_t *icmp0;
+ u32 proto0;
+ u32 required_thread_index = thread_index;
+
+ /* speculatively enqueue b0 to the current next frame */
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ next0 = NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP;
+
+ ip0 = vlib_buffer_get_current (b0);
+ udp0 = ip4_next_header (ip0);
+ tcp0 = (tcp_header_t *) udp0;
+ icmp0 = (icmp46_header_t *) udp0;
+
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ proto0 = ip_proto_to_nat_proto (ip0->protocol);
+
+ switch (proto0)
+ {
+ case NAT_PROTOCOL_TCP:
+ // fallthrough
+ case NAT_PROTOCOL_UDP:
+ is_hairpinning = snat_hairpinning (
+ vm, node, sm, thread_index, b0, ip0, udp0, tcp0, proto0,
+ 0 /* do_trace */, &required_thread_index);
+ break;
+ case NAT_PROTOCOL_ICMP:
+ is_hairpinning =
+ (0 == snat_icmp_hairpinning (sm, b0, thread_index, ip0, icmp0,
+ &required_thread_index));
+ break;
+ case NAT_PROTOCOL_OTHER:
+ // this should never happen
+ next0 = NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP;
+ break;
+ }
+
+ if (thread_index != required_thread_index)
+ {
+ // but we already did a handoff ...
+ next0 = NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP;
+ }
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ snat_in2out_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sw_if_index = sw_if_index0;
+ t->next_index = next0;
+ t->is_hairpinning = is_hairpinning;
+ }
+
+ if (next0 != NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP)
+ {
+ vlib_increment_simple_counter (
+ &sm->counters.fastpath.in2out.other, sw_if_index0,
+ vm->thread_index, 1);
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+ n_left_to_next, bi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+VLIB_NODE_FN (nat44_in2out_hairpinning_finish_ip4_lookup_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return nat44_in2out_hairpinning_finish_inline (vm, node, frame);
+}
+
+VLIB_REGISTER_NODE (nat44_in2out_hairpinning_finish_ip4_lookup_node) = {
+ .name = "nat44-in2out-hairpinning-finish-ip4-lookup",
+ .vector_size = sizeof (u32),
+ .format_trace = format_snat_in2out_fast_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(snat_in2out_error_strings),
+ .error_strings = snat_in2out_error_strings,
+
+ .runtime_data_bytes = sizeof (snat_runtime_t),
+
+ .n_next_nodes = NAT44_IN2OUT_HAIRPINNING_FINISH_N_NEXT,
+
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP] = "error-drop",
+ [NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP] = "ip4-lookup",
+ },
+};
+
+VLIB_NODE_FN (nat44_in2out_hairpinning_finish_interface_output_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return nat44_in2out_hairpinning_finish_inline (vm, node, frame);
+}
+
+VLIB_REGISTER_NODE (nat44_in2out_hairpinning_finish_interface_output_node) = {
+ .name = "nat44-in2out-hairpinning-finish-interface-output",
+ .vector_size = sizeof (u32),
+ .format_trace = format_snat_in2out_fast_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(snat_in2out_error_strings),
+ .error_strings = snat_in2out_error_strings,
+
+ .runtime_data_bytes = sizeof (snat_runtime_t),
+
+ .n_next_nodes = NAT44_IN2OUT_HAIRPINNING_FINISH_N_NEXT,
+
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP] = "error-drop",
+ [NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP] = "interface-output",
},
};
-/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/plugins/nat/nat44_hairpinning.c b/src/plugins/nat/nat44_hairpinning.c
index f458909df20..a2cb2eb7547 100644
--- a/src/plugins/nat/nat44_hairpinning.c
+++ b/src/plugins/nat/nat44_hairpinning.c
@@ -22,6 +22,7 @@
#include <vnet/fib/ip4_fib.h>
#include <nat/nat.h>
#include <nat/nat_inlines.h>
+#include <nat/nat44_hairpinning.h>
typedef enum
{
@@ -36,6 +37,7 @@ typedef enum
{
NAT_HAIRPIN_NEXT_LOOKUP,
NAT_HAIRPIN_NEXT_DROP,
+ NAT_HAIRPIN_NEXT_HANDOFF,
NAT_HAIRPIN_N_NEXT,
} nat_hairpin_next_t;
@@ -77,13 +79,11 @@ is_hairpinning (snat_main_t * sm, ip4_address_t * dst_addr)
snat_address_t *ap;
clib_bihash_kv_8_8_t kv, value;
- /* *INDENT-OFF* */
vec_foreach (ap, sm->addresses)
{
if (ap->addr.as_u32 == dst_addr->as_u32)
return 1;
}
- /* *INDENT-ON* */
init_nat_k (&kv, *dst_addr, 0, 0, 0);
if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
@@ -95,13 +95,14 @@ is_hairpinning (snat_main_t * sm, ip4_address_t * dst_addr)
#ifndef CLIB_MARCH_VARIANT
int
snat_hairpinning (vlib_main_t *vm, vlib_node_runtime_t *node, snat_main_t *sm,
- vlib_buffer_t *b0, ip4_header_t *ip0, udp_header_t *udp0,
- tcp_header_t *tcp0, u32 proto0, int do_trace)
+ u32 thread_index, vlib_buffer_t *b0, ip4_header_t *ip0,
+ udp_header_t *udp0, tcp_header_t *tcp0, u32 proto0,
+ int do_trace, u32 *required_thread_index)
{
snat_session_t *s0 = NULL;
clib_bihash_kv_8_8_t kv0, value0;
ip_csum_t sum0;
- u32 new_dst_addr0 = 0, old_dst_addr0, ti = 0, si = ~0;
+ u32 new_dst_addr0 = 0, old_dst_addr0, si = ~0;
u16 new_dst_port0 = ~0, old_dst_port0;
int rv;
ip4_address_t sm0_addr;
@@ -120,13 +121,6 @@ snat_hairpinning (vlib_main_t *vm, vlib_node_runtime_t *node, snat_main_t *sm,
/* or active session */
else
{
- if (sm->num_workers > 1)
- ti =
- (clib_net_to_host_u16 (udp0->dst_port) -
- 1024) / sm->port_per_thread;
- else
- ti = sm->num_workers;
-
init_nat_k (&kv0, ip0->dst_address, udp0->dst_port,
sm->outside_fib_index, proto0);
rv = clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0);
@@ -136,8 +130,14 @@ snat_hairpinning (vlib_main_t *vm, vlib_node_runtime_t *node, snat_main_t *sm,
goto trace;
}
+ if (thread_index != nat_value_get_thread_index (&value0))
+ {
+ *required_thread_index = nat_value_get_thread_index (&value0);
+ return 0;
+ }
+
si = nat_value_get_session_index (&value0);
- s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
+ s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, si);
new_dst_addr0 = s0->in2out.addr.as_u32;
new_dst_port0 = s0->in2out.port;
vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
@@ -220,8 +220,9 @@ trace:
#ifndef CLIB_MARCH_VARIANT
u32
-snat_icmp_hairpinning (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0,
- icmp46_header_t *icmp0)
+snat_icmp_hairpinning (snat_main_t *sm, vlib_buffer_t *b0, u32 thread_index,
+ ip4_header_t *ip0, icmp46_header_t *icmp0,
+ u32 *required_thread_index)
{
clib_bihash_kv_8_8_t kv0, value0;
u32 old_dst_addr0, new_dst_addr0;
@@ -250,6 +251,12 @@ snat_icmp_hairpinning (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0,
sm->outside_fib_index, protocol);
if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
return 1;
+ ti = nat_value_get_thread_index (&value0);
+ if (ti != thread_index)
+ {
+ *required_thread_index = ti;
+ return 1;
+ }
si = nat_value_get_session_index (&value0);
s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
new_dst_addr0 = s0->in2out.addr.as_u32;
@@ -295,14 +302,15 @@ snat_icmp_hairpinning (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0,
u16 icmp_id0 = echo0->identifier;
init_nat_k (&kv0, ip0->dst_address, icmp_id0, sm->outside_fib_index,
NAT_PROTOCOL_ICMP);
- if (sm->num_workers > 1)
- ti =
- (clib_net_to_host_u16 (icmp_id0) - 1024) / sm->port_per_thread;
- else
- ti = sm->num_workers;
int rv = clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0);
if (!rv)
{
+ ti = nat_value_get_thread_index (&value0);
+ if (ti != thread_index)
+ {
+ *required_thread_index = ti;
+ return 1;
+ }
si = nat_value_get_session_index (&value0);
s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
new_dst_addr0 = s0->in2out.addr.as_u32;
@@ -371,6 +379,7 @@ nat44_hairpinning_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
vlib_frame_t *frame)
{
u32 n_left_from, *from, *to_next;
+ u32 thread_index = vm->thread_index;
nat_hairpin_next_t next_index;
snat_main_t *sm = &snat_main;
vnet_feature_main_t *fm = &feature_main;
@@ -397,6 +406,7 @@ nat44_hairpinning_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
udp_header_t *udp0;
tcp_header_t *tcp0;
u32 sw_if_index0;
+ u32 required_thread_index = thread_index;
/* speculatively enqueue b0 to the current next frame */
bi0 = from[0];
@@ -413,13 +423,27 @@ nat44_hairpinning_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
proto0 = ip_proto_to_nat_proto (ip0->protocol);
+ int next0_resolved = 0;
+
+ if (snat_hairpinning (vm, node, sm, thread_index, b0, ip0, udp0,
+ tcp0, proto0, 1 /* do_trace */,
+ &required_thread_index))
+ {
+ next0 = NAT_HAIRPIN_NEXT_LOOKUP;
+ next0_resolved = 1;
+ }
- vnet_get_config_data (&cm->config_main, &b0->current_config_index,
- &next0, 0);
+ if (thread_index != required_thread_index)
+ {
+ vnet_buffer (b0)->snat.required_thread_index =
+ required_thread_index;
+ next0 = NAT_HAIRPIN_NEXT_HANDOFF;
+ next0_resolved = 1;
+ }
- if (snat_hairpinning (vm, node, sm, b0, ip0, udp0, tcp0, proto0,
- 1 /* do_trace */))
- next0 = NAT_HAIRPIN_NEXT_LOOKUP;
+ if (!next0_resolved)
+ vnet_get_config_data (&cm->config_main, &b0->current_config_index,
+ &next0, 0);
if (next0 != NAT_HAIRPIN_NEXT_DROP)
{
@@ -447,7 +471,6 @@ VLIB_NODE_FN (nat44_hairpinning_node) (vlib_main_t * vm,
return nat44_hairpinning_fn_inline (vm, node, frame);
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (nat44_hairpinning_node) = {
.name = "nat44-hairpinning",
.vector_size = sizeof (u32),
@@ -457,15 +480,37 @@ VLIB_REGISTER_NODE (nat44_hairpinning_node) = {
.next_nodes = {
[NAT_HAIRPIN_NEXT_DROP] = "error-drop",
[NAT_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
+ [NAT_HAIRPIN_NEXT_HANDOFF] = "nat44-hairpinning-handoff",
+ },
+};
+
+VLIB_NODE_FN (nat44_hairpinning_handoff_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return nat44_hairpinning_handoff_fn_inline (
+ vm, node, frame, snat_main.nat44_hairpinning_fq_index);
+}
+
+VLIB_REGISTER_NODE (nat44_hairpinning_handoff_node) = {
+ .name = "nat44-hairpinning-handoff",
+ .vector_size = sizeof (u32),
+ .n_errors = ARRAY_LEN(nat44_hairpinning_handoff_error_strings),
+ .error_strings = nat44_hairpinning_handoff_error_strings,
+ .format_trace = format_nat44_hairpinning_handoff_trace,
+
+ .n_next_nodes = 1,
+
+ .next_nodes = {
+ [0] = "error-drop",
},
};
-/* *INDENT-ON* */
static inline uword
snat_hairpin_dst_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
vlib_frame_t *frame)
{
u32 n_left_from, *from, *to_next;
+ u32 thread_index = vm->thread_index;
nat_hairpin_next_t next_index;
snat_main_t *sm = &snat_main;
@@ -487,6 +532,7 @@ snat_hairpin_dst_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
ip4_header_t *ip0;
u32 proto0;
u32 sw_if_index0;
+ u32 required_thread_index = thread_index;
/* speculatively enqueue b0 to the current next frame */
bi0 = from[0];
@@ -511,14 +557,16 @@ snat_hairpin_dst_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
udp_header_t *udp0 = ip4_next_header (ip0);
tcp_header_t *tcp0 = (tcp_header_t *) udp0;
- snat_hairpinning (vm, node, sm, b0, ip0, udp0, tcp0, proto0,
- 1 /* do_trace */);
+ snat_hairpinning (vm, node, sm, thread_index, b0, ip0, udp0,
+ tcp0, proto0, 1 /* do_trace */,
+ &required_thread_index);
}
else if (proto0 == NAT_PROTOCOL_ICMP)
{
icmp46_header_t *icmp0 = ip4_next_header (ip0);
- snat_icmp_hairpinning (sm, b0, ip0, icmp0);
+ snat_icmp_hairpinning (sm, b0, thread_index, ip0, icmp0,
+ &required_thread_index);
}
else
{
@@ -528,6 +576,12 @@ snat_hairpin_dst_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
vnet_buffer (b0)->snat.flags = SNAT_FLAG_HAIRPINNING;
}
+ if (thread_index != required_thread_index)
+ {
+ vnet_buffer (b0)->snat.required_thread_index =
+ required_thread_index;
+ next0 = NAT_HAIRPIN_NEXT_HANDOFF;
+ }
if (next0 != NAT_HAIRPIN_NEXT_DROP)
{
@@ -555,7 +609,6 @@ VLIB_NODE_FN (snat_hairpin_dst_node) (vlib_main_t * vm,
return snat_hairpin_dst_fn_inline (vm, node, frame);
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (snat_hairpin_dst_node) = {
.name = "nat44-hairpin-dst",
.vector_size = sizeof (u32),
@@ -565,9 +618,30 @@ VLIB_REGISTER_NODE (snat_hairpin_dst_node) = {
.next_nodes = {
[NAT_HAIRPIN_NEXT_DROP] = "error-drop",
[NAT_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
+ [NAT_HAIRPIN_NEXT_HANDOFF] = "nat44-hairpin-dst-handoff",
+ },
+};
+
+VLIB_NODE_FN (nat44_hairpinning_dst_handoff_node)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return nat44_hairpinning_handoff_fn_inline (
+ vm, node, frame, snat_main.snat_hairpin_dst_fq_index);
+}
+
+VLIB_REGISTER_NODE (nat44_hairpinning_dst_handoff_node) = {
+ .name = "nat44-hairpin-dst-handoff",
+ .vector_size = sizeof (u32),
+ .n_errors = ARRAY_LEN(nat44_hairpinning_handoff_error_strings),
+ .error_strings = nat44_hairpinning_handoff_error_strings,
+ .format_trace = format_nat44_hairpinning_handoff_trace,
+
+ .n_next_nodes = 1,
+
+ .next_nodes = {
+ [0] = "error-drop",
},
};
-/* *INDENT-ON* */
static inline uword
snat_hairpin_src_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
@@ -607,7 +681,6 @@ snat_hairpin_src_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
vnet_feature_next (&next0, b0);
- /* *INDENT-OFF* */
pool_foreach (i, sm->output_feature_interfaces)
{
/* Only packets from NAT inside interface */
@@ -624,7 +697,6 @@ snat_hairpin_src_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
break;
}
}
- /* *INDENT-ON* */
if (next0 != SNAT_HAIRPIN_SRC_NEXT_DROP)
{
@@ -652,7 +724,6 @@ VLIB_NODE_FN (snat_hairpin_src_node) (vlib_main_t * vm,
return snat_hairpin_src_fn_inline (vm, node, frame);
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (snat_hairpin_src_node) = {
.name = "nat44-hairpin-src",
.vector_size = sizeof (u32),
@@ -665,7 +736,6 @@ VLIB_REGISTER_NODE (snat_hairpin_src_node) = {
[SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "nat44-in2out-output-worker-handoff",
},
};
-/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/plugins/nat/nat44_hairpinning.h b/src/plugins/nat/nat44_hairpinning.h
new file mode 100644
index 00000000000..2a8b73db6ab
--- /dev/null
+++ b/src/plugins/nat/nat44_hairpinning.h
@@ -0,0 +1,92 @@
+#ifndef __included_nat44_hairpinning_h__
+#define __included_nat44_hairpinning_h__
+
+#include <nat/nat.h>
+
+#define foreach_nat44_hairpinning_handoff_error \
+ _ (CONGESTION_DROP, "congestion drop")
+
+typedef enum
+{
+#define _(sym, str) NAT44_HAIRPINNING_HANDOFF_ERROR_##sym,
+ foreach_nat44_hairpinning_handoff_error
+#undef _
+ NAT44_HAIRPINNING_HANDOFF_N_ERROR,
+} nat44_hairpinning_handoff_error_t;
+
+static char *nat44_hairpinning_handoff_error_strings[] = {
+#define _(sym, string) string,
+ foreach_nat44_hairpinning_handoff_error
+#undef _
+};
+
+typedef struct
+{
+ u32 next_worker_index;
+} nat44_hairpinning_handoff_trace_t;
+
+static u8 *
+format_nat44_hairpinning_handoff_trace (u8 *s, va_list *args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ nat44_hairpinning_handoff_trace_t *t =
+ va_arg (*args, nat44_hairpinning_handoff_trace_t *);
+
+ s = format (s, "nat-hairpinning-handoff: next-worker %d",
+ t->next_worker_index);
+
+ return s;
+}
+
+always_inline uword
+nat44_hairpinning_handoff_fn_inline (vlib_main_t *vm,
+ vlib_node_runtime_t *node,
+ vlib_frame_t *frame, u32 fq_index)
+{
+ vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
+ u32 n_enq, n_left_from, *from;
+ u16 thread_indices[VLIB_FRAME_SIZE], *ti;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ vlib_get_buffers (vm, from, bufs, n_left_from);
+
+ b = bufs;
+ ti = thread_indices;
+
+ while (n_left_from > 0)
+ {
+ ti[0] = vnet_buffer (b[0])->snat.required_thread_index;
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ nat44_hairpinning_handoff_trace_t *t =
+ vlib_add_trace (vm, node, b[0], sizeof (*t));
+ t->next_worker_index = ti[0];
+ }
+
+ n_left_from -= 1;
+ ti += 1;
+ b += 1;
+ }
+ n_enq = vlib_buffer_enqueue_to_thread (vm, fq_index, from, thread_indices,
+ frame->n_vectors, 1);
+
+ if (n_enq < frame->n_vectors)
+ vlib_node_increment_counter (
+ vm, node->node_index, NAT44_HAIRPINNING_HANDOFF_ERROR_CONGESTION_DROP,
+ frame->n_vectors - n_enq);
+ return frame->n_vectors;
+}
+
+#endif // __included_nat44_hairpinning_h__
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nat/test/test_nat44_ei.py b/src/plugins/nat/test/test_nat44_ei.py
index e69e24b2911..471d1483cf8 100644
--- a/src/plugins/nat/test/test_nat44_ei.py
+++ b/src/plugins/nat/test/test_nat44_ei.py
@@ -3859,5 +3859,345 @@ class TestNAT44Out2InDPO(MethodHolder):
self.verify_capture_in(capture, self.pg0)
+class TestNAT44EIMW(MethodHolder):
+ """ NAT44EI Test Cases (multiple workers) """
+
+ worker_config = "workers %d" % 2
+
+ max_translations = 10240
+ max_users = 10240
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestNAT44EIMW, cls).setUpClass()
+ cls.vapi.cli("set log class nat level debug")
+
+ cls.tcp_port_in = 6303
+ cls.tcp_port_out = 6303
+ cls.udp_port_in = 6304
+ cls.udp_port_out = 6304
+ cls.icmp_id_in = 6305
+ cls.icmp_id_out = 6305
+ cls.nat_addr = '10.0.0.3'
+ cls.ipfix_src_port = 4739
+ cls.ipfix_domain_id = 1
+ cls.tcp_external_port = 80
+ cls.udp_external_port = 69
+
+ cls.create_pg_interfaces(range(10))
+ cls.interfaces = list(cls.pg_interfaces[0:4])
+
+ for i in cls.interfaces:
+ i.admin_up()
+ i.config_ip4()
+ i.resolve_arp()
+
+ cls.pg0.generate_remote_hosts(3)
+ cls.pg0.configure_ipv4_neighbors()
+
+ cls.pg1.generate_remote_hosts(1)
+ cls.pg1.configure_ipv4_neighbors()
+
+ cls.overlapping_interfaces = list(list(cls.pg_interfaces[4:7]))
+ cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 10})
+ cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 20})
+
+ cls.pg4._local_ip4 = "172.16.255.1"
+ cls.pg4._remote_hosts[0]._ip4 = "172.16.255.2"
+ cls.pg4.set_table_ip4(10)
+ cls.pg5._local_ip4 = "172.17.255.3"
+ cls.pg5._remote_hosts[0]._ip4 = "172.17.255.4"
+ cls.pg5.set_table_ip4(10)
+ cls.pg6._local_ip4 = "172.16.255.1"
+ cls.pg6._remote_hosts[0]._ip4 = "172.16.255.2"
+ cls.pg6.set_table_ip4(20)
+ for i in cls.overlapping_interfaces:
+ i.config_ip4()
+ i.admin_up()
+ i.resolve_arp()
+
+ cls.pg7.admin_up()
+ cls.pg8.admin_up()
+
+ cls.pg9.generate_remote_hosts(2)
+ cls.pg9.config_ip4()
+ cls.vapi.sw_interface_add_del_address(
+ sw_if_index=cls.pg9.sw_if_index,
+ prefix="10.0.0.1/24")
+
+ cls.pg9.admin_up()
+ cls.pg9.resolve_arp()
+ cls.pg9._remote_hosts[1]._ip4 = cls.pg9._remote_hosts[0]._ip4
+ cls.pg4._remote_ip4 = cls.pg9._remote_hosts[0]._ip4 = "10.0.0.2"
+ cls.pg9.resolve_arp()
+
+ def setUp(self):
+ super(TestNAT44EIMW, self).setUp()
+ self.vapi.nat44_plugin_enable_disable(
+ sessions=self.max_translations,
+ users=self.max_users, enable=1)
+
+ def tearDown(self):
+ super(TestNAT44EIMW, self).tearDown()
+ if not self.vpp_dead:
+ self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id,
+ src_port=self.ipfix_src_port,
+ enable=0)
+ self.ipfix_src_port = 4739
+ self.ipfix_domain_id = 1
+
+ self.vapi.nat44_plugin_enable_disable(enable=0)
+ self.vapi.cli("clear logging")
+
+ def test_hairpinning(self):
+ """ NAT44EI hairpinning - 1:1 NAPT """
+
+ host = self.pg0.remote_hosts[0]
+ server = self.pg0.remote_hosts[1]
+ host_in_port = 1234
+ host_out_port = 0
+ server_in_port = 5678
+ server_out_port = 8765
+ worker_1 = 1
+ worker_2 = 2
+
+ self.nat44_add_address(self.nat_addr)
+ flags = self.config_flags.NAT_IS_INSIDE
+ self.vapi.nat44_interface_add_del_feature(
+ sw_if_index=self.pg0.sw_if_index,
+ flags=flags, is_add=1)
+ self.vapi.nat44_interface_add_del_feature(
+ sw_if_index=self.pg1.sw_if_index,
+ is_add=1)
+
+ # add static mapping for server
+ self.nat44_add_static_mapping(server.ip4, self.nat_addr,
+ server_in_port, server_out_port,
+ proto=IP_PROTOS.tcp)
+
+ cnt = self.statistics.get_counter('/nat44/hairpinning')
+ # send packet from host to server
+ p = (Ether(src=host.mac, dst=self.pg0.local_mac) /
+ IP(src=host.ip4, dst=self.nat_addr) /
+ TCP(sport=host_in_port, dport=server_out_port))
+ self.pg0.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg0.get_capture(1)
+ p = capture[0]
+ try:
+ ip = p[IP]
+ tcp = p[TCP]
+ self.assertEqual(ip.src, self.nat_addr)
+ self.assertEqual(ip.dst, server.ip4)
+ self.assertNotEqual(tcp.sport, host_in_port)
+ self.assertEqual(tcp.dport, server_in_port)
+ self.assert_packet_checksums_valid(p)
+ host_out_port = tcp.sport
+ except:
+ self.logger.error(ppp("Unexpected or invalid packet:", p))
+ raise
+
+ after = self.statistics.get_counter('/nat44/hairpinning')
+
+ if_idx = self.pg0.sw_if_index
+ self.assertEqual(after[worker_2][if_idx] - cnt[worker_1][if_idx], 1)
+
+ # send reply from server to host
+ p = (Ether(src=server.mac, dst=self.pg0.local_mac) /
+ IP(src=server.ip4, dst=self.nat_addr) /
+ TCP(sport=server_in_port, dport=host_out_port))
+ self.pg0.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg0.get_capture(1)
+ p = capture[0]
+ try:
+ ip = p[IP]
+ tcp = p[TCP]
+ self.assertEqual(ip.src, self.nat_addr)
+ self.assertEqual(ip.dst, host.ip4)
+ self.assertEqual(tcp.sport, server_out_port)
+ self.assertEqual(tcp.dport, host_in_port)
+ self.assert_packet_checksums_valid(p)
+ except:
+ self.logger.error(ppp("Unexpected or invalid packet:", p))
+ raise
+
+ after = self.statistics.get_counter('/nat44/hairpinning')
+ if_idx = self.pg0.sw_if_index
+ self.assertEqual(after[worker_1][if_idx] - cnt[worker_1][if_idx], 1)
+ self.assertEqual(after[worker_2][if_idx] - cnt[worker_2][if_idx], 2)
+
+ def test_hairpinning2(self):
+ """ NAT44EI hairpinning - 1:1 NAT"""
+
+ server1_nat_ip = "10.0.0.10"
+ server2_nat_ip = "10.0.0.11"
+ host = self.pg0.remote_hosts[0]
+ server1 = self.pg0.remote_hosts[1]
+ server2 = self.pg0.remote_hosts[2]
+ server_tcp_port = 22
+ server_udp_port = 20
+
+ self.nat44_add_address(self.nat_addr)
+ flags = self.config_flags.NAT_IS_INSIDE
+ self.vapi.nat44_interface_add_del_feature(
+ sw_if_index=self.pg0.sw_if_index,
+ flags=flags, is_add=1)
+ self.vapi.nat44_interface_add_del_feature(
+ sw_if_index=self.pg1.sw_if_index,
+ is_add=1)
+
+ # add static mapping for servers
+ self.nat44_add_static_mapping(server1.ip4, server1_nat_ip)
+ self.nat44_add_static_mapping(server2.ip4, server2_nat_ip)
+
+ # host to server1
+ pkts = []
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=host.ip4, dst=server1_nat_ip) /
+ TCP(sport=self.tcp_port_in, dport=server_tcp_port))
+ pkts.append(p)
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=host.ip4, dst=server1_nat_ip) /
+ UDP(sport=self.udp_port_in, dport=server_udp_port))
+ pkts.append(p)
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=host.ip4, dst=server1_nat_ip) /
+ ICMP(id=self.icmp_id_in, type='echo-request'))
+ pkts.append(p)
+ self.pg0.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg0.get_capture(len(pkts))
+ for packet in capture:
+ try:
+ self.assertEqual(packet[IP].src, self.nat_addr)
+ self.assertEqual(packet[IP].dst, server1.ip4)
+ if packet.haslayer(TCP):
+ self.assertNotEqual(packet[TCP].sport, self.tcp_port_in)
+ self.assertEqual(packet[TCP].dport, server_tcp_port)
+ self.tcp_port_out = packet[TCP].sport
+ self.assert_packet_checksums_valid(packet)
+ elif packet.haslayer(UDP):
+ self.assertNotEqual(packet[UDP].sport, self.udp_port_in)
+ self.assertEqual(packet[UDP].dport, server_udp_port)
+ self.udp_port_out = packet[UDP].sport
+ else:
+ self.assertNotEqual(packet[ICMP].id, self.icmp_id_in)
+ self.icmp_id_out = packet[ICMP].id
+ except:
+ self.logger.error(ppp("Unexpected or invalid packet:", packet))
+ raise
+
+ # server1 to host
+ pkts = []
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=server1.ip4, dst=self.nat_addr) /
+ TCP(sport=server_tcp_port, dport=self.tcp_port_out))
+ pkts.append(p)
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=server1.ip4, dst=self.nat_addr) /
+ UDP(sport=server_udp_port, dport=self.udp_port_out))
+ pkts.append(p)
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=server1.ip4, dst=self.nat_addr) /
+ ICMP(id=self.icmp_id_out, type='echo-reply'))
+ pkts.append(p)
+ self.pg0.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg0.get_capture(len(pkts))
+ for packet in capture:
+ try:
+ self.assertEqual(packet[IP].src, server1_nat_ip)
+ self.assertEqual(packet[IP].dst, host.ip4)
+ if packet.haslayer(TCP):
+ self.assertEqual(packet[TCP].dport, self.tcp_port_in)
+ self.assertEqual(packet[TCP].sport, server_tcp_port)
+ self.assert_packet_checksums_valid(packet)
+ elif packet.haslayer(UDP):
+ self.assertEqual(packet[UDP].dport, self.udp_port_in)
+ self.assertEqual(packet[UDP].sport, server_udp_port)
+ else:
+ self.assertEqual(packet[ICMP].id, self.icmp_id_in)
+ except:
+ self.logger.error(ppp("Unexpected or invalid packet:", packet))
+ raise
+
+ # server2 to server1
+ pkts = []
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=server2.ip4, dst=server1_nat_ip) /
+ TCP(sport=self.tcp_port_in, dport=server_tcp_port))
+ pkts.append(p)
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=server2.ip4, dst=server1_nat_ip) /
+ UDP(sport=self.udp_port_in, dport=server_udp_port))
+ pkts.append(p)
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=server2.ip4, dst=server1_nat_ip) /
+ ICMP(id=self.icmp_id_in, type='echo-request'))
+ pkts.append(p)
+ self.pg0.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg0.get_capture(len(pkts))
+ for packet in capture:
+ try:
+ self.assertEqual(packet[IP].src, server2_nat_ip)
+ self.assertEqual(packet[IP].dst, server1.ip4)
+ if packet.haslayer(TCP):
+ self.assertEqual(packet[TCP].sport, self.tcp_port_in)
+ self.assertEqual(packet[TCP].dport, server_tcp_port)
+ self.tcp_port_out = packet[TCP].sport
+ self.assert_packet_checksums_valid(packet)
+ elif packet.haslayer(UDP):
+ self.assertEqual(packet[UDP].sport, self.udp_port_in)
+ self.assertEqual(packet[UDP].dport, server_udp_port)
+ self.udp_port_out = packet[UDP].sport
+ else:
+ self.assertEqual(packet[ICMP].id, self.icmp_id_in)
+ self.icmp_id_out = packet[ICMP].id
+ except:
+ self.logger.error(ppp("Unexpected or invalid packet:", packet))
+ raise
+
+ # server1 to server2
+ pkts = []
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=server1.ip4, dst=server2_nat_ip) /
+ TCP(sport=server_tcp_port, dport=self.tcp_port_out))
+ pkts.append(p)
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=server1.ip4, dst=server2_nat_ip) /
+ UDP(sport=server_udp_port, dport=self.udp_port_out))
+ pkts.append(p)
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=server1.ip4, dst=server2_nat_ip) /
+ ICMP(id=self.icmp_id_out, type='echo-reply'))
+ pkts.append(p)
+ self.pg0.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg0.get_capture(len(pkts))
+ for packet in capture:
+ try:
+ self.assertEqual(packet[IP].src, server1_nat_ip)
+ self.assertEqual(packet[IP].dst, server2.ip4)
+ if packet.haslayer(TCP):
+ self.assertEqual(packet[TCP].dport, self.tcp_port_in)
+ self.assertEqual(packet[TCP].sport, server_tcp_port)
+ self.assert_packet_checksums_valid(packet)
+ elif packet.haslayer(UDP):
+ self.assertEqual(packet[UDP].dport, self.udp_port_in)
+ self.assertEqual(packet[UDP].sport, server_udp_port)
+ else:
+ self.assertEqual(packet[ICMP].id, self.icmp_id_in)
+ except:
+ self.logger.error(ppp("Unexpected or invalid packet:", packet))
+ raise
+
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)
diff --git a/src/vnet/buffer.h b/src/vnet/buffer.h
index aae999620ac..88637e1d902 100644
--- a/src/vnet/buffer.h
+++ b/src/vnet/buffer.h
@@ -378,6 +378,7 @@ typedef struct
struct
{
u32 flags;
+ u32 required_thread_index;
} snat;
u32 unused[6];
diff --git a/test/run_tests.py b/test/run_tests.py
index a88a69a0eb2..1a29d148d9e 100644
--- a/test/run_tests.py
+++ b/test/run_tests.py
@@ -272,6 +272,8 @@ def handle_failed_suite(logger, last_test_temp_dir, vpp_pid):
except Exception as e:
logger.exception("Unexpected error running `file' utility "
"on core-file")
+ logger.error("gdb %s %s" %
+ (os.getenv('VPP_BIN', 'vpp'), core_path))
if vpp_pid:
# Copy api post mortem