aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2017-10-04 08:03:56 -0700
committerOle Trøan <otroan@employees.org>2017-10-09 10:53:40 +0000
commit87da476db0cd804e11463cc453a2bb41c6808542 (patch)
tree19bf8317d9d20dd53df96bdb3593b7c905dfcdd5
parentdeabc7f731410122c2efb873e8da3c9f68270033 (diff)
NAT: hairpinning rework (VPP-1003)
Change-Id: I7c6911cd6ac366fe62675fd0ff8b0246a25ea1db Signed-off-by: Matus Fabian <matfabia@cisco.com>
-rwxr-xr-xsrc/plugins/nat/in2out.c114
-rw-r--r--src/plugins/nat/nat.c14
-rw-r--r--src/vnet/buffer.h3
-rwxr-xr-xsrc/vnet/ip/ip4_forward.c10
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;