From 98d82ca04ba438cd2ba3c03de6e1e82e4786cd83 Mon Sep 17 00:00:00 2001 From: Klement Sekera Date: Tue, 2 Feb 2021 13:25:40 +0100 Subject: 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 Change-Id: I1c50d188393a610f5564fa230c75771a8065f273 --- src/plugins/nat/nat.c | 18 ++ src/plugins/nat/nat.h | 16 +- src/plugins/nat/nat44-ei/nat44_ei_in2out.c | 266 ++++++++++++++++++++-- src/plugins/nat/nat44_hairpinning.c | 142 +++++++++--- src/plugins/nat/nat44_hairpinning.h | 92 ++++++++ src/plugins/nat/test/test_nat44_ei.py | 340 +++++++++++++++++++++++++++++ src/vnet/buffer.h | 1 + test/run_tests.py | 2 + 8 files changed, 813 insertions(+), 64 deletions(-) create mode 100644 src/plugins/nat/nat44_hairpinning.h 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 #include #include +#include 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 #include #include +#include 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 + +#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 -- cgit 1.2.3-korg