summaryrefslogtreecommitdiffstats
path: root/src/plugins/snat
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2017-07-21 03:46:03 -0700
committerMatus Fabian <matfabia@cisco.com>2017-07-21 03:46:47 -0700
commit161c59c75c667ce7a3c1d6173723831dc30e994c (patch)
tree8e7ed4f26a2e9120fde75401f3d738b546a037fe /src/plugins/snat
parentfaee17e8b866fe16ce706af31d1e9cbc6d06b961 (diff)
SNAT: in2out translation as an output feature hairpinning (VPP-913)
Change-Id: I3790739683c6090ffb2aefb4758bd4275856c09a Signed-off-by: Matus Fabian <matfabia@cisco.com>
Diffstat (limited to 'src/plugins/snat')
-rw-r--r--src/plugins/snat/in2out.c466
-rw-r--r--src/plugins/snat/out2in.c7
-rw-r--r--src/plugins/snat/snat.c21
-rw-r--r--src/plugins/snat/snat.h4
4 files changed, 396 insertions, 102 deletions
diff --git a/src/plugins/snat/in2out.c b/src/plugins/snat/in2out.c
index 9ee2ebd47c6..eb5b9da5dc7 100644
--- a/src/plugins/snat/in2out.c
+++ b/src/plugins/snat/in2out.c
@@ -91,6 +91,9 @@ vlib_node_registration_t snat_det_in2out_node;
vlib_node_registration_t snat_in2out_output_node;
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;
+
#define foreach_snat_in2out_error \
_(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \
@@ -121,6 +124,14 @@ typedef enum {
SNAT_IN2OUT_N_NEXT,
} snat_in2out_next_t;
+typedef enum {
+ SNAT_HAIRPIN_SRC_NEXT_DROP,
+ SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT,
+ SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH,
+ SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT,
+ SNAT_HAIRPIN_SRC_N_NEXT,
+} snat_hairpin_next_t;
+
/**
* @brief Check if packet should be translated
*
@@ -903,6 +914,80 @@ snat_hairpinning (snat_main_t *sm,
}
}
+static inline void
+snat_icmp_hairpinning (snat_main_t *sm,
+ vlib_buffer_t * b0,
+ ip4_header_t * ip0,
+ icmp46_header_t * icmp0)
+{
+ snat_session_key_t key0, sm0;
+ clib_bihash_kv_8_8_t kv0, value0;
+ snat_worker_key_t k0;
+ u32 new_dst_addr0 = 0, old_dst_addr0, si, ti = 0;
+ ip_csum_t sum0;
+ snat_session_t *s0;
+
+ if (!icmp_is_error_message (icmp0))
+ {
+ icmp_echo_header_t *echo0 = (icmp_echo_header_t *)(icmp0+1);
+ u16 icmp_id0 = echo0->identifier;
+ key0.addr = ip0->dst_address;
+ key0.port = icmp_id0;
+ key0.protocol = SNAT_PROTOCOL_ICMP;
+ key0.fib_index = sm->outside_fib_index;
+ kv0.key = key0.as_u64;
+
+ /* Check if destination is in active sessions */
+ if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
+ {
+ /* or static mappings */
+ if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0))
+ {
+ new_dst_addr0 = sm0.addr.as_u32;
+ vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
+ }
+ }
+ else
+ {
+ si = value0.value;
+ if (sm->num_workers > 1)
+ {
+ k0.addr = ip0->dst_address;
+ k0.port = icmp_id0;
+ k0.fib_index = sm->outside_fib_index;
+ kv0.key = k0.as_u64;
+ if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0))
+ ASSERT(0);
+ else
+ ti = value0.value;
+ }
+ else
+ ti = sm->num_workers;
+
+ s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
+ new_dst_addr0 = s0->in2out.addr.as_u32;
+ vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
+ echo0->identifier = s0->in2out.port;
+ sum0 = icmp0->checksum;
+ sum0 = ip_csum_update (sum0, icmp_id0, s0->in2out.port,
+ icmp_echo_header_t, identifier);
+ icmp0->checksum = ip_csum_fold (sum0);
+ }
+
+ /* Destination is behind the same NAT, use internal address and port */
+ if (new_dst_addr0)
+ {
+ old_dst_addr0 = ip0->dst_address.as_u32;
+ ip0->dst_address.as_u32 = new_dst_addr0;
+ sum0 = ip0->checksum;
+ sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
+ ip4_header_t, dst_address);
+ ip0->checksum = ip_csum_fold (sum0);
+ }
+ }
+
+}
+
static inline u32 icmp_in2out_slow_path (snat_main_t *sm,
vlib_buffer_t * b0,
ip4_header_t * ip0,
@@ -915,77 +1000,14 @@ static inline u32 icmp_in2out_slow_path (snat_main_t *sm,
u32 thread_index,
snat_session_t ** p_s0)
{
- snat_session_key_t key0, sm0;
- clib_bihash_kv_8_8_t kv0, value0;
- snat_worker_key_t k0;
- u32 new_dst_addr0 = 0, old_dst_addr0, si, ti = 0;
- ip_csum_t sum0;
-
next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
next0, thread_index, p_s0, 0);
snat_session_t * s0 = *p_s0;
if (PREDICT_TRUE(next0 != SNAT_IN2OUT_NEXT_DROP && s0))
{
/* Hairpinning */
- if (!icmp_is_error_message (icmp0))
- {
- icmp_echo_header_t *echo0 = (icmp_echo_header_t *)(icmp0+1);
- u16 icmp_id0 = echo0->identifier;
- key0.addr = ip0->dst_address;
- key0.port = icmp_id0;
- key0.protocol = SNAT_PROTOCOL_ICMP;
- key0.fib_index = sm->outside_fib_index;
- kv0.key = key0.as_u64;
-
- /* Check if destination is in active sessions */
- if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
- {
- /* or static mappings */
- if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0))
- {
- new_dst_addr0 = sm0.addr.as_u32;
- vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
- }
- }
- else
- {
- si = value0.value;
- if (sm->num_workers > 1)
- {
- k0.addr = ip0->dst_address;
- k0.port = icmp_id0;
- k0.fib_index = sm->outside_fib_index;
- kv0.key = k0.as_u64;
- if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0))
- ASSERT(0);
- else
- ti = value0.value;
- }
- else
- ti = sm->num_workers;
-
- s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
- new_dst_addr0 = s0->in2out.addr.as_u32;
- vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
- echo0->identifier = s0->in2out.port;
- sum0 = icmp0->checksum;
- sum0 = ip_csum_update (sum0, icmp_id0, s0->in2out.port,
- icmp_echo_header_t, identifier);
- icmp0->checksum = ip_csum_fold (sum0);
- }
-
- /* Destination is behind the same NAT, use internal address and port */
- if (new_dst_addr0)
- {
- old_dst_addr0 = ip0->dst_address.as_u32;
- ip0->dst_address.as_u32 = new_dst_addr0;
- sum0 = ip0->checksum;
- sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
- ip4_header_t, dst_address);
- ip0->checksum = ip_csum_fold (sum0);
- }
- }
-
+ if (vnet_buffer(b0)->sw_if_index[VLIB_TX] == 0)
+ snat_icmp_hairpinning(sm, b0, ip0, icmp0);
/* Accounting */
s0->last_heard = now;
s0->total_pkts++;
@@ -1002,6 +1024,69 @@ static inline u32 icmp_in2out_slow_path (snat_main_t *sm,
}
return next0;
}
+static inline void
+snat_hairpinning_unknown_proto (snat_main_t *sm,
+ vlib_buffer_t * b,
+ ip4_header_t * ip)
+{
+ u32 old_addr, new_addr = 0, ti = 0;
+ clib_bihash_kv_8_8_t kv, value;
+ clib_bihash_kv_16_8_t s_kv, s_value;
+ snat_unk_proto_ses_key_t key;
+ snat_session_key_t m_key;
+ snat_worker_key_t w_key;
+ snat_static_mapping_t *m;
+ ip_csum_t sum;
+ snat_session_t *s;
+
+ old_addr = ip->dst_address.as_u32;
+ key.l_addr.as_u32 = ip->dst_address.as_u32;
+ key.r_addr.as_u32 = ip->src_address.as_u32;
+ key.fib_index = sm->outside_fib_index;
+ key.proto = ip->protocol;
+ key.rsvd[0] = key.rsvd[1] = key.rsvd[2] = 0;
+ s_kv.key[0] = key.as_u64[0];
+ s_kv.key[1] = key.as_u64[1];
+ if (clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value))
+ {
+ m_key.addr = ip->dst_address;
+ m_key.fib_index = sm->outside_fib_index;
+ m_key.port = 0;
+ m_key.protocol = 0;
+ kv.key = m_key.as_u64;
+ if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
+ return;
+
+ m = pool_elt_at_index (sm->static_mappings, value.value);
+ if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0)
+ vnet_buffer(b)->sw_if_index[VLIB_TX] = m->fib_index;
+ new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32;
+ }
+ else
+ {
+ if (sm->num_workers > 1)
+ {
+ w_key.addr = ip->dst_address;
+ w_key.port = 0;
+ w_key.fib_index = sm->outside_fib_index;
+ kv.key = w_key.as_u64;
+ if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv, &value))
+ return;
+ else
+ ti = value.value;
+ }
+ else
+ ti = sm->num_workers;
+
+ s = pool_elt_at_index (sm->per_thread_data[ti].sessions, s_value.value);
+ if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0)
+ vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index;
+ new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32;
+ }
+ sum = ip->checksum;
+ sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
+ ip->checksum = ip_csum_fold (sum);
+}
static void
snat_in2out_unknown_proto (snat_main_t *sm,
@@ -1273,37 +1358,11 @@ create_ses:
s->per_user_index);
/* Hairpinning */
- old_addr = ip->dst_address.as_u32;
- key.l_addr.as_u32 = ip->dst_address.as_u32;
- key.r_addr.as_u32 = new_addr;
- key.fib_index = sm->outside_fib_index;
- s_kv.key[0] = key.as_u64[0];
- s_kv.key[1] = key.as_u64[1];
- if (clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value))
- {
- m_key.addr = ip->dst_address;
- m_key.fib_index = sm->outside_fib_index;
- kv.key = m_key.as_u64;
- if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
- {
- if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0)
- vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index;
- return;
- }
+ if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0)
+ snat_hairpinning_unknown_proto(sm, b, ip);
- m = pool_elt_at_index (sm->static_mappings, value.value);
- vnet_buffer(b)->sw_if_index[VLIB_TX] = m->fib_index;
- new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32;
- }
- else
- {
- s = pool_elt_at_index (tsm->sessions, s_value.value);
- vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index;
- new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32;
- }
- sum = ip->checksum;
- sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
- ip->checksum = ip_csum_fold (sum);
+ if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0)
+ vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index;
}
static inline uword
@@ -1499,7 +1558,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
}
/* Hairpinning */
- snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0);
+ if (!is_output_feature)
+ snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0);
/* Accounting */
s0->last_heard = now;
@@ -1649,7 +1709,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
}
/* Hairpinning */
- snat_hairpinning (sm, b1, ip1, udp1, tcp1, proto1);
+ if (!is_output_feature)
+ snat_hairpinning (sm, b1, ip1, udp1, tcp1, proto1);
/* Accounting */
s1->last_heard = now;
@@ -1836,7 +1897,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
}
/* Hairpinning */
- snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0);
+ if (!is_output_feature)
+ snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0);
/* Accounting */
s0->last_heard = now;
@@ -2986,6 +3048,214 @@ VLIB_REGISTER_NODE (snat_in2out_output_worker_handoff_node) = {
VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_output_worker_handoff_node,
snat_in2out_output_worker_handoff_fn);
+static_always_inline int
+is_hairpinning (snat_main_t *sm, ip4_address_t * dst_addr)
+{
+ snat_address_t * ap;
+ clib_bihash_kv_8_8_t kv, value;
+ snat_session_key_t m_key;
+
+ vec_foreach (ap, sm->addresses)
+ {
+ if (ap->addr.as_u32 == dst_addr->as_u32)
+ return 1;
+ }
+
+ m_key.addr.as_u32 = dst_addr->as_u32;
+ m_key.fib_index = sm->outside_fib_index;
+ m_key.port = 0;
+ m_key.protocol = 0;
+ kv.key = m_key.as_u64;
+ if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
+ return 1;
+
+ return 0;
+}
+
+static uword
+snat_hairpin_dst_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;
+
+ 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;
+
+ /* 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 = SNAT_IN2OUT_NEXT_LOOKUP;
+ ip0 = vlib_buffer_get_current (b0);
+
+ proto0 = ip_proto_to_snat_proto (ip0->protocol);
+
+ vnet_buffer (b0)->snat.flags = 0;
+ if (PREDICT_FALSE (is_hairpinning (sm, &ip0->dst_address)))
+ {
+ if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP)
+ {
+ udp_header_t * udp0 = ip4_next_header (ip0);
+ tcp_header_t * tcp0 = (tcp_header_t *) udp0;
+
+ snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0);
+ }
+ else if (proto0 == SNAT_PROTOCOL_ICMP)
+ {
+ icmp46_header_t * icmp0 = ip4_next_header (ip0);
+
+ snat_icmp_hairpinning (sm, b0, ip0, icmp0);
+ }
+ else
+ {
+ snat_hairpinning_unknown_proto (sm, b0, ip0);
+ }
+
+ vnet_buffer (b0)->snat.flags = SNAT_FLAG_HAIRPINNING;
+ clib_warning("is hairpinning");
+ }
+
+ 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, snat_hairpin_dst_node.index,
+ SNAT_IN2OUT_ERROR_IN2OUT_PACKETS,
+ pkts_processed);
+ return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (snat_hairpin_dst_node) = {
+ .function = snat_hairpin_dst_fn,
+ .name = "snat-hairpin-dst",
+ .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 (snat_hairpin_dst_node,
+ snat_hairpin_dst_fn);
+
+static uword
+snat_hairpin_src_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;
+
+ 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;
+
+ /* 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 = SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT;
+
+ if (PREDICT_FALSE ((vnet_buffer (b0)->snat.flags) & SNAT_FLAG_HAIRPINNING))
+ {
+ if (PREDICT_TRUE (sm->num_workers > 1))
+ next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH;
+ else
+ next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT;
+ }
+
+ 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, snat_hairpin_src_node.index,
+ SNAT_IN2OUT_ERROR_IN2OUT_PACKETS,
+ pkts_processed);
+ return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (snat_hairpin_src_node) = {
+ .function = snat_hairpin_src_fn,
+ .name = "snat-hairpin-src",
+ .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 = SNAT_HAIRPIN_SRC_N_NEXT,
+ .next_nodes = {
+ [SNAT_HAIRPIN_SRC_NEXT_DROP] = "error-drop",
+ [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT] = "snat-in2out-output",
+ [SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT] = "interface-output",
+ [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "snat-in2out-output-worker-handoff",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (snat_hairpin_src_node,
+ snat_hairpin_src_fn);
+
static uword
snat_in2out_fast_static_map_fn (vlib_main_t * vm,
vlib_node_runtime_t * node,
diff --git a/src/plugins/snat/out2in.c b/src/plugins/snat/out2in.c
index cba42465e91..329d67dc3bf 100644
--- a/src/plugins/snat/out2in.c
+++ b/src/plugins/snat/out2in.c
@@ -818,7 +818,10 @@ snat_out2in_node_fn (vlib_main_t * vm,
b0 = vlib_get_buffer (vm, bi0);
b1 = vlib_get_buffer (vm, bi1);
-
+
+ vnet_buffer (b0)->snat.flags = 0;
+ vnet_buffer (b1)->snat.flags = 0;
+
ip0 = vlib_buffer_get_current (b0);
udp0 = ip4_next_header (ip0);
tcp0 = (tcp_header_t *) udp0;
@@ -1131,6 +1134,8 @@ snat_out2in_node_fn (vlib_main_t * vm,
b0 = vlib_get_buffer (vm, bi0);
+ vnet_buffer (b0)->snat.flags = 0;
+
ip0 = vlib_buffer_get_current (b0);
udp0 = ip4_next_header (ip0);
tcp0 = (tcp_header_t *) udp0;
diff --git a/src/plugins/snat/snat.c b/src/plugins/snat/snat.c
index bce7d210767..1820aef8829 100644
--- a/src/plugins/snat/snat.c
+++ b/src/plugins/snat/snat.c
@@ -72,6 +72,11 @@ VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = {
.node_name = "snat-out2in-fast",
.runs_before = VNET_FEATURES ("ip4-lookup"),
};
+VNET_FEATURE_INIT (ip4_snat_hairpin_dst, static) = {
+ .arc_name = "ip4-unicast",
+ .node_name = "snat-hairpin-dst",
+ .runs_before = VNET_FEATURES ("ip4-lookup"),
+};
/* Hook up output features */
VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = {
@@ -79,12 +84,16 @@ VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = {
.node_name = "snat-in2out-output",
.runs_before = VNET_FEATURES ("interface-output"),
};
-
VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = {
.arc_name = "ip4-output",
.node_name = "snat-in2out-output-worker-handoff",
.runs_before = VNET_FEATURES ("interface-output"),
};
+VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = {
+ .arc_name = "ip4-output",
+ .node_name = "snat-hairpin-src",
+ .runs_before = VNET_FEATURES ("interface-output"),
+};
/* *INDENT-OFF* */
@@ -798,7 +807,13 @@ int snat_interface_add_del_output_feature (u32 sw_if_index,
return VNET_API_ERROR_UNSUPPORTED;
if (is_inside)
- goto find;
+ {
+ vnet_feature_enable_disable ("ip4-unicast", "snat-hairpin-dst",
+ sw_if_index, !is_del, 0, 0);
+ vnet_feature_enable_disable ("ip4-output", "snat-hairpin-src",
+ sw_if_index, !is_del, 0, 0);
+ goto fq;
+ }
if (sm->num_workers > 1)
{
@@ -816,6 +831,7 @@ int snat_interface_add_del_output_feature (u32 sw_if_index,
sw_if_index, !is_del, 0, 0);
}
+fq:
if (sm->fq_in2out_output_index == ~0 && sm->num_workers > 1)
sm->fq_in2out_output_index =
vlib_frame_queue_main_init (sm->in2out_output_node_index, 0);
@@ -823,7 +839,6 @@ int snat_interface_add_del_output_feature (u32 sw_if_index,
if (sm->fq_out2in_index == ~0 && sm->num_workers > 1)
sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0);
-find:
pool_foreach (i, sm->output_feature_interfaces,
({
if (i->sw_if_index == sw_if_index)
diff --git a/src/plugins/snat/snat.h b/src/plugins/snat/snat.h
index 2546815b5f4..61ed52f61e2 100644
--- a/src/plugins/snat/snat.h
+++ b/src/plugins/snat/snat.h
@@ -37,6 +37,8 @@
#define SNAT_TCP_INCOMING_SYN 6
#define SNAT_ICMP_TIMEOUT 60
+#define SNAT_FLAG_HAIRPINNING (1 << 0)
+
/* Key */
typedef struct {
union
@@ -369,6 +371,8 @@ extern vlib_node_registration_t snat_in2out_output_worker_handoff_node;
extern vlib_node_registration_t snat_out2in_worker_handoff_node;
extern vlib_node_registration_t snat_det_in2out_node;
extern vlib_node_registration_t snat_det_out2in_node;
+extern vlib_node_registration_t snat_hairpin_dst_node;
+extern vlib_node_registration_t snat_hairpin_src_node;
void snat_free_outside_address_and_port (snat_main_t * sm,
snat_session_key_t * k,
ss="k">def __ne__(self, other): return not self.__eq__(other) def __repr__(self): return "Host { mac:%s ip4:%s ip6:%s ip6_ll:%s }" % (self.mac, self.ip4, self.ip6, self.ip6_ll) def __hash__(self): return hash(self.__repr__()) def __init__(self, mac=None, ip4=None, ip6=None, ip6_ll=None): self._mac = mac self._ip4 = ip4 self._ip6 = ip6 self._ip6_ll = ip6_ll class ForeignAddressFactory(object): count = 0 prefix_len = 24 net_template = '10.10.10.{}' net = net_template.format(0) + '/' + str(prefix_len) def get_ip4(self): if self.count > 255: raise Exception("Network host address exhaustion") self.count += 1 return self.net_template.format(self.count) class L4_Conn(): """ L4 'connection' tied to two VPP interfaces """ def __init__(self, testcase, if1, if2, af, l4proto, port1, port2): self.testcase = testcase self.ifs = [None, None] self.ifs[0] = if1 self.ifs[1] = if2 self.address_family = af self.l4proto = l4proto self.ports = [None, None] self.ports[0] = port1 self.ports[1] = port2 self def pkt(self, side, l4args={}, payload="x"): is_ip6 = 1 if self.address_family == AF_INET6 else 0 s0 = side s1 = 1 - side src_if = self.ifs[s0] dst_if = self.ifs[s1] layer_3 = [IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4), IPv6(src=src_if.remote_ip6, dst=dst_if.remote_ip6)] merged_l4args = {'sport': self.ports[s0], 'dport': self.ports[s1]} merged_l4args.update(l4args) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / layer_3[is_ip6] / self.l4proto(**merged_l4args) / Raw(payload)) return p def send(self, side, flags=None, payload=""): l4args = {} if flags is not None: l4args['flags'] = flags self.ifs[side].add_stream(self.pkt(side, l4args=l4args, payload=payload)) self.ifs[1 - side].enable_capture() self.testcase.pg_start() def recv(self, side): p = self.ifs[side].wait_for_packet(1) return p def send_through(self, side, flags=None, payload=""): self.send(side, flags, payload) p = self.recv(1 - side) return p def send_pingpong(self, side, flags1=None, flags2=None): p1 = self.send_through(side, flags1) p2 = self.send_through(1 - side, flags2) return [p1, p2] class L4_CONN_SIDE: L4_CONN_SIDE_ZERO = 0 L4_CONN_SIDE_ONE = 1 class LoggerWrapper(object): def __init__(self, logger=None): self._logger = logger def debug(self, *args, **kwargs): if self._logger: self._logger.debug(*args, **kwargs) def error(self, *args, **kwargs): if self._logger: self._logger.error(*args, **kwargs) def fragment_rfc791(packet, fragsize, _logger=None): """ Fragment an IPv4 packet per RFC 791 :param packet: packet to fragment :param fragsize: size at which to fragment :note: IP options are not supported :returns: list of fragments """ logger = LoggerWrapper(_logger) logger.debug(ppp("Fragmenting packet:", packet)) packet = packet.__class__(str(packet)) # recalculate all values if len(packet[IP].options) > 0: raise Exception("Not implemented") if len(packet) <= fragsize: return [packet] pre_ip_len = len(packet) - len(packet[IP]) ip_header_len = packet[IP].ihl * 4 hex_packet = str(packet) hex_headers = hex_packet[:(pre_ip_len + ip_header_len)] hex_payload = hex_packet[(pre_ip_len + ip_header_len):] pkts = [] ihl = packet[IP].ihl otl = len(packet[IP]) nfb = (fragsize - pre_ip_len - ihl * 4) / 8 fo = packet[IP].frag p = packet.__class__(hex_headers + hex_payload[:nfb * 8]) p[IP].flags = "MF" p[IP].frag = fo p[IP].len = ihl * 4 + nfb * 8 del p[IP].chksum pkts.append(p) p = packet.__class__(hex_headers + hex_payload[nfb * 8:]) p[IP].len = otl - nfb * 8 p[IP].frag = fo + nfb del p[IP].chksum more_fragments = fragment_rfc791(p, fragsize, _logger) pkts.extend(more_fragments) return pkts def fragment_rfc8200(packet, identification, fragsize, _logger=None): """ Fragment an IPv6 packet per RFC 8200 :param packet: packet to fragment :param fragsize: size at which to fragment :note: IP options are not supported :returns: list of fragments """ logger = LoggerWrapper(_logger) packet = packet.__class__(str(packet)) # recalculate all values if len(packet) <= fragsize: return [packet] logger.debug(ppp("Fragmenting packet:", packet)) pkts = [] counter = 0 routing_hdr = None hop_by_hop_hdr = None upper_layer = None seen_ipv6 = False ipv6_nr = -1 l = packet.getlayer(counter) while l is not None: if l.__class__ is IPv6: if seen_ipv6: # ignore 2nd IPv6 header and everything below.. break ipv6_nr = counter seen_ipv6 = True elif l.__class__ is IPv6ExtHdrFragment: raise Exception("Already fragmented") elif l.__class__ is IPv6ExtHdrRouting: routing_hdr = counter elif l.__class__ is IPv6ExtHdrHopByHop: hop_by_hop_hdr = counter elif seen_ipv6 and not upper_layer and \ not l.__class__.__name__.startswith('IPv6ExtHdr'): upper_layer = counter counter = counter + 1 l = packet.getlayer(counter) logger.debug( "Layers seen: IPv6(#%s), Routing(#%s), HopByHop(#%s), upper(#%s)" % (ipv6_nr, routing_hdr, hop_by_hop_hdr, upper_layer)) if upper_layer is None: raise Exception("Upper layer header not found in IPv6 packet") last_per_fragment_hdr = ipv6_nr if routing_hdr is None: if hop_by_hop_hdr is not None: last_per_fragment_hdr = hop_by_hop_hdr else: last_per_fragment_hdr = routing_hdr logger.debug("Last per-fragment hdr is #%s" % (last_per_fragment_hdr)) per_fragment_headers = packet.copy() per_fragment_headers[last_per_fragment_hdr].remove_payload() logger.debug(ppp("Per-fragment headers:", per_fragment_headers)) ext_and_upper_layer = packet.getlayer(last_per_fragment_hdr)[1] hex_payload = str(ext_and_upper_layer) logger.debug("Payload length is %s" % len(hex_payload)) logger.debug(ppp("Ext and upper layer:", ext_and_upper_layer)) fragment_ext_hdr = IPv6ExtHdrFragment() logger.debug(ppp("Fragment header:", fragment_ext_hdr)) if len(per_fragment_headers) + len(fragment_ext_hdr) +\ len(ext_and_upper_layer) - len(ext_and_upper_layer.payload)\ > fragsize: raise Exception("Cannot fragment this packet - MTU too small " "(%s, %s, %s, %s, %s)" % ( len(per_fragment_headers), len(fragment_ext_hdr), len(ext_and_upper_layer), len(ext_and_upper_layer.payload), fragsize)) orig_nh = packet[IPv6].nh p = per_fragment_headers del p[IPv6].plen del p[IPv6].nh p = p / fragment_ext_hdr del p[IPv6ExtHdrFragment].nh first_payload_len_nfb = (fragsize - len(p)) / 8 p = p / Raw(hex_payload[:first_payload_len_nfb * 8]) del p[IPv6].plen p[IPv6ExtHdrFragment].nh = orig_nh p[IPv6ExtHdrFragment].id = identification p[IPv6ExtHdrFragment].offset = 0 p[IPv6ExtHdrFragment].m = 1 p = p.__class__(str(p)) logger.debug(ppp("Fragment %s:" % len(pkts), p)) pkts.append(p) offset = first_payload_len_nfb * 8 logger.debug("Offset after first fragment: %s" % offset) while len(hex_payload) > offset: p = per_fragment_headers del p[IPv6].plen del p[IPv6].nh p = p / fragment_ext_hdr del p[IPv6ExtHdrFragment].nh l_nfb = (fragsize - len(p)) / 8 p = p / Raw(hex_payload[offset:offset + l_nfb * 8]) p[IPv6ExtHdrFragment].nh = orig_nh p[IPv6ExtHdrFragment].id = identification p[IPv6ExtHdrFragment].offset = offset / 8 p[IPv6ExtHdrFragment].m = 1 p = p.__class__(str(p)) logger.debug(ppp("Fragment %s:" % len(pkts), p)) pkts.append(p) offset = offset + l_nfb * 8 pkts[-1][IPv6ExtHdrFragment].m = 0 # reset more-flags in last fragment return pkts def reassemble4_core(listoffragments, return_ip): buffer = BytesIO() first = listoffragments[0] buffer.seek(20) for pkt in listoffragments: buffer.seek(pkt[IP].frag*8) buffer.write(bytes(pkt[IP].payload)) first.len = len(buffer.getvalue()) + 20 first.flags = 0 del(first.chksum) if return_ip: header = bytes(first[IP])[:20] return first[IP].__class__(header + buffer.getvalue()) else: header = bytes(first[Ether])[:34] return first[Ether].__class__(header + buffer.getvalue()) def reassemble4_ether(listoffragments): return reassemble4_core(listoffragments, False) def reassemble4(listoffragments): return reassemble4_core(listoffragments, True)