From 87da476db0cd804e11463cc453a2bb41c6808542 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Wed, 4 Oct 2017 08:03:56 -0700 Subject: [PATCH] NAT: hairpinning rework (VPP-1003) Change-Id: I7c6911cd6ac366fe62675fd0ff8b0246a25ea1db Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 114 ++++++++++++++++++++++++++++++++++++++++------ src/plugins/nat/nat.c | 14 +++++- src/vnet/buffer.h | 3 +- src/vnet/ip/ip4_forward.c | 10 +++- 4 files changed, 125 insertions(+), 16 deletions(-) diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index 8e583313a1c..24ff38602c5 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -93,6 +93,7 @@ vlib_node_registration_t snat_in2out_output_slowpath_node; vlib_node_registration_t snat_in2out_output_worker_handoff_node; vlib_node_registration_t snat_hairpin_dst_node; vlib_node_registration_t snat_hairpin_src_node; +vlib_node_registration_t nat44_hairpinning_node; #define foreach_snat_in2out_error \ @@ -818,7 +819,7 @@ out: * @param tcp0 TCP header. * @param proto0 NAT protocol. */ -static inline void +static inline int snat_hairpinning (snat_main_t *sm, vlib_buffer_t * b0, ip4_header_t * ip0, @@ -904,7 +905,9 @@ snat_hairpinning (snat_main_t *sm, tcp0->checksum = ip_csum_fold(sum0); } } + return 1; } + return 0; } static inline void @@ -1691,6 +1694,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } } + b0->flags |= VNET_BUFFER_F_IS_NATED; + old_addr0 = ip0->src_address.as_u32; ip0->src_address = s0->out2in.addr; new_addr0 = ip0->src_address.as_u32; @@ -1725,10 +1730,6 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, udp0->checksum = 0; } - /* Hairpinning */ - if (!is_output_feature) - snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); - /* Accounting */ s0->last_heard = now; s0->total_pkts++; @@ -1814,6 +1815,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } } + b1->flags |= VNET_BUFFER_F_IS_NATED; + key1.addr = ip1->src_address; key1.port = udp1->src_port; key1.protocol = proto1; @@ -1901,10 +1904,6 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, udp1->checksum = 0; } - /* Hairpinning */ - if (!is_output_feature) - snat_hairpinning (sm, b1, ip1, udp1, tcp1, proto1); - /* Accounting */ s1->last_heard = now; s1->total_pkts++; @@ -2080,6 +2079,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } } + b0->flags |= VNET_BUFFER_F_IS_NATED; + old_addr0 = ip0->src_address.as_u32; ip0->src_address = s0->out2in.addr; new_addr0 = ip0->src_address.as_u32; @@ -2114,10 +2115,6 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, udp0->checksum = 0; } - /* Hairpinning */ - if (!is_output_feature) - snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); - /* Accounting */ s0->last_heard = now; s0->total_pkts++; @@ -2298,6 +2295,97 @@ VLIB_REGISTER_NODE (snat_in2out_output_slowpath_node) = { VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_output_slowpath_node, snat_in2out_output_slow_path_fn); +extern vnet_feature_arc_registration_t vnet_feat_arc_ip4_local; + +static uword +nat44_hairpinning_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + snat_in2out_next_t next_index; + u32 pkts_processed = 0; + snat_main_t * sm = &snat_main; + vnet_feature_main_t *fm = &feature_main; + u8 arc_index = vnet_feat_arc_ip4_local.feature_arc_index; + vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index]; + + 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; + ip4_header_t * ip0; + u32 proto0; + udp_header_t * udp0; + tcp_header_t * tcp0; + + /* 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); + ip0 = vlib_buffer_get_current (b0); + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + vnet_get_config_data (&cm->config_main, &b0->current_config_index, + &next0, 0); + + if (snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0)) + next0 = SNAT_IN2OUT_NEXT_LOOKUP; + + pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; + + /* 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); + } + + vlib_node_increment_counter (vm, nat44_hairpinning_node.index, + SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, + pkts_processed); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (nat44_hairpinning_node) = { + .function = nat44_hairpinning_fn, + .name = "nat44-hairpinning", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + .n_next_nodes = 2, + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_hairpinning_node, + nat44_hairpinning_fn); + /**************************/ /*** deterministic mode ***/ /**************************/ diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index c2f9586ce7d..8b4f50c76cb 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -95,6 +95,14 @@ VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = { .runs_before = VNET_FEATURES ("interface-output"), }; +/* Hook up ip4-local features */ +VNET_FEATURE_INIT (ip4_nat_hairpinning, static) = +{ + .arc_name = "ip4-local", + .node_name = "nat44-hairpinning", + .runs_before = VNET_FEATURES("ip4-local-end-of-arc"), +}; + /* *INDENT-OFF* */ VLIB_PLUGIN_REGISTER () = { @@ -993,7 +1001,11 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) /* Add/delete external addresses to FIB */ fib: if (is_inside) - return 0; + { + vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", + sw_if_index, !is_del, 0, 0); + return 0; + } vec_foreach (ap, sm->addresses) snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del); diff --git a/src/vnet/buffer.h b/src/vnet/buffer.h index fbefe7c2f56..e774a084436 100644 --- a/src/vnet/buffer.h +++ b/src/vnet/buffer.h @@ -54,7 +54,8 @@ _( 9, IS_IP6) \ _(10, OFFLOAD_IP_CKSUM) \ _(11, OFFLOAD_TCP_CKSUM) \ - _(12, OFFLOAD_UDP_CKSUM) + _(12, OFFLOAD_UDP_CKSUM) \ + _(13, IS_NATED) #define VNET_BUFFER_FLAGS_VLAN_BITS \ (VNET_BUFFER_F_VLAN_1_DEEP | VNET_BUFFER_F_VLAN_2_DEEP) diff --git a/src/vnet/ip/ip4_forward.c b/src/vnet/ip/ip4_forward.c index 64e5e8e829c..3aebb181fce 100755 --- a/src/vnet/ip/ip4_forward.c +++ b/src/vnet/ip/ip4_forward.c @@ -1710,6 +1710,9 @@ ip4_local_inline (vlib_main_t * vm, * - uRPF check for any route to source - accept if passes. * - allow packets destined to the broadcast address from unknown sources */ + if (p0->flags & VNET_BUFFER_F_IS_NATED) + goto skip_check0; + error0 = ((error0 == IP4_ERROR_UNKNOWN_PROTOCOL && dpo0->dpoi_type == DPO_RECEIVE) ? IP4_ERROR_SPOOFED_LOCAL_PACKETS : error0); @@ -1717,6 +1720,11 @@ ip4_local_inline (vlib_main_t * vm, !fib_urpf_check_size (lb0->lb_urpf) && ip0->dst_address.as_u32 != 0xFFFFFFFF) ? IP4_ERROR_SRC_LOOKUP_MISS : error0); + + skip_check0: + if (p1->flags & VNET_BUFFER_F_IS_NATED) + goto skip_checks; + error1 = ((error1 == IP4_ERROR_UNKNOWN_PROTOCOL && dpo1->dpoi_type == DPO_RECEIVE) ? IP4_ERROR_SPOOFED_LOCAL_PACKETS : error1); @@ -1781,7 +1789,7 @@ ip4_local_inline (vlib_main_t * vm, until support of IP frag reassembly is implemented */ proto0 = ip4_is_fragment (ip0) ? 0xfe : ip0->protocol; - if (head_of_feature_arc == 0) + if (head_of_feature_arc == 0 || p0->flags & VNET_BUFFER_F_IS_NATED) goto skip_check; is_udp0 = proto0 == IP_PROTOCOL_UDP; -- 2.16.6