summaryrefslogtreecommitdiffstats
path: root/plugins/snat-plugin/snat
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2016-08-26 05:45:27 -0700
committerOle Trøan <otroan@employees.org>2016-10-05 11:41:51 +0000
commitdb6498848126839010eaad0bc79571a9ccbfc1bc (patch)
treeed1ad922173bc6187bc7da77b8596f7164272ed2 /plugins/snat-plugin/snat
parentb8602b595fc89fe8b8fb00cef4820eedf225a93e (diff)
VPP-339: SNAT static mapping
1:1 NAT 1:1 NAT with ports 1:1 NAT static mapping only - add "static mapping only [connection tracking]" to snat startup config Change-Id: I37cd06a9d71d1943eb6618034d7c547c4a9348c4 Signed-off-by: Matus Fabian <matfabia@cisco.com>
Diffstat (limited to 'plugins/snat-plugin/snat')
-rw-r--r--plugins/snat-plugin/snat/in2out.c422
-rw-r--r--plugins/snat-plugin/snat/out2in.c510
-rw-r--r--plugins/snat-plugin/snat/snat.api173
-rw-r--r--plugins/snat-plugin/snat/snat.c808
-rw-r--r--plugins/snat-plugin/snat/snat.h56
-rw-r--r--plugins/snat-plugin/snat/snat_test.c203
6 files changed, 2017 insertions, 155 deletions
diff --git a/plugins/snat-plugin/snat/in2out.c b/plugins/snat-plugin/snat/in2out.c
index 3f7df910fe5..25dad934cad 100644
--- a/plugins/snat-plugin/snat/in2out.c
+++ b/plugins/snat-plugin/snat/in2out.c
@@ -25,8 +25,6 @@
#include <vppinfra/error.h>
#include <vppinfra/elog.h>
-vlib_node_registration_t snat_in2out_node, snat_in2out_slowpath_node;
-
typedef struct {
u32 sw_if_index;
u32 next_index;
@@ -50,14 +48,29 @@ static u8 * format_snat_in2out_trace (u8 * s, va_list * args)
return s;
}
+static u8 * format_snat_in2out_fast_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 *);
+ snat_in2out_trace_t * t = va_arg (*args, snat_in2out_trace_t *);
+
+ s = format (s, "SANT_IN2OUT_FAST: sw_if_index %d, next index %d",
+ t->sw_if_index, t->next_index);
+
+ return s;
+}
+
vlib_node_registration_t snat_in2out_node;
+vlib_node_registration_t snat_in2out_slowpath_node;
+vlib_node_registration_t snat_in2out_fast_node;
#define foreach_snat_in2out_error \
_(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \
_(IN2OUT_PACKETS, "Good in2out packets processed") \
_(OUT_OF_PORTS, "Out of ports") \
_(BAD_OUTSIDE_FIB, "Outside VRF ID not found") \
-_(BAD_ICMP_TYPE, "icmp type not echo-request")
+_(BAD_ICMP_TYPE, "icmp type not echo-request") \
+_(NO_TRANSLATION, "No translation")
typedef enum {
#define _(sym,str) SNAT_IN2OUT_ERROR_##sym,
@@ -98,7 +111,7 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
dlist_elt_t * per_user_list_head_elt;
u32 session_index;
snat_session_key_t key1;
- u32 address_index;
+ u32 address_index = ~0;
u32 outside_fib_index;
uword * p;
@@ -128,9 +141,9 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
sm->list_pool;
clib_dlist_init (sm->list_pool, u->sessions_per_user_list_head_index);
-
+
kv0.value = u - sm->users;
-
+
/* add user */
clib_bihash_add_del_8_8 (&sm->user_hash, &kv0, 1 /* is_add */);
}
@@ -139,30 +152,32 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
u = pool_elt_at_index (sm->users, value0.value);
}
- /* Over quota? Recycle the least recently used translation */
+ /* Over quota? Recycle the least recently used dynamic translation */
if (u->nsessions >= sm->max_translations_per_user)
{
- /* Remove the oldest translation */
- oldest_per_user_translation_list_index =
- clib_dlist_remove_head
- (sm->list_pool, u->sessions_per_user_list_head_index);
-
- ASSERT (oldest_per_user_translation_list_index != ~0);
-
- /* add it back to the end of the LRU list */
- clib_dlist_addtail (sm->list_pool, u->sessions_per_user_list_head_index,
- oldest_per_user_translation_list_index);
-
- /* Get the list element */
- oldest_per_user_translation_list_elt =
- pool_elt_at_index (sm->list_pool,
- oldest_per_user_translation_list_index);
-
- /* Get the session index from the list element */
- session_index = oldest_per_user_translation_list_elt->value;
-
- /* Get the session */
- s = pool_elt_at_index (sm->sessions, session_index);
+ /* Remove the oldest dynamic translation */
+ do {
+ oldest_per_user_translation_list_index =
+ clib_dlist_remove_head
+ (sm->list_pool, u->sessions_per_user_list_head_index);
+
+ ASSERT (oldest_per_user_translation_list_index != ~0);
+
+ /* add it back to the end of the LRU list */
+ clib_dlist_addtail (sm->list_pool,
+ u->sessions_per_user_list_head_index,
+ oldest_per_user_translation_list_index);
+ /* Get the list element */
+ oldest_per_user_translation_list_elt =
+ pool_elt_at_index (sm->list_pool,
+ oldest_per_user_translation_list_index);
+
+ /* Get the session index from the list element */
+ session_index = oldest_per_user_translation_list_elt->value;
+
+ /* Get the session */
+ s = pool_elt_at_index (sm->sessions, session_index);
+ } while (!snat_is_session_static (s));
/* Remove in2out, out2in keys */
kv0.key = s->in2out.as_u64;
@@ -187,12 +202,18 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
}
else
{
- if (snat_alloc_outside_address_and_port (sm, &key1, &address_index))
- {
- ASSERT(0);
+ u8 static_mapping = 1;
- b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
- return SNAT_IN2OUT_NEXT_DROP;
+ /* First try to match static mapping by local address and port */
+ if (snat_static_mapping_match (sm, *key0, &key1, 0))
+ {
+ static_mapping = 0;
+ /* Try to create dynamic translation */
+ if (snat_alloc_outside_address_and_port (sm, &key1, &address_index))
+ {
+ b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
+ return SNAT_IN2OUT_NEXT_DROP;
+ }
}
/* Create a new session */
@@ -201,19 +222,28 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
s->outside_address_index = address_index;
+ if (static_mapping)
+ {
+ u->nstaticsessions++;
+ s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
+ }
+ else
+ {
+ u->nsessions++;
+ }
+
/* Create list elts */
pool_get (sm->list_pool, per_user_translation_list_elt);
clib_dlist_init (sm->list_pool, per_user_translation_list_elt -
sm->list_pool);
-
+
per_user_translation_list_elt->value = s - sm->sessions;
s->per_user_index = per_user_translation_list_elt - sm->list_pool;
s->per_user_list_head_index = u->sessions_per_user_list_head_index;
-
+
clib_dlist_addtail (sm->list_pool, s->per_user_list_head_index,
per_user_translation_list_elt - sm->list_pool);
- u->nsessions++;
- }
+ }
s->in2out = *key0;
s->out2in = key1;
@@ -317,13 +347,17 @@ static inline u32 icmp_in2out_slow_path (snat_main_t *sm,
identifier);
icmp0->checksum = ip_csum_fold (sum0);
- /* Accounting, per-user LRU list maintenance */
+ /* Accounting */
s0->last_heard = now;
s0->total_pkts++;
s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0);
- clib_dlist_remove (sm->list_pool, s0->per_user_index);
- clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
- s0->per_user_index);
+ /* Per-user LRU list maintenance for dynamic translations */
+ if (!snat_is_session_static (s0))
+ {
+ clib_dlist_remove (sm->list_pool, s0->per_user_index);
+ clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+ s0->per_user_index);
+ }
return next0;
}
@@ -523,13 +557,17 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
udp0->checksum = 0;
}
- /* Accounting, per-user LRU list maintenance */
+ /* Accounting */
s0->last_heard = now;
s0->total_pkts++;
s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
- clib_dlist_remove (sm->list_pool, s0->per_user_index);
- clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
- s0->per_user_index);
+ /* Per-user LRU list maintenance for dynamic translation */
+ if (!snat_is_session_static (s0))
+ {
+ clib_dlist_remove (sm->list_pool, s0->per_user_index);
+ clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+ s0->per_user_index);
+ }
trace00:
if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -668,13 +706,17 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
udp1->checksum = 0;
}
- /* Accounting, per-user LRU list maintenance */
+ /* Accounting */
s1->last_heard = now;
s1->total_pkts++;
s1->total_bytes += vlib_buffer_length_in_chain (vm, b1);
- clib_dlist_remove (sm->list_pool, s1->per_user_index);
- clib_dlist_addtail (sm->list_pool, s1->per_user_list_head_index,
- s1->per_user_index);
+ /* Per-user LRU list maintenance for dynamic translation */
+ if (!snat_is_session_static (s1))
+ {
+ clib_dlist_remove (sm->list_pool, s1->per_user_index);
+ clib_dlist_addtail (sm->list_pool, s1->per_user_list_head_index,
+ s1->per_user_index);
+ }
trace01:
if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -849,13 +891,17 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
udp0->checksum = 0;
}
- /* Accounting, per-user LRU list maintenance */
+ /* Accounting */
s0->last_heard = now;
s0->total_pkts++;
s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
- clib_dlist_remove (sm->list_pool, s0->per_user_index);
- clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
- s0->per_user_index);
+ /* Per-user LRU list maintenance for dynamic translation */
+ if (!snat_is_session_static (s0))
+ {
+ clib_dlist_remove (sm->list_pool, s0->per_user_index);
+ clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+ s0->per_user_index);
+ }
trace0:
if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -951,3 +997,273 @@ VLIB_REGISTER_NODE (snat_in2out_slowpath_node) = {
};
VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_slowpath_node, snat_in2out_slow_path_fn);
+
+static inline u32 icmp_in2out_static_map (snat_main_t *sm,
+ vlib_buffer_t * b0,
+ ip4_header_t * ip0,
+ icmp46_header_t * icmp0,
+ u32 sw_if_index0,
+ vlib_node_runtime_t * node,
+ u32 next0)
+{
+ snat_session_key_t key0, sm0;
+ icmp_echo_header_t *echo0;
+ u32 new_addr0, old_addr0;
+ u16 old_id0, new_id0;
+ ip_csum_t sum0;
+ snat_runtime_t * rt = (snat_runtime_t *)node->runtime_data;
+
+ echo0 = (icmp_echo_header_t *)(icmp0+1);
+
+ key0.addr = ip0->src_address;
+ key0.port = echo0->identifier;
+
+ if (snat_static_mapping_match(sm, key0, &sm0, 0))
+ {
+ ip4_address_t * first_int_addr;
+
+ if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0))
+ {
+ first_int_addr =
+ ip4_interface_first_address (sm->ip4_main, sw_if_index0,
+ 0 /* just want the address */);
+ rt->cached_sw_if_index = sw_if_index0;
+ rt->cached_ip4_address = first_int_addr->as_u32;
+ }
+
+ /* Don't NAT packet aimed at the intfc address */
+ if (PREDICT_FALSE(ip0->dst_address.as_u32 ==
+ rt->cached_ip4_address))
+ return next0;
+
+ b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION];
+ return SNAT_IN2OUT_NEXT_DROP;
+ }
+
+ new_addr0 = sm0.addr.as_u32;
+ new_id0 = sm0.port;
+ vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
+ old_addr0 = ip0->src_address.as_u32;
+ ip0->src_address.as_u32 = new_addr0;
+
+ sum0 = ip0->checksum;
+ sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+ ip4_header_t,
+ src_address /* changed member */);
+ ip0->checksum = ip_csum_fold (sum0);
+
+ if (PREDICT_FALSE(new_id0 != echo0->identifier))
+ {
+ old_id0 = echo0->identifier;
+ echo0->identifier = new_id0;
+
+ sum0 = icmp0->checksum;
+ sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
+ identifier);
+ icmp0->checksum = ip_csum_fold (sum0);
+ }
+
+ return next0;
+}
+
+static uword
+snat_in2out_fast_static_map_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;
+ snat_runtime_t * rt = (snat_runtime_t *)node->runtime_data;
+ u32 stats_node_index;
+
+ stats_node_index = snat_in2out_fast_node.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;
+ u32 sw_if_index0;
+ ip4_header_t * ip0;
+ ip_csum_t sum0;
+ u32 new_addr0, old_addr0;
+ u16 old_port0, new_port0;
+ udp_header_t * udp0;
+ tcp_header_t * tcp0;
+ icmp46_header_t * icmp0;
+ snat_session_key_t key0, sm0;
+ 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);
+ 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 = ~0;
+ proto0 = (ip0->protocol == IP_PROTOCOL_UDP)
+ ? SNAT_PROTOCOL_UDP : proto0;
+ proto0 = (ip0->protocol == IP_PROTOCOL_TCP)
+ ? SNAT_PROTOCOL_TCP : proto0;
+ proto0 = (ip0->protocol == IP_PROTOCOL_ICMP)
+ ? SNAT_PROTOCOL_ICMP : proto0;
+
+ if (PREDICT_FALSE (proto0 == ~0))
+ goto trace0;
+
+ if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
+ {
+ ip4_address_t * first_int_addr;
+
+ if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0))
+ {
+ first_int_addr =
+ ip4_interface_first_address (sm->ip4_main, sw_if_index0,
+ 0 /* just want the address */);
+ rt->cached_sw_if_index = sw_if_index0;
+ rt->cached_ip4_address = first_int_addr->as_u32;
+ }
+
+ /* Don't NAT packet aimed at the intfc address */
+ if (PREDICT_FALSE(ip0->dst_address.as_u32 ==
+ rt->cached_ip4_address))
+ goto trace0;
+
+ next0 = icmp_in2out_static_map
+ (sm, b0, ip0, icmp0, sw_if_index0, node, next0);
+ goto trace0;
+ }
+
+ key0.addr = ip0->src_address;
+ key0.port = udp0->src_port;
+
+ if (snat_static_mapping_match(sm, key0, &sm0, 0))
+ {
+ b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION];
+ next0= SNAT_IN2OUT_NEXT_DROP;
+ goto trace0;
+ }
+
+ new_addr0 = sm0.addr.as_u32;
+ new_port0 = sm0.port;
+ vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
+ old_addr0 = ip0->src_address.as_u32;
+ ip0->src_address.as_u32 = new_addr0;
+
+ sum0 = ip0->checksum;
+ sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+ ip4_header_t,
+ src_address /* changed member */);
+ ip0->checksum = ip_csum_fold (sum0);
+
+ if (PREDICT_FALSE(new_port0 != udp0->dst_port))
+ {
+ if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+ {
+ old_port0 = tcp0->ports.src;
+ tcp0->ports.src = new_port0;
+
+ sum0 = tcp0->checksum;
+ sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+ ip4_header_t,
+ dst_address /* changed member */);
+ sum0 = ip_csum_update (sum0, old_port0, new_port0,
+ ip4_header_t /* cheat */,
+ length /* changed member */);
+ tcp0->checksum = ip_csum_fold(sum0);
+ }
+ else
+ {
+ old_port0 = udp0->src_port;
+ udp0->src_port = new_port0;
+ udp0->checksum = 0;
+ }
+ }
+ else
+ {
+ if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+ {
+ sum0 = tcp0->checksum;
+ sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+ ip4_header_t,
+ dst_address /* changed member */);
+ tcp0->checksum = ip_csum_fold(sum0);
+ }
+ }
+
+ trace0:
+ 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;
+ }
+
+ 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, stats_node_index,
+ SNAT_IN2OUT_ERROR_IN2OUT_PACKETS,
+ pkts_processed);
+ return frame->n_vectors;
+}
+
+
+VLIB_REGISTER_NODE (snat_in2out_fast_node) = {
+ .function = snat_in2out_fast_static_map_fn,
+ .name = "snat-in2out-fast",
+ .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 = SNAT_IN2OUT_N_NEXT,
+
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [SNAT_IN2OUT_NEXT_DROP] = "error-drop",
+ [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
+ [SNAT_IN2OUT_NEXT_SLOW_PATH] = "snat-in2out-slowpath",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_fast_node, snat_in2out_fast_static_map_fn);
diff --git a/plugins/snat-plugin/snat/out2in.c b/plugins/snat-plugin/snat/out2in.c
index 861dae63b0a..b3497c40d31 100644
--- a/plugins/snat-plugin/snat/out2in.c
+++ b/plugins/snat-plugin/snat/out2in.c
@@ -25,8 +25,6 @@
#include <vppinfra/error.h>
#include <vppinfra/elog.h>
-vlib_node_registration_t snat_out2in_node;
-
typedef struct {
u32 sw_if_index;
u32 next_index;
@@ -45,7 +43,20 @@ static u8 * format_snat_out2in_trace (u8 * s, va_list * args)
return s;
}
+static u8 * format_snat_out2in_fast_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 *);
+ snat_out2in_trace_t * t = va_arg (*args, snat_out2in_trace_t *);
+
+ s = format (s, "SNAT_OUT2IN_FAST: sw_if_index %d, next index %d",
+ t->sw_if_index, t->next_index);
+ return s;
+}
+
+
vlib_node_registration_t snat_out2in_node;
+vlib_node_registration_t snat_out2in_fast_node;
#define foreach_snat_out2in_error \
_(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \
@@ -71,6 +82,101 @@ typedef enum {
SNAT_OUT2IN_N_NEXT,
} snat_out2in_next_t;
+/**
+ * @brief Create session for static mapping.
+ *
+ * Create NAT session initiated by host from external network with static
+ * mapping.
+ *
+ * @param sm SNAT main.
+ * @param b0 Vlib buffer.
+ * @param in2out In2out SNAT session key.
+ * @param out2in Out2in SNAT session key.
+ * @param node Vlib node.
+ *
+ * @returns SNAT session if successfully created otherwise 0.
+ */
+static inline snat_session_t *
+create_session_for_static_mapping (snat_main_t *sm,
+ vlib_buffer_t *b0,
+ snat_session_key_t in2out,
+ snat_session_key_t out2in,
+ vlib_node_runtime_t * node)
+{
+ snat_user_t *u;
+ snat_user_key_t user_key;
+ snat_session_t *s;
+ clib_bihash_kv_8_8_t kv0, value0;
+ dlist_elt_t * per_user_translation_list_elt;
+ dlist_elt_t * per_user_list_head_elt;
+
+ user_key.addr = in2out.addr;
+ user_key.fib_index = in2out.fib_index;
+ kv0.key = user_key.as_u64;
+
+ /* Ever heard of the "user" = inside ip4 address before? */
+ if (clib_bihash_search_8_8 (&sm->user_hash, &kv0, &value0))
+ {
+ /* no, make a new one */
+ pool_get (sm->users, u);
+ memset (u, 0, sizeof (*u));
+ u->addr = in2out.addr;
+
+ pool_get (sm->list_pool, per_user_list_head_elt);
+
+ u->sessions_per_user_list_head_index = per_user_list_head_elt -
+ sm->list_pool;
+
+ clib_dlist_init (sm->list_pool, u->sessions_per_user_list_head_index);
+
+ kv0.value = u - sm->users;
+
+ /* add user */
+ clib_bihash_add_del_8_8 (&sm->user_hash, &kv0, 1 /* is_add */);
+ }
+ else
+ {
+ u = pool_elt_at_index (sm->users, value0.value);
+ }
+
+ pool_get (sm->sessions, s);
+ memset (s, 0, sizeof (*s));
+
+ s->outside_address_index = ~0;
+ s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
+ u->nstaticsessions++;
+
+ /* Create list elts */
+ pool_get (sm->list_pool, per_user_translation_list_elt);
+ clib_dlist_init (sm->list_pool, per_user_translation_list_elt -
+ sm->list_pool);
+
+ per_user_translation_list_elt->value = s - sm->sessions;
+ s->per_user_index = per_user_translation_list_elt - sm->list_pool;
+ s->per_user_list_head_index = u->sessions_per_user_list_head_index;
+
+ clib_dlist_addtail (sm->list_pool, s->per_user_list_head_index,
+ per_user_translation_list_elt - sm->list_pool);
+
+ s->in2out = in2out;
+ s->out2in = out2in;
+ s->in2out.protocol = out2in.protocol;
+
+ /* Add to translation hashes */
+ kv0.key = s->in2out.as_u64;
+ kv0.value = s - sm->sessions;
+ if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 1 /* is_add */))
+ clib_warning ("in2out key add failed");
+
+ kv0.key = s->out2in.as_u64;
+ kv0.value = s - sm->sessions;
+
+ if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 1 /* is_add */))
+ clib_warning ("out2in key add failed");
+
+ return s;
+}
+
static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
vlib_buffer_t * b0,
ip4_header_t * ip0,
@@ -80,7 +186,7 @@ static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
vlib_node_runtime_t * node,
u32 next0, f64 now)
{
- snat_session_key_t key0;
+ snat_session_key_t key0, sm0;
icmp_echo_header_t *echo0;
clib_bihash_kv_8_8_t kv0, value0;
snat_session_t * s0;
@@ -100,24 +206,35 @@ static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
{
- ip4_address_t * first_int_addr;
-
- if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0))
+ /* Try to match static mapping by external address and port,
+ destination address and port in packet */
+ if (snat_static_mapping_match(sm, key0, &sm0, 1))
{
- first_int_addr =
- ip4_interface_first_address (sm->ip4_main, sw_if_index0,
- 0 /* just want the address */);
- rt->cached_sw_if_index = sw_if_index0;
- rt->cached_ip4_address = first_int_addr->as_u32;
+ ip4_address_t * first_int_addr;
+
+ if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0))
+ {
+ first_int_addr =
+ ip4_interface_first_address (sm->ip4_main, sw_if_index0,
+ 0 /* just want the address */);
+ rt->cached_sw_if_index = sw_if_index0;
+ rt->cached_ip4_address = first_int_addr->as_u32;
+ }
+
+ /* Don't NAT packet aimed at the intfc address */
+ if (PREDICT_FALSE(ip0->dst_address.as_u32 ==
+ rt->cached_ip4_address))
+ return next0;
+
+ b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ return SNAT_OUT2IN_NEXT_DROP;
}
-
- /* Don't NAT packet aimed at the intfc address */
- if (PREDICT_FALSE(ip0->dst_address.as_u32 ==
- rt->cached_ip4_address))
- return next0;
- b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
- return SNAT_OUT2IN_NEXT_DROP;
+ /* Create session initiated by host from external network */
+ s0 = create_session_for_static_mapping(sm, b0, sm0, key0,
+ node);
+ if (!s0)
+ return SNAT_OUT2IN_NEXT_DROP;
}
else
s0 = pool_elt_at_index (sm->sessions, value0.value);
@@ -142,13 +259,17 @@ static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
identifier);
icmp0->checksum = ip_csum_fold (sum0);
- /* Accounting, per-user LRU list maintenance */
+ /* Accounting */
s0->last_heard = now;
s0->total_pkts++;
s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0);
- clib_dlist_remove (sm->list_pool, s0->per_user_index);
- clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
- s0->per_user_index);
+ /* Per-user LRU list maintenance for dynamic translation */
+ if (!snat_is_session_static (s0))
+ {
+ clib_dlist_remove (sm->list_pool, s0->per_user_index);
+ clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+ s0->per_user_index);
+ }
return next0;
}
@@ -193,7 +314,7 @@ snat_out2in_node_fn (vlib_main_t * vm,
udp_header_t * udp0, * udp1;
tcp_header_t * tcp0, * tcp1;
icmp46_header_t * icmp0, * icmp1;
- snat_session_key_t key0, key1;
+ snat_session_key_t key0, key1, sm0, sm1;
u32 rx_fib_index0, rx_fib_index1;
u32 proto0, proto1;
snat_session_t * s0 = 0, * s1 = 0;
@@ -264,14 +385,27 @@ snat_out2in_node_fn (vlib_main_t * vm,
kv0.key = key0.as_u64;
if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
- goto trace0;
+ {
+ /* Try to match static mapping by external address and port,
+ destination address and port in packet */
+ if (snat_static_mapping_match(sm, key0, &sm0, 1))
+ {
+ b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ goto trace0;
+ }
+
+ /* Create session initiated by host from external network */
+ s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node);
+ if (!s0)
+ goto trace0;
+ }
else
s0 = pool_elt_at_index (sm->sessions, value0.value);
old_addr0 = ip0->dst_address.as_u32;
ip0->dst_address = s0->in2out.addr;
new_addr0 = ip0->dst_address.as_u32;
- vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
+ vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
sum0 = ip0->checksum;
sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
@@ -302,13 +436,17 @@ snat_out2in_node_fn (vlib_main_t * vm,
udp0->checksum = 0;
}
- /* Accounting, per-user LRU list maintenance */
+ /* Accounting */
s0->last_heard = now;
s0->total_pkts++;
s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
- clib_dlist_remove (sm->list_pool, s0->per_user_index);
- clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
- s0->per_user_index);
+ /* Per-user LRU list maintenance for dynamic translation */
+ if (!snat_is_session_static (s0))
+ {
+ clib_dlist_remove (sm->list_pool, s0->per_user_index);
+ clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+ s0->per_user_index);
+ }
trace0:
if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -366,14 +504,27 @@ snat_out2in_node_fn (vlib_main_t * vm,
kv1.key = key1.as_u64;
if (clib_bihash_search_8_8 (&sm->out2in, &kv1, &value1))
- goto trace1;
+ {
+ /* Try to match static mapping by external address and port,
+ destination address and port in packet */
+ if (snat_static_mapping_match(sm, key1, &sm1, 1))
+ {
+ b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ goto trace1;
+ }
+
+ /* Create session initiated by host from external network */
+ s1 = create_session_for_static_mapping(sm, b1, sm1, key1, node);
+ if (!s1)
+ goto trace1;
+ }
else
s1 = pool_elt_at_index (sm->sessions, value1.value);
old_addr1 = ip1->dst_address.as_u32;
ip1->dst_address = s1->in2out.addr;
new_addr1 = ip1->dst_address.as_u32;
- vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->out2in.fib_index;
+ vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->in2out.fib_index;
sum1 = ip1->checksum;
sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
@@ -404,13 +555,17 @@ snat_out2in_node_fn (vlib_main_t * vm,
udp1->checksum = 0;
}
- /* Accounting, per-user LRU list maintenance */
+ /* Accounting */
s1->last_heard = now;
s1->total_pkts++;
s1->total_bytes += vlib_buffer_length_in_chain (vm, b1);
- clib_dlist_remove (sm->list_pool, s1->per_user_index);
- clib_dlist_addtail (sm->list_pool, s1->per_user_list_head_index,
- s1->per_user_index);
+ /* Per-user LRU list maintenance for dynamic translation */
+ if (!snat_is_session_static (s1))
+ {
+ clib_dlist_remove (sm->list_pool, s1->per_user_index);
+ clib_dlist_addtail (sm->list_pool, s1->per_user_list_head_index,
+ s1->per_user_index);
+ }
trace1:
if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -447,7 +602,7 @@ snat_out2in_node_fn (vlib_main_t * vm,
udp_header_t * udp0;
tcp_header_t * tcp0;
icmp46_header_t * icmp0;
- snat_session_key_t key0;
+ snat_session_key_t key0, sm0;
u32 rx_fib_index0;
u32 proto0;
snat_session_t * s0 = 0;
@@ -503,14 +658,27 @@ snat_out2in_node_fn (vlib_main_t * vm,
kv0.key = key0.as_u64;
if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
- goto trace00;
+ {
+ /* Try to match static mapping by external address and port,
+ destination address and port in packet */
+ if (snat_static_mapping_match(sm, key0, &sm0, 1))
+ {
+ b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ goto trace00;
+ }
+
+ /* Create session initiated by host from external network */
+ s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node);
+ if (!s0)
+ goto trace00;
+ }
else
s0 = pool_elt_at_index (sm->sessions, value0.value);
old_addr0 = ip0->dst_address.as_u32;
ip0->dst_address = s0->in2out.addr;
new_addr0 = ip0->dst_address.as_u32;
- vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
+ vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
sum0 = ip0->checksum;
sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
@@ -541,13 +709,17 @@ snat_out2in_node_fn (vlib_main_t * vm,
udp0->checksum = 0;
}
- /* Accounting, per-user LRU list maintenance */
+ /* Accounting */
s0->last_heard = now;
s0->total_pkts++;
s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
- clib_dlist_remove (sm->list_pool, s0->per_user_index);
- clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
- s0->per_user_index);
+ /* Per-user LRU list maintenance for dynamic translation */
+ if (!snat_is_session_static (s0))
+ {
+ clib_dlist_remove (sm->list_pool, s0->per_user_index);
+ clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+ s0->per_user_index);
+ }
trace00:
if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -599,3 +771,257 @@ VLIB_REGISTER_NODE (snat_out2in_node) = {
},
};
VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_node, snat_out2in_node_fn);
+
+static inline u32 icmp_out2in_fast (snat_main_t *sm,
+ vlib_buffer_t * b0,
+ ip4_header_t * ip0,
+ icmp46_header_t * icmp0,
+ u32 sw_if_index0,
+ vlib_node_runtime_t * node,
+ u32 next0)
+{
+ snat_session_key_t key0, sm0;
+ icmp_echo_header_t *echo0;
+ u32 new_addr0, old_addr0;
+ u16 old_id0, new_id0;
+ ip_csum_t sum0;
+ snat_runtime_t * rt = (snat_runtime_t *)node->runtime_data;
+
+ echo0 = (icmp_echo_header_t *)(icmp0+1);
+
+ key0.addr = ip0->dst_address;
+ key0.port = echo0->identifier;
+
+ if (snat_static_mapping_match(sm, key0, &sm0, 1))
+ {
+ ip4_address_t * first_int_addr;
+
+ if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0))
+ {
+ first_int_addr =
+ ip4_interface_first_address (sm->ip4_main, sw_if_index0,
+ 0 /* just want the address */);
+ rt->cached_sw_if_index = sw_if_index0;
+ rt->cached_ip4_address = first_int_addr->as_u32;
+ }
+
+ /* Don't NAT packet aimed at the intfc address */
+ if (PREDICT_FALSE(ip0->dst_address.as_u32 ==
+ rt->cached_ip4_address))
+ return next0;
+
+ b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ return SNAT_OUT2IN_NEXT_DROP;
+ }
+
+ new_addr0 = sm0.addr.as_u32;
+ new_id0 = sm0.port;
+ vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
+
+ old_addr0 = ip0->dst_address.as_u32;
+ ip0->dst_address.as_u32 = new_addr0;
+
+ sum0 = ip0->checksum;
+ sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+ ip4_header_t,
+ dst_address /* changed member */);
+ ip0->checksum = ip_csum_fold (sum0);
+
+ if (PREDICT_FALSE(new_id0 != echo0->identifier))
+ {
+ old_id0 = echo0->identifier;
+ echo0->identifier = new_id0;
+
+ sum0 = icmp0->checksum;
+ sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
+ identifier);
+ icmp0->checksum = ip_csum_fold (sum0);
+ }
+
+ return next0;
+}
+
+static uword
+snat_out2in_fast_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 n_left_from, * from, * to_next;
+ snat_out2in_next_t next_index;
+ u32 pkts_processed = 0;
+ snat_main_t * sm = &snat_main;
+ ip_lookup_main_t * lm = sm->ip4_lookup_main;
+ ip_config_main_t * cm = &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
+
+ 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 = SNAT_OUT2IN_NEXT_DROP;
+ u32 sw_if_index0;
+ ip4_header_t * ip0;
+ ip_csum_t sum0;
+ u32 new_addr0, old_addr0;
+ u16 new_port0, old_port0;
+ udp_header_t * udp0;
+ tcp_header_t * tcp0;
+ icmp46_header_t * icmp0;
+ snat_session_key_t key0, sm0;
+ 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);
+
+ 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];
+
+ vnet_get_config_data (&cm->config_main,
+ &b0->current_config_index,
+ &next0,
+ 0 /* sizeof config data */);
+ proto0 = ~0;
+ proto0 = (ip0->protocol == IP_PROTOCOL_UDP)
+ ? SNAT_PROTOCOL_UDP : proto0;
+ proto0 = (ip0->protocol == IP_PROTOCOL_TCP)
+ ? SNAT_PROTOCOL_TCP : proto0;
+ proto0 = (ip0->protocol == IP_PROTOCOL_ICMP)
+ ? SNAT_PROTOCOL_ICMP : proto0;
+
+ if (PREDICT_FALSE (proto0 == ~0))
+ goto trace00;
+
+ if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
+ {
+ next0 = icmp_out2in_fast
+ (sm, b0, ip0, icmp0, sw_if_index0, node, next0);
+ goto trace00;
+ }
+
+ key0.addr = ip0->dst_address;
+ key0.port = udp0->dst_port;
+
+ if (snat_static_mapping_match(sm, key0, &sm0, 1))
+ {
+ b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ goto trace00;
+ }
+
+ new_addr0 = sm0.addr.as_u32;
+ new_port0 = sm0.port;
+ vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
+ old_addr0 = ip0->dst_address.as_u32;
+ ip0->dst_address.as_u32 = new_addr0;
+
+ sum0 = ip0->checksum;
+ sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+ ip4_header_t,
+ dst_address /* changed member */);
+ ip0->checksum = ip_csum_fold (sum0);
+
+ if (PREDICT_FALSE(new_port0 != udp0->dst_port))
+ {
+ if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+ {
+ old_port0 = tcp0->ports.dst;
+ tcp0->ports.dst = new_port0;
+
+ sum0 = tcp0->checksum;
+ sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+ ip4_header_t,
+ dst_address /* changed member */);
+
+ sum0 = ip_csum_update (sum0, old_port0, new_port0,
+ ip4_header_t /* cheat */,
+ length /* changed member */);
+ tcp0->checksum = ip_csum_fold(sum0);
+ }
+ else
+ {
+ old_port0 = udp0->dst_port;
+ udp0->dst_port = new_port0;
+ udp0->checksum = 0;
+ }
+ }
+ else
+ {
+ if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+ {
+ sum0 = tcp0->checksum;
+ sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+ ip4_header_t,
+ dst_address /* changed member */);
+
+ tcp0->checksum = ip_csum_fold(sum0);
+ }
+ }
+
+ trace00:
+
+ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ snat_out2in_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sw_if_index = sw_if_index0;
+ t->next_index = next0;
+ }
+
+ pkts_processed += next0 != SNAT_OUT2IN_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_out2in_fast_node.index,
+ SNAT_OUT2IN_ERROR_OUT2IN_PACKETS,
+ pkts_processed);
+ return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (snat_out2in_fast_node) = {
+ .function = snat_out2in_fast_node_fn,
+ .name = "snat-out2in-fast",
+ .vector_size = sizeof (u32),
+ .format_trace = format_snat_out2in_fast_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(snat_out2in_error_strings),
+ .error_strings = snat_out2in_error_strings,
+
+ .runtime_data_bytes = sizeof (snat_runtime_t),
+
+ .n_next_nodes = SNAT_OUT2IN_N_NEXT,
+
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [SNAT_OUT2IN_NEXT_DROP] = "error-drop",
+ },
+};
+VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_fast_node, snat_out2in_fast_node_fn);
diff --git a/plugins/snat-plugin/snat/snat.api b/plugins/snat-plugin/snat/snat.api
index 0c5f4373f87..daacf9ff624 100644
--- a/plugins/snat-plugin/snat/snat.api
+++ b/plugins/snat-plugin/snat/snat.api
@@ -1,3 +1,32 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @file snat.api
+ * @brief VPP control-plane API messages.
+ *
+ * This file defines VPP control-plane API messages which are generally
+ * called through a shared memory interface.
+ */
+
+/** \brief Add S-NAT address range
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param is_ip4 - 1 if address type is IPv4
+ @first_ip_address - first IP address
+ @last_ip_address - last IP address
+*/
define snat_add_address_range {
u32 client_index;
u32 context;
@@ -6,11 +35,23 @@ define snat_add_address_range {
u8 last_ip_address[16];
};
+/** \brief Add S-NAT address range reply
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param retval - return code
+*/
define snat_add_address_range_reply {
u32 context;
i32 retval;
};
+/** \brief Enable/disable S-NAT feature on the interface
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param is_add - 1 if add, 0 if delete
+ @param is_inside - 1 if inside, 0 if outside
+ @param sw_if_index - software index of the interface
+*/
define snat_interface_add_del_feature {
u32 client_index;
u32 context;
@@ -19,7 +60,139 @@ define snat_interface_add_del_feature {
u32 sw_if_index;
};
+/** \brief Enable/disable S-NAT feature on the interface reply
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param retval - return code
+*/
define snat_interface_add_del_feature_reply {
u32 context;
i32 retval;
};
+
+/** \brief Add/delete S-NAT static mapping
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param is_add - 1 if add, 0 if delete
+ @param is_ip4 - 1 if address type is IPv4
+ @param addr_only - 1 if address only mapping
+ @param local_ip_address - local IP address
+ @param external_ip_address - external IP address
+ @param local_port - local port number
+ @param external_port - external port number
+ @param vfr_id - VRF ID
+*/
+define snat_add_static_mapping {
+ u32 client_index;
+ u32 context;
+ u8 is_add;
+ u8 is_ip4;
+ u8 addr_only;
+ u8 local_ip_address[16];
+ u8 external_ip_address[16];
+ u16 local_port;
+ u16 external_port;
+ u32 vrf_id;
+};
+
+/** \brief Add/delete S-NAT static mapping reply
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param retval - return code
+*/
+define snat_add_static_mapping_reply {
+ u32 context;
+ i32 retval;
+};
+
+/** \brief Dump S-NAT static mappings
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define snat_static_mapping_dump {
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief S-NAT static mapping details response
+ @param context - sender context, to match reply w/ request
+ @param is_ip4 - 1 if address type is IPv4
+ @param addr_only - 1 if address only mapping
+ @param local_ip_address - local IP address
+ @param external_ip_address - external IP address
+ @param local_port - local port number
+ @param external_port - external port number
+ @param vfr_id - VRF ID
+*/
+define snat_static_mapping_details {
+ u32 context;
+ u8 is_ip4;
+ u8 addr_only;
+ u8 local_ip_address[16];
+ u8 external_ip_address[16];
+ u16 local_port;
+ u16 external_port;
+ u32 vrf_id;
+};
+
+/** \brief Control ping from client to api server request
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define snat_control_ping
+{
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Control ping from the client to the server response
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param retval - return code for the request
+ @param vpe_pid - the pid of the vpe, returned by the server
+*/
+define snat_control_ping_reply
+{
+ u32 context;
+ i32 retval;
+ u32 client_index;
+ u32 vpe_pid;
+};
+
+/** \brief Show S-NAT plugin startup config
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define snat_show_config
+{
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Show S-NAT plugin startup config reply
+ @param context - sender context, to match reply w/ request
+ @param retval - return code for the request
+ @param static_mapping_only - if 1 dynamic translations disabled
+ @param static_mapping_connection_tracking - if 1 create session data
+ @param translation_buckets - number of translation hash buckets
+ @param translation_memory_size - translation hash memory size
+ @param user_buckets - number of user hash buckets
+ @param user_memory_size - user hash memory size
+ @param max_translations_per_user - maximum number of translations per user
+ @param outside_vrf_id - outside VRF id
+ @param inside_vrf_id - default inside VRF id
+*/
+define snat_show_config_reply
+{
+ u32 context;
+ i32 retval;
+ u8 static_mapping_only;
+ u8 static_mapping_connection_tracking;
+ u32 translation_buckets;
+ u32 translation_memory_size;
+ u32 user_buckets;
+ u32 user_memory_size;
+ u32 max_translations_per_user;
+ u32 outside_vrf_id;
+ u32 inside_vrf_id;
+};
diff --git a/plugins/snat-plugin/snat/snat.c b/plugins/snat-plugin/snat/snat.c
index f3e278bfad6..3fd9ecb41b7 100644
--- a/plugins/snat-plugin/snat/snat.c
+++ b/plugins/snat-plugin/snat/snat.c
@@ -76,6 +76,21 @@ do { \
vl_msg_api_send_shmem (q, (u8 *)&rmp); \
} while(0);
+#define REPLY_MACRO2(t, body) \
+do { \
+ unix_shared_memory_queue_t * q = \
+ vl_api_client_index_to_input_queue (mp->client_index); \
+ if (!q) \
+ return; \
+ \
+ rmp = vl_msg_api_alloc (sizeof (*rmp)); \
+ rmp->_vl_msg_id = ntohs((t)+sm->msg_id_base); \
+ rmp->context = mp->context; \
+ rmp->retval = ntohl(rv); \
+ do {body;} while (0); \
+ vl_msg_api_send_shmem (q, (u8 *)&rmp); \
+} while(0);
+
/* Hook up input features */
VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_in2out, static) = {
@@ -88,6 +103,17 @@ VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_out2in, static) = {
.runs_before = (char *[]){"ip4-lookup", 0},
.feature_index = &snat_main.rx_feature_out2in,
};
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_in2out_fast, static) = {
+ .node_name = "snat-in2out-fast",
+ .runs_before = (char *[]){"snat-out2in-fast", 0},
+ .feature_index = &snat_main.rx_feature_in2out_fast,
+};
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_out2in_fast, static) = {
+ .node_name = "snat-out2in-fast",
+ .runs_before = (char *[]){"ip4-lookup", 0},
+ .feature_index = &snat_main.rx_feature_out2in_fast,
+};
+
/*
* This routine exists to convince the vlib plugin framework that
@@ -189,6 +215,240 @@ static void increment_v4_address (ip4_address_t * a)
a->as_u32 = clib_host_to_net_u32(v);
}
+/**
+ * @brief Add static mapping.
+ *
+ * Create static mapping between local addr+port and external addr+port.
+ *
+ * @param l_addr Local IPv4 address.
+ * @param e_addr External IPv4 address.
+ * @param l_port Local port number.
+ * @param e_port External port number.
+ * @param vrf_id VRF ID.
+ * @param addr_only If 0 address port and pair mapping, otherwise address only.
+ * @param is_add If 0 delete static mapping, otherwise add.
+ *
+ * @returns
+ */
+int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
+ u16 l_port, u16 e_port, u32 vrf_id, int addr_only,
+ int is_add)
+{
+ snat_main_t * sm = &snat_main;
+ snat_static_mapping_t *m;
+ snat_static_mapping_key_t m_key;
+ clib_bihash_kv_8_8_t kv, value;
+ snat_address_t *a = 0;
+ u32 fib_index = ~0;
+ uword * p;
+ int i;
+
+ m_key.addr = e_addr;
+ m_key.port = addr_only ? 0 : e_port;
+ m_key.pad = 0;
+ kv.key = m_key.as_u64;
+ if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
+ m = 0;
+ else
+ m = pool_elt_at_index (sm->static_mappings, value.value);
+
+ if (is_add)
+ {
+ if (m)
+ return VNET_API_ERROR_VALUE_EXIST;
+
+ /* Convert VRF id to FIB index */
+ if (vrf_id != ~0)
+ {
+ p = hash_get (sm->ip4_main->fib_index_by_table_id, vrf_id);
+ if (!p)
+ return VNET_API_ERROR_NO_SUCH_FIB;
+ fib_index = p[0];
+ }
+ /* If not specified use inside VRF id from SNAT plugin startup config */
+ else
+ {
+ if (sm->inside_fib_index == ~0)
+ {
+ p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->inside_vrf_id);
+ if (!p)
+ return VNET_API_ERROR_NO_SUCH_FIB;
+ fib_index = p[0];
+ sm->inside_fib_index = fib_index;
+ }
+ else
+ fib_index = sm->inside_fib_index;
+
+ vrf_id = sm->inside_vrf_id;
+ }
+
+ /* If outside FIB index is not resolved yet */
+ if (sm->outside_fib_index == ~0)
+ {
+ p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->outside_vrf_id);
+ if (!p)
+ return VNET_API_ERROR_NO_SUCH_FIB;
+ sm->outside_fib_index = p[0];
+ }
+
+ /* Find external address in allocated addresses and reserve port for
+ address and port pair mapping when dynamic translations enabled */
+ if (!addr_only && !(sm->static_mapping_only))
+ {
+ for (i = 0; i < vec_len (sm->addresses); i++)
+ {
+ if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
+ {
+ a = sm->addresses + i;
+ /* External port must be unused */
+ if (clib_bitmap_get (a->busy_port_bitmap, e_port))
+ return VNET_API_ERROR_INVALID_VALUE;
+ a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap,
+ e_port, 1);
+ if (e_port > 1024)
+ a->busy_ports++;
+
+ break;
+ }
+ }
+ /* External address must be allocated */
+ if (!a)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+ }
+
+ pool_get (sm->static_mappings, m);
+ memset (m, 0, sizeof (*m));
+ m->local_addr = l_addr;
+ m->external_addr = e_addr;
+ m->addr_only = addr_only;
+ m->vrf_id = vrf_id;
+ m->fib_index = fib_index;
+ if (!addr_only)
+ {
+ m->local_port = l_port;
+ m->external_port = e_port;
+ }
+
+ m_key.addr = m->local_addr;
+ m_key.port = m->local_port;
+ m_key.pad = 0;
+ kv.key = m_key.as_u64;
+ kv.value = m - sm->static_mappings;
+ clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
+
+ m_key.addr = m->external_addr;
+ m_key.port = m->external_port;
+ kv.key = m_key.as_u64;
+ kv.value = m - sm->static_mappings;
+ clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1);
+ }
+ else
+ {
+ if (!m)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ /* Free external address port */
+ if (!addr_only && !(sm->static_mapping_only))
+ {
+ for (i = 0; i < vec_len (sm->addresses); i++)
+ {
+ if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
+ {
+ a = sm->addresses + i;
+ a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap,
+ e_port, 0);
+ a->busy_ports--;
+
+ break;
+ }
+ }
+ }
+
+ m_key.addr = m->local_addr;
+ m_key.port = m->local_port;
+ m_key.pad = 0;
+ kv.key = m_key.as_u64;
+ clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0);
+
+ m_key.addr = m->external_addr;
+ m_key.port = m->external_port;
+ kv.key = m_key.as_u64;
+ clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0);
+
+ /* Delete session(s) for static mapping if exist */
+ if (!(sm->static_mapping_only) ||
+ (sm->static_mapping_only && sm->static_mapping_connection_tracking))
+ {
+ snat_user_key_t u_key;
+ snat_user_t *u;
+ dlist_elt_t * head, * elt;
+ u32 elt_index, head_index;
+ u32 ses_index;
+ snat_session_t * s;
+
+ u_key.addr = m->local_addr;
+ u_key.fib_index = m->fib_index;
+ kv.key = u_key.as_u64;
+ if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value))
+ {
+ u = pool_elt_at_index (sm->users, value.value);
+ if (u->nstaticsessions)
+ {
+ head_index = u->sessions_per_user_list_head_index;
+ head = pool_elt_at_index (sm->list_pool, head_index);
+ elt_index = head->next;
+ elt = pool_elt_at_index (sm->list_pool, elt_index);
+ ses_index = elt->value;
+ while (ses_index != ~0)
+ {
+ s = pool_elt_at_index (sm->sessions, ses_index);
+
+ if (!addr_only)
+ {
+ if ((s->out2in.addr.as_u32 != e_addr.as_u32) &&
+ (clib_net_to_host_u16 (s->out2in.port) != e_port))
+ continue;
+ }
+ value.key = s->in2out.as_u64;
+ clib_bihash_add_del_8_8 (&sm->in2out, &value, 0);
+ value.key = s->out2in.as_u64;
+ clib_bihash_add_del_8_8 (&sm->out2in, &value, 0);
+ pool_put (sm->sessions, s);
+
+ if (!addr_only)
+ break;
+
+ elt_index = elt->next;
+ elt = pool_elt_at_index (sm->list_pool, elt_index);
+ ses_index = elt->value;
+ }
+ if (addr_only)
+ {
+ while ((elt_index = clib_dlist_remove_head(sm->list_pool, head_index)) != ~0)
+ pool_put_index (sm->list_pool, elt_index);
+ pool_put (sm->users, u);
+ clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 0);
+ }
+ else
+ {
+ if (ses_index != ~0)
+ {
+ clib_dlist_remove (sm->list_pool, elt_index);
+ pool_put (sm->list_pool, elt);
+ u->nstaticsessions--;
+ }
+ }
+ }
+ }
+ }
+
+ /* Delete static mapping from pool */
+ pool_put (sm->static_mappings, m);
+ }
+
+ return 0;
+}
+
static void
vl_api_snat_add_address_range_t_handler
(vl_api_snat_add_address_range_t * mp)
@@ -207,6 +467,12 @@ vl_api_snat_add_address_range_t_handler
goto send_reply;
}
+ if (sm->static_mapping_only)
+ {
+ rv = VNET_API_ERROR_FEATURE_DISABLED;
+ goto send_reply;
+ }
+
tmp = (u32 *) mp->first_ip_address;
start_host_order = clib_host_to_net_u32 (tmp[0]);
tmp = (u32 *) mp->last_ip_address;
@@ -263,8 +529,12 @@ vl_api_snat_interface_add_del_feature_t_handler
VALIDATE_SW_IF_INDEX(mp);
- feature_index = mp->is_inside ? sm->rx_feature_in2out
- : sm->rx_feature_out2in;
+ if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
+ feature_index = mp->is_inside ? sm->rx_feature_in2out_fast
+ : sm->rx_feature_out2in_fast;
+ else
+ feature_index = mp->is_inside ? sm->rx_feature_in2out
+ : sm->rx_feature_out2in;
ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
ci = (is_del
@@ -296,10 +566,176 @@ static void *vl_api_snat_interface_add_del_feature_t_print
FINISH;
}
+static void
+vl_api_snat_add_static_mapping_t_handler
+(vl_api_snat_add_static_mapping_t * mp)
+{
+ snat_main_t * sm = &snat_main;
+ vl_api_snat_add_static_mapping_reply_t * rmp;
+ ip4_address_t local_addr, external_addr;
+ u16 local_port = 0, external_port = 0;
+ u32 vrf_id;
+ int rv = 0;
+
+ if (mp->is_ip4 != 1)
+ {
+ rv = VNET_API_ERROR_UNIMPLEMENTED;
+ goto send_reply;
+ }
+
+ memcpy (&local_addr.as_u8, mp->local_ip_address, 4);
+ memcpy (&external_addr.as_u8, mp->external_ip_address, 4);
+ if (mp->addr_only == 0)
+ {
+ local_port = clib_net_to_host_u16 (mp->local_port);
+ external_port = clib_net_to_host_u16 (mp->external_port);
+ }
+ vrf_id = clib_net_to_host_u32 (mp->vrf_id);
+
+ rv = snat_add_static_mapping(local_addr, external_addr, local_port,
+ external_port, vrf_id, mp->addr_only,
+ mp->is_add);
+
+ send_reply:
+ REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY);
+}
+
+static void *vl_api_snat_add_static_mapping_t_print
+(vl_api_snat_add_static_mapping_t *mp, void * handle)
+{
+ u8 * s;
+
+ s = format (0, "SCRIPT: snat_add_static_mapping ");
+ s = format (s, "local_addr %U external_addr %U ",
+ format_ip4_address, mp->local_ip_address,
+ format_ip4_address, mp->external_ip_address);
+
+ if (mp->addr_only == 0)
+ s = format (s, "local_port %d external_port %d ",
+ clib_net_to_host_u16 (mp->local_port),
+ clib_net_to_host_u16 (mp->external_port));
+
+ if (mp->vrf_id != ~0)
+ s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id));
+
+ FINISH;
+}
+
+static void
+send_snat_static_mapping_details
+(snat_static_mapping_t * m, unix_shared_memory_queue_t * q, u32 context)
+{
+ vl_api_snat_static_mapping_details_t *rmp;
+ snat_main_t * sm = &snat_main;
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ memset (rmp, 0, sizeof (*rmp));
+ rmp->_vl_msg_id = ntohs (VL_API_SNAT_STATIC_MAPPING_DETAILS+sm->msg_id_base);
+ rmp->is_ip4 = 1;
+ rmp->addr_only = m->addr_only;
+ clib_memcpy (rmp->local_ip_address, &(m->local_addr), 4);
+ clib_memcpy (rmp->external_ip_address, &(m->external_addr), 4);
+ rmp->local_port = htons (m->local_port);
+ rmp->external_port = htons (m->external_port);
+ rmp->vrf_id = htonl (m->vrf_id);
+ rmp->context = context;
+
+ vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+vl_api_snat_static_mapping_dump_t_handler
+(vl_api_snat_static_mapping_dump_t * mp)
+{
+ unix_shared_memory_queue_t *q;
+ snat_main_t * sm = &snat_main;
+ snat_static_mapping_t * m;
+
+ q = vl_api_client_index_to_input_queue (mp->client_index);
+ if (q == 0)
+ return;
+
+ pool_foreach (m, sm->static_mappings,
+ ({
+ send_snat_static_mapping_details (m, q, mp->context);
+ }));
+}
+
+static void *vl_api_snat_static_mapping_dump_t_print
+(vl_api_snat_static_mapping_dump_t *mp, void * handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: snat_static_mapping_dump ");
+
+ FINISH;
+}
+
+static void
+vl_api_snat_control_ping_t_handler
+(vl_api_snat_control_ping_t * mp)
+{
+ vl_api_snat_control_ping_reply_t *rmp;
+ snat_main_t * sm = &snat_main;
+ int rv = 0;
+
+ REPLY_MACRO2(VL_API_SNAT_CONTROL_PING_REPLY,
+ ({
+ rmp->vpe_pid = ntohl (getpid());
+ }));
+}
+
+static void *vl_api_snat_control_ping_t_print
+(vl_api_snat_control_ping_t *mp, void * handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: snat_control_ping ");
+
+ FINISH;
+}
+
+static void
+vl_api_snat_show_config_t_handler
+(vl_api_snat_show_config_t * mp)
+{
+ vl_api_snat_show_config_reply_t *rmp;
+ snat_main_t * sm = &snat_main;
+ int rv = 0;
+
+ REPLY_MACRO2(VL_API_SNAT_SHOW_CONFIG_REPLY,
+ ({
+ rmp->translation_buckets = htons (sm->translation_buckets);
+ rmp->translation_memory_size = htons (sm->translation_memory_size);
+ rmp->user_buckets = htons (sm->user_buckets);
+ rmp->user_memory_size = htons (sm->user_memory_size);
+ rmp->max_translations_per_user = htons (sm->max_translations_per_user);
+ rmp->outside_vrf_id = htons (sm->outside_vrf_id);
+ rmp->inside_vrf_id = htons (sm->inside_vrf_id);
+ rmp->static_mapping_only = sm->static_mapping_only;
+ rmp->static_mapping_connection_tracking =
+ sm->static_mapping_connection_tracking;
+ }));
+}
+
+static void *vl_api_snat_show_config_t_print
+(vl_api_snat_show_config_t *mp, void * handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: snat_show_config ");
+
+ FINISH;
+}
+
/* List of message types that this plugin understands */
#define foreach_snat_plugin_api_msg \
_(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range) \
-_(SNAT_INTERFACE_ADD_DEL_FEATURE, snat_interface_add_del_feature)
+_(SNAT_INTERFACE_ADD_DEL_FEATURE, snat_interface_add_del_feature) \
+_(SNAT_ADD_STATIC_MAPPING, snat_add_static_mapping) \
+_(SNAT_CONTROL_PING, snat_control_ping) \
+_(SNAT_STATIC_MAPPING_DUMP, snat_static_mapping_dump) \
+_(SNAT_SHOW_CONFIG, snat_show_config)
/* Set up the API message handling tables */
static clib_error_t *
@@ -376,6 +812,67 @@ void snat_free_outside_address_and_port (snat_main_t * sm,
a->busy_ports--;
}
+/**
+ * @brief Match SNAT static mapping.
+ *
+ * @param sm SNAT main.
+ * @param match Address and port to match.
+ * @param mapping External or local address and port of the matched mapping.
+ * @param by_external If 0 match by local address otherwise match by external
+ * address.
+ *
+ * @returns 0 if match found otherwise 1.
+ */
+int snat_static_mapping_match (snat_main_t * sm,
+ snat_session_key_t match,
+ snat_session_key_t * mapping,
+ u8 by_external)
+{
+ clib_bihash_kv_8_8_t kv, value;
+ snat_static_mapping_t *m;
+ snat_static_mapping_key_t m_key;
+ clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local;
+
+ if (by_external)
+ mapping_hash = &sm->static_mapping_by_external;
+
+ m_key.addr = match.addr;
+ m_key.port = clib_net_to_host_u16 (match.port);
+ m_key.pad = 0;
+
+ kv.key = m_key.as_u64;
+
+ if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
+ {
+ /* Try address only mapping */
+ m_key.port = 0;
+ kv.key = m_key.as_u64;
+ if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
+ return 1;
+ }
+
+ m = pool_elt_at_index (sm->static_mappings, value.value);
+
+ if (by_external)
+ {
+ mapping->addr = m->local_addr;
+ /* Address only mapping doesn't change port */
+ mapping->port = m->addr_only ? match.port
+ : clib_host_to_net_u16 (m->local_port);
+ mapping->fib_index = m->fib_index;
+ }
+ else
+ {
+ mapping->addr = m->external_addr;
+ /* Address only mapping doesn't change port */
+ mapping->port = m->addr_only ? match.port
+ : clib_host_to_net_u16 (m->external_port);
+ mapping->fib_index = sm->outside_fib_index;
+ }
+
+ return 0;
+}
+
int snat_alloc_outside_address_and_port (snat_main_t * sm,
snat_session_key_t * k,
u32 * address_indexp)
@@ -419,17 +916,29 @@ add_address_command_fn (vlib_main_t * vm,
unformat_input_t * input,
vlib_cli_command_t * cmd)
{
+ unformat_input_t _line_input, *line_input = &_line_input;
snat_main_t * sm = &snat_main;
ip4_address_t start_addr, end_addr, this_addr;
u32 start_host_order, end_host_order;
int i, count;
- if (unformat (input, "%U - %U",
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ if (unformat (line_input, "%U - %U",
unformat_ip4_address, &start_addr,
unformat_ip4_address, &end_addr))
;
- else if (unformat (input, "%U", unformat_ip4_address, &start_addr))
+ else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
end_addr = start_addr;
+ else
+ return clib_error_return (0, "unknown input '%U'", format_unformat_error,
+ input);
+ unformat_free (line_input);
+
+ if (sm->static_mapping_only)
+ return clib_error_return (0, "static mapping only mode");
start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
@@ -467,6 +976,7 @@ snat_feature_command_fn (vlib_main_t * vm,
unformat_input_t * input,
vlib_cli_command_t * cmd)
{
+ unformat_input_t _line_input, *line_input = &_line_input;
vnet_main_t * vnm = vnet_get_main();
snat_main_t * sm = &snat_main;
ip4_main_t * im = &ip4_main;
@@ -482,23 +992,32 @@ snat_feature_command_fn (vlib_main_t * vm,
sw_if_index = ~0;
- while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
{
- if (unformat (input, "in %U", unformat_vnet_sw_interface,
+ if (unformat (line_input, "in %U", unformat_vnet_sw_interface,
vnm, &sw_if_index))
vec_add1 (inside_sw_if_indices, sw_if_index);
- else if (unformat (input, "out %U", unformat_vnet_sw_interface,
+ else if (unformat (line_input, "out %U", unformat_vnet_sw_interface,
vnm, &sw_if_index))
vec_add1 (outside_sw_if_indices, sw_if_index);
- else if (unformat (input, "del"))
+ else if (unformat (line_input, "del"))
is_del = 1;
else
- break;
+ return clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, input);
}
+ unformat_free (line_input);
if (vec_len (inside_sw_if_indices))
{
- feature_index = sm->rx_feature_in2out;
+ if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
+ feature_index = sm->rx_feature_in2out_fast;
+ else
+ feature_index = sm->rx_feature_in2out;
for (i = 0; i < vec_len(inside_sw_if_indices); i++)
{
@@ -518,7 +1037,10 @@ snat_feature_command_fn (vlib_main_t * vm,
if (vec_len (outside_sw_if_indices))
{
- feature_index = sm->rx_feature_out2in;
+ if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
+ feature_index = sm->rx_feature_out2in_fast;
+ else
+ feature_index = sm->rx_feature_out2in;
for (i = 0; i < vec_len(outside_sw_if_indices); i++)
{
@@ -549,6 +1071,92 @@ VLIB_CLI_COMMAND (set_interface_snat_command, static) = {
};
static clib_error_t *
+add_static_mapping_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ clib_error_t * error = 0;
+ ip4_address_t l_addr, e_addr;
+ u32 l_port = 0, e_port = 0, vrf_id = ~0;
+ int is_add = 1;
+ int addr_only = 1;
+ int rv;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "local %U %u", unformat_ip4_address, &l_addr,
+ &l_port))
+ addr_only = 0;
+ else if (unformat (line_input, "local %U", unformat_ip4_address, &l_addr))
+ ;
+ else if (unformat (line_input, "external %U %u", unformat_ip4_address,
+ &e_addr, &e_port))
+ addr_only = 0;
+ else if (unformat (line_input, "external %U", unformat_ip4_address,
+ &e_addr))
+ ;
+ else if (unformat (line_input, "vrf %u", &vrf_id))
+ ;
+ else if (unformat (line_input, "del"))
+ is_add = 0;
+ else
+ return clib_error_return (0, "unknown input: '%U'",
+ format_unformat_error, line_input);
+ }
+ unformat_free (line_input);
+
+ rv = snat_add_static_mapping(l_addr, e_addr, (u16) l_port, (u16) e_port,
+ vrf_id, addr_only, is_add);
+
+ switch (rv)
+ {
+ case VNET_API_ERROR_INVALID_VALUE:
+ return clib_error_return (0, "External port already in use.");
+ break;
+ case VNET_API_ERROR_NO_SUCH_ENTRY:
+ if (is_add)
+ return clib_error_return (0, "External addres must be allocated.");
+ else
+ return clib_error_return (0, "Mapping not exist.");
+ break;
+ case VNET_API_ERROR_NO_SUCH_FIB:
+ return clib_error_return (0, "No such VRF id.");
+ case VNET_API_ERROR_VALUE_EXIST:
+ return clib_error_return (0, "Mapping already exist.");
+ default:
+ break;
+ }
+
+ return error;
+}
+
+/*?
+ * @cliexpar
+ * @cliexstart{snat add static mapping}
+ * Static mapping allows hosts on the external network to initiate connection
+ * to to the local network host.
+ * To create static mapping between local host address 10.0.0.3 port 6303 and
+ * external address 4.4.4.4 port 3606 use:
+ * vpp# snat add static mapping local 10.0.0.3 6303 external 4.4.4.4 3606
+ * If not runnig "static mapping only" S-NAT plugin mode use before:
+ * vpp# snat add address 4.4.4.4
+ * To create static mapping between local and external address use:
+ * vpp# snat add static mapping local 10.0.0.3 external 4.4.4.4
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (add_static_mapping_command, static) = {
+ .path = "snat add static mapping",
+ .function = add_static_mapping_command_fn,
+ .short_help =
+ "snat add static mapping local <addr> [<port>] external <addr> [<port>] [vrf <table-id>] [del]",
+};
+
+static clib_error_t *
snat_config (vlib_main_t * vm, unformat_input_t * input)
{
snat_main_t * sm = &snat_main;
@@ -558,6 +1166,11 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
u32 user_memory_size = 64<<20;
u32 max_translations_per_user = 100;
u32 outside_vrf_id = 0;
+ u32 inside_vrf_id = 0;
+ u32 static_mapping_buckets = 1024;
+ u32 static_mapping_memory_size = 64<<20;
+ u8 static_mapping_only = 0;
+ u8 static_mapping_connection_tracking = 0;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
@@ -576,8 +1189,17 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
else if (unformat (input, "outside VRF id %d",
&outside_vrf_id))
;
+ else if (unformat (input, "inside VRF id %d",
+ &inside_vrf_id))
+ ;
+ else if (unformat (input, "static mapping only"))
+ {
+ static_mapping_only = 1;
+ if (unformat (input, "connection tracking"))
+ static_mapping_connection_tracking = 1;
+ }
else
- return clib_error_return (0, "unknown input `%U'",
+ return clib_error_return (0, "unknown input '%U'",
format_unformat_error, input);
}
@@ -588,15 +1210,31 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
sm->user_memory_size = user_memory_size;
sm->max_translations_per_user = max_translations_per_user;
sm->outside_vrf_id = outside_vrf_id;
+ sm->outside_fib_index = ~0;
+ sm->inside_vrf_id = inside_vrf_id;
+ sm->inside_fib_index = ~0;
+ sm->static_mapping_only = static_mapping_only;
+ sm->static_mapping_connection_tracking = static_mapping_connection_tracking;
+
+ if (!static_mapping_only ||
+ (static_mapping_only && static_mapping_connection_tracking))
+ {
+ clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets,
+ translation_memory_size);
- clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets,
- translation_memory_size);
-
- clib_bihash_init_8_8 (&sm->out2in, "out2in", translation_buckets,
- translation_memory_size);
+ clib_bihash_init_8_8 (&sm->out2in, "out2in", translation_buckets,
+ translation_memory_size);
- clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets,
- user_memory_size);
+ clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets,
+ user_memory_size);
+ }
+ clib_bihash_init_8_8 (&sm->static_mapping_by_local,
+ "static_mapping_by_local", static_mapping_buckets,
+ static_mapping_memory_size);
+
+ clib_bihash_init_8_8 (&sm->static_mapping_by_external,
+ "static_mapping_by_external", static_mapping_buckets,
+ static_mapping_memory_size);
return 0;
}
@@ -617,7 +1255,7 @@ u8 * format_snat_key (u8 * s, va_list * args)
s = format (s, "%U proto %s port %d fib %d",
format_ip4_address, &key->addr, protocol_string,
- key->port, key->fib_index);
+ clib_net_to_host_u16 (key->port), key->fib_index);
return s;
}
@@ -631,6 +1269,10 @@ u8 * format_snat_session (u8 * s, va_list * args)
s = format (s, " last heard %.2f\n", sess->last_heard);
s = format (s, " total pkts %d, total bytes %lld\n",
sess->total_pkts, sess->total_bytes);
+ if (snat_is_session_static (sess))
+ s = format (s, " static translation\n");
+ else
+ s = format (s, " dynamic translation\n");
return s;
}
@@ -645,33 +1287,54 @@ u8 * format_snat_user (u8 * s, va_list * args)
u32 session_index;
snat_session_t * sess;
- s = format (s, "%U: %d translations\n",
- format_ip4_address, &u->addr, u->nsessions);
+ s = format (s, "%U: %d dynamic translations, %d static translations\n",
+ format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions);
if (verbose == 0)
return s;
- head_index = u->sessions_per_user_list_head_index;
- head = pool_elt_at_index (sm->list_pool, head_index);
-
- elt_index = head->next;
- elt = pool_elt_at_index (sm->list_pool, elt_index);
- session_index = elt->value;
-
- while (session_index != ~0)
+ if (u->nsessions || u->nstaticsessions)
{
- sess = pool_elt_at_index (sm->sessions, session_index);
-
- s = format (s, " %U\n", format_snat_session, sm, sess);
+ head_index = u->sessions_per_user_list_head_index;
+ head = pool_elt_at_index (sm->list_pool, head_index);
- elt_index = elt->next;
+ elt_index = head->next;
elt = pool_elt_at_index (sm->list_pool, elt_index);
session_index = elt->value;
+
+ while (session_index != ~0)
+ {
+ sess = pool_elt_at_index (sm->sessions, session_index);
+
+ s = format (s, " %U\n", format_snat_session, sm, sess);
+
+ elt_index = elt->next;
+ elt = pool_elt_at_index (sm->list_pool, elt_index);
+ session_index = elt->value;
+ }
}
return s;
}
+u8 * format_snat_static_mapping (u8 * s, va_list * args)
+{
+ snat_static_mapping_t *m = va_arg (*args, snat_static_mapping_t *);
+
+ if (m->addr_only)
+ s = format (s, "local %U external %U vrf %d",
+ format_ip4_address, &m->local_addr,
+ format_ip4_address, &m->external_addr,
+ m->vrf_id);
+ else
+ s = format (s, "local %U:%d external %U:%d vrf %d",
+ format_ip4_address, &m->local_addr, m->local_port,
+ format_ip4_address, &m->external_addr, m->external_port,
+ m->vrf_id);
+
+ return s;
+}
+
static clib_error_t *
show_snat_command_fn (vlib_main_t * vm,
unformat_input_t * input,
@@ -680,30 +1343,71 @@ show_snat_command_fn (vlib_main_t * vm,
int verbose = 0;
snat_main_t * sm = &snat_main;
snat_user_t * u;
+ snat_static_mapping_t *m;
if (unformat (input, "detail"))
verbose = 1;
else if (unformat (input, "verbose"))
verbose = 2;
- vlib_cli_output (vm, "%d users, %d outside addresses, %d active sessions",
- pool_elts (sm->users),
- vec_len (sm->addresses),
- pool_elts (sm->sessions));
-
- if (verbose > 0)
+ if (sm->static_mapping_only)
+ {
+ if (sm->static_mapping_connection_tracking)
+ vlib_cli_output (vm, "SNAT mode: static mapping only connection "
+ "tracking");
+ else
+ vlib_cli_output (vm, "SNAT mode: static mapping only");
+ }
+ else
+ {
+ vlib_cli_output (vm, "SNAT mode: dynamic translations enabled");
+ }
+
+ if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
+ {
+ vlib_cli_output (vm, "%d static mappings",
+ pool_elts (sm->static_mappings));
+
+ if (verbose > 0)
+ {
+ pool_foreach (m, sm->static_mappings,
+ ({
+ vlib_cli_output (vm, "%U", format_snat_static_mapping, m);
+ }));
+ }
+ }
+ else
{
- vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->in2out,
- verbose - 1);
- vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in,
- verbose - 1);
- vlib_cli_output (vm, "%d list pool elements",
- pool_elts (sm->list_pool));
-
- pool_foreach (u, sm->users,
- ({
- vlib_cli_output (vm, "%U", format_snat_user, sm, u, verbose - 1);
- }));
+ vlib_cli_output (vm, "%d users, %d outside addresses, %d active sessions,"
+ " %d static mappings",
+ pool_elts (sm->users),
+ vec_len (sm->addresses),
+ pool_elts (sm->sessions),
+ pool_elts (sm->static_mappings));
+
+ if (verbose > 0)
+ {
+ vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->in2out,
+ verbose - 1);
+ vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in,
+ verbose - 1);
+ vlib_cli_output (vm, "%d list pool elements",
+ pool_elts (sm->list_pool));
+
+ pool_foreach (u, sm->users,
+ ({
+ vlib_cli_output (vm, "%U", format_snat_user, sm, u, verbose - 1);
+ }));
+
+ if (pool_elts (sm->static_mappings))
+ {
+ vlib_cli_output (vm, "static mappings:");
+ pool_foreach (m, sm->static_mappings,
+ ({
+ vlib_cli_output (vm, "%U", format_snat_static_mapping, m);
+ }));
+ }
+ }
}
return 0;
diff --git a/plugins/snat-plugin/snat/snat.h b/plugins/snat-plugin/snat/snat.h
index e3aa56276fb..823b8608b1a 100644
--- a/plugins/snat-plugin/snat/snat.h
+++ b/plugins/snat-plugin/snat/snat.h
@@ -55,6 +55,19 @@ typedef struct {
};
} snat_user_key_t;
+typedef struct {
+ union
+ {
+ struct
+ {
+ ip4_address_t addr;
+ u16 port;
+ u16 pad;
+ };
+ u64 as_u64;
+ };
+} snat_static_mapping_key_t;
+
typedef enum {
SNAT_PROTOCOL_UDP = 0,
@@ -63,6 +76,8 @@ typedef enum {
} snat_protocol_t;
+#define SNAT_SESSION_FLAG_STATIC_MAPPING 1
+
typedef CLIB_PACKED(struct {
snat_session_key_t out2in; /* 0-15 */
@@ -87,12 +102,12 @@ typedef CLIB_PACKED(struct {
}) snat_session_t;
-#define SNAT_SESSION_STATIC (1<<0)
typedef struct {
ip4_address_t addr;
u32 sessions_per_user_list_head_index;
u32 nsessions;
+ u32 nstaticsessions;
} snat_user_t;
typedef struct {
@@ -102,6 +117,16 @@ typedef struct {
} snat_address_t;
typedef struct {
+ ip4_address_t local_addr;
+ ip4_address_t external_addr;
+ u16 local_port;
+ u16 external_port;
+ u8 addr_only;
+ u32 vrf_id;
+ u32 fib_index;
+} snat_static_mapping_t;
+
+typedef struct {
/* Main lookup tables */
clib_bihash_8_8_t out2in;
clib_bihash_8_8_t in2out;
@@ -109,12 +134,21 @@ typedef struct {
/* Find-a-user => src address lookup */
clib_bihash_8_8_t user_hash;
+ /* Find a static mapping by local */
+ clib_bihash_8_8_t static_mapping_by_local;
+
+ /* Find a static mapping by external */
+ clib_bihash_8_8_t static_mapping_by_external;
+
/* User pool */
snat_user_t * users;
/* Session pool */
snat_session_t * sessions;
+ /* Static mapping pool */
+ snat_static_mapping_t * static_mappings;
+
/* Vector of outside addresses */
snat_address_t * addresses;
@@ -127,8 +161,12 @@ typedef struct {
/* ip4 feature path indices */
u32 rx_feature_in2out;
u32 rx_feature_out2in;
+ u32 rx_feature_in2out_fast;
+ u32 rx_feature_out2in_fast;
/* Config parameters */
+ u8 static_mapping_only;
+ u8 static_mapping_connection_tracking;
u32 translation_buckets;
u32 translation_memory_size;
u32 user_buckets;
@@ -136,6 +174,8 @@ typedef struct {
u32 max_translations_per_user;
u32 outside_vrf_id;
u32 outside_fib_index;
+ u32 inside_vrf_id;
+ u32 inside_fib_index;
/* API message ID base */
u16 msg_id_base;
@@ -152,6 +192,8 @@ typedef struct {
extern snat_main_t snat_main;
extern vlib_node_registration_t snat_in2out_node;
extern vlib_node_registration_t snat_out2in_node;
+extern vlib_node_registration_t snat_in2out_fast_node;
+extern vlib_node_registration_t snat_out2in_fast_node;
void snat_free_outside_address_and_port (snat_main_t * sm,
snat_session_key_t * k,
@@ -160,6 +202,12 @@ void snat_free_outside_address_and_port (snat_main_t * sm,
int snat_alloc_outside_address_and_port (snat_main_t * sm,
snat_session_key_t * k,
u32 * address_indexp);
+
+int snat_static_mapping_match (snat_main_t * sm,
+ snat_session_key_t match,
+ snat_session_key_t * mapping,
+ u8 by_external);
+
format_function_t format_snat_user;
typedef struct {
@@ -167,6 +215,12 @@ typedef struct {
u32 cached_ip4_address;
} snat_runtime_t;
+/** \brief Check if SNAT session is created from static mapping.
+ @param s SNAT session
+ @return 1 if SNAT session is created from static mapping otherwise 0
+*/
+#define snat_is_session_static(s) s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING
+
/*
* Why is this here? Because we don't need to touch this layer to
* simply reply to an icmp. We need to change id to a unique
diff --git a/plugins/snat-plugin/snat/snat_test.c b/plugins/snat-plugin/snat/snat_test.c
index cd2656cbdaf..453c63f28bd 100644
--- a/plugins/snat-plugin/snat/snat_test.c
+++ b/plugins/snat-plugin/snat/snat_test.c
@@ -59,7 +59,8 @@ snat_test_main_t snat_test_main;
#define foreach_standard_reply_retval_handler \
_(snat_add_address_range_reply) \
-_(snat_interface_add_del_feature_reply)
+_(snat_interface_add_del_feature_reply) \
+_(snat_add_static_mapping_reply)
#define _(n) \
static void vl_api_##n##_t_handler \
@@ -83,8 +84,12 @@ foreach_standard_reply_retval_handler;
*/
#define foreach_vpe_api_reply_msg \
_(SNAT_ADD_ADDRESS_RANGE_REPLY, snat_add_address_range_reply) \
- _(SNAT_INTERFACE_ADD_DEL_FEATURE_REPLY, \
- snat_interface_add_del_feature_reply)
+_(SNAT_INTERFACE_ADD_DEL_FEATURE_REPLY, \
+ snat_interface_add_del_feature_reply) \
+_(SNAT_ADD_STATIC_MAPPING_REPLY, snat_add_static_mapping_reply) \
+_(SNAT_CONTROL_PING_REPLY, snat_control_ping_reply) \
+_(SNAT_STATIC_MAPPING_DETAILS, snat_static_mapping_details) \
+_(SNAT_SHOW_CONFIG_REPLY, snat_show_config_reply)
/* M: construct, but don't yet send a message */
#define M(T,t) \
@@ -137,6 +142,11 @@ static int api_snat_add_address_range (vat_main_t * vam)
;
else if (unformat (i, "%U", unformat_ip4_address, &start_addr))
end_addr = start_addr;
+ else
+ {
+ clib_warning("unknown input '%U'", format_unformat_error, i);
+ return -99;
+ }
start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
@@ -192,6 +202,11 @@ static int api_snat_interface_add_del_feature (vat_main_t * vam)
is_inside = 1;
else if (unformat (i, "del"))
is_add = 0;
+ else
+ {
+ clib_warning("unknown input '%U'", format_unformat_error, i);
+ return -99;
+ }
}
if (sw_if_index_set == 0)
@@ -210,14 +225,188 @@ static int api_snat_interface_add_del_feature (vat_main_t * vam)
return 0;
}
+static int api_snat_add_static_mapping(vat_main_t * vam)
+{
+ snat_test_main_t * sm = &snat_test_main;
+ unformat_input_t * i = vam->input;
+ f64 timeout;
+ vl_api_snat_add_static_mapping_t * mp;
+ u8 addr_set_n = 0;
+ u8 is_add = 1;
+ u8 addr_only = 1;
+ ip4_address_t local_addr, external_addr;
+ u32 local_port = 0, external_port = 0, vrf_id = ~0;
+
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "local_addr %U", unformat_ip4_address, &local_addr))
+ addr_set_n++;
+ else if (unformat (i, "external_addr %U", unformat_ip4_address,
+ &external_addr))
+ addr_set_n++;
+ else if (unformat (i, "local_port %u", &local_port))
+ addr_only = 0;
+ else if (unformat (i, "external_port %u", &external_port))
+ addr_only = 0;
+ else if (unformat (i, "vrf %u", &vrf_id))
+ ;
+ else if (unformat (i, "del"))
+ is_add = 0;
+ else
+ {
+ clib_warning("unknown input '%U'", format_unformat_error, i);
+ return -99;
+ }
+ }
+
+ if (addr_set_n != 2)
+ {
+ errmsg ("local_addr and remote_addr required\n");
+ return -99;
+ }
+
+ M(SNAT_ADD_STATIC_MAPPING, snat_add_static_mapping);
+ mp->is_add = is_add;
+ mp->is_ip4 = 1;
+ mp->addr_only = addr_only;
+ mp->local_port = ntohs ((u16) local_port);
+ mp->external_port = ntohs ((u16) external_port);
+ mp->vrf_id = ntohl (vrf_id);
+ memcpy (mp->local_ip_address, &local_addr, 4);
+ memcpy (mp->external_ip_address, &external_addr, 4);
+
+ S; W;
+ /* NOTREACHED */
+ return 0;
+}
+
+static void vl_api_snat_control_ping_reply_t_handler
+ (vl_api_snat_control_ping_reply_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ i32 retval = ntohl (mp->retval);
+ if (vam->async_mode)
+ {
+ vam->async_errors += (retval < 0);
+ }
+ else
+ {
+ vam->retval = retval;
+ vam->result_ready = 1;
+ }
+}
+
+static void vl_api_snat_static_mapping_details_t_handler
+ (vl_api_snat_static_mapping_details_t *mp)
+{
+ snat_test_main_t * sm = &snat_test_main;
+ vat_main_t *vam = sm->vat_main;
+
+ if (mp->addr_only)
+ fformat (vam->ofp, "%15U%6s%15U%6s%11d\n",
+ format_ip4_address, &mp->local_ip_address, "",
+ format_ip4_address, &mp->external_ip_address, "",
+ ntohl (mp->vrf_id));
+ else
+ fformat (vam->ofp, "%15U%6d%15U%6d%11d\n",
+ format_ip4_address, &mp->local_ip_address,
+ ntohs (mp->local_port),
+ format_ip4_address, &mp->external_ip_address,
+ ntohs (mp->external_port),
+ ntohl (mp->vrf_id));
+
+}
+
+static int api_snat_static_mapping_dump(vat_main_t * vam)
+{
+ snat_test_main_t * sm = &snat_test_main;
+ f64 timeout;
+ vl_api_snat_static_mapping_dump_t * mp;
+
+ if (vam->json_output)
+ {
+ clib_warning ("JSON output not supported for snat_static_mapping_dump");
+ return -99;
+ }
+
+ fformat (vam->ofp, "%21s%21s\n", "local", "external");
+ fformat (vam->ofp, "%15s%6s%15s%6s%11s\n", "address", "port", "address",
+ "port", "vrf");
+
+ M(SNAT_STATIC_MAPPING_DUMP, snat_static_mapping_dump);
+ S;
+ /* Use a control ping for synchronization */
+ {
+ vl_api_snat_control_ping_t *mp;
+ M (SNAT_CONTROL_PING, snat_control_ping);
+ S;
+ }
+ W;
+ /* NOTREACHED */
+ return 0;
+}
+
+static void vl_api_snat_show_config_reply_t_handler
+ (vl_api_snat_show_config_reply_t *mp)
+{
+ snat_test_main_t * sm = &snat_test_main;
+ vat_main_t *vam = sm->vat_main;
+ i32 retval = ntohl (mp->retval);
+
+ if (retval >= 0)
+ {
+ fformat (vam->ofp, "translation hash buckets %d\n",
+ ntohl (mp->translation_buckets));
+ fformat (vam->ofp, "translation hash memory %d\n",
+ ntohl (mp->translation_memory_size));
+ fformat (vam->ofp, "user hash buckets %d\n", ntohl (mp->user_buckets));
+ fformat (vam->ofp, "user hash memory %d\n", ntohl (mp->user_memory_size));
+ fformat (vam->ofp, "max translations per user %d\n",
+ ntohl (mp->max_translations_per_user));
+ fformat (vam->ofp, "outside VRF id %d\n", ntohl (mp->outside_vrf_id));
+ fformat (vam->ofp, "inside VRF id %d\n", ntohl (mp->inside_vrf_id));
+ if (mp->static_mapping_only)
+ {
+ fformat (vam->ofp, "static mapping only");
+ if (mp->static_mapping_connection_tracking)
+ fformat (vam->ofp, " connection tracking");
+ fformat (vam->ofp, "\n");
+ }
+ }
+ vam->retval = retval;
+ vam->result_ready = 1;
+}
+
+static int api_snat_show_config(vat_main_t * vam)
+{
+ snat_test_main_t * sm = &snat_test_main;
+ f64 timeout;
+ vl_api_snat_show_config_t * mp;
+
+ if (vam->json_output)
+ {
+ clib_warning ("JSON output not supported for snat_show_config");
+ return -99;
+ }
+
+ M(SNAT_SHOW_CONFIG, snat_show_config);
+ S; W;
+ /* NOTREACHED */
+ return 0;
+}
+
/*
* List of messages that the api test plugin sends,
* and that the data plane plugin processes
*/
-#define foreach_vpe_api_msg \
-_(snat_add_address_range, "<start-addr> [- <end-addr]") \
-_(snat_interface_add_del_feature, \
- "<intfc> | sw_if_index <id> [in] [out] [del]")
+#define foreach_vpe_api_msg \
+_(snat_add_address_range, "<start-addr> [- <end-addr]") \
+_(snat_interface_add_del_feature, \
+ "<intfc> | sw_if_index <id> [in] [out] [del]") \
+_(snat_add_static_mapping, "local_addr <ip> external_addr <ip> " \
+ "[local_port <n>] [external_port <n>] [vrf <table-id>] [del]") \
+_(snat_static_mapping_dump, "") \
+_(snat_show_config, "")
void vat_api_hookup (vat_main_t *vam)
{